@zzclub/pipeline 0.13.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARTICLE-THEMES.md +86 -0
- package/LICENSE +21 -0
- package/README.md +182 -788
- package/THIRD-PARTY-NOTICES.md +25 -0
- package/package.json +6 -2
- package/src/adapter-types.ts +3 -0
- package/src/adapters/builtin-markdown-renderer.ts +1 -0
- package/src/article-theme.test.ts +87 -0
- package/src/article-theme.ts +115 -0
- package/src/commands/abandon.test.ts +1 -1
- package/src/commands/article-theme.ts +16 -0
- package/src/commands/checkpoint.test.ts +2 -2
- package/src/commands/ingest-handoff.ts +6 -0
- package/src/commands/init.ts +10 -0
- package/src/commands/prepare.ts +5 -1
- package/src/commands/publish.ts +4 -0
- package/src/commands/reset.ts +2 -0
- package/src/commands/wechat-export.ts +4 -0
- package/src/commands/wechat-preview.ts +35 -0
- package/src/plugins.ts +2 -0
- package/src/providers/index.ts +17 -2
- package/src/providers/publish-core.ts +32 -1
- package/src/providers/wechat.test.ts +12 -0
- package/src/providers/wechat.ts +60 -39
- package/src/render-branding.ts +1 -1
- package/src/schema/config.ts +12 -0
- package/src/schema/state.ts +5 -0
- package/src/wechat-preview/assets/browser-dist/.vite/manifest.json +21 -1
- package/src/wechat-preview/assets/browser-dist/assets/style-C9-YtTIm.css +1 -0
- package/src/wechat-preview/assets/browser-dist/assets/wechat-renderer-D1jHoaM2.js +624 -0
- package/src/wechat-preview/assets/browser-dist/editor-export.js +1 -512
- package/src/wechat-preview/assets/browser-dist/src/wechat-preview/browser/studio/studio.html +293 -0
- package/src/wechat-preview/assets/browser-dist/studio.js +332 -0
- package/src/wechat-preview/browser/editor-export.ts +17 -2
- package/src/wechat-preview/browser/highlight.ts +2 -0
- package/src/wechat-preview/browser/studio/color-palettes.test.ts +31 -0
- package/src/wechat-preview/browser/studio/color-palettes.ts +115 -0
- package/src/wechat-preview/browser/studio/studio.css +638 -0
- package/src/wechat-preview/browser/studio/studio.html +292 -0
- package/src/wechat-preview/browser/studio/studio.ts +721 -0
- package/src/wechat-preview/browser/studio/syntax-presets.test.ts +74 -0
- package/src/wechat-preview/browser/studio/syntax-presets.ts +590 -0
- package/src/wechat-preview/fixtures/all-nodes.md +38 -0
- package/src/wechat-preview/index.ts +36 -5
- package/src/wechat-preview/plugins/builtin-badge.ts +152 -0
- package/src/wechat-preview/plugins/builtin-callout.ts +261 -0
- package/src/wechat-preview/plugins/builtin-highlight.ts +46 -0
- package/src/wechat-preview/plugins/builtin-kbd.ts +133 -0
- package/src/wechat-preview/plugins/index.test.ts +313 -0
- package/src/wechat-preview/plugins/index.ts +6 -0
- package/src/wechat-preview/plugins/registry.ts +62 -0
- package/src/wechat-preview/plugins/types.ts +31 -0
- package/src/wechat-preview/server/http.ts +160 -3
- package/src/wechat-preview/server/paths.ts +2 -2
- package/src/wechat-preview/server/singleton.ts +14 -12
- package/src/wechat-preview/server/studio.test.ts +92 -0
- package/src/wechat-preview/server/ui.ts +1 -0
- package/src/wechat-preview/wechat-export.integration.test.ts +9 -1
- package/src/wechat-preview/wechat-renderer.ts +72 -7
- package/src/workflow.test.ts +1 -0
package/README.md
CHANGED
|
@@ -1,897 +1,291 @@
|
|
|
1
1
|
# zzhub-pipeline
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个可恢复的内容发布工作流。它把 Markdown、图片、账号和发布意图组织成一条明确的流水线,生成微信公众号草稿,或把文章同步到自己的博客项目。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Pipeline 可以独立使用,也可以作为 Web、App、桌面客户端背后的执行服务:
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- 中断后按实际状态恢复,而不是靠会话历史猜测
|
|
7
|
+
- **本机 CLI**:在本地准备、排版、渲染和写入草稿。
|
|
8
|
+
- **私有 Cloud Worker**:在服务器执行同一套 Pipeline,业务后端通过 HTTP 提交任务。
|
|
9
|
+
- **Agent 编排**:Agent 负责写稿、审核和修订,Pipeline 负责确定性的状态推进。
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
前置条件:
|
|
15
|
-
|
|
16
|
-
- Bun 运行时
|
|
17
|
-
- Chrome,用于 `render` 和 `wechat-export`
|
|
18
|
-
|
|
19
|
-
安装:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
# 从 npm
|
|
23
|
-
npm install -g @zzclub/pipeline
|
|
24
|
-
|
|
25
|
-
# 或从源码
|
|
26
|
-
bun install
|
|
27
|
-
bun install --global .
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
二进制别名:
|
|
31
|
-
|
|
32
|
-
- `zzhub-pipeline`
|
|
33
|
-
- `zzp`
|
|
11
|
+
微信的 `publish` 只会创建或更新草稿,不会群发或公开发布。博客路由会执行你配置的发布命令,实际效果由该命令决定。
|
|
34
12
|
|
|
35
|
-
|
|
13
|
+
> Cloud Worker 已在本仓库实现,可从源码构建 Docker 镜像部署。它不包含在 npm CLI 包中;npm 只分发 CLI 和渲染资源。`v0.14.0` tag 包含初版 Worker,账号同步和正文模板等接口以实际部署的 revision 为准,生产环境请固定 commit/tag。
|
|
36
14
|
|
|
37
|
-
##
|
|
15
|
+
## 能做什么
|
|
38
16
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
- 运行态,临时文件,`{workspace}/.zzhub-media/runs/{run_id}.json`
|
|
52
|
-
- 定稿态,最终文件,`{workspace}/posts/{date-slug}/workflow-state.json`
|
|
53
|
-
|
|
54
|
-
正文全文不会进入 state,正文内容统一放在 `{workspace}/.zzhub-media/tmp/{run_id}/`。
|
|
55
|
-
|
|
56
|
-
## 核心流程
|
|
17
|
+
| 能力 | 说明 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| 公众号文章 | Markdown 转微信兼容 HTML,生成封面,上传图片,创建或更新文章草稿 |
|
|
20
|
+
| 公众号贴图 | 渲染单页卡片或多页 PNG 图集,创建 `newspic` 草稿 |
|
|
21
|
+
| 博客同步 | 把 Markdown 和图片同步到配置的博客项目,并执行发布命令 |
|
|
22
|
+
| 可恢复工作流 | 保存任务状态、素材、审核决定、渲染版本和发布结果,失败后可以继续或重置 |
|
|
23
|
+
| 多账号与多目标 | 支持多个公众号账号,以及微信、博客等发布目标 |
|
|
24
|
+
| 排版扩展 | 封面主题、正文模板、品牌配置和可替换渲染插件 |
|
|
25
|
+
| 排版工作台 | `zzp wechat-preview studio` 提供所见即所得排版调优、配色选型、语法预设与配置持久化 |
|
|
26
|
+
| 本机监控 | `zzp monitor` 提供任务快照、日志和 SSE 事件流 |
|
|
27
|
+
| 云端执行 | 私有 HTTP Worker 提供账号同步、串行队列、持久化任务和状态轮询 |
|
|
57
28
|
|
|
58
|
-
|
|
29
|
+
Pipeline 不内置 LLM、用户登录、计费或配额服务。调用方需要自己提供正文和审核决定;云端 Worker 接收已经确认的完整正文,并只推进支持的 CLI 动作。
|
|
59
30
|
|
|
60
|
-
|
|
31
|
+
## 工作方式
|
|
61
32
|
|
|
62
33
|
```mermaid
|
|
63
|
-
flowchart
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
DONE -- "否" --> STATUS
|
|
72
|
-
DONE -- "是" --> END["任务完成"]
|
|
73
|
-
|
|
74
|
-
style START fill:#e0e7ff,stroke:#6366f1,color:#3730a3
|
|
75
|
-
style END fill:#d1fae5,stroke:#10b981,color:#065f46
|
|
76
|
-
style STATUS fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
77
|
-
style EXEC fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 如何正确调用 Agent Skill
|
|
81
|
-
|
|
82
|
-
Agent 通过 `.claude/skills/zzhub-publish/SKILL.md` 获取操作指引。任务描述的质量直接决定执行效果。
|
|
83
|
-
|
|
84
|
-
**反模式(避免):**
|
|
85
|
-
|
|
86
|
-
| 不要这样说 | 问题 |
|
|
87
|
-
|-----------|------|
|
|
88
|
-
| "帮我发一篇文章到公众号" | Agent 不知道主题、没有具体方向。大概率跳过 status 检查或写出空洞内容。 |
|
|
89
|
-
| "发一篇关于 AI 的文章" | 主题太泛,agent 会写出套话文章,review 也难以判断好坏。 |
|
|
90
|
-
| 不提 handoff 里的已有文件 | Agent 按默认路径重新写稿,浪费已有素材。 |
|
|
91
|
-
|
|
92
|
-
**模式 A:Agent 从头写稿**
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
调用 zzhub-publish skill,完成以下任务:
|
|
96
|
-
|
|
97
|
-
文章主题:[具体 topic,一句话说清]
|
|
98
|
-
发布目标:大号
|
|
99
|
-
要求:
|
|
100
|
-
- 每完成一步报告当前状态
|
|
101
|
-
- 到 content review 通过后先暂停,等我确认再继续
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
给了具体主题 agent 才有明确的写稿方向,设边界让发布前有人工确认点,每步报告减少盲跳。
|
|
105
|
-
|
|
106
|
-
**模式 B:从准备好的文件交接**
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
调用 zzhub-publish skill,从我准备好的素材继续:
|
|
110
|
-
|
|
111
|
-
我已在 /path/to/handoff.json 提供了 handoff 文件,
|
|
112
|
-
body 文件也在 handoff 里指定好了(已经是一篇完整文章)。
|
|
113
|
-
|
|
114
|
-
1. 先读 skill 文件,然后从 ingest-handoff 开始
|
|
115
|
-
2. body 已经在 handoff 中指定了路径,不需要重新写稿
|
|
116
|
-
3. 按 skill 的 loop 走完后续所有步骤(prepare → review → render → publish)
|
|
117
|
-
4. 每一步报告动作和结果
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
明确告知 body 已就绪防止重复劳动,指出入口(ingest-handoff)让 agent 不用猜测,强调"后续所有"让 agent 一口气跑完。
|
|
121
|
-
|
|
122
|
-
**模式 C:快速出草稿,暂不发布**
|
|
123
|
-
|
|
34
|
+
flowchart LR
|
|
35
|
+
Client[Web / App / 桌面端] --> Backend[你的业务后端]
|
|
36
|
+
Backend --> Worker[私有 Cloud Worker]
|
|
37
|
+
Worker --> Pipeline[Pipeline CLI]
|
|
38
|
+
Pipeline --> Relay[微信中转 API]
|
|
39
|
+
Relay --> Draft[公众号草稿箱]
|
|
40
|
+
Pipeline --> Blog[博客项目]
|
|
41
|
+
CLI[本机 zzp CLI] --> Pipeline
|
|
124
42
|
```
|
|
125
|
-
调用 zzhub-publish skill:
|
|
126
43
|
|
|
127
|
-
|
|
128
|
-
发布目标:大号
|
|
129
|
-
重要:只到 render 完成就停,不要 publish。我要在草稿箱里人工预览后再决定。
|
|
130
|
-
```
|
|
44
|
+
客户端不需要运行 Electron,也不需要直接访问 Worker。推荐让业务后端负责用户鉴权、账号归属、任务归属、配额和幂等键隔离,再通过内部网络调用 Worker。
|
|
131
45
|
|
|
132
|
-
|
|
46
|
+
## 快速开始
|
|
133
47
|
|
|
134
|
-
|
|
48
|
+
### 安装
|
|
135
49
|
|
|
136
|
-
|
|
137
|
-
2. **明说上下文差异** — 已有素材或 body 文件一定要说出来,否则 agent 默认走 "init → 写稿"。
|
|
138
|
-
3. **在分叉点给提示** — 比如 "review 通过后暂停等我确认",或 "body 已在 handoff 里准备好了,跳过写稿"。
|
|
139
|
-
4. **用负向约束设边界** — "不要 publish"、"到 review 就停" 比正向的 "帮我做 X" 更能防止 agent 跑过头。
|
|
140
|
-
5. **先读 skill 再行动** — 指令里加上 "先读 SKILL.md",尤其是对 sub-agent。不假设 agent 已经知道规则。
|
|
50
|
+
Pipeline 需要 [Bun](https://bun.sh/) 运行。npm 可以安装包,但命令本身仍由 Bun 执行;渲染文章还需要 Chrome 或 Chromium。
|
|
141
51
|
|
|
142
|
-
|
|
52
|
+
```bash
|
|
53
|
+
# 从 npm 安装
|
|
54
|
+
npm install -g @zzclub/pipeline
|
|
143
55
|
|
|
144
|
-
|
|
145
|
-
|
|
56
|
+
# 或从源码运行
|
|
57
|
+
git clone https://github.com/aatrooox/zzhub-pipeline.git
|
|
58
|
+
cd zzhub-pipeline
|
|
59
|
+
bun install
|
|
60
|
+
bun run src/cli.ts --help
|
|
146
61
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
S["📄 读取 workflow state"] --> C1{"mode=done 或 phase=done?"}
|
|
150
|
-
C1 -- "是" --> A1["✅ complete"]
|
|
151
|
-
C1 -- "否" --> C2{"mode=failed?"}
|
|
152
|
-
C2 -- "是" --> A2["🔧 reset-or-repair<br/>checkpoint 诊断 → reset 重置"]
|
|
153
|
-
C2 -- "否" --> C3{"mode=handoff?"}
|
|
154
|
-
C3 -- "是" --> C3a{"body_inputs 待处理?"}
|
|
155
|
-
C3a -- "是" --> A3a["🖼️ attach-body-images<br/>等待用户提供插图"]
|
|
156
|
-
C3a -- "否" --> A3b["📎 resolve-handoff<br/>checkpoint 解决外部依赖"]
|
|
157
|
-
C3 -- "否" --> C4{"正文是否已挂载?<br/>source_body_path 或 asset_path"}
|
|
158
|
-
C4 -- "否" --> A4["✍️ attach-body<br/>Worker 撰写正文"]
|
|
159
|
-
C4 -- "是" --> C5{"body_inputs 待处理?"}
|
|
160
|
-
C5 -- "是" --> A5["🖼️ attach-body-images"]
|
|
161
|
-
C5 -- "否" --> C6{"元数据是否就绪?<br/>title, slug, date"}
|
|
162
|
-
C6 -- "否" --> A6["📝 prepare<br/>路由+作者+排版+元数据"]
|
|
163
|
-
C6 -- "是" --> C7{"content_review 状态?"}
|
|
164
|
-
C7 -- "needs_revision" --> A7["🔁 revise-content<br/>Worker 改写正文"]
|
|
165
|
-
C7 -- "unchecked" --> A8["👀 review-content<br/>Worker 审核内容"]
|
|
166
|
-
C7 -- "passed" --> C7a{"asset_path 是否存在?"}
|
|
167
|
-
C7a -- "否" --> A9["📦 prepare-finalize<br/>生成正式产物到 posts/"]
|
|
168
|
-
C7a -- "是" --> C9{"render 是否需推进?"}
|
|
169
|
-
C9 -- "是" --> A10["🎨 render<br/>imgx 图片渲染"]
|
|
170
|
-
C9 -- "否" --> C10{"publish 是否需推进?"}
|
|
171
|
-
C10 -- "是" --> A11["🚀 publish<br/>推送到发布渠道"]
|
|
172
|
-
C10 -- "否" --> A12["📝 prepare<br/>兜底重新 prepare"]
|
|
173
|
-
|
|
174
|
-
style A1 fill:#d1fae5,stroke:#10b981,color:#065f46
|
|
175
|
-
style A2 fill:#fee2e2,stroke:#ef4444,color:#991b1b
|
|
176
|
-
style A3a fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
177
|
-
style A3b fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
178
|
-
style A4 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
179
|
-
style A5 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
180
|
-
style A6 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
181
|
-
style A7 fill:#ede9fe,stroke:#8b5cf6,color:#5b21b6
|
|
182
|
-
style A8 fill:#ede9fe,stroke:#8b5cf6,color:#5b21b6
|
|
183
|
-
style A9 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
184
|
-
style A10 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
185
|
-
style A11 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
186
|
-
style A12 fill:#e5e7eb,stroke:#6b7280,color:#374151
|
|
62
|
+
# 可选:把当前源码安装成全局命令
|
|
63
|
+
bun install --global .
|
|
187
64
|
```
|
|
188
65
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
- C7(content_review)仅在 `asset_path` 尚未存在时触发,`review-content` 和 `prepare-finalize` 都在同一 `!asset_path` 外层分支内
|
|
192
|
-
- C9/C10 进入时 `asset_path` 已必然存在(由 `prepare-finalize` 写入),因此直接按 render/publish 阶段状态判断
|
|
193
|
-
- `attach-newspic-spec` 不在决策树中 — 它是可选命令,在 render 前由 Agent 自行决定是否附加分页规格
|
|
194
|
-
- `reconcile` 不在决策树中 — 它是手动维护命令,用于刷新素材对账,不自动触发
|
|
195
|
-
|
|
196
|
-
每个 `next_action` 包含:
|
|
197
|
-
|
|
198
|
-
| 字段 | 说明 |
|
|
199
|
-
| --- | --- |
|
|
200
|
-
| `action` | 动作标识(如 `attach-body`、`prepare`、`render`) |
|
|
201
|
-
| `executor` | 执行者类型:`cli` / `worker` / `await-input` / `repair` / `complete` |
|
|
202
|
-
| `command` | 建议的 CLI 命令,含 `--state` 路径 |
|
|
203
|
-
| `params` | 执行参数:`state_path`、`source_body_path`、`worker_mode` 等 |
|
|
204
|
-
| `on_complete` | 执行后的后续指令(通常是 "re-run status") |
|
|
66
|
+
两个命令别名都可用:
|
|
205
67
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
```mermaid
|
|
211
|
-
flowchart TD
|
|
212
|
-
ENTRY{"如何启动任务?"}
|
|
213
|
-
ENTRY -- "Agent 初始化" --> INIT["init --task-kind ... --content-form ...<br/>--targets ... --intent-text ..."]
|
|
214
|
-
ENTRY -- "外部交接" --> HANDOFF["ingest-handoff --file handoff.json"]
|
|
215
|
-
|
|
216
|
-
HANDOFF --> HF_TYPE{"handoff 类型?"}
|
|
217
|
-
HF_TYPE -- "publish_handoff" --> NEW1["mode=new(固定)<br/>解析 body_path / title / account<br/>创建新任务"]
|
|
218
|
-
HF_TYPE -- "workflow_handoff" --> HF_MODE{"handoff.mode?"}
|
|
219
|
-
|
|
220
|
-
HF_MODE -- "new 新建" --> NEW2["解析 content_form / body_path<br/>或 materials_path / policies<br/>创建新任务"]
|
|
221
|
-
HF_MODE -- "resume 续接" --> FIND["findTask<br/>按 state_path 或 run_id 查找已有任务"]
|
|
222
|
-
FIND --> COMPARE{"核心输入是否变化?<br/>正文 / 标题 / 路由 / intent_text<br/>/ explicit_constraints"}
|
|
223
|
-
COMPARE -- "有变化" --> RESET["resetDerivedState()<br/>全部阶段重置为 pending<br/>保留 run_id 与 workspace_root"]
|
|
224
|
-
COMPARE -- "无变化" --> POLICY{"仅 review_policy 变化<br/>且为 trust_user?"}
|
|
225
|
-
POLICY -- "是 + 有 handoff.body_path" --> AUTOPASS["自动设置<br/>content_review=passed<br/>跳过审核步骤"]
|
|
226
|
-
POLICY -- "否" --> KEEP["保持当前状态不变"]
|
|
227
|
-
|
|
228
|
-
NEW1 --> STATUS["status 重新计算 → next_action"]
|
|
229
|
-
NEW2 --> STATUS
|
|
230
|
-
RESET --> STATUS
|
|
231
|
-
AUTOPASS --> STATUS
|
|
232
|
-
KEEP --> STATUS
|
|
233
|
-
INIT --> STATUS
|
|
234
|
-
|
|
235
|
-
style HANDOFF fill:#e0e7ff,stroke:#6366f1,color:#3730a3
|
|
236
|
-
style RESET fill:#fee2e2,stroke:#ef4444,color:#991b1b
|
|
237
|
-
style AUTOPASS fill:#d1fae5,stroke:#10b981,color:#065f46
|
|
238
|
-
style STATUS fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
68
|
+
```bash
|
|
69
|
+
zzp --help
|
|
70
|
+
zzhub-pipeline --help
|
|
239
71
|
```
|
|
240
72
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
```json
|
|
244
|
-
// workflow_handoff — 新建任务
|
|
245
|
-
{
|
|
246
|
-
"workflow_handoff": {
|
|
247
|
-
"mode": "new",
|
|
248
|
-
"content_form": "article",
|
|
249
|
-
"body_path": "/abs/path/body.md",
|
|
250
|
-
"target_account": "default",
|
|
251
|
-
"title": "文章标题",
|
|
252
|
-
"user_intent_text": "发公众号文章给大号",
|
|
253
|
-
"review_policy": "required"
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// workflow_handoff — 续接已有任务
|
|
258
|
-
{
|
|
259
|
-
"workflow_handoff": {
|
|
260
|
-
"mode": "resume",
|
|
261
|
-
"state_path": "/abs/path/workflow-state.json",
|
|
262
|
-
"body_path": "/abs/path/revised-body.md",
|
|
263
|
-
"review_policy": "trust_user"
|
|
264
|
-
}
|
|
265
|
-
}
|
|
73
|
+
检查本机依赖:
|
|
266
74
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
"publish_handoff": {
|
|
270
|
-
"content_form": "article",
|
|
271
|
-
"body_path": "/abs/path/body.md",
|
|
272
|
-
"target_account": "default",
|
|
273
|
-
"title": "文章标题",
|
|
274
|
-
"user_intent_text": "发公众号"
|
|
275
|
-
}
|
|
276
|
-
}
|
|
75
|
+
```bash
|
|
76
|
+
zzp doctor
|
|
277
77
|
```
|
|
278
78
|
|
|
279
|
-
`
|
|
79
|
+
图片渲染需要 `@napi-rs/canvas`,COS 上传需要 `cos-nodejs-sdk-v5`。云端 Docker 镜像会安装 Chromium 和 CJK 字体。
|
|
280
80
|
|
|
281
|
-
|
|
282
|
-
- `asset_path` → 清空,`formatted_body_path` → null
|
|
283
|
-
- `metadata`(保留 title)→ 默认值
|
|
284
|
-
- `artifacts` / `images` / `publish` / `content_review` → 默认值
|
|
285
|
-
- 保留:`run_id`、`workspace_root`、`state_path`、`intent.content_form`
|
|
81
|
+
### 先预览一篇文章
|
|
286
82
|
|
|
287
|
-
|
|
83
|
+
下面的命令只生成本地产物,不访问公众号:
|
|
288
84
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
85
|
+
```bash
|
|
86
|
+
zzp wechat-export \
|
|
87
|
+
--markdown ./article.md \
|
|
88
|
+
--out ./article.html \
|
|
89
|
+
--preview-shell-out ./article-preview.html \
|
|
90
|
+
--no-preview
|
|
294
91
|
```
|
|
295
92
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
1. `channel-route` — 解析路由与公众号账号
|
|
299
|
-
2. `author-select` — 确定改写权限与风格模式(`rewrite_allowed` / `style_mode`)
|
|
300
|
-
3. `format` — 文本排版规则处理
|
|
301
|
-
4. `asset-meta` — 提取标题/日期/摘要
|
|
302
|
-
5. `body-inputs` — 扫描正文中的插图标记
|
|
303
|
-
6. `highlight-words` — 从标题提取封面高亮词(`prepare-finalize` 中完成)
|
|
304
|
-
7. `asset-save` — 写入正式文件到 `posts/` 目录(`prepare-finalize` 中完成)
|
|
305
|
-
|
|
306
|
-
> `style` 润色不在 `prepare` 命令内 —— 它需要 LLM 调用,由外部 Worker 完成后再交回 pipeline。
|
|
307
|
-
|
|
308
|
-
**render 阶段子步骤**:
|
|
309
|
-
|
|
310
|
-
1. `image-plan` — 模板选择(wechat-cover-split / poster-3-4 / longform-3-4)
|
|
311
|
-
2. `body-inputs` — 收集插图图片(longform 模式下可能暂停等待用户输入)
|
|
312
|
-
3. `adapter-render` — 调用图片渲染插件
|
|
313
|
-
4. 记录 `render_assets`,递增 `render_version`
|
|
314
|
-
|
|
315
|
-
**publish 阶段子步骤**:
|
|
316
|
-
|
|
317
|
-
1. 收集发布路由(primary + extras)
|
|
318
|
-
2. 幂等性检查(content_version + render_version 匹配则跳过)
|
|
319
|
-
3. `provider.publish()` — 微信公众号 / COS / 博客
|
|
320
|
-
4. 记录发布结果,全部成功则 mode=done
|
|
93
|
+
### 视觉排版工作台 (WeChat Visual Studio)
|
|
321
94
|
|
|
322
|
-
|
|
95
|
+
Pipeline 提供了开箱即用的所见即所得排版工作台:
|
|
323
96
|
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
{workspace}/.zzhub-media/ {workspace}/posts/{date-slug}/
|
|
327
|
-
runs/{run_id}.json ←───────────────── workflow-state.json
|
|
328
|
-
tmp/{run_id}/source-body.md ──────→ post.md
|
|
329
|
-
images/wechat/ ← 文章渲染图
|
|
330
|
-
images/newspic/ ← 贴图渲染图
|
|
97
|
+
```bash
|
|
98
|
+
zzp wechat-preview studio
|
|
331
99
|
```
|
|
332
100
|
|
|
333
|
-
|
|
101
|
+
终端启动后会自动唤起浏览器打开 `http://127.0.0.1:18765/studio`。你可以在界面中直观调整主题配色、二级标题与表格语法预设、提示卡片(Callout)自定义 Emoji,并一键复制到微信后台或保存为全局配置。
|
|
334
102
|
|
|
335
|
-
|
|
103
|
+
> **云端使用者注意**:本地 Studio 保存的样式会写入本地 `config.json`。若使用私有云端 Worker,需将调整后的配置同步至服务器,详见 [Visual Studio 排版调优与云端配置同步指南](docs/visual-studio-and-config-sync.md)。
|
|
336
104
|
|
|
337
|
-
|
|
338
|
-
src/
|
|
339
|
-
cli.ts # 入口,命令分发
|
|
340
|
-
plugins.ts # 命令注册表,workflow 和 ops 两组
|
|
341
|
-
state.ts # WorkflowState 类型 + CRUD,权威合约
|
|
342
|
-
task-manager.ts # 任务列举,查找,状态报告
|
|
343
|
-
task-views.ts # markdown / agent 视图渲染
|
|
344
|
-
workflow-materials.ts # 正文路径解析,素材对账
|
|
345
|
-
routes.ts # 确定性路由表,关键词匹配 + 账号解析
|
|
346
|
-
profiles.ts # 创作规则,改写许可,风格模式决策树
|
|
347
|
-
text.ts # 文本格式化工具,frontmatter,markdown 规范化
|
|
348
|
-
output.ts # TTY 感知输出层,pretty / JSON
|
|
349
|
-
config.ts # 配置加载,env 覆盖,工作区路径解析
|
|
350
|
-
args.ts # 参数解析
|
|
351
|
-
spawn.ts # 子进程封装,PATH 增强,bun 二进制定位
|
|
352
|
-
adapter-types.ts # 插件接口定义(ImageRenderPlugin / MarkdownRenderPlugin)
|
|
353
|
-
adapter-loader.ts # 插件加载器,resolveImageRenderer / resolveMarkdownRenderer
|
|
354
|
-
runtime-paths.ts # 资产路径解析(dev/compiled/npm 三种模式),字体缓存
|
|
355
|
-
adapters/ # 内置适配器实现
|
|
356
|
-
builtin-image-renderer.ts # 包装 imgx 的图片渲染适配器
|
|
357
|
-
builtin-markdown-renderer.ts # 包装 wechat-preview 的 Markdown 渲染适配器
|
|
358
|
-
commands/ # 每个 CLI 命令一个文件
|
|
359
|
-
imgx/ # 图片渲染子系统,Chrome headless + @napi-rs/canvas
|
|
360
|
-
runtime.ts # Chrome 截图,DOM dump,模板工具
|
|
361
|
-
render-article.ts # longform-3-4 长文渲染器
|
|
362
|
-
render-card.ts # 封面卡片渲染器
|
|
363
|
-
render-ascii-portrait.ts # ASCII 人像渲染
|
|
364
|
-
render-x-like-posts.ts # X / Twitter 风格帖子渲染
|
|
365
|
-
poster-recipe.ts # 海报配方系统
|
|
366
|
-
geometry.ts # 页面几何计算
|
|
367
|
-
longform-theme.ts # 长文主题定义
|
|
368
|
-
pretext-adapter.ts # pretext 分页适配器
|
|
369
|
-
pretext-runtime.ts # 进程内分页运行时(@napi-rs/canvas 懒加载)
|
|
370
|
-
providers/ # 发布提供者
|
|
371
|
-
index.ts # 提供者注册表
|
|
372
|
-
wechat.ts # 微信公众号,文章草稿和图片消息
|
|
373
|
-
cos.ts # 腾讯云 COS 图片 CDN
|
|
374
|
-
blog.ts # 博客 markdown 同步
|
|
375
|
-
wechat-preview/ # 微信文章 HTML 预览,Milkdown + 主题
|
|
376
|
-
index.ts
|
|
377
|
-
themes.ts
|
|
378
|
-
wechat-renderer.ts # 元素 renderer 注册表、CSS 内联和微信安全清理
|
|
379
|
-
frontmatter-handler.ts
|
|
380
|
-
browser/
|
|
381
|
-
editor-export.ts
|
|
382
|
-
editor-export.css
|
|
383
|
-
assets/
|
|
384
|
-
templates/
|
|
385
|
-
fonts/
|
|
386
|
-
browser-dist/
|
|
387
|
-
```
|
|
105
|
+
### 推进一个发布任务
|
|
388
106
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
`workflow` 组,17 个命令:
|
|
392
|
-
|
|
393
|
-
| 命令 | 说明 |
|
|
394
|
-
| --- | --- |
|
|
395
|
-
| `init` | Create run state from intent classification |
|
|
396
|
-
| `ingest-handoff` | Create or resume a task from a publish_handoff/workflow_handoff JSON file |
|
|
397
|
-
| `attach-body` | Attach a source body file to a managed task |
|
|
398
|
-
| `attach-body-images` | Attach body image marker files to a managed task |
|
|
399
|
-
| `attach-newspic-spec` | Attach or update newspic render intent |
|
|
400
|
-
| `prepare` | Route + author + format + metadata |
|
|
401
|
-
| `prepare-finalize` | Highlight words + asset save |
|
|
402
|
-
| `render` | Image plan + imgx render |
|
|
403
|
-
| `publish` | Execute publish routes |
|
|
404
|
-
| `reconcile` | Reconcile managed task materials and derived state |
|
|
405
|
-
| `checkpoint` | Read task state and validate current phase |
|
|
406
|
-
| `status` | Read a managed task with gaps and next action |
|
|
407
|
-
| `find-run` | Find the best matching managed task |
|
|
408
|
-
| `tasks` | List managed tasks in the workspace |
|
|
409
|
-
| `reset` | Reset phases for revision |
|
|
410
|
-
| `review` | Update content review status |
|
|
411
|
-
| `abandon` | Mark one or more tasks as abandoned |
|
|
412
|
-
|
|
413
|
-
`ops` 组,11 个命令:
|
|
414
|
-
|
|
415
|
-
| 命令 | 说明 |
|
|
416
|
-
| --- | --- |
|
|
417
|
-
| `sync-blog` | Copy post.md to blog repo, publish, and record result in state JSON |
|
|
418
|
-
| `republish` | Publish a completed task to an additional account or platform |
|
|
419
|
-
| `imgx` | Run bundled imgx renderer subcommands |
|
|
420
|
-
| `wechat-export` | Render markdown to WeChat HTML with bundled preview styles |
|
|
421
|
-
| `wechat-preview` | Local singleton server to preview exported WeChat HTML |
|
|
422
|
-
| `cos-upload` | Upload a local image to configured COS CDN |
|
|
423
|
-
| `config` | Read or update pipeline config |
|
|
424
|
-
| `doctor` | Inspect resolved paths and provider health |
|
|
425
|
-
| `hermes-metrics` | Show Hermes execution metrics per task |
|
|
426
|
-
| `wx-drafts` | List or get drafts from WeChat draft box |
|
|
427
|
-
| `wx-draft-delete` | Delete a draft from WeChat draft box |
|
|
428
|
-
|
|
429
|
-
## 常用命令
|
|
430
|
-
|
|
431
|
-
### 任务管理
|
|
107
|
+
Pipeline 每次只推进一个动作。创建任务后,反复执行 `status`,按照返回的 `next_action` 继续;不要直接修改 `workflow-state.json`。
|
|
432
108
|
|
|
433
109
|
```bash
|
|
434
|
-
|
|
435
|
-
bun run src/cli.ts tasks --workspace {workspace} --active
|
|
436
|
-
bun run src/cli.ts tasks --workspace {workspace} --active --view markdown
|
|
437
|
-
bun run src/cli.ts find-run --workspace {workspace} --active
|
|
438
|
-
bun run src/cli.ts find-run --workspace {workspace} --active --view agent
|
|
439
|
-
bun run src/cli.ts status --workspace {workspace}
|
|
440
|
-
bun run src/cli.ts status --state {workspace}/posts/{date-slug}/workflow-state.json
|
|
441
|
-
bun run src/cli.ts status --state {workspace}/posts/{date-slug}/workflow-state.json --view agent
|
|
442
|
-
bun run src/cli.ts reconcile --state {workspace}/posts/{date-slug}/workflow-state.json
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
### 新建与接入素材
|
|
110
|
+
WORKSPACE="$PWD/workspace"
|
|
446
111
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
--workspace {workspace} \
|
|
112
|
+
zzp init \
|
|
113
|
+
--workspace "$WORKSPACE" \
|
|
450
114
|
--task-kind publish \
|
|
451
115
|
--content-form article \
|
|
452
|
-
--targets wechat \
|
|
453
116
|
--content-origin user \
|
|
454
|
-
--
|
|
455
|
-
--
|
|
456
|
-
--requires-publish
|
|
117
|
+
--targets wechat \
|
|
118
|
+
--intent-text "发一篇公众号文章"
|
|
457
119
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
--body-text "正文内容"
|
|
120
|
+
# init 会返回 state_path;后续命令使用它
|
|
121
|
+
zzp status --state "$WORKSPACE/.zzhub-media/runs/<run_id>.json" --view agent
|
|
461
122
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
--
|
|
123
|
+
# 正文可以来自文件,也可以使用 --body-text
|
|
124
|
+
zzp attach-body \
|
|
125
|
+
--state "$WORKSPACE/.zzhub-media/runs/<run_id>.json" \
|
|
126
|
+
--body ./article.md
|
|
465
127
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
128
|
+
# 后续按 status 的 next_action 执行,常见顺序如下
|
|
129
|
+
zzp prepare --state <state_path>
|
|
130
|
+
zzp review --state <state_path> --status passed
|
|
131
|
+
zzp prepare-finalize --state <state_path>
|
|
132
|
+
zzp render --state <state_path>
|
|
133
|
+
zzp publish --state <state_path>
|
|
469
134
|
```
|
|
470
135
|
|
|
471
|
-
`
|
|
136
|
+
实际顺序以 `status --view agent` 为准。需要发布贴图时,把 `--content-form` 改为 `newspic`;多页图片可以在渲染前附加 `newspic` 规格。
|
|
472
137
|
|
|
473
|
-
|
|
474
|
-
[
|
|
475
|
-
{ "marker": "插图1", "path": "{workspace}/1.png" },
|
|
476
|
-
{ "marker": "插图2", "path": "{workspace}/2.png" }
|
|
477
|
-
]
|
|
478
|
-
```
|
|
138
|
+
如果正文由 Agent 生成或审核,可以直接使用仓库里的 [`zzhub-publish` Skill](skills/zzhub-publish/SKILL.md)。Skill 只负责编排 Agent,文章正文仍由调用方提供。
|
|
479
139
|
|
|
480
|
-
|
|
140
|
+
## 云端 Worker
|
|
481
141
|
|
|
482
|
-
|
|
483
|
-
{
|
|
484
|
-
"插图1": "{workspace}/1.png",
|
|
485
|
-
"插图2": "{workspace}/2.png"
|
|
486
|
-
}
|
|
487
|
-
```
|
|
142
|
+
Worker 是仓库里的独立 Bun 服务,入口为 `services/cloud-worker/server.ts`。它在服务器上调用 Pipeline CLI,当前支持公众号 `article` 和 `newspic` 任务,不支持博客任务,也不提供任意命令执行。
|
|
488
143
|
|
|
489
|
-
|
|
144
|
+
推荐链路:
|
|
490
145
|
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
146
|
+
```text
|
|
147
|
+
Web / App / 桌面端
|
|
148
|
+
↓
|
|
149
|
+
已鉴权的业务后端
|
|
150
|
+
↓ 内部网络 + PIPELINE_WORKER_TOKEN
|
|
151
|
+
Cloud Worker
|
|
152
|
+
↓
|
|
153
|
+
Pipeline CLI → 微信中转 API → 草稿箱
|
|
497
154
|
```
|
|
498
155
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
```bash
|
|
502
|
-
bun run src/cli.ts ingest-handoff --file handoff.json
|
|
503
|
-
```
|
|
156
|
+
Worker 已经可以部署,但应作为**内部服务**使用:
|
|
504
157
|
|
|
505
|
-
|
|
158
|
+
- 默认监听 `127.0.0.1:18887`。
|
|
159
|
+
- 非回环监听必须显式设置 `PIPELINE_WORKER_PRIVATE_NETWORK=1`,并由容器网络或防火墙完成隔离。
|
|
160
|
+
- `PIPELINE_WORKER_TOKEN` 是服务令牌,不是用户登录 token,也不是微信中转 PAT;不要下发给客户端。
|
|
161
|
+
- 当前是单进程串行队列,使用 JSON 文件保存任务、正文和账号;数据目录必须挂载持久卷,不支持多个副本共享同一目录。
|
|
162
|
+
- 任务状态为 `queued`、`running`、`succeeded`、`failed` 或 `interrupted`,`step` 表示当前 Pipeline 动作。
|
|
163
|
+
- 当前 Worker 只提供步骤和结果轮询,不提供云端 SSE、逐文件进度或原始日志。本机 `zzp monitor` 是另一套观测接口。
|
|
506
164
|
|
|
507
|
-
|
|
165
|
+
最短 Docker 启动示例:
|
|
508
166
|
|
|
509
167
|
```bash
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
bun run src/cli.ts reset --state {state_path} --mode redo.format # 仅重做排版
|
|
513
|
-
bun run src/cli.ts reset --state {state_path} --mode redo.metadata # 仅重做元数据提取
|
|
514
|
-
bun run src/cli.ts reset --state {state_path} --mode redo.route # 仅重做路由解析
|
|
515
|
-
bun run src/cli.ts reset --state {state_path} --mode render # 重做渲染
|
|
516
|
-
bun run src/cli.ts reset --state {state_path} --mode publish # 重做发布
|
|
517
|
-
bun run src/cli.ts reset --state {state_path} --mode full # 完全放弃当前任务(mode=failed)
|
|
518
|
-
```
|
|
168
|
+
docker build -f services/cloud-worker/Dockerfile \
|
|
169
|
+
-t zzhub-pipeline-worker:local .
|
|
519
170
|
|
|
520
|
-
|
|
171
|
+
# 让业务后端和 Worker 只在内部网络通信
|
|
172
|
+
docker network create pipeline-internal
|
|
521
173
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
174
|
+
docker run -d --name pipeline-worker --restart unless-stopped \
|
|
175
|
+
--network pipeline-internal \
|
|
176
|
+
-e PIPELINE_WORKER_TOKEN \
|
|
177
|
+
-e PIPELINE_WORKER_HOST=0.0.0.0 \
|
|
178
|
+
-e PIPELINE_WORKER_PRIVATE_NETWORK=1 \
|
|
179
|
+
-e PIPELINE_CONFIG_FILE=/run/secrets/pipeline.json \
|
|
180
|
+
--mount type=bind,src=/absolute/path/to/pipeline.json,dst=/run/secrets/pipeline.json,readonly \
|
|
181
|
+
--mount type=bind,src=/absolute/path/to/worker-data,dst=/data \
|
|
182
|
+
zzhub-pipeline-worker:local
|
|
526
183
|
```
|
|
527
184
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
- 读取 canonical `post.md`
|
|
531
|
-
- 复制到博客仓库 `content/posts/<slug>.md`
|
|
532
|
-
- 执行博客发布命令
|
|
533
|
-
- 将 blog publish result 写回 `workflow-state.json`,方便筛查是否遗漏同步
|
|
534
|
-
|
|
535
|
-
### 配置管理
|
|
536
|
-
|
|
537
|
-
```bash
|
|
538
|
-
bun run src/cli.ts config
|
|
539
|
-
bun run src/cli.ts config --key paths.workspaceRoot
|
|
540
|
-
bun run src/cli.ts config --key paths.workspaceRoot --value /abs/path
|
|
541
|
-
```
|
|
185
|
+
业务后端使用同一个服务令牌调用:
|
|
542
186
|
|
|
543
|
-
|
|
187
|
+
| 方法 | 路径 | 用途 |
|
|
188
|
+
| --- | --- | --- |
|
|
189
|
+
| `GET` | `/health` | 查看 Worker 是否存活、排队数和账号数 |
|
|
190
|
+
| `PUT` | `/v1/accounts/:account` | 写入或替换公众号凭据 |
|
|
191
|
+
| `DELETE` | `/v1/accounts/:account` | 删除 Worker 中的公众号凭据 |
|
|
192
|
+
| `POST` | `/v1/jobs` | 提交完整正文,返回 job ID |
|
|
193
|
+
| `GET` | `/v1/jobs/:id` | 查询步骤、状态和发布结果 |
|
|
544
194
|
|
|
545
|
-
|
|
546
|
-
bun run src/cli.ts doctor
|
|
547
|
-
bun run src/cli.ts hermes-metrics --workspace /abs/workspace
|
|
548
|
-
```
|
|
195
|
+
正文必须在 Worker 环境中可读取。客户端本机路径不会自动上传;正文中的图片应使用 Worker 可访问的 URL,或使用请求允许的 Base64 数据。相同 `idempotencyKey` 且输入不变时会返回已有任务,输入变化要使用新的键。
|
|
549
196
|
|
|
550
|
-
|
|
197
|
+
详细的环境变量、请求字段、Docker 网络、更新和故障恢复流程见 [`services/cloud-worker/README.md`](services/cloud-worker/README.md)。
|
|
551
198
|
|
|
552
|
-
|
|
553
|
-
bun run src/cli.ts wechat-export \
|
|
554
|
-
--markdown /abs/path/body.md \
|
|
555
|
-
--out /abs/path/article.html \
|
|
556
|
-
--account default \
|
|
557
|
-
--preview-shell-out /abs/path/article-preview.html
|
|
558
|
-
```
|
|
199
|
+
### 云端恢复
|
|
559
200
|
|
|
560
|
-
|
|
561
|
-
中的内联样式片段,不再加载 Milkdown 编辑器或复制浏览器 bundle。
|
|
201
|
+
`WX_RESULT_UNKNOWN`、超时或 `interrupted` 不代表草稿一定没有创建。先检查原 Pipeline run 和公众号草稿箱;确认已有草稿后,优先使用 `existingDraftMediaId` 更新。不要为了盲目重试而更换幂等键,否则可能创建重复草稿。
|
|
562
202
|
|
|
563
|
-
|
|
564
|
-
侧栏切换多篇产物,无需发到微信草稿箱。服务未启动时会自动拉起;失败导出也会登记并展示
|
|
565
|
-
`error_kind` / timeout / Chrome stderr 等调试信息。
|
|
203
|
+
## 配置微信和外部服务
|
|
566
204
|
|
|
567
|
-
|
|
568
|
-
# 显式启动预览服务(全局只允许一个实例;重复启动会复用)
|
|
569
|
-
bun run src/cli.ts wechat-preview serve --open
|
|
570
|
-
|
|
571
|
-
# 导出并打开该条目
|
|
572
|
-
bun run src/cli.ts wechat-export \
|
|
573
|
-
--markdown /abs/path/body.md \
|
|
574
|
-
--out /tmp/article.html \
|
|
575
|
-
--account default \
|
|
576
|
-
--open
|
|
577
|
-
|
|
578
|
-
# 调试中间产物
|
|
579
|
-
bun run src/cli.ts wechat-export \
|
|
580
|
-
--markdown /abs/path/body.md \
|
|
581
|
-
--out /tmp/article.html \
|
|
582
|
-
--debug-dir /tmp/wx-debug \
|
|
583
|
-
--timeout-ms 20000
|
|
584
|
-
|
|
585
|
-
# 关闭预览登记
|
|
586
|
-
bun run src/cli.ts wechat-export ... --no-preview
|
|
587
|
-
```
|
|
205
|
+
Pipeline 不直接调用 `api.weixin.qq.com`。公众号发布需要一个兼容以下路径的微信中转 API:
|
|
588
206
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
207
|
+
- `/api/v1/wx/cgi-bin/token`
|
|
208
|
+
- `/api/v1/wx/cgi-bin/material/add_material`
|
|
209
|
+
- `/api/v1/wx/cgi-bin/draft/add`
|
|
210
|
+
- `/api/v1/wx/cgi-bin/draft/update`
|
|
211
|
+
- `/api/v1/wx/cgi-bin/draft/batchget`
|
|
212
|
+
- `/api/v1/wx/cgi-bin/draft/get`
|
|
213
|
+
- `/api/v1/wx/cgi-bin/draft/delete`
|
|
596
214
|
|
|
597
|
-
|
|
598
|
-
`ZZHUB_WECHAT_PREVIEW_DIR`(锁与条目落盘目录)。发布草稿时默认也会登记一条
|
|
599
|
-
`[publish] ...` 预览;设 `ZZHUB_WECHAT_PREVIEW_ON_PUBLISH=0` 可关闭。
|
|
215
|
+
本仓库不包含中转服务。`wx.baseUrl` 应填写中转服务地址,账号 PAT、公众号 AppID 和 AppSecret 由使用者自行保管。COS 上传使用单独的 STS API;只做本地排版时无需配置微信或 COS。
|
|
600
216
|
|
|
601
|
-
|
|
602
|
-
`bundle_stale`。手动重建:`bun run build:wechat-preview`。
|
|
217
|
+
本机默认配置文件:
|
|
603
218
|
|
|
604
|
-
|
|
219
|
+
- macOS:`~/Library/Application Support/zzhub-pipeline/config.json`
|
|
220
|
+
- Linux:`~/.config/zzhub-pipeline/config.json`
|
|
221
|
+
- Windows:`%APPDATA%/zzhub-pipeline/config.json`
|
|
605
222
|
|
|
606
|
-
|
|
223
|
+
最小配置示例:
|
|
607
224
|
|
|
608
225
|
```json
|
|
609
226
|
{
|
|
227
|
+
"paths": {
|
|
228
|
+
"workspaceRoot": "/absolute/path/to/workspace"
|
|
229
|
+
},
|
|
610
230
|
"wx": {
|
|
231
|
+
"baseUrl": "https://wx-api.example.com",
|
|
232
|
+
"defaultAccount": "main",
|
|
611
233
|
"accounts": {
|
|
612
|
-
"
|
|
613
|
-
"
|
|
234
|
+
"main": {
|
|
235
|
+
"name": "我的公众号",
|
|
236
|
+
"appId": "YOUR_WECHAT_APP_ID",
|
|
237
|
+
"appSecret": "YOUR_WECHAT_APP_SECRET",
|
|
238
|
+
"pat": "YOUR_RELAY_API_PAT"
|
|
614
239
|
}
|
|
615
240
|
}
|
|
616
241
|
}
|
|
617
242
|
}
|
|
618
243
|
```
|
|
619
244
|
|
|
620
|
-
|
|
621
|
-
bun run src/cli.ts wechat-export \
|
|
622
|
-
--markdown /abs/path/body.md \
|
|
623
|
-
--out /abs/path/article.html \
|
|
624
|
-
--custom-css ./local-wechat.css
|
|
625
|
-
```
|
|
245
|
+
配置文件含凭据,请放在受控目录,不要提交到 Git。也可以使用 `ZZHUB_PIPELINE_CONFIG` 指定其他配置路径;`ZZHUB_WX_BASE_URL`、`WX_APPID`、`WX_APPSECRET` 和 `ZZCLUB_PAT` 可覆盖对应配置。
|
|
626
246
|
|
|
627
|
-
|
|
628
|
-
`--custom-css` 时,它会替代账号 `customCss`。CLI 相对路径按当前工作目录解析,
|
|
629
|
-
配置中的相对路径按 `config.json` 所在目录解析。
|
|
630
|
-
|
|
631
|
-
推荐使用语义标签或稳定节点钩子:
|
|
632
|
-
|
|
633
|
-
```css
|
|
634
|
-
.milkdown .editor p { line-height: 1.9; }
|
|
635
|
-
.milkdown .editor h2 { color: #a94473; }
|
|
636
|
-
.milkdown .editor [data-wechat-node="inline-code"] { background: #f7eef2; }
|
|
637
|
-
.milkdown .editor [data-wechat-node="image-caption"] { font-size: 13px; }
|
|
638
|
-
```
|
|
247
|
+
## 状态、监控与恢复
|
|
639
248
|
|
|
640
|
-
|
|
641
|
-
`data-wechat-node`。外部 CSS 支持变量、层叠和常规选择器,随后由 Juice 解析为
|
|
642
|
-
内联样式。最终片段只保留微信稳定标签、图片/链接/表格必要属性和保守 CSS;
|
|
643
|
-
脚本、事件属性、外部 `<style>`、未解析变量、`flex`/`grid`、定位和伪元素都会被移除。
|
|
249
|
+
`workflow-state.json` 是单个任务的业务真相源,记录路由、素材、阶段、渲染版本和发布结果。正文文件保存在工作区,不直接写入状态 JSON。
|
|
644
250
|
|
|
645
|
-
|
|
251
|
+
本机监控是可选的只读服务:
|
|
646
252
|
|
|
647
253
|
```bash
|
|
648
|
-
|
|
254
|
+
zzp monitor start
|
|
255
|
+
zzp monitor status
|
|
256
|
+
zzp monitor stop
|
|
649
257
|
```
|
|
650
258
|
|
|
651
|
-
|
|
259
|
+
它提供 HTTP 快照和 SSE,适合桌面端或本机 GUI 观察多个 CLI 执行。它不负责调度、重试或取消,也不应暴露到公网。详见 [`docs/monitor.md`](docs/monitor.md)。
|
|
652
260
|
|
|
653
|
-
|
|
654
|
-
# 列出草稿(账号不传时使用 wx.defaultAccount)
|
|
655
|
-
bun run src/cli.ts wx-drafts --limit 10
|
|
656
|
-
|
|
657
|
-
# 获取某篇草稿的完整内容(含 HTML)
|
|
658
|
-
bun run src/cli.ts wx-drafts --media-id MEDIA_ID
|
|
659
|
-
|
|
660
|
-
# 删除一篇草稿
|
|
661
|
-
bun run src/cli.ts wx-draft-delete --media-id MEDIA_ID
|
|
662
|
-
```
|
|
261
|
+
## 其他文档
|
|
663
262
|
|
|
664
|
-
|
|
263
|
+
- [Visual Studio 排版调优与云端配置同步指南](docs/visual-studio-and-config-sync.md)
|
|
264
|
+
- [文章发布入门](docs/cli-intro-for-article.md):面向第一次接入的使用说明
|
|
265
|
+
- [云端 Worker API、部署与更新](services/cloud-worker/README.md)
|
|
266
|
+
- [封面主题和图片排版](docs/render-themes.md)
|
|
267
|
+
- [离线正文模板](ARTICLE-THEMES.md)
|
|
268
|
+
- [Agent 发布 Skill](skills/zzhub-publish/SKILL.md)
|
|
269
|
+
- [第三方声明](THIRD-PARTY-NOTICES.md)
|
|
665
270
|
|
|
666
|
-
|
|
271
|
+
## 开发与验证
|
|
667
272
|
|
|
668
273
|
```bash
|
|
669
|
-
|
|
670
|
-
bun
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
bun run
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
## 状态文件
|
|
677
|
-
|
|
678
|
-
最重要的字段是:
|
|
679
|
-
|
|
680
|
-
| 字段 | 作用 |
|
|
681
|
-
| --- | --- |
|
|
682
|
-
| `run_id` | 任务唯一标识 |
|
|
683
|
-
| `created_at` / `updated_at` | 任务时间信息 |
|
|
684
|
-
| `phase.current` | 当前阶段 |
|
|
685
|
-
| `intent` | 上游分类结果与发布要求 |
|
|
686
|
-
| `route` | 当前微信路由和账号 |
|
|
687
|
-
| `metadata` | 标题、slug、日期、摘要 |
|
|
688
|
-
| `images.body_inputs` | 正文插图缺口与已接入图片 |
|
|
689
|
-
| `images.render_assets` | 已渲染出的封面与分页图 |
|
|
690
|
-
| `publish.results` | 发布结果 |
|
|
691
|
-
|
|
692
|
-
正文全文不进 state。state 只保存恢复流程需要的事实、路径、版本和结果,正文内容放在 `{workspace}/.zzhub-media/tmp/{run_id}/`。
|
|
693
|
-
|
|
694
|
-
## 配置系统
|
|
695
|
-
|
|
696
|
-
配置文件位置:
|
|
697
|
-
|
|
698
|
-
- macOS, `~/Library/Application Support/zzhub-pipeline/config.json`
|
|
699
|
-
- Linux, `~/.config/zzhub-pipeline/config.json`
|
|
700
|
-
|
|
701
|
-
可以用环境变量覆盖配置文件路径:
|
|
702
|
-
|
|
703
|
-
- `ZZHUB_PIPELINE_CONFIG=/abs/path/config.json`
|
|
704
|
-
|
|
705
|
-
其他环境变量覆盖:
|
|
706
|
-
|
|
707
|
-
- `ZZHUB_PIPELINE_WORKSPACE_ROOT`,workspace root path
|
|
708
|
-
- `ZZHUB_PIPELINE_POSTS_DIR`,posts directory name,默认 `posts`
|
|
709
|
-
- `ZZHUB_PIPELINE_BLOG_ROOT`,blog repository root
|
|
710
|
-
|
|
711
|
-
兼容旧配置时,会自动读取旧版 CLI 的配置。
|
|
712
|
-
|
|
713
|
-
配置结构概览:
|
|
714
|
-
|
|
715
|
-
- `paths`
|
|
716
|
-
- `services`
|
|
717
|
-
- `commands`
|
|
718
|
-
- `wx`,accounts
|
|
719
|
-
- `cos`
|
|
720
|
-
- `plugins`,渲染插件覆盖
|
|
721
|
-
|
|
722
|
-
### 插件配置
|
|
723
|
-
|
|
724
|
-
通过 `config.plugins` 可以替换内置渲染实现:
|
|
725
|
-
|
|
726
|
-
```json
|
|
727
|
-
{
|
|
728
|
-
"plugins": {
|
|
729
|
-
"imageRenderer": "./my-image-renderer.js",
|
|
730
|
-
"markdownRenderer": "./my-md-renderer.js"
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
不设置时使用内置适配器(`builtin-imgx` 和 `builtin-wechat-preview`)。
|
|
736
|
-
|
|
737
|
-
运行 `zzp doctor` 可以检查所有渲染依赖的状态(Chrome、@napi-rs/canvas、字体等)。
|
|
738
|
-
|
|
739
|
-
## 输出系统
|
|
740
|
-
|
|
741
|
-
- 非 TTY,或者重定向和管道场景,固定输出原始 JSON
|
|
742
|
-
- TTY 场景输出带 ANSI 颜色的 pretty 结果
|
|
743
|
-
- `FORCE_COLOR=1` 可以在非 TTY 下强制 pretty 输出
|
|
744
|
-
- `NO_COLOR=1` 可以在 TTY 下强制原始 JSON
|
|
745
|
-
- `--view` 支持 `json`,`markdown`,`agent`,`agent-json`
|
|
746
|
-
- `--view agent` 更适合 orchestrator 直接读取下一步
|
|
747
|
-
|
|
748
|
-
## imgx 渲染子系统
|
|
749
|
-
|
|
750
|
-
封面支持 `render.cover` 配置、十套主题、背景图与透明边框,以及供 App 使用的 JSON Schema 和预览输出。详见 [封面主题与图片排版](docs/render-themes.md),可直接导入 [配置示例](examples/cover-themes.json)。
|
|
751
|
-
|
|
752
|
-
- 基于 Chrome headless 和 `@napi-rs/canvas`
|
|
753
|
-
- 模板包括 `longform-3-4`(newspic 长文)、`poster-3-4`(单页卡片)和 `wechat-cover-split`(文章封面)
|
|
754
|
-
- 主题包括 `paper-sage`(默认账号)和 `linen-news`(`ancientone` 账号)
|
|
755
|
-
- 几何参数会从 header,footer,padding 自动推导,也可以通过 CLI 旗标显式控制
|
|
756
|
-
- 分页在进程内通过 `@chenglou/pretext` 完成,不依赖 Chrome dump
|
|
757
|
-
- `render` 和 `wechat-export` 都需要 Chrome
|
|
758
|
-
|
|
759
|
-
### newspic 分页规格
|
|
760
|
-
|
|
761
|
-
`newspic` 的渲染通过 `pagination_mode` 控制分页行为:
|
|
762
|
-
|
|
763
|
-
| 模式 | 行为 |
|
|
764
|
-
| --- | --- |
|
|
765
|
-
| `single` | 单页卡片(poster-3-4) |
|
|
766
|
-
| `multi` | 多页图集(longform-3-4) |
|
|
767
|
-
|
|
768
|
-
`newspic-spec.json` 常用字段:
|
|
769
|
-
|
|
770
|
-
```json
|
|
771
|
-
{
|
|
772
|
-
"pagination_mode": "multi",
|
|
773
|
-
"min_pages": 3,
|
|
774
|
-
"max_pages": 0,
|
|
775
|
-
"require_image_every_page": true,
|
|
776
|
-
"default_image_layout": "editorial",
|
|
777
|
-
"target_fill_ratio": 0.8,
|
|
778
|
-
"page_specs": [
|
|
779
|
-
{
|
|
780
|
-
"page": 1,
|
|
781
|
-
"image_markers": ["插图1", "插图2"],
|
|
782
|
-
"image_layout": "staggered",
|
|
783
|
-
"target_fill_ratio": 0.85
|
|
784
|
-
},
|
|
785
|
-
{
|
|
786
|
-
"page": 2,
|
|
787
|
-
"image_markers": ["插图3"]
|
|
788
|
-
}
|
|
789
|
-
]
|
|
790
|
-
}
|
|
791
|
-
```
|
|
792
|
-
|
|
793
|
-
- `target_fill_ratio` 是可选字段,默认 `0.8`,实际值会被规范化到 `0.35` 到 `0.95`
|
|
794
|
-
- `page_specs[].target_fill_ratio` 优先级高于顶层
|
|
795
|
-
- 长文分页测量优先在进程内跑 `pretext`,不再依赖 Chrome dump
|
|
796
|
-
|
|
797
|
-
**页标记**:如果外部工具需要把某段文字固定到某一页,除了传 `page_specs`,还应该在正文里加页标记:
|
|
798
|
-
|
|
799
|
-
```text
|
|
800
|
-
【第一页】
|
|
801
|
-
第一页正文
|
|
802
|
-
|
|
803
|
-
【第二页】
|
|
804
|
-
第二页正文
|
|
274
|
+
bun install
|
|
275
|
+
bun test
|
|
276
|
+
bun x tsc --noEmit
|
|
277
|
+
bun run build:wechat-preview
|
|
278
|
+
bun run build
|
|
805
279
|
```
|
|
806
280
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
不加页标记时走自动流排;加了页标记并且存在 `page_specs` 时切换到 spec 驱动分页,页标记变成硬边界。
|
|
810
|
-
|
|
811
|
-
`longform-3-4` 的几何参数包括:
|
|
812
|
-
|
|
813
|
-
- `--page-width` / `--page-height`
|
|
814
|
-
- `--body-padding-x` / `--body-padding-y`
|
|
815
|
-
- `--logo-size` / `--logo-gap`
|
|
816
|
-
- `--footer-height` / `--footer-margin-top`
|
|
817
|
-
- `--content-width` / `--content-height`
|
|
818
|
-
- `--content-bottom-gap`
|
|
819
|
-
|
|
820
|
-
## wechat-preview 子系统
|
|
821
|
-
|
|
822
|
-
- 基于 Milkdown core + CommonMark/GFM 生成语义 HTML,不读取 Crepe/CodeMirror 可视 DOM
|
|
823
|
-
- 固定管线:Markdown → Milkdown HTML → 元素 renderer prepare → Juice 内联 → renderer finalize → 微信白名单清理
|
|
824
|
-
- 元素 renderer 覆盖段落/标题、强调、行内代码、代码块、引用、图片标题、链接脚注、列表、表格、分隔线和页脚
|
|
825
|
-
- 内置默认采用“静默编辑”:深色标题、低装饰引用和中性行内代码,品牌色只用于链接等识别点
|
|
826
|
-
- `default` 与 `ancientone` 共用排版骨架,只通过主题令牌切换品牌色、字体栈和页脚
|
|
827
|
-
- 构建命令:`bun run build:wechat-preview`
|
|
828
|
-
- `wechat-export` 和微信草稿发布使用同一份最终内联 HTML
|
|
829
|
-
|
|
830
|
-
`@zzclub/milkdown-article-style`(`article.css`)会通过 Vite 以 raw CSS 打入浏览器
|
|
831
|
-
bundle。修改该 CSS 或浏览器 renderer 后必须执行:
|
|
281
|
+
只修改 Worker 时可以先运行:
|
|
832
282
|
|
|
833
283
|
```bash
|
|
834
|
-
bun
|
|
835
|
-
bun install --global .
|
|
284
|
+
bun test services/cloud-worker/server.test.ts
|
|
836
285
|
```
|
|
837
286
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
## 插件系统
|
|
841
|
-
|
|
842
|
-
渲染通过 adapter 接口实现可插拔:
|
|
843
|
-
|
|
844
|
-
- `ImageRenderPlugin` — 替换 imgx 图片渲染
|
|
845
|
-
- `MarkdownRenderPlugin` — 替换 wechat-preview HTML 导出
|
|
846
|
-
|
|
847
|
-
接口定义在 `src/adapter-types.ts`,加载逻辑在 `src/adapter-loader.ts`。
|
|
848
|
-
|
|
849
|
-
内置适配器:
|
|
850
|
-
- `src/adapters/builtin-image-renderer.ts` — 包装 imgx
|
|
851
|
-
- `src/adapters/builtin-markdown-renderer.ts` — 包装 wechat-preview
|
|
852
|
-
|
|
853
|
-
每个适配器可实现 `doctor()` 方法,报告运行时依赖状态。
|
|
854
|
-
|
|
855
|
-
运行时依赖检查:
|
|
856
|
-
- `@napi-rs/canvas` — 图片渲染需要,懒加载,缺失时提示安装命令
|
|
857
|
-
- Chrome — HTML 导出需要,缺失时提示安装命令
|
|
858
|
-
- CJK 字体 — 随 CLI 附带,生成图片时按需读取本地文件,不触发网络下载;封面也支持本机字体名称
|
|
287
|
+
`bun run build:npm` 会生成独立的 npm bundle;`services/cloud-worker/` 不在 npm 包的 `files` 清单中,Worker 需要从源码仓库构建。
|
|
859
288
|
|
|
860
|
-
##
|
|
861
|
-
|
|
862
|
-
| 提供者 | 路由 | 说明 |
|
|
863
|
-
| --- | --- | --- |
|
|
864
|
-
| wechat | `wechat-article` | 创建公众号文章草稿 |
|
|
865
|
-
| wechat | `wechat-newspic` | 发送图片消息 |
|
|
866
|
-
| cos | - | 腾讯云 COS 图片 CDN |
|
|
867
|
-
| blog | - | Markdown 同步到博客仓库 |
|
|
868
|
-
|
|
869
|
-
Markdown → WeChat HTML 转换通过插件系统完成(默认使用内置 `builtin-wechat-preview` 适配器)。
|
|
870
|
-
|
|
871
|
-
## 本机多任务监控
|
|
872
|
-
|
|
873
|
-
`zzp monitor start` 按需启动共享 HTTP/SSE 服务,`monitor status` 查看状态,`monitor stop` 只停止监控。
|
|
874
|
-
CLI 独立记录执行和错误,GUI 可同时订阅多个任务。新版业务失败会退出 1,调用方仍应读取失败响应和最新任务状态。
|
|
875
|
-
|
|
876
|
-
详见 [监控接口与退出码迁移](docs/monitor.md)。
|
|
877
|
-
|
|
878
|
-
## 测试与验证
|
|
879
|
-
|
|
880
|
-
```bash
|
|
881
|
-
bun test
|
|
882
|
-
bun x tsc --noEmit
|
|
883
|
-
```
|
|
289
|
+
## 许可
|
|
884
290
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
1. `src/cli.ts`
|
|
888
|
-
2. `src/plugins.ts`
|
|
889
|
-
3. `src/state.ts`
|
|
890
|
-
4. `src/task-manager.ts`
|
|
891
|
-
5. `src/routes.ts`
|
|
892
|
-
6. `src/profiles.ts`
|
|
893
|
-
7. `src/workflow-materials.ts`
|
|
894
|
-
8. `src/commands/prepare.ts`
|
|
895
|
-
9. `src/commands/prepare-finalize.ts`
|
|
896
|
-
10. `src/commands/render.ts`
|
|
897
|
-
11. `src/commands/publish.ts`
|
|
291
|
+
Pipeline 使用 [MIT License](LICENSE)。排版基础和第三方依赖的授权说明见 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md);外部中转服务、COS 服务和第三方模板的费用、权限及许可由各自提供方负责。
|