@zzclub/pipeline 0.2.0 → 0.4.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/README.md +366 -170
- package/package.json +4 -7
- package/src/adapter-loader.ts +138 -0
- package/src/adapter-types.ts +172 -0
- package/src/adapters/builtin-image-renderer.ts +353 -0
- package/src/adapters/builtin-markdown-renderer.ts +63 -0
- package/src/adapters/index.ts +2 -0
- package/src/args.test.ts +118 -0
- package/src/args.ts +90 -0
- package/src/cli.ts +51 -0
- package/src/commands/abandon.test.ts +104 -0
- package/src/commands/abandon.ts +202 -0
- package/src/commands/attach-body-images.ts +88 -0
- package/src/commands/attach-body.ts +86 -0
- package/src/commands/attach-newspic-spec.ts +41 -0
- package/src/commands/checkpoint.test.ts +91 -0
- package/src/commands/checkpoint.ts +66 -0
- package/src/commands/config.ts +106 -0
- package/src/commands/cos-upload.ts +62 -0
- package/src/commands/doctor.ts +66 -0
- package/src/commands/find-run.ts +55 -0
- package/src/commands/hermes-metrics.ts +226 -0
- package/src/commands/imgx.ts +60 -0
- package/src/commands/ingest-handoff.ts +509 -0
- package/src/commands/init.ts +126 -0
- package/src/commands/prepare-finalize.ts +348 -0
- package/src/commands/prepare.ts +310 -0
- package/src/commands/publish.ts +147 -0
- package/src/commands/reconcile.ts +36 -0
- package/src/commands/render.ts +244 -0
- package/src/commands/reset.test.ts +151 -0
- package/src/commands/reset.ts +159 -0
- package/src/commands/review.ts +77 -0
- package/src/commands/status.ts +68 -0
- package/src/commands/sync-blog.ts +39 -0
- package/src/commands/tasks.ts +50 -0
- package/src/commands/wechat-export.ts +68 -0
- package/src/config.ts +0 -0
- package/src/imgx/ascii-portrait.ts +77 -0
- package/src/imgx/assets/fonts/AlimamaShuHeiTi-Bold.ttf +0 -0
- package/src/imgx/assets/fonts/LXGWNeoZhiSongPlus.ttf +0 -0
- package/src/imgx/assets/fonts/LXGWWenKai-Regular.ttf +0 -0
- package/src/imgx/assets/icons/logo.png +0 -0
- package/src/imgx/assets/icons/logo.svg +8 -0
- package/{dist/assets → src}/imgx/assets/templates/longform-3-4.html +1 -1
- package/{dist/assets → src}/imgx/assets/templates/poster-3-4.html +69 -30
- package/{dist/assets → src}/imgx/assets/templates/wechat-cover-split.html +58 -27
- package/src/imgx/cli.ts +57 -0
- package/src/imgx/geometry.ts +68 -0
- package/src/imgx/index.ts +5 -0
- package/src/imgx/longform-theme.ts +243 -0
- package/src/imgx/poster-recipe.ts +81 -0
- package/src/imgx/pretext-adapter.ts +14 -0
- package/src/imgx/pretext-runtime.ts +61 -0
- package/src/imgx/references/ascii-portrait-3-4.md +59 -0
- package/src/imgx/references/longform-3-4.md +173 -0
- package/src/imgx/references/poster-3-4.md +87 -0
- package/src/imgx/references/tips-3-4.md +69 -0
- package/src/imgx/references/wechat-cover-split.md +89 -0
- package/src/imgx/references/x-like-posts.md +139 -0
- package/src/imgx/render-article.test.ts +53 -0
- package/src/imgx/render-article.ts +1364 -0
- package/src/imgx/render-ascii-portrait.ts +53 -0
- package/src/imgx/render-card.ts +232 -0
- package/src/imgx/render-x-like-posts.ts +235 -0
- package/src/imgx/runtime.ts +403 -0
- package/src/imgx/typographic-scale.test.ts +56 -0
- package/src/imgx/typographic-scale.ts +44 -0
- package/src/output.ts +632 -0
- package/src/plugins.ts +101 -0
- package/src/profiles.ts +149 -0
- package/src/providers/blog.test.ts +58 -0
- package/src/providers/blog.ts +154 -0
- package/src/providers/cos.ts +183 -0
- package/src/providers/index.test.ts +26 -0
- package/src/providers/index.ts +185 -0
- package/src/providers/wechat.test.ts +215 -0
- package/src/providers/wechat.ts +548 -0
- package/src/routes.ts +287 -0
- package/src/runtime-paths.ts +179 -0
- package/src/schema/config.ts +133 -0
- package/src/schema/state.ts +376 -0
- package/src/spawn.ts +103 -0
- package/src/state.ts +265 -0
- package/src/task-manager.ts +657 -0
- package/src/task-views.ts +222 -0
- package/src/text.test.ts +689 -0
- package/src/text.ts +683 -0
- package/src/wechat-preview/assets/fonts/SweiCurveLegCJKsc-Regular.woff +0 -0
- package/src/wechat-preview/browser/editor-export.css +241 -0
- package/src/wechat-preview/browser/editor-export.ts +103 -0
- package/src/wechat-preview/frontmatter-handler.ts +73 -0
- package/src/wechat-preview/index.test.ts +154 -0
- package/src/wechat-preview/index.ts +294 -0
- package/src/wechat-preview/themes.ts +141 -0
- package/src/wechat-preview/wechat-formatter.test.ts +288 -0
- package/src/wechat-preview/wechat-formatter.ts +1506 -0
- package/src/wechat-preview.test.ts +73 -0
- package/src/workflow-materials.test.ts +225 -0
- package/src/workflow-materials.ts +217 -0
- package/src/workflow.test.ts +2087 -0
- package/dist/assets/imgx/assets/icons/avatar_jinx_cartoon.jpg +0 -0
- package/dist/assets/imgx/assets/icons/fishbone-logo-square.png +0 -0
- package/dist/assets/imgx/assets/icons/fishbone-logo.jpg +0 -0
- package/dist/assets/imgx/assets/icons/fishbone-logo.png +0 -0
- package/dist/assets/imgx/assets/icons/openclaw-logo.svg +0 -22
- package/dist/assets/imgx/assets/icons/zzclub-logo-black.jpg +0 -0
- package/dist/assets/imgx/assets/icons/zzclub-logo-gray.svg +0 -25
- package/dist/cli.js +0 -432
- package/dist/node_modules/@chenglou/pretext/dist/analysis.d.ts +0 -33
- package/dist/node_modules/@chenglou/pretext/dist/analysis.js +0 -1063
- package/dist/node_modules/@chenglou/pretext/dist/bidi.d.ts +0 -1
- package/dist/node_modules/@chenglou/pretext/dist/bidi.js +0 -175
- package/dist/node_modules/@chenglou/pretext/dist/generated/bidi-data.d.ts +0 -4
- package/dist/node_modules/@chenglou/pretext/dist/generated/bidi-data.js +0 -979
- package/dist/node_modules/@chenglou/pretext/dist/layout.d.ts +0 -70
- package/dist/node_modules/@chenglou/pretext/dist/layout.js +0 -496
- package/dist/node_modules/@chenglou/pretext/dist/line-break.d.ts +0 -36
- package/dist/node_modules/@chenglou/pretext/dist/line-break.js +0 -820
- package/dist/node_modules/@chenglou/pretext/dist/measurement.d.ts +0 -28
- package/dist/node_modules/@chenglou/pretext/dist/measurement.js +0 -219
- package/dist/node_modules/@chenglou/pretext/dist/rich-inline.d.ts +0 -51
- package/dist/node_modules/@chenglou/pretext/dist/rich-inline.js +0 -401
- /package/{dist/assets → src}/imgx/assets/browser/obstacle-flow.d.ts +0 -0
- /package/{dist/assets → src}/imgx/assets/browser/obstacle-flow.js +0 -0
- /package/{dist/assets → src}/imgx/assets/icons/clover.svg +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/ascii-portrait-3-4.html +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/ascii-portrait-tile.html +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/tips-3-4.html +0 -0
- /package/{dist/assets → src}/imgx/assets/templates/x-like-posts.html +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/.vite/manifest.json +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/SweiCurveLegCJKsc-Regular-B3Ut5bwH.woff +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/assets/style-BYNXnUvc.css +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/browser-dist/editor-export.js +0 -0
- /package/{dist/assets → src}/wechat-preview/assets/templates/export-shell.html +0 -0
package/README.md
CHANGED
|
@@ -16,10 +16,15 @@
|
|
|
16
16
|
- Bun 运行时
|
|
17
17
|
- Chrome,用于 `render` 和 `wechat-export`
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
安装:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
# 从 npm
|
|
23
|
+
npm install -g @zzclub/pipeline
|
|
24
|
+
|
|
25
|
+
# 或从源码
|
|
22
26
|
bun install
|
|
27
|
+
bun install --global .
|
|
23
28
|
```
|
|
24
29
|
|
|
25
30
|
二进制别名:
|
|
@@ -35,10 +40,10 @@ bun install
|
|
|
35
40
|
|
|
36
41
|
当前只有两条主工作流路由:
|
|
37
42
|
|
|
38
|
-
- `wechat-article`
|
|
39
|
-
- `wechat-newspic`
|
|
43
|
+
- `wechat-article` — 公众号文章草稿(HTML)
|
|
44
|
+
- `wechat-newspic` — 公众号图片消息(PNG 卡片/图集)
|
|
40
45
|
|
|
41
|
-
`blog`
|
|
46
|
+
`blog` 只做事后同步(`sync-blog`),不参与主工作流分支。
|
|
42
47
|
|
|
43
48
|
状态文件分两类:
|
|
44
49
|
|
|
@@ -47,6 +52,285 @@ bun install
|
|
|
47
52
|
|
|
48
53
|
正文全文不会进入 state,正文内容统一放在 `{workspace}/.zzhub-media/tmp/{run_id}/`。
|
|
49
54
|
|
|
55
|
+
## 核心流程
|
|
56
|
+
|
|
57
|
+
### Agent 编排循环
|
|
58
|
+
|
|
59
|
+
Agent 通过无状态循环驱动流水线,每轮只做一个动作。所有上下文存储在状态文件中,中断后重新执行 `status` 即可精准续接。
|
|
60
|
+
|
|
61
|
+
```mermaid
|
|
62
|
+
flowchart TD
|
|
63
|
+
START["Agent 启动"] --> FIND["find-run --view agent"]
|
|
64
|
+
FIND --> HAS{"存在活跃任务?"}
|
|
65
|
+
HAS -- "否" --> INIT["init 创建新任务"]
|
|
66
|
+
INIT --> STATUS
|
|
67
|
+
HAS -- "是" --> STATUS["status --view agent<br/>读取 next_action"]
|
|
68
|
+
STATUS --> EXEC["执行 next_action.action<br/>对应的命令"]
|
|
69
|
+
EXEC --> DONE{"action == complete?"}
|
|
70
|
+
DONE -- "否" --> STATUS
|
|
71
|
+
DONE -- "是" --> END["任务完成"]
|
|
72
|
+
|
|
73
|
+
style START fill:#e0e7ff,stroke:#6366f1,color:#3730a3
|
|
74
|
+
style END fill:#d1fae5,stroke:#10b981,color:#065f46
|
|
75
|
+
style STATUS fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
76
|
+
style EXEC fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 如何正确调用 Agent Skill
|
|
80
|
+
|
|
81
|
+
Agent 通过 `.claude/skills/zzhub-publish/SKILL.md` 获取操作指引。任务描述的质量直接决定执行效果。
|
|
82
|
+
|
|
83
|
+
**反模式(避免):**
|
|
84
|
+
|
|
85
|
+
| 不要这样说 | 问题 |
|
|
86
|
+
|-----------|------|
|
|
87
|
+
| "帮我发一篇文章到公众号" | Agent 不知道主题、没有具体方向。大概率跳过 status 检查或写出空洞内容。 |
|
|
88
|
+
| "发一篇关于 AI 的文章" | 主题太泛,agent 会写出套话文章,review 也难以判断好坏。 |
|
|
89
|
+
| 不提 handoff 里的已有文件 | Agent 按默认路径重新写稿,浪费已有素材。 |
|
|
90
|
+
|
|
91
|
+
**模式 A:Agent 从头写稿**
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
调用 zzhub-publish skill,完成以下任务:
|
|
95
|
+
|
|
96
|
+
文章主题:[具体 topic,一句话说清]
|
|
97
|
+
发布目标:大号
|
|
98
|
+
要求:
|
|
99
|
+
- 每完成一步报告当前状态
|
|
100
|
+
- 到 content review 通过后先暂停,等我确认再继续
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
给了具体主题 agent 才有明确的写稿方向,设边界让发布前有人工确认点,每步报告减少盲跳。
|
|
104
|
+
|
|
105
|
+
**模式 B:从准备好的文件交接**
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
调用 zzhub-publish skill,从我准备好的素材继续:
|
|
109
|
+
|
|
110
|
+
我已在 /path/to/handoff.json 提供了 handoff 文件,
|
|
111
|
+
body 文件也在 handoff 里指定好了(已经是一篇完整文章)。
|
|
112
|
+
|
|
113
|
+
1. 先读 skill 文件,然后从 ingest-handoff 开始
|
|
114
|
+
2. body 已经在 handoff 中指定了路径,不需要重新写稿
|
|
115
|
+
3. 按 skill 的 loop 走完后续所有步骤(prepare → review → render → publish)
|
|
116
|
+
4. 每一步报告动作和结果
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
明确告知 body 已就绪防止重复劳动,指出入口(ingest-handoff)让 agent 不用猜测,强调"后续所有"让 agent 一口气跑完。
|
|
120
|
+
|
|
121
|
+
**模式 C:快速出草稿,暂不发布**
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
调用 zzhub-publish skill:
|
|
125
|
+
|
|
126
|
+
主题:周末咖啡馆随笔,轻松风格,~500字
|
|
127
|
+
发布目标:大号
|
|
128
|
+
重要:只到 render 完成就停,不要 publish。我要在草稿箱里人工预览后再决定。
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
风格+长度约束写稿质量,负向约束("不要 publish")设明确边界。
|
|
132
|
+
|
|
133
|
+
**核心原则:**
|
|
134
|
+
|
|
135
|
+
1. **给具体主题** — "咖啡馆里的宇宙学" 远好于 "写一篇文章"。主题越具体产出越可控。
|
|
136
|
+
2. **明说上下文差异** — 已有素材或 body 文件一定要说出来,否则 agent 默认走 "init → 写稿"。
|
|
137
|
+
3. **在分叉点给提示** — 比如 "review 通过后暂停等我确认",或 "body 已在 handoff 里准备好了,跳过写稿"。
|
|
138
|
+
4. **用负向约束设边界** — "不要 publish"、"到 review 就停" 比正向的 "帮我做 X" 更能防止 agent 跑过头。
|
|
139
|
+
5. **先读 skill 再行动** — 指令里加上 "先读 SKILL.md",尤其是对 sub-agent。不假设 agent 已经知道规则。
|
|
140
|
+
|
|
141
|
+
### next_action 决策树(优先级 1→12)
|
|
142
|
+
|
|
143
|
+
`buildTaskStatus()`(`src/task-manager.ts`)按以下优先级依次判断,输出唯一的下一步动作。
|
|
144
|
+
每个分支互斥,一旦命中即返回,不会继续判断后续条件。
|
|
145
|
+
|
|
146
|
+
```mermaid
|
|
147
|
+
flowchart TD
|
|
148
|
+
S["📄 读取 workflow state"] --> C1{"mode=done 或 phase=done?"}
|
|
149
|
+
C1 -- "是" --> A1["✅ complete"]
|
|
150
|
+
C1 -- "否" --> C2{"mode=failed?"}
|
|
151
|
+
C2 -- "是" --> A2["🔧 reset-or-repair<br/>checkpoint 诊断 → reset 重置"]
|
|
152
|
+
C2 -- "否" --> C3{"mode=handoff?"}
|
|
153
|
+
C3 -- "是" --> C3a{"body_inputs 待处理?"}
|
|
154
|
+
C3a -- "是" --> A3a["🖼️ attach-body-images<br/>等待用户提供插图"]
|
|
155
|
+
C3a -- "否" --> A3b["📎 resolve-handoff<br/>checkpoint 解决外部依赖"]
|
|
156
|
+
C3 -- "否" --> C4{"正文是否已挂载?<br/>source_body_path 或 asset_path"}
|
|
157
|
+
C4 -- "否" --> A4["✍️ attach-body<br/>Worker 撰写正文"]
|
|
158
|
+
C4 -- "是" --> C5{"body_inputs 待处理?"}
|
|
159
|
+
C5 -- "是" --> A5["🖼️ attach-body-images"]
|
|
160
|
+
C5 -- "否" --> C6{"元数据是否就绪?<br/>title, slug, date"}
|
|
161
|
+
C6 -- "否" --> A6["📝 prepare<br/>路由+作者+排版+元数据"]
|
|
162
|
+
C6 -- "是" --> C7{"content_review 状态?"}
|
|
163
|
+
C7 -- "needs_revision" --> A7["🔁 revise-content<br/>Worker 改写正文"]
|
|
164
|
+
C7 -- "unchecked" --> A8["👀 review-content<br/>Worker 审核内容"]
|
|
165
|
+
C7 -- "passed" --> C7a{"asset_path 是否存在?"}
|
|
166
|
+
C7a -- "否" --> A9["📦 prepare-finalize<br/>生成正式产物到 posts/"]
|
|
167
|
+
C7a -- "是" --> C9{"render 是否需推进?"}
|
|
168
|
+
C9 -- "是" --> A10["🎨 render<br/>imgx 图片渲染"]
|
|
169
|
+
C9 -- "否" --> C10{"publish 是否需推进?"}
|
|
170
|
+
C10 -- "是" --> A11["🚀 publish<br/>推送到发布渠道"]
|
|
171
|
+
C10 -- "否" --> A12["📝 prepare<br/>兜底重新 prepare"]
|
|
172
|
+
|
|
173
|
+
style A1 fill:#d1fae5,stroke:#10b981,color:#065f46
|
|
174
|
+
style A2 fill:#fee2e2,stroke:#ef4444,color:#991b1b
|
|
175
|
+
style A3a fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
176
|
+
style A3b fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
177
|
+
style A4 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
178
|
+
style A5 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
179
|
+
style A6 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
180
|
+
style A7 fill:#ede9fe,stroke:#8b5cf6,color:#5b21b6
|
|
181
|
+
style A8 fill:#ede9fe,stroke:#8b5cf6,color:#5b21b6
|
|
182
|
+
style A9 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
183
|
+
style A10 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
184
|
+
style A11 fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
185
|
+
style A12 fill:#e5e7eb,stroke:#6b7280,color:#374151
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
关键说明:
|
|
189
|
+
|
|
190
|
+
- C7(content_review)仅在 `asset_path` 尚未存在时触发,`review-content` 和 `prepare-finalize` 都在同一 `!asset_path` 外层分支内
|
|
191
|
+
- C9/C10 进入时 `asset_path` 已必然存在(由 `prepare-finalize` 写入),因此直接按 render/publish 阶段状态判断
|
|
192
|
+
- `attach-newspic-spec` 不在决策树中 — 它是可选命令,在 render 前由 Agent 自行决定是否附加分页规格
|
|
193
|
+
- `reconcile` 不在决策树中 — 它是手动维护命令,用于刷新素材对账,不自动触发
|
|
194
|
+
|
|
195
|
+
每个 `next_action` 包含:
|
|
196
|
+
|
|
197
|
+
| 字段 | 说明 |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `action` | 动作标识(如 `attach-body`、`prepare`、`render`) |
|
|
200
|
+
| `executor` | 执行者类型:`cli` / `worker` / `await-input` / `repair` / `complete` |
|
|
201
|
+
| `command` | 建议的 CLI 命令,含 `--state` 路径 |
|
|
202
|
+
| `params` | 执行参数:`state_path`、`source_body_path`、`worker_mode` 等 |
|
|
203
|
+
| `on_complete` | 执行后的后续指令(通常是 "re-run status") |
|
|
204
|
+
|
|
205
|
+
### 任务入口:init 与 ingest-handoff
|
|
206
|
+
|
|
207
|
+
任务有两种创建方式:`init`(Agent 直接初始化)和 `ingest-handoff`(外部系统交接 JSON 文件)。
|
|
208
|
+
|
|
209
|
+
```mermaid
|
|
210
|
+
flowchart TD
|
|
211
|
+
ENTRY{"如何启动任务?"}
|
|
212
|
+
ENTRY -- "Agent 初始化" --> INIT["init --task-kind ... --content-form ...<br/>--targets ... --intent-text ..."]
|
|
213
|
+
ENTRY -- "外部交接" --> HANDOFF["ingest-handoff --file handoff.json"]
|
|
214
|
+
|
|
215
|
+
HANDOFF --> HF_TYPE{"handoff 类型?"}
|
|
216
|
+
HF_TYPE -- "publish_handoff" --> NEW1["mode=new(固定)<br/>解析 body_path / title / account<br/>创建新任务"]
|
|
217
|
+
HF_TYPE -- "workflow_handoff" --> HF_MODE{"handoff.mode?"}
|
|
218
|
+
|
|
219
|
+
HF_MODE -- "new 新建" --> NEW2["解析 content_form / body_path<br/>或 materials_path / policies<br/>创建新任务"]
|
|
220
|
+
HF_MODE -- "resume 续接" --> FIND["findTask<br/>按 state_path 或 run_id 查找已有任务"]
|
|
221
|
+
FIND --> COMPARE{"核心输入是否变化?<br/>正文 / 标题 / 路由 / intent_text<br/>/ explicit_constraints"}
|
|
222
|
+
COMPARE -- "有变化" --> RESET["resetDerivedState()<br/>全部阶段重置为 pending<br/>保留 run_id 与 workspace_root"]
|
|
223
|
+
COMPARE -- "无变化" --> POLICY{"仅 review_policy 变化<br/>且为 trust_user?"}
|
|
224
|
+
POLICY -- "是 + 有 handoff.body_path" --> AUTOPASS["自动设置<br/>content_review=passed<br/>跳过审核步骤"]
|
|
225
|
+
POLICY -- "否" --> KEEP["保持当前状态不变"]
|
|
226
|
+
|
|
227
|
+
NEW1 --> STATUS["status 重新计算 → next_action"]
|
|
228
|
+
NEW2 --> STATUS
|
|
229
|
+
RESET --> STATUS
|
|
230
|
+
AUTOPASS --> STATUS
|
|
231
|
+
KEEP --> STATUS
|
|
232
|
+
INIT --> STATUS
|
|
233
|
+
|
|
234
|
+
style HANDOFF fill:#e0e7ff,stroke:#6366f1,color:#3730a3
|
|
235
|
+
style RESET fill:#fee2e2,stroke:#ef4444,color:#991b1b
|
|
236
|
+
style AUTOPASS fill:#d1fae5,stroke:#10b981,color:#065f46
|
|
237
|
+
style STATUS fill:#fef3c7,stroke:#f59e0b,color:#92400e
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**handoff JSON 格式示例**:
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
// workflow_handoff — 新建任务
|
|
244
|
+
{
|
|
245
|
+
"workflow_handoff": {
|
|
246
|
+
"mode": "new",
|
|
247
|
+
"content_form": "article",
|
|
248
|
+
"body_path": "/abs/path/body.md",
|
|
249
|
+
"target_account": "default",
|
|
250
|
+
"title": "文章标题",
|
|
251
|
+
"user_intent_text": "发公众号文章给大号",
|
|
252
|
+
"review_policy": "required"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// workflow_handoff — 续接已有任务
|
|
257
|
+
{
|
|
258
|
+
"workflow_handoff": {
|
|
259
|
+
"mode": "resume",
|
|
260
|
+
"state_path": "/abs/path/workflow-state.json",
|
|
261
|
+
"body_path": "/abs/path/revised-body.md",
|
|
262
|
+
"review_policy": "trust_user"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// publish_handoff — 简化格式(固定新建)
|
|
267
|
+
{
|
|
268
|
+
"publish_handoff": {
|
|
269
|
+
"content_form": "article",
|
|
270
|
+
"body_path": "/abs/path/body.md",
|
|
271
|
+
"target_account": "default",
|
|
272
|
+
"title": "文章标题",
|
|
273
|
+
"user_intent_text": "发公众号"
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`resetDerivedState()` 会重置的内容:
|
|
279
|
+
|
|
280
|
+
- 全部阶段状态(prepare / render / publish)→ pending
|
|
281
|
+
- `asset_path` → 清空,`formatted_body_path` → null
|
|
282
|
+
- `metadata`(保留 title)→ 默认值
|
|
283
|
+
- `artifacts` / `images` / `publish` / `content_review` → 默认值
|
|
284
|
+
- 保留:`run_id`、`workspace_root`、`state_path`、`intent.content_form`
|
|
285
|
+
|
|
286
|
+
### 三阶段流水线
|
|
287
|
+
|
|
288
|
+
三个活跃阶段和两个终态:
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
prepare → render → publish → done
|
|
292
|
+
↘ failed
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**prepare 阶段子步骤**(在 `prepare` 命令内部按序执行):
|
|
296
|
+
|
|
297
|
+
1. `channel-route` — 解析路由与公众号账号
|
|
298
|
+
2. `author-select` — 确定改写权限与风格模式(`rewrite_allowed` / `style_mode`)
|
|
299
|
+
3. `format` — 文本排版规则处理
|
|
300
|
+
4. `asset-meta` — 提取标题/日期/摘要
|
|
301
|
+
5. `body-inputs` — 扫描正文中的插图标记
|
|
302
|
+
6. `highlight-words` — 从标题提取封面高亮词(`prepare-finalize` 中完成)
|
|
303
|
+
7. `asset-save` — 写入正式文件到 `posts/` 目录(`prepare-finalize` 中完成)
|
|
304
|
+
|
|
305
|
+
> `style` 润色不在 `prepare` 命令内 —— 它需要 LLM 调用,由外部 Worker 完成后再交回 pipeline。
|
|
306
|
+
|
|
307
|
+
**render 阶段子步骤**:
|
|
308
|
+
|
|
309
|
+
1. `image-plan` — 模板选择(wechat-cover-split / poster-3-4 / longform-3-4)
|
|
310
|
+
2. `body-inputs` — 收集插图图片(longform 模式下可能暂停等待用户输入)
|
|
311
|
+
3. `adapter-render` — 调用图片渲染插件
|
|
312
|
+
4. 记录 `render_assets`,递增 `render_version`
|
|
313
|
+
|
|
314
|
+
**publish 阶段子步骤**:
|
|
315
|
+
|
|
316
|
+
1. 收集发布路由(primary + extras)
|
|
317
|
+
2. 幂等性检查(content_version + render_version 匹配则跳过)
|
|
318
|
+
3. `provider.publish()` — 微信公众号 / COS / 博客
|
|
319
|
+
4. 记录发布结果,全部成功则 mode=done
|
|
320
|
+
|
|
321
|
+
### 状态文件生命周期
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
临时区(准备阶段) 正式区(prepare-finalize 之后)
|
|
325
|
+
{workspace}/.zzhub-media/ {workspace}/posts/{date-slug}/
|
|
326
|
+
runs/{run_id}.json ←───────────────── workflow-state.json
|
|
327
|
+
tmp/{run_id}/source-body.md ──────→ post.md
|
|
328
|
+
images/wechat/ ← 文章渲染图
|
|
329
|
+
images/newspic/ ← 贴图渲染图
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`prepare-finalize` 命令负责将临时状态固化到正式目录,此后所有命令自动解析 `posts/{date-slug}/workflow-state.json` 作为权威状态源。
|
|
333
|
+
|
|
50
334
|
## 目录结构
|
|
51
335
|
|
|
52
336
|
```text
|
|
@@ -103,11 +387,12 @@ src/
|
|
|
103
387
|
|
|
104
388
|
## 命令参考
|
|
105
389
|
|
|
106
|
-
`workflow` 组,
|
|
390
|
+
`workflow` 组,17 个命令:
|
|
107
391
|
|
|
108
392
|
| 命令 | 说明 |
|
|
109
393
|
| --- | --- |
|
|
110
394
|
| `init` | Create run state from intent classification |
|
|
395
|
+
| `ingest-handoff` | Create or resume a task from a publish_handoff/workflow_handoff JSON file |
|
|
111
396
|
| `attach-body` | Attach a source body file to a managed task |
|
|
112
397
|
| `attach-body-images` | Attach body image marker files to a managed task |
|
|
113
398
|
| `attach-newspic-spec` | Attach or update newspic render intent |
|
|
@@ -136,34 +421,6 @@ src/
|
|
|
136
421
|
| `doctor` | Inspect resolved paths and provider health |
|
|
137
422
|
| `hermes-metrics` | Show Hermes execution metrics per task |
|
|
138
423
|
|
|
139
|
-
## 核心流程
|
|
140
|
-
|
|
141
|
-
```mermaid
|
|
142
|
-
flowchart TD
|
|
143
|
-
A["find-run / tasks / status"] --> B{"任务是否已存在?"}
|
|
144
|
-
B -- "否" --> C["init"]
|
|
145
|
-
B -- "是" --> D["读取当前 state"]
|
|
146
|
-
C --> D
|
|
147
|
-
D --> E{"缺什么?"}
|
|
148
|
-
E -- "缺正文" --> F["attach-body"]
|
|
149
|
-
E -- "缺正文图片" --> G["attach-body-images"]
|
|
150
|
-
E -- "缺 newspic 规格" --> H["attach-newspic-spec"]
|
|
151
|
-
E -- "缺 prepare 数据" --> I["prepare"]
|
|
152
|
-
E -- "缺 review 结论" --> J["review"]
|
|
153
|
-
E -- "缺 canonical 产物" --> K["prepare-finalize"]
|
|
154
|
-
E -- "缺渲染" --> L["render"]
|
|
155
|
-
E -- "缺发布" --> M["publish"]
|
|
156
|
-
F --> N["reconcile / status"]
|
|
157
|
-
G --> N
|
|
158
|
-
H --> N
|
|
159
|
-
I --> N
|
|
160
|
-
J --> N
|
|
161
|
-
K --> N
|
|
162
|
-
L --> N
|
|
163
|
-
M --> N
|
|
164
|
-
N --> E
|
|
165
|
-
```
|
|
166
|
-
|
|
167
424
|
## 常用命令
|
|
168
425
|
|
|
169
426
|
### 任务管理
|
|
@@ -224,87 +481,39 @@ bun run src/cli.ts attach-newspic-spec \
|
|
|
224
481
|
}
|
|
225
482
|
```
|
|
226
483
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
```json
|
|
230
|
-
{
|
|
231
|
-
"pagination_mode": "multi",
|
|
232
|
-
"min_pages": 3,
|
|
233
|
-
"max_pages": 0,
|
|
234
|
-
"require_image_every_page": true,
|
|
235
|
-
"default_image_layout": "editorial",
|
|
236
|
-
"target_fill_ratio": 0.8,
|
|
237
|
-
"page_specs": [
|
|
238
|
-
{
|
|
239
|
-
"page": 1,
|
|
240
|
-
"image_markers": ["插图1", "插图2"],
|
|
241
|
-
"image_layout": "staggered",
|
|
242
|
-
"target_fill_ratio": 0.85
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
"page": 2,
|
|
246
|
-
"image_markers": ["插图3"]
|
|
247
|
-
}
|
|
248
|
-
]
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
说明:
|
|
253
|
-
|
|
254
|
-
- `target_fill_ratio` 是可选字段,默认 `0.8`
|
|
255
|
-
- `page_specs[].target_fill_ratio` 优先级高于顶层
|
|
256
|
-
- imgx 会把它当作这一页文字和图片尽量占到内容区多少的近似目标
|
|
257
|
-
- 实际值会被规范化到 `0.35` 到 `0.95`
|
|
258
|
-
|
|
259
|
-
如果外部工具需要某段文字固定落在某一页,除了传 `page_specs`,还应该在正文里加页标记:
|
|
260
|
-
|
|
261
|
-
```text
|
|
262
|
-
【第一页】
|
|
263
|
-
第一页正文
|
|
484
|
+
### 推进工作流
|
|
264
485
|
|
|
265
|
-
|
|
266
|
-
|
|
486
|
+
```bash
|
|
487
|
+
bun run src/cli.ts prepare --state {workspace}/.zzhub-media/runs/{run_id}.json
|
|
488
|
+
bun run src/cli.ts review --state {workspace}/.zzhub-media/runs/{run_id}.json --status passed
|
|
489
|
+
bun run src/cli.ts prepare-finalize --state {workspace}/.zzhub-media/runs/{run_id}.json
|
|
490
|
+
bun run src/cli.ts render --state {workspace}/posts/{date-slug}/workflow-state.json
|
|
491
|
+
bun run src/cli.ts publish --state {workspace}/posts/{date-slug}/workflow-state.json
|
|
267
492
|
```
|
|
268
493
|
|
|
269
|
-
|
|
494
|
+
### 外部交接
|
|
270
495
|
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
...
|
|
274
|
-
【Page 2】
|
|
275
|
-
...
|
|
496
|
+
```bash
|
|
497
|
+
bun run src/cli.ts ingest-handoff --file handoff.json
|
|
276
498
|
```
|
|
277
499
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- 不加页标记,继续走自动流排,按正文 block 顺序自动分配到多页
|
|
281
|
-
- 加了页标记并且存在 `page_specs`,会切换到 spec 驱动分页,页标记变成硬边界
|
|
282
|
-
- 旧调用方可以继续工作,只有需要固定某段文字属于某一页时,才需要补正文页标记或新增 `target_fill_ratio`
|
|
283
|
-
|
|
284
|
-
`longform-3-4` 的几何约定:
|
|
285
|
-
|
|
286
|
-
- 长文分页测量优先在进程内跑 `pretext`,不再依赖 Chrome dump
|
|
287
|
-
- 内容区不再假定固定值,默认由页面尺寸、header、footer、padding 推导
|
|
288
|
-
- 调用方需要显式指定内容区或页面几何时,可以传这些参数:
|
|
289
|
-
- `--page-width` / `--page-height`
|
|
290
|
-
- `--body-padding-x` / `--body-padding-y`
|
|
291
|
-
- `--logo-size` / `--logo-gap`
|
|
292
|
-
- `--footer-height` / `--footer-margin-top`
|
|
293
|
-
- `--content-width` / `--content-height`
|
|
294
|
-
- `--content-bottom-gap`
|
|
295
|
-
|
|
296
|
-
如果没有显式传 `--content-width` 和 `--content-height`,imgx 会按当前页面几何自动推导内容区。
|
|
500
|
+
`handoff.json` 支持 `publish_handoff`(简化,固定新建)和 `workflow_handoff`(支持 new/resume 模式,可控制 research/authoring/review 策略)两种格式,详见上方"任务入口:init 与 ingest-handoff"。
|
|
297
501
|
|
|
298
|
-
###
|
|
502
|
+
### Reset 回退
|
|
299
503
|
|
|
300
504
|
```bash
|
|
301
|
-
bun run src/cli.ts
|
|
302
|
-
bun run src/cli.ts
|
|
303
|
-
bun run src/cli.ts
|
|
304
|
-
bun run src/cli.ts
|
|
305
|
-
bun run src/cli.ts
|
|
505
|
+
bun run src/cli.ts reset --state {state_path} --mode content # 完整重新准备(回到 prepare 起点)
|
|
506
|
+
bun run src/cli.ts reset --state {state_path} --mode redo.style # 仅重做风格润色
|
|
507
|
+
bun run src/cli.ts reset --state {state_path} --mode redo.format # 仅重做排版
|
|
508
|
+
bun run src/cli.ts reset --state {state_path} --mode redo.metadata # 仅重做元数据提取
|
|
509
|
+
bun run src/cli.ts reset --state {state_path} --mode redo.route # 仅重做路由解析
|
|
510
|
+
bun run src/cli.ts reset --state {state_path} --mode render # 重做渲染
|
|
511
|
+
bun run src/cli.ts reset --state {state_path} --mode publish # 重做发布
|
|
512
|
+
bun run src/cli.ts reset --state {state_path} --mode full # 完全放弃当前任务(mode=failed)
|
|
306
513
|
```
|
|
307
514
|
|
|
515
|
+
每种模式均会设置 `redo_hint`,确保 Agent 恢复时知道从哪个子步骤开始。
|
|
516
|
+
|
|
308
517
|
### Blog 同步
|
|
309
518
|
|
|
310
519
|
```bash
|
|
@@ -379,7 +588,7 @@ bun run src/cli.ts cos-upload --file /abs/path/image.png --folder notes/note-id
|
|
|
379
588
|
- `ZZHUB_PIPELINE_POSTS_DIR`,posts directory name,默认 `posts`
|
|
380
589
|
- `ZZHUB_PIPELINE_BLOG_ROOT`,blog repository root
|
|
381
590
|
|
|
382
|
-
|
|
591
|
+
兼容旧配置时,会自动读取旧版 CLI 的配置。
|
|
383
592
|
|
|
384
593
|
配置结构概览:
|
|
385
594
|
|
|
@@ -413,65 +622,28 @@ bun run src/cli.ts cos-upload --file /abs/path/image.png --folder notes/note-id
|
|
|
413
622
|
- TTY 场景输出带 ANSI 颜色的 pretty 结果
|
|
414
623
|
- `FORCE_COLOR=1` 可以在非 TTY 下强制 pretty 输出
|
|
415
624
|
- `NO_COLOR=1` 可以在 TTY 下强制原始 JSON
|
|
416
|
-
- `--view` 支持 `json`,`markdown`,`agent`
|
|
625
|
+
- `--view` 支持 `json`,`markdown`,`agent`,`agent-json`
|
|
417
626
|
- `--view agent` 更适合 orchestrator 直接读取下一步
|
|
418
627
|
|
|
419
628
|
## imgx 渲染子系统
|
|
420
629
|
|
|
421
630
|
- 基于 Chrome headless 和 `@napi-rs/canvas`
|
|
422
|
-
- 模板包括 `longform-3-4
|
|
423
|
-
- 主题包括 `paper-sage
|
|
631
|
+
- 模板包括 `longform-3-4`(newspic 长文)、`poster-3-4`(单页卡片)和 `wechat-cover-split`(文章封面)
|
|
632
|
+
- 主题包括 `paper-sage`(默认账号)和 `linen-news`(`ancientone` 账号)
|
|
424
633
|
- 几何参数会从 header,footer,padding 自动推导,也可以通过 CLI 旗标显式控制
|
|
425
634
|
- 分页在进程内通过 `@chenglou/pretext` 完成,不依赖 Chrome dump
|
|
426
635
|
- `render` 和 `wechat-export` 都需要 Chrome
|
|
427
636
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
- 基于 Milkdown 的 markdown 到微信 HTML 转换
|
|
431
|
-
- 支持多主题
|
|
432
|
-
- 构建命令:`bun run build:wechat-preview`
|
|
433
|
-
- `wechat-export` 会直接使用这套预览样式
|
|
434
|
-
|
|
435
|
-
## 插件系统
|
|
436
|
-
|
|
437
|
-
渲染通过 adapter 接口实现可插拔:
|
|
438
|
-
|
|
439
|
-
- `ImageRenderPlugin` — 替换 imgx 图片渲染
|
|
440
|
-
- `MarkdownRenderPlugin` — 替换 wechat-preview HTML 导出
|
|
441
|
-
|
|
442
|
-
接口定义在 `src/adapter-types.ts`,加载逻辑在 `src/adapter-loader.ts`。
|
|
443
|
-
|
|
444
|
-
内置适配器:
|
|
445
|
-
- `src/adapters/builtin-image-renderer.ts` — 包装 imgx
|
|
446
|
-
- `src/adapters/builtin-markdown-renderer.ts` — 包装 wechat-preview
|
|
447
|
-
|
|
448
|
-
每个适配器可实现 `doctor()` 方法,报告运行时依赖状态。
|
|
637
|
+
### newspic 分页规格
|
|
449
638
|
|
|
450
|
-
|
|
451
|
-
- `@napi-rs/canvas` — 图片渲染需要,懒加载,缺失时提示安装命令
|
|
452
|
-
- Chrome — HTML 导出需要,缺失时提示安装命令
|
|
453
|
-
- CJK 字体 — npm 模式下自动从 CDN 下载到 `~/.config/zzhub-pipeline/fonts/`
|
|
639
|
+
`newspic` 的渲染通过 `pagination_mode` 控制分页行为:
|
|
454
640
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
|
458
|
-
|
|
|
459
|
-
| wechat | `wechat-article` | 创建公众号文章草稿 |
|
|
460
|
-
| wechat | `wechat-newspic` | 发送图片消息 |
|
|
461
|
-
| cos | - | 腾讯云 COS 图片 CDN |
|
|
462
|
-
| blog | - | Markdown 同步到博客仓库 |
|
|
463
|
-
|
|
464
|
-
Markdown → WeChat HTML 转换通过插件系统完成(默认使用内置 `builtin-wechat-preview` 适配器)。
|
|
465
|
-
|
|
466
|
-
## newspic 规格
|
|
467
|
-
|
|
468
|
-
`newspic` 的渲染规格支持两种分页模式:
|
|
469
|
-
|
|
470
|
-
- `auto`,自动流排
|
|
471
|
-
- `single`,单页
|
|
472
|
-
- `multi`,多页
|
|
641
|
+
| 模式 | 行为 |
|
|
642
|
+
| --- | --- |
|
|
643
|
+
| `single` | 单页卡片(poster-3-4) |
|
|
644
|
+
| `multi` | 多页图集(longform-3-4) |
|
|
473
645
|
|
|
474
|
-
|
|
646
|
+
`newspic-spec.json` 常用字段:
|
|
475
647
|
|
|
476
648
|
```json
|
|
477
649
|
{
|
|
@@ -496,14 +668,11 @@ Markdown → WeChat HTML 转换通过插件系统完成(默认使用内置 `bu
|
|
|
496
668
|
}
|
|
497
669
|
```
|
|
498
670
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
- `target_fill_ratio` 默认是 `0.8`
|
|
671
|
+
- `target_fill_ratio` 是可选字段,默认 `0.8`,实际值会被规范化到 `0.35` 到 `0.95`
|
|
502
672
|
- `page_specs[].target_fill_ratio` 优先级高于顶层
|
|
503
|
-
-
|
|
504
|
-
- 实际值会被规范化到 `0.35` 到 `0.95`
|
|
673
|
+
- 长文分页测量优先在进程内跑 `pretext`,不再依赖 Chrome dump
|
|
505
674
|
|
|
506
|
-
|
|
675
|
+
**页标记**:如果外部工具需要把某段文字固定到某一页,除了传 `page_specs`,还应该在正文里加页标记:
|
|
507
676
|
|
|
508
677
|
```text
|
|
509
678
|
【第一页】
|
|
@@ -513,20 +682,9 @@ Markdown → WeChat HTML 转换通过插件系统完成(默认使用内置 `bu
|
|
|
513
682
|
第二页正文
|
|
514
683
|
```
|
|
515
684
|
|
|
516
|
-
|
|
685
|
+
也支持 `【Page 1】` / `【Page 2】` 等英文页标记。
|
|
517
686
|
|
|
518
|
-
|
|
519
|
-
【Page 1】
|
|
520
|
-
...
|
|
521
|
-
【Page 2】
|
|
522
|
-
...
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
兼容性说明:
|
|
526
|
-
|
|
527
|
-
- 不加页标记时,仍然走自动流排,按正文 block 顺序分配到多页
|
|
528
|
-
- 加了页标记并且存在 `page_specs` 时,会切换到 spec 驱动分页,页标记变成硬边界
|
|
529
|
-
- 旧调用方可以继续工作,只有需要固定页面归属时才需要补页标记或调整 `target_fill_ratio`
|
|
687
|
+
不加页标记时走自动流排;加了页标记并且存在 `page_specs` 时切换到 spec 驱动分页,页标记变成硬边界。
|
|
530
688
|
|
|
531
689
|
`longform-3-4` 的几何参数包括:
|
|
532
690
|
|
|
@@ -537,6 +695,44 @@ Markdown → WeChat HTML 转换通过插件系统完成(默认使用内置 `bu
|
|
|
537
695
|
- `--content-width` / `--content-height`
|
|
538
696
|
- `--content-bottom-gap`
|
|
539
697
|
|
|
698
|
+
## wechat-preview 子系统
|
|
699
|
+
|
|
700
|
+
- 基于 Milkdown 的 markdown 到微信 HTML 转换
|
|
701
|
+
- 支持多主题
|
|
702
|
+
- 构建命令:`bun run build:wechat-preview`
|
|
703
|
+
- `wechat-export` 会直接使用这套预览样式
|
|
704
|
+
|
|
705
|
+
## 插件系统
|
|
706
|
+
|
|
707
|
+
渲染通过 adapter 接口实现可插拔:
|
|
708
|
+
|
|
709
|
+
- `ImageRenderPlugin` — 替换 imgx 图片渲染
|
|
710
|
+
- `MarkdownRenderPlugin` — 替换 wechat-preview HTML 导出
|
|
711
|
+
|
|
712
|
+
接口定义在 `src/adapter-types.ts`,加载逻辑在 `src/adapter-loader.ts`。
|
|
713
|
+
|
|
714
|
+
内置适配器:
|
|
715
|
+
- `src/adapters/builtin-image-renderer.ts` — 包装 imgx
|
|
716
|
+
- `src/adapters/builtin-markdown-renderer.ts` — 包装 wechat-preview
|
|
717
|
+
|
|
718
|
+
每个适配器可实现 `doctor()` 方法,报告运行时依赖状态。
|
|
719
|
+
|
|
720
|
+
运行时依赖检查:
|
|
721
|
+
- `@napi-rs/canvas` — 图片渲染需要,懒加载,缺失时提示安装命令
|
|
722
|
+
- Chrome — HTML 导出需要,缺失时提示安装命令
|
|
723
|
+
- CJK 字体 — npm 模式下自动从 CDN 下载到 `~/.config/zzhub-pipeline/fonts/`
|
|
724
|
+
|
|
725
|
+
## 发布提供者
|
|
726
|
+
|
|
727
|
+
| 提供者 | 路由 | 说明 |
|
|
728
|
+
| --- | --- | --- |
|
|
729
|
+
| wechat | `wechat-article` | 创建公众号文章草稿 |
|
|
730
|
+
| wechat | `wechat-newspic` | 发送图片消息 |
|
|
731
|
+
| cos | - | 腾讯云 COS 图片 CDN |
|
|
732
|
+
| blog | - | Markdown 同步到博客仓库 |
|
|
733
|
+
|
|
734
|
+
Markdown → WeChat HTML 转换通过插件系统完成(默认使用内置 `builtin-wechat-preview` 适配器)。
|
|
735
|
+
|
|
540
736
|
## 测试与验证
|
|
541
737
|
|
|
542
738
|
```bash
|
package/package.json
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zzclub/pipeline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
|
-
"zzp": "./
|
|
7
|
-
"zzhub-pipeline": "./
|
|
6
|
+
"zzp": "./src/cli.ts",
|
|
7
|
+
"zzhub-pipeline": "./src/cli.ts"
|
|
8
8
|
},
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
|
-
"
|
|
14
|
-
"dist/assets/",
|
|
15
|
-
"dist/node_modules/"
|
|
13
|
+
"src/"
|
|
16
14
|
],
|
|
17
15
|
"scripts": {
|
|
18
16
|
"dev": "bun run src/cli.ts",
|
|
19
17
|
"build": "bun run scripts/build.ts",
|
|
20
18
|
"build:npm": "bun run scripts/build-npm.ts",
|
|
21
19
|
"build:wechat-preview": "vite build --config vite.wechat-preview.config.ts",
|
|
22
|
-
"prepublishOnly": "bun run scripts/build-npm.ts",
|
|
23
20
|
"test": "bun test",
|
|
24
21
|
"typecheck": "bun x tsc --noEmit",
|
|
25
22
|
"release:patch": "changelogen --release --patch --push",
|