@deployxai/dxc 0.1.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 +131 -0
- package/dist/chunks/chunk-I6VZLNRZ.js +2118 -0
- package/dist/chunks/chunk-XIHX5YAF.js +16391 -0
- package/dist/chunks/knowledge-Q6MHPG6I.js +1248 -0
- package/dist/chunks/monitor-VPRVRQIS.js +694 -0
- package/dist/index.js +32367 -0
- package/docs/00-project-context.md +125 -0
- package/docs/01-north-star-architecture.md +234 -0
- package/docs/02-mvp-technical-design.md +553 -0
- package/docs/03-domain-state-api.md +599 -0
- package/docs/04-security-and-operations.md +413 -0
- package/docs/05-delivery-plan.md +407 -0
- package/docs/README.md +44 -0
- package/docs/decisions/0001-initial-architecture.md +57 -0
- package/docs/decisions/0002-mongodb-environment-boundary.md +42 -0
- package/docs/decisions/0003-staged-production-topology.md +33 -0
- package/docs/decisions/0004-local-first-agent-research-runtime.md +71 -0
- package/docs/decisions/0005-official-skill-orchestration-and-local-content-memory.md +97 -0
- package/docs/decisions/0006-separate-wechat-user-login-from-account-authorization.md +87 -0
- package/docs/decisions/0007-explicit-personal-wechat-start.md +67 -0
- package/docs/decisions/0008-end-to-end-content-workflow-continuity.md +115 -0
- package/docs/decisions/0009-privileged-multitenant-draft-scheduling.md +36 -0
- package/docs/decisions/0009-versioned-cloud-template-catalog.md +39 -0
- package/docs/eight-stage-implementation-audit.md +62 -0
- package/docs/first-user-guide.md +187 -0
- package/docs/history/content-forge-prd-v0.2-summary.md +81 -0
- package/docs/local-development.md +511 -0
- package/docs/references/aliyun-oss-production-setup.md +89 -0
- package/docs/references/legacy-content-to-wechat-contract.md +223 -0
- package/docs/references/renderer-compatibility-report.md +68 -0
- package/docs/references/source-inventory.md +179 -0
- package/docs/references/wechat-renderer-platform-validation.md +92 -0
- package/docs/references/wechat-third-party-platform-setup.md +159 -0
- package/docs/references/wechat-website-login-setup.md +137 -0
- package/docs/references/wemd-template-attribution.md +25 -0
- package/docs/research-monitoring-design.md +235 -0
- package/docs/todo-preview-local-first.md +31 -0
- package/docs/workbuddy-first-user-runbook.md +246 -0
- package/docs//345/221/230/345/267/245BCDE/347/232/204skill/employee-b-research-analyst/SKILL.md +230 -0
- package/docs//345/221/230/345/267/245BCDE/347/232/204skill/employee-c-outline-architect/SKILL.md +194 -0
- package/docs//345/221/230/345/267/245BCDE/347/232/204skill/employee-d-content-writer/SKILL.md +296 -0
- package/docs//345/221/230/345/267/245BCDE/347/232/204skill/employee-e-visual-designer/SKILL.md +268 -0
- package/package.json +25 -0
- package/skills/dxc-article-outline/SKILL.md +82 -0
- package/skills/dxc-article-outline/agents/openai.yaml +6 -0
- package/skills/dxc-article-outline/references/outline-methods.md +38 -0
- package/skills/dxc-article-write/SKILL.md +85 -0
- package/skills/dxc-article-write/agents/openai.yaml +6 -0
- package/skills/dxc-article-write/references/writing-methods.md +42 -0
- package/skills/dxc-content-brief/SKILL.md +81 -0
- package/skills/dxc-content-brief/agents/openai.yaml +6 -0
- package/skills/dxc-content-brief/references/brief-method.md +34 -0
- package/skills/dxc-content-review/SKILL.md +84 -0
- package/skills/dxc-content-review/agents/openai.yaml +6 -0
- package/skills/dxc-content-review/references/review-checklist.md +35 -0
- package/skills/dxc-content-workflow/SKILL.md +190 -0
- package/skills/dxc-content-workflow/agents/openai.yaml +6 -0
- package/skills/dxc-content-workflow/references/catalog.json +136 -0
- package/skills/dxc-content-workflow/references/onboarding-questions.md +107 -0
- package/skills/dxc-content-workflow/references/stage-contract.md +70 -0
- package/skills/dxc-research/SKILL.md +110 -0
- package/skills/dxc-research/agents/openai.yaml +6 -0
- package/skills/dxc-research/references/research-method.md +53 -0
- package/skills/dxc-title-write/SKILL.md +112 -0
- package/skills/dxc-title-write/agents/openai.yaml +6 -0
- package/skills/dxc-title-write/references/title-methods.md +26 -0
- package/skills/dxc-visual-plan/SKILL.md +119 -0
- package/skills/dxc-visual-plan/agents/openai.yaml +6 -0
- package/skills/dxc-visual-plan/references/visual-methods.md +35 -0
- package/skills/dxc-wechat-publisher/SKILL.md +157 -0
- package/skills/dxc-wechat-publisher/agents/openai.yaml +6 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
# MVP 技术方案
|
|
2
|
+
|
|
3
|
+
更新日期:2026-07-30
|
|
4
|
+
状态:架构基线;九个官方 Skill、跨会话自动续跑、本地内容记忆、身份、公众号绑定、
|
|
5
|
+
通用云端预览和草稿回读闭环已实现;生产版本以发布后的健康检查和真实首位用户验收为
|
|
6
|
+
准。显式本地正文图片、素材目录采集和 Agent 宿主视觉生产已经进入工作流;CLI 不内置
|
|
7
|
+
生成服务或占位封面。权益和支付未实现。
|
|
8
|
+
|
|
9
|
+
## 1. 目标与非目标
|
|
10
|
+
|
|
11
|
+
### 1.1 唯一纵向闭环
|
|
12
|
+
|
|
13
|
+
1. 用户在 WorkBuddy 安装 `dxc-content-workflow` 和 `dxc` CLI;自然语言隐式触发总控后,
|
|
14
|
+
总控从本地项目 contract 恢复并自动调用下游 Skill,最后交付步骤调用
|
|
15
|
+
`dxc-wechat-publisher`。
|
|
16
|
+
2. CLI 完成环境检查,并创建短时设备绑定会话。
|
|
17
|
+
3. 用户通过微信第三方平台官方页面扫码授权公众号。
|
|
18
|
+
4. 用户在 Agent 中给出主题、参考材料或已有 Markdown;初始化同时展示本地知识库
|
|
19
|
+
状态并允许显式导入历史文章,总控自动推进研究、Brief、大纲、正文、标题、视觉生产
|
|
20
|
+
和审校。
|
|
21
|
+
5. Agent 生成或选择实际内容封面和必要正文图片,CLI 只收集、校验和上传用户明确指定
|
|
22
|
+
的本地文件。
|
|
23
|
+
6. 云端按已发布模板目录进行权威渲染、清洗和预检。
|
|
24
|
+
7. Agent 在宿主右侧内置浏览器打开云端手机样式预览。
|
|
25
|
+
8. 用户回到 Agent 明确确认目标公众号和当前快照。
|
|
26
|
+
9. 云端检查订阅/额度;无权益时通过 WorkBuddy SkillPay 完成购买。
|
|
27
|
+
10. Worker 上传正文图片和封面,创建微信草稿并回读核验。
|
|
28
|
+
11. Agent 展示结果,CLI 保存本地结果,云端保留审计和可重试错误。
|
|
29
|
+
|
|
30
|
+
### 1.2 明确不做
|
|
31
|
+
|
|
32
|
+
- 自动扫描用户目录、云端研究抓取或 DxC 内置的第三方图片生成服务;
|
|
33
|
+
- 通用 Agent 框架;
|
|
34
|
+
- 知乎、头条、小红书等平台;
|
|
35
|
+
- 正式发布、群发、粉丝和消息能力;
|
|
36
|
+
- 模板商城、用户自定义 CSS 或模板管理界面;
|
|
37
|
+
- 完整 Web 内容编辑器或管理后台;
|
|
38
|
+
- 自动续费;
|
|
39
|
+
- MCP;
|
|
40
|
+
- Redis 或独立队列服务;
|
|
41
|
+
- 扫描 PDF 的云端 OCR。
|
|
42
|
+
|
|
43
|
+
## 2. 技术选型
|
|
44
|
+
|
|
45
|
+
| 层 | MVP 选型 | 原因 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| Monorepo | pnpm workspace | 单仓库共享契约,单人迭代成本低 |
|
|
48
|
+
| 语言 | TypeScript strict | CLI、API、Worker 和共享契约统一 |
|
|
49
|
+
| Node | Node.js 24 LTS | 当前 LTS,覆盖 macOS/Windows/服务器 |
|
|
50
|
+
| CLI | Commander + 原生 `fetch` | 依赖少、跨平台、易输出 JSON |
|
|
51
|
+
| 本地内容记忆 | `node:sqlite` + FTS5 + Tokenizers.js + ONNX Runtime Web/WASM | 用户文章留在本机,兼顾字面和中文语义召回;避免安装多平台原生推理包 |
|
|
52
|
+
| API | Fastify | 启动轻、Schema/插件边界清晰 |
|
|
53
|
+
| 运行时校验 | Zod | CLI/API/配置/事件统一校验 |
|
|
54
|
+
| Mongo | 官方 MongoDB Node Driver | 避免 ODM 隐式行为,显式索引和状态更新 |
|
|
55
|
+
| Markdown | `marked` + 自定义 token renderer | 与旧工具接近,便于建立行为兼容 |
|
|
56
|
+
| HTML 清洗 | `sanitize-html` + 二次约束检查 | 使用明确 allowlist,避免字符串替换成为安全边界 |
|
|
57
|
+
| 测试 | Vitest | TypeScript 单元、契约和黄金样例统一 |
|
|
58
|
+
| 日志 | Pino JSON + 脱敏配置 | 结构化、Fastify 原生友好 |
|
|
59
|
+
| 对象存储 | S3-compatible adapter | 供应商可替换,MVP 不把 URL 写死 |
|
|
60
|
+
|
|
61
|
+
首版不引入前端框架。预览页由 API 服务输出一个最小 HTML 外壳,将经过清洗的文章 HTML 放入固定手机阅读容器。出现真实管理后台需求后,再单独选择前端方案。
|
|
62
|
+
|
|
63
|
+
## 3. 仓库和组件
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
apps/
|
|
67
|
+
cli/
|
|
68
|
+
src/
|
|
69
|
+
server/
|
|
70
|
+
src/
|
|
71
|
+
worker/
|
|
72
|
+
src/
|
|
73
|
+
skills/
|
|
74
|
+
dxc-content-workflow/
|
|
75
|
+
SKILL.md
|
|
76
|
+
agents/
|
|
77
|
+
references/
|
|
78
|
+
dxc-title-write/
|
|
79
|
+
dxc-wechat-publisher/
|
|
80
|
+
packages/
|
|
81
|
+
contracts/
|
|
82
|
+
config/
|
|
83
|
+
security/
|
|
84
|
+
renderer-wechat/
|
|
85
|
+
workflow-state/
|
|
86
|
+
wechat-client/
|
|
87
|
+
billing/
|
|
88
|
+
object-storage/
|
|
89
|
+
fixtures/
|
|
90
|
+
renderer/
|
|
91
|
+
wechat/
|
|
92
|
+
docs/
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3.1 CLI
|
|
96
|
+
|
|
97
|
+
CLI 不持有微信第三方平台密钥或 authorizer token。
|
|
98
|
+
|
|
99
|
+
职责:
|
|
100
|
+
|
|
101
|
+
- `doctor`:检查 Node、网络、版本、可写目录和可选工具;
|
|
102
|
+
- `profile`:校验并保存版本化本地创作偏好;
|
|
103
|
+
- `project`:初始化固定八步内容项目,以轻量本地索引跨会话定位项目,并通过包含
|
|
104
|
+
`inputs`、`outputs`、`metadata`、`waitingFor` 和结构化确认绑定的 v3 checkpoint
|
|
105
|
+
恢复进度;
|
|
106
|
+
- `knowledge`:显式导入历史文章并执行本地关键词/语义混合召回;
|
|
107
|
+
- `setup`:复用已有状态,顺序完成个人微信登录/明确注册和公众号绑定;
|
|
108
|
+
- `auth`:显式首次注册、关联个人微信、登录、续期和管理 DxC 设备会话;
|
|
109
|
+
- `wechat connect/accounts`:发起授权和查询账号;
|
|
110
|
+
- `wechat draft preview/preview-refresh/create/status`:本地预检和显式素材采集;
|
|
111
|
+
上传正文、正文图片和封面;为 Agent 内置浏览器提供云端预览;提交不可变确认并查询
|
|
112
|
+
用户可理解的处理进展;
|
|
113
|
+
- `skills install`:把九个受控官方 Skill 安装到 WorkBuddy、Codex 或显式目录;
|
|
114
|
+
- 将机器结果写到 stdout JSON,将人类提示写到 stderr。
|
|
115
|
+
|
|
116
|
+
本地只进行廉价且稳定的检查,例如阶段 frontmatter、固定 Skill 版本、输入哈希、图片
|
|
117
|
+
真实性与体积、文章包和显而易见的摘要长度。最终微信兼容判定必须来自云端权威渲染。
|
|
118
|
+
|
|
119
|
+
本地知识库是例外的纯本机能力:SQLite 保存文章分段、哈希和向量,固定 revision 的小型中文嵌入模型计算语义相似度,RRF 合并关键词与语义排名。它不决定发布合规,也不把历史全文上传云端。总控和各步骤边界见 [ADR-0005](decisions/0005-official-skill-orchestration-and-local-content-memory.md)。
|
|
120
|
+
|
|
121
|
+
`dxc-content-workflow` 是唯一允许隐式调用的官方 Skill。每次调用先解析当前目录或项目
|
|
122
|
+
标题,再以实际产物和 checkpoint 为真值循环推进。普通内部阶段完成后自动进入下一步;
|
|
123
|
+
只有会改变路线的用户输入、最终内容选择、公众号选择、不可变快照确认或不可安全恢复
|
|
124
|
+
错误才暂停。CLI 只提供确定性的状态、哈希和项目定位,不引入后台工作流引擎。
|
|
125
|
+
|
|
126
|
+
### 3.2 Server
|
|
127
|
+
|
|
128
|
+
一个 Fastify 服务承载:
|
|
129
|
+
|
|
130
|
+
- 健康和配置就绪检查;
|
|
131
|
+
- 设备会话和短时绑定;
|
|
132
|
+
- 独立的网站应用微信登录、个人身份关联和新设备恢复;
|
|
133
|
+
- 微信第三方平台授权发起、回调和授权事件;
|
|
134
|
+
- 文章上传会话、权威渲染和预检;
|
|
135
|
+
- 短时预览;
|
|
136
|
+
- 目标账号、确认和草稿意图;
|
|
137
|
+
- 权益、购买意图、SkillPay X402 和微信支付通知;
|
|
138
|
+
- 查询任务和结果;
|
|
139
|
+
- 管理性接口只在内部网络或强认证下开放。
|
|
140
|
+
|
|
141
|
+
MVP 可以使用同一个 OCI 镜像,通过不同入口启动 Server 和 Worker。
|
|
142
|
+
|
|
143
|
+
### 3.3 Worker
|
|
144
|
+
|
|
145
|
+
Worker 领取 Mongo 中的租约任务:
|
|
146
|
+
|
|
147
|
+
- 刷新/获取 authorizer access token;
|
|
148
|
+
- 上传正文图片;
|
|
149
|
+
- 上传或复用永久封面素材;
|
|
150
|
+
- 替换正文图片 URL;
|
|
151
|
+
- 执行最终 HTML 约束检查;
|
|
152
|
+
- 调用 `draft/add`;
|
|
153
|
+
- 调用 `draft/get` 回读;
|
|
154
|
+
- 对可安全重试的错误退避;
|
|
155
|
+
- 写入审计和最终结果。
|
|
156
|
+
|
|
157
|
+
Worker 是唯一允许执行微信草稿副作用的组件。
|
|
158
|
+
|
|
159
|
+
当前 Worker 保留 2026-07-26 固定测试意图兼容路径,并优先领取通用文章意图。通用路径
|
|
160
|
+
从对象存储读取不可变 HTML、正文图片和封面,复核清单哈希、租户、公众号状态及 ID 11,
|
|
161
|
+
上传或复用封面、逐张上传正文图片并替换逻辑 URL,只调用一次 `draft/add`,随后
|
|
162
|
+
`draft/get` 回读。它仍由配置锁定一个首位用户租户,尚未成为带 heartbeat(心跳)的
|
|
163
|
+
通用多租户 Job 租约系统。
|
|
164
|
+
|
|
165
|
+
## 4. 身份、租户与公众号绑定
|
|
166
|
+
|
|
167
|
+
### 4.1 MVP 身份
|
|
168
|
+
|
|
169
|
+
MVP 不建设密码系统,但把用户登录和公众号授权明确拆开:
|
|
170
|
+
|
|
171
|
+
1. 推荐首次运行 `dxc setup`:CLI 先完成设备持钥证明,再用个人微信 `start` 登录已有
|
|
172
|
+
owner,或在文案明确的首次流程中创建新 owner/tenant;
|
|
173
|
+
2. 随后第三方平台管理员扫码把公众号绑定到当前租户;公众号授权不能创建或接管另一
|
|
174
|
+
个已有 owner;
|
|
175
|
+
3. 新设备可以执行 `dxc auth start` 恢复已关联身份;严格的 `dxc auth login` 仍只接受
|
|
176
|
+
已关联身份,未关联时固定失败;
|
|
177
|
+
4. 旧公众号 bootstrap 创建的 owner 仍可执行 `dxc auth link-wechat` 完成迁移;
|
|
178
|
+
5. 同一未关联个人微信的并发 `start` 使用确定性候选 owner 和 Mongo 原子 bootstrap
|
|
179
|
+
收敛,不留下第二个随机租户;
|
|
180
|
+
6. 个人登录只使用 `snsapi_login`,不保存微信 OAuth access/refresh token,只保存按
|
|
181
|
+
Website AppID 作用域派生的 openid/unionid 摘要;
|
|
182
|
+
7. 设备访问 Token 有效 24 小时,30 天设备刷新凭据配合 Ed25519 签名完成单次轮换和
|
|
183
|
+
同一 `rotationId` 重放恢复。
|
|
184
|
+
|
|
185
|
+
固定网站登录入口为 `https://content.deployxai.com/auth/wechat/login`,回调为
|
|
186
|
+
`https://content.deployxai.com/callbacks/wechat/login`。它们不复用第三方平台事件/
|
|
187
|
+
消息回调或 Component AppID/AppSecret。详细决策见
|
|
188
|
+
[ADR-0006](decisions/0006-separate-wechat-user-login-from-account-authorization.md) 和
|
|
189
|
+
[ADR-0007](decisions/0007-explicit-personal-wechat-start.md)。
|
|
190
|
+
|
|
191
|
+
设备长期秘密优先保存在系统凭据存储中。当前 CLI 退化为用户目录中仅当前用户可读的 `0600` 文件,并明确提示风险;迁移到系统钥匙串不改变协议。团队、邮箱/手机/OIDC 和设备管理界面后续按真实需求增加。
|
|
192
|
+
|
|
193
|
+
### 4.2 租户
|
|
194
|
+
|
|
195
|
+
- 一个租户代表一个 DxC 内容空间,而不是固定等于一个公众号。
|
|
196
|
+
- 一个租户可以授权多个公众号。
|
|
197
|
+
- 一个公众号授权在同一时间只能归属一个有效租户绑定。
|
|
198
|
+
- 所有业务集合都带 `tenantId`。
|
|
199
|
+
- 设备、用户、公众号和支付身份通过显式关联表连接,不依赖 Agent 对话文本。
|
|
200
|
+
|
|
201
|
+
## 5. 微信第三方平台链路
|
|
202
|
+
|
|
203
|
+
### 5.1 权限
|
|
204
|
+
|
|
205
|
+
MVP 只依赖:
|
|
206
|
+
|
|
207
|
+
- ID 3:公众号账号信息服务;
|
|
208
|
+
- ID 11:素材管理和草稿所需能力。
|
|
209
|
+
|
|
210
|
+
ID 7 不进入授权请求和代码路径。
|
|
211
|
+
|
|
212
|
+
### 5.2 票据和令牌
|
|
213
|
+
|
|
214
|
+
```text
|
|
215
|
+
component_verify_ticket
|
|
216
|
+
→ component_access_token
|
|
217
|
+
→ pre_auth_code
|
|
218
|
+
→ authorization_code
|
|
219
|
+
→ authorizer_refresh_token
|
|
220
|
+
→ authorizer_access_token
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
实现规则:
|
|
224
|
+
|
|
225
|
+
- 授权事件回调先验证 `msg_signature`、时间戳、nonce、解密结果 AppID 和重放窗口;
|
|
226
|
+
- 保存最近有效的 component ticket,不记录明文日志;
|
|
227
|
+
- component access token 和 authorizer access token 使用“提前刷新 + 单飞锁”;
|
|
228
|
+
- authorizer refresh token 加密持久化;
|
|
229
|
+
- 收到取消授权事件后立即标记 revoked,停止新任务并撤销本地会话访问;
|
|
230
|
+
- 已进入微信副作用阶段的任务根据状态安全终止或核验,不能继续使用已撤销授权;
|
|
231
|
+
- 微信调用通过授权账号自己的 authorizer token 完成,不使用客户 AppSecret。
|
|
232
|
+
|
|
233
|
+
### 5.3 固定出口
|
|
234
|
+
|
|
235
|
+
所有微信素材和草稿 API 从受控 Worker 出口发起。2026-07-26 已确认当前阶段 Server 位于 `alivps`,Worker 位于 `worker_vps`;Worker 通过只允许 `api.weixin.qq.com:443` 的私网 CONNECT 代理复用 `alivps` 固定出口。代理不缓存、不解密 TLS,且不把微信副作用转交给 Server。
|
|
236
|
+
|
|
237
|
+
后续可以新增应用 ECS 并将 PM2/Node 从 `alivps` 迁走,但这是独立迁移阶段。当前生产 ECS 禁止依赖安装、TypeScript 编译、测试或镜像构建,只运行本地或 CI 已构建且完成哈希校验的产物。详细决策见 [ADR 0003](decisions/0003-staged-production-topology.md)。
|
|
238
|
+
|
|
239
|
+
### 5.4 微信 API 加密与签名
|
|
240
|
+
|
|
241
|
+
第三方平台开启“全部 API 加密及签名校验”时,Worker 必须遵循微信 API 安全协议:
|
|
242
|
+
|
|
243
|
+
- 第三方平台管理接口 `api_component_token`、`api_create_preauthcode`、`api_query_auth`、`api_get_authorizer_info` 和 `api_authorizer_token` 按各自官方契约发送普通 HTTPS JSON,不添加 `Wechatmp-*` 请求头,也不要求响应提供安全协议签名头;
|
|
244
|
+
- 支持 API 安全协议的公众号 JSON 接口先加入微信要求的安全字段,用 AES-256-GCM 加密正文,再用应用 RSA 私钥按 RSA-SHA256-PSS 签名;安全字段、AAD 和签名身份统一使用第三方平台 Component AppID,即使 query 中携带的是 authorizer access token;
|
|
245
|
+
- RSA-PSS 使用 SHA-256 和 32 字节 salt(盐值);JSON API 的签名覆盖不含 query 的完整 URL、Component AppID、时间戳和最终请求原文;
|
|
246
|
+
- RSA 请求只发送 `Wechatmp-AppId`、`Wechatmp-TimeStamp` 和 `Wechatmp-Signature` 三个安全请求头,不发送 `Wechatmp-Serial`;
|
|
247
|
+
- 收到响应后先按平台证书序列号选择证书并验证签名,验证成功后才解密和信任响应正文;
|
|
248
|
+
- 资源上传使用普通 HTTPS `multipart/form-data`,不添加 `Wechatmp-*` 请求头。微信明确
|
|
249
|
+
说明资源上传类 API 不支持正文加密;真实平台已验证永久封面
|
|
250
|
+
`material/add_material`,正文图片 `media/uploadimg` 则已按 2026-07-30 官方契约完成
|
|
251
|
+
假服务测试、尚待真实公众号验证。该例外只适用于这两个明确列出的资源上传端点;
|
|
252
|
+
- 任何私钥、对称密钥、序列号和证书路径只从安全环境配置读取,不进入数据库、日志或 Git。
|
|
253
|
+
|
|
254
|
+
2026-07-26 在已开启“全部 API 加密及签名校验”的真实第三方平台上核验:`api_component_token` 对加密信封返回普通 JSON 且不带 `Wechatmp-*` 响应头,证明不能把 API 安全协议强制套到未声明支持的管理接口。另以同一 authorizer access token 探测 `batchget_material`:使用 authorizer AppID 签名时收到无安全响应头的明文 `40237`,改用 Component AppID 后收到可验签、可解密的成功响应。对 `material/add_material` 使用必定无效的空文件、零填充文件和截断 PNG 探测时,完整含零字节正文签名返回 `40234`,首个零字节之前的 multipart 前缀签名通过安全层后返回 `40097`;同一截断 PNG 完全不带 `Wechatmp-*` 时返回更具体的 `40113` 文件类型错误,证明普通 multipart 能被业务端正确解析。两次真实有效 PNG 的签名上传也都返回 `40097`,随后 `batchget_material` 的素材总数仍为 165、同名新增为 0。因此代码按端点显式选择传输协议:所有安全模式请求统一使用 Component AppID,不能按 access token 类型改变签名身份;`material/add_material` 则使用普通 HTTPS multipart。应用非对称密钥编号保留为本地受控配置,不进入 RSA 请求头;响应 `Wechatmp-Serial` 仍用于选择微信平台证书。对于安全模式端点完全缺少安全响应头的明文 `40230`–`40240`,客户端只保留错误码并将其视为确定性未执行;其他无签名整数错误码只作为不可验证诊断后缀保留,不驱动状态或重试。无签名成功或非 JSON 响应仍不可验证。API 安全材料仍不同于第三方平台事件回调的 Token/EncodingAESKey;两条协议不能混用。官方来源:
|
|
255
|
+
|
|
256
|
+
- [API 签名加密指南](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/getting_started/api_signature.html)
|
|
257
|
+
- [新增永久素材](https://developers.weixin.qq.com/doc/service/api/material/permanent/api_addmaterial)
|
|
258
|
+
- [上传图文消息内图片](https://developers.weixin.qq.com/doc/service/api/material/permanent/api_uploadimg)
|
|
259
|
+
- [新增草稿](https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_add)
|
|
260
|
+
- [获取草稿](https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_getdraft)
|
|
261
|
+
|
|
262
|
+
## 6. 渲染、预检和预览
|
|
263
|
+
|
|
264
|
+
### 6.1 固定云端模板目录
|
|
265
|
+
|
|
266
|
+
云端目录当前提供既有 `wechat-minimal@1`,以及依据 WeMD(MIT)主题设计转写的十套
|
|
267
|
+
受控模板:`wechat-academic-paper@1`、`wechat-aurora-glass@1`、`wechat-bauhaus@1`、
|
|
268
|
+
`wechat-cyberpunk-neon@1`、`wechat-knowledge-base@1`、`wechat-luxury-gold@1`、
|
|
269
|
+
`wechat-morandi-forest@1`、`wechat-neo-brutalism@1`、`wechat-receipt@1`、
|
|
270
|
+
`wechat-sunset-film@1`。CLI 不展示或提交模板选项。Agent 可根据文章类型在 frontmatter
|
|
271
|
+
写入 `dxc_wechat_template_hint`,作为首次预览的建议;最终选择只在短时预览页完成,服务端
|
|
272
|
+
校验目录并生成新的不可变快照。
|
|
273
|
+
|
|
274
|
+
模板是版本化样式令牌,不是用户可提交的 CSS。`wechat-minimal@1` 继续保留,以保证既有
|
|
275
|
+
快照的 HTML 与哈希可复现;新增模板只能增加新 ID,不能改写已发布版本。
|
|
276
|
+
|
|
277
|
+
兼容目标来自旧 `content-to-wechat`:
|
|
278
|
+
|
|
279
|
+
- 所有关键样式内联;
|
|
280
|
+
- 不输出 `<style>`、`class`、`id`、脚本或 iframe;
|
|
281
|
+
- 表格转为段落/行式结构;
|
|
282
|
+
- `<hr>` 转为带顶部边框的段落;
|
|
283
|
+
- Markdown 外部链接去重并改成文末纯文本 URL;
|
|
284
|
+
- 不输出 `<a>` 或 `href`;
|
|
285
|
+
- 处理标题、段落、引用、列表、代码块和图片;
|
|
286
|
+
- 图片先保持逻辑引用,交付阶段替换为微信 URL;
|
|
287
|
+
- 检查渲染后 HTML 长度。
|
|
288
|
+
|
|
289
|
+
旧实现的字符串替换逻辑只作为行为样例。新实现必须:
|
|
290
|
+
|
|
291
|
+
1. 解析 Markdown token;
|
|
292
|
+
2. 用模板 renderer 生成有限 HTML;
|
|
293
|
+
3. 用 allowlist 清洗;
|
|
294
|
+
4. 再做禁止标签/属性和长度扫描;
|
|
295
|
+
5. 生成结构化 warnings/errors。
|
|
296
|
+
|
|
297
|
+
### 6.2 不可变快照
|
|
298
|
+
|
|
299
|
+
每个 `RenderSnapshot` 固定:
|
|
300
|
+
|
|
301
|
+
- `sourceHash`
|
|
302
|
+
- `assetManifestHash`
|
|
303
|
+
- `templateId`
|
|
304
|
+
- `rendererVersion`
|
|
305
|
+
- `htmlHash`
|
|
306
|
+
- `preflightResult`
|
|
307
|
+
|
|
308
|
+
预览 URL 指向快照,不指向可变文章。用户在预览页切换模板时,服务端为同一原始快照记录
|
|
309
|
+
最终选择并生成新的不可变快照;随后 CLI 仍以原始快照发起确认,服务端会解析为该最终快照。
|
|
310
|
+
用户确认后生成绑定 `snapshotId + targetAccountId` 的 `Approval`。任何输入或模板选择变化都会使
|
|
311
|
+
之前的确认不再适用。
|
|
312
|
+
|
|
313
|
+
### 6.3 预览
|
|
314
|
+
|
|
315
|
+
- 模仿微信公众号手机阅读宽度和基础字体;
|
|
316
|
+
- 明确标注“高保真模拟,不保证与微信客户端逐像素一致”;
|
|
317
|
+
- 短时签名 URL,默认 30 分钟;
|
|
318
|
+
- 创建响应同时返回 `createdAt`、`expiresAt` 和 `expiresInSeconds`,CLI 明确展示剩余
|
|
319
|
+
时间;过期后重新生成链接时保留同一原始快照已记录的最终模板选择;
|
|
320
|
+
- `Cache-Control: private, no-store`;
|
|
321
|
+
- `X-Robots-Tag: noindex, nofollow`;
|
|
322
|
+
- 严格 CSP,不加载第三方脚本;
|
|
323
|
+
- 预览时可以展示权益状态,但草稿确认接口必须再次权威检查。
|
|
324
|
+
|
|
325
|
+
### 6.4 已保留的固定真实验证切片
|
|
326
|
+
|
|
327
|
+
在完整 P5/P6 前,本轮只验证一篇固定测试文章能否进入一个明确选择的已授权公众号草稿箱:
|
|
328
|
+
|
|
329
|
+
1. Server 根据固定文章、标题、作者、摘要、评论开关、固定封面文件字节、模板和渲染器版本生成不可变 `snapshotHash`;
|
|
330
|
+
2. CLI 展示目标公众号、固定文章预览元数据和快照哈希;
|
|
331
|
+
3. 用户明确确认后,Server 以 `tenantId + idempotencyKeyHash` 创建一次草稿意图;
|
|
332
|
+
4. 受控 Worker 通过特权调度仓储跨租户原子领取意图;任务必须携带 `tenantId`,领取后
|
|
333
|
+
再次按该租户核对目标账号、ACTIVE 授权、ID 11 权限和快照;
|
|
334
|
+
5. Worker 刷新或读取 authorizer access token;仅当同一租户、同一账号已有字节完全一致(`coverHash` 相同)且确认上传成功的固定联调封面时,复用其永久 MediaID,否则上传一次永久封面;随后只调用一次 `draft/add`;
|
|
335
|
+
6. 返回 media ID 后调用 `draft/get`,回读一致才进入 `SUCCEEDED`。
|
|
336
|
+
|
|
337
|
+
该旧切片仍不接受任意文章上传,作为真实 API 安全和回归夹具保留;正式首位用户入口不再
|
|
338
|
+
依赖它。
|
|
339
|
+
|
|
340
|
+
### 6.5 首位用户通用文章闭环
|
|
341
|
+
|
|
342
|
+
当前通用路径已经实现:
|
|
343
|
+
|
|
344
|
+
1. Agent 视觉阶段实际生成或选择内容封面和正文图片并保存到显式项目素材目录;CLI
|
|
345
|
+
只读取用户明确指定的 Markdown、PNG/JPEG 文件,先做大小、metadata(元数据)与
|
|
346
|
+
本地预检;没有真实封面时停止并给出回到视觉阶段的处理建议;
|
|
347
|
+
2. Server 发放同源、短时、哈希绑定的上传 URL,正文、正文图片和封面进入对象存储;
|
|
348
|
+
3. Server 解析正文,按已选固定模板权威渲染并把不可变 HTML、素材清单哈希写入
|
|
349
|
+
对象存储和快照;
|
|
350
|
+
4. `/previews/{token}` 以严格 CSP、no-store、noindex 返回同一快照、封面和 token
|
|
351
|
+
绑定的正文图片;
|
|
352
|
+
5. Agent 把短时预览 URL 直接交给宿主右侧内置浏览器,不写入对话或项目产物;
|
|
353
|
+
6. `Approval` 精确绑定 `tenantId + userId + snapshotId + snapshotHash + accountId`;
|
|
354
|
+
7. 相同 `Idempotency-Key` 和请求返回同一草稿意图,不同请求固定冲突;
|
|
355
|
+
8. Worker 从同一对象存储读取正文图片、封面和 HTML,复核哈希,调用正文图片上传接口
|
|
356
|
+
获得微信 HTTPS URL 并替换逻辑路径,再创建草稿并回读标题、作者、摘要和正文;
|
|
357
|
+
9. 素材或草稿结果不确定时进入 `ASSET_UPLOAD_UNVERIFIED` 或
|
|
358
|
+
`CREATED_UNVERIFIED`,不盲目重试。
|
|
359
|
+
|
|
360
|
+
当前限制是正文图片只接受显式本地 PNG/JPEG,且不自动裁切或压缩;权益/SkillPay 尚未
|
|
361
|
+
成为草稿意图闸门;Worker 仍按配置锁定首位用户 `tenantId`。因此首位用户闭环可用,
|
|
362
|
+
但完整多租户 P3、正文图片真实平台验证和商业 P7 仍未关闭。
|
|
363
|
+
|
|
364
|
+
## 7. 图片与封面
|
|
365
|
+
|
|
366
|
+
- CLI 根据 Markdown 和用户显式指定的非递归素材目录收集本地图片;不扫描其他目录,
|
|
367
|
+
文件读取拒绝符号链接,并对数量和体积设上限。
|
|
368
|
+
- 正文图片只接受 PNG/JPEG,单张小于 1 MiB、单篇最多 20 张;封面不超过 5 MiB。
|
|
369
|
+
- 上传前计算 SHA-256,内容寻址并去重。
|
|
370
|
+
- 对象存储保存原始文件;当前不自动生成裁切或压缩变体。
|
|
371
|
+
- 云端检查格式、像素、体积和宽高比。
|
|
372
|
+
- 封面目标比例接近 2.35:1,并提供中心 1:1 安全区提示。
|
|
373
|
+
- 视觉阶段必须生成或选择内容相关封面并保存到项目素材目录。CLI 的 `--cover auto`
|
|
374
|
+
只解析文章声明或 `cover.*`,不生成占位图。Agent 生成图片前仍须披露执行位置、数据
|
|
375
|
+
去向和可能的额度消耗。
|
|
376
|
+
- 正文图片和封面上传微信是不同端点和不同素材语义,不能混用 media ID。
|
|
377
|
+
- 对象存储 URL 不直接进入最终微信 HTML;必须替换为微信返回的正文图片 URL。
|
|
378
|
+
|
|
379
|
+
## 8. MongoDB 与异步任务
|
|
380
|
+
|
|
381
|
+
### 8.1 为什么 MVP 不需要独立队列
|
|
382
|
+
|
|
383
|
+
草稿创建是低吞吐、长延迟、需要强幂等的任务。Mongo 已经是业务状态真值,可以用一个 `jobs` 集合实现:
|
|
384
|
+
|
|
385
|
+
- `findOneAndUpdate` 原子领取;
|
|
386
|
+
- `leaseOwner` + `leaseExpiresAt`;
|
|
387
|
+
- `attempt` + `nextRunAt`;
|
|
388
|
+
- Worker 心跳延长租约;
|
|
389
|
+
- Worker 异常退出后租约到期可重新领取;
|
|
390
|
+
- 终态任务不再领取。
|
|
391
|
+
|
|
392
|
+
MVP 不依赖 change stream,也不要求 Redis。
|
|
393
|
+
|
|
394
|
+
### 8.2 已确认的环境边界
|
|
395
|
+
|
|
396
|
+
环境布局已经确认:
|
|
397
|
+
|
|
398
|
+
- 本地开发连接本机 Docker MongoDB,使用独立 `dxc` 数据库;
|
|
399
|
+
- 自动化测试使用隔离测试数据库,不连接生产;
|
|
400
|
+
- 生产使用 `worker_vps` 上的现有 MongoDB,使用独立 `dxc` 数据库;
|
|
401
|
+
- 应用仅通过 `DXC_MONGODB_URI` 和 `DXC_MONGODB_DATABASE` 读取连接配置,默认数据库名为 `dxc`;
|
|
402
|
+
- 数据库口令和完整生产 URI 只进入安全环境配置,不写入 CLI 参数、代码、Git 或日志;
|
|
403
|
+
- `ssh worker_vps` 是运维入口,不是应用运行时数据库协议;应用到 MongoDB 的实际网络链路须在部署前确定。
|
|
404
|
+
|
|
405
|
+
生产启用前仍须只读核实:
|
|
406
|
+
|
|
407
|
+
- 实例身份认证;
|
|
408
|
+
- TLS 或受控私网链路;
|
|
409
|
+
- 可恢复备份;
|
|
410
|
+
- MongoDB 版本和单节点/副本集拓扑;
|
|
411
|
+
- 磁盘、内存和连接数余量;
|
|
412
|
+
- 最小权限账号只能访问 `dxc` 数据库;
|
|
413
|
+
- 索引和 TTL 策略不会影响其他业务。
|
|
414
|
+
|
|
415
|
+
跨集合流程使用幂等 Saga(补偿式事务)和唯一索引,不把 Mongo 副本集事务作为 MVP 硬依赖。
|
|
416
|
+
|
|
417
|
+
## 9. 对象存储
|
|
418
|
+
|
|
419
|
+
对象存储保存:
|
|
420
|
+
|
|
421
|
+
- 上传的文章包;
|
|
422
|
+
- 原始和规范化图片;
|
|
423
|
+
- 渲染 HTML;
|
|
424
|
+
- 预览所需快照;
|
|
425
|
+
- 可选的短期诊断产物。
|
|
426
|
+
|
|
427
|
+
数据库只保存对象 key、哈希、大小、媒体类型和生命周期,不保存永久公开 URL。
|
|
428
|
+
|
|
429
|
+
MVP 使用 `ObjectStorage` 端口:
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
putObject()
|
|
433
|
+
getObject()
|
|
434
|
+
headObject()
|
|
435
|
+
deleteObject()
|
|
436
|
+
createSignedGetUrl()
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
实际供应商在部署前选择。测试使用内存/临时目录实现;生产不得依赖容器本地磁盘。
|
|
440
|
+
|
|
441
|
+
## 10. SkillPay、微信支付和权益
|
|
442
|
+
|
|
443
|
+
### 10.1 交互
|
|
444
|
+
|
|
445
|
+
1. 渲染和预览免费。
|
|
446
|
+
2. 用户确认创建草稿。
|
|
447
|
+
3. Server 检查 `Entitlement` 和额度。
|
|
448
|
+
4. 有权益:原子预留额度并接受任务。
|
|
449
|
+
5. 无权益:返回单次/月卡/年卡选择。
|
|
450
|
+
6. Agent 调用对应支付入口。
|
|
451
|
+
7. DxC 创建微信支付订单和 SkillPay X402 预下单,向 Agent 返回支付触发信息。
|
|
452
|
+
8. WorkBuddy 内置支付插件让用户确认。
|
|
453
|
+
9. Agent 携带订单号重试;DxC 以支付回调或查单确认成功。
|
|
454
|
+
10. DxC 幂等授予权益,然后重试同一个草稿意图。
|
|
455
|
+
|
|
456
|
+
浏览器预览页不能假设会自动触发 SkillPay。支付动作回到 Agent;未来 Native 二维码支付页作为其他 Agent 的适配器。
|
|
457
|
+
|
|
458
|
+
### 10.2 MVP 商品
|
|
459
|
+
|
|
460
|
+
- `draft_credit`:一次草稿额度;
|
|
461
|
+
- `pass_monthly`:购买后授予 30 天权益;
|
|
462
|
+
- `pass_yearly`:购买后授予 365 天权益。
|
|
463
|
+
|
|
464
|
+
月卡/年卡是固定期限预付权益,不是自动续费。
|
|
465
|
+
|
|
466
|
+
SkillHub 上架前必须验证:
|
|
467
|
+
|
|
468
|
+
- 一次 Pay Skill 调用是否允许授予固定期限权益;
|
|
469
|
+
- 是否允许一个 Pay Skill 多价格/多 SKU;
|
|
470
|
+
- CLI 输出的 402 Header/Body 能否被 WorkBuddy 稳定识别;
|
|
471
|
+
- SkillHub 调用来源认证、结算、退款和审核规则。
|
|
472
|
+
|
|
473
|
+
如果一个 Pay Skill 只有一个价格,保守包装为:
|
|
474
|
+
|
|
475
|
+
- `dxc-draft-credit`
|
|
476
|
+
- `dxc-pass-monthly`
|
|
477
|
+
- `dxc-pass-yearly`
|
|
478
|
+
|
|
479
|
+
用户主流程仍由 `dxc-wechat-publisher` 组织,不让用户理解内部支付适配器。
|
|
480
|
+
|
|
481
|
+
### 10.3 扣费规则
|
|
482
|
+
|
|
483
|
+
- 接受任务前预留额度;
|
|
484
|
+
- 回读确认草稿存在后消费;
|
|
485
|
+
- 明确失败时释放;
|
|
486
|
+
- 结果不确定时冻结并进入核验;
|
|
487
|
+
- 同一任务重试不重复扣费;
|
|
488
|
+
- 任务在权益有效时被接受后,即使随后到期,也允许完成安全重试;
|
|
489
|
+
- 支付订单成功但后续任务失败时,已购买的月卡/年卡仍有效;单次额度按失败类型释放。
|
|
490
|
+
|
|
491
|
+
## 11. 部署拓扑
|
|
492
|
+
|
|
493
|
+
2026-07-26 已确认的当前阶段组件放置方案如下:
|
|
494
|
+
|
|
495
|
+
```mermaid
|
|
496
|
+
flowchart LR
|
|
497
|
+
WB["WorkBuddy + dxc CLI"]
|
|
498
|
+
NG["content.deployxai.com<br/>现有 Nginx/HTTPS"]
|
|
499
|
+
API["DxC Server<br/>alivps"]
|
|
500
|
+
PX["受限 CONNECT 代理<br/>alivps / 固定出口"]
|
|
501
|
+
WK["DxC Worker<br/>worker_vps"]
|
|
502
|
+
MG["MongoDB<br/>worker_vps / 独立 dxc DB"]
|
|
503
|
+
OS["Object Storage"]
|
|
504
|
+
WX["微信开放平台<br/>公众号 API / 微信支付"]
|
|
505
|
+
|
|
506
|
+
WB --> NG
|
|
507
|
+
NG --> API
|
|
508
|
+
API <--> MG
|
|
509
|
+
API <--> OS
|
|
510
|
+
WK <--> MG
|
|
511
|
+
WK <--> OS
|
|
512
|
+
API <--> WX
|
|
513
|
+
WK --> PX
|
|
514
|
+
PX <--> WX
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
计划中的路由边界:
|
|
518
|
+
|
|
519
|
+
- `/`、`/privacy/`、`/terms/`:继续由 `choir_site` 静态站点服务;
|
|
520
|
+
- `/api/v1/*`:DxC API;
|
|
521
|
+
- `/callbacks/wechat/*`:微信开放平台回调;
|
|
522
|
+
- `/callbacks/wechat-pay/*`:微信支付通知;
|
|
523
|
+
- `/authorize/*`:短时授权页面;
|
|
524
|
+
- `/preview/*`:短时预览。
|
|
525
|
+
|
|
526
|
+
这只是目标拓扑。修改 DNS、Nginx、证书或服务前必须另行获得明确授权。
|
|
527
|
+
|
|
528
|
+
## 12. 可观测性
|
|
529
|
+
|
|
530
|
+
MVP 最小可观测性:
|
|
531
|
+
|
|
532
|
+
- `/health/live`:进程存活,不访问外部依赖;
|
|
533
|
+
- `/health/ready`:配置结构、Mongo、对象存储的只读检查;
|
|
534
|
+
- 每个请求、任务、草稿意图和支付订单有独立 ID;
|
|
535
|
+
- 结构化日志默认脱敏;
|
|
536
|
+
- 指标:请求错误、回调失败、token 刷新、任务积压、重试、草稿成功/不确定、支付对账异常;
|
|
537
|
+
- 告警不包含文章正文、密钥或完整第三方响应。
|
|
538
|
+
|
|
539
|
+
## 13. MVP 验收条件
|
|
540
|
+
|
|
541
|
+
商业 MVP 只有在以下闭环全部通过后成立:
|
|
542
|
+
|
|
543
|
+
- macOS 和 Windows 的 WorkBuddy 安装与 `dxc doctor`;
|
|
544
|
+
- 一个测试公众号完整扫码授权、撤销和重新授权;
|
|
545
|
+
- Markdown + 多张本地图片 + 封面完整预检;
|
|
546
|
+
- 云端预览与最终 HTML 使用同一快照;
|
|
547
|
+
- 有效订阅无支付提示;
|
|
548
|
+
- 无权益时 WorkBuddy 能完成 SkillPay;
|
|
549
|
+
- 同一个意图不会重复支付或重复创建草稿;
|
|
550
|
+
- `draft/add` 成功后 `draft/get` 回读一致;
|
|
551
|
+
- 模拟超时能够进入 `CREATED_UNVERIFIED` 并安全核验;
|
|
552
|
+
- 所有日志和错误输出通过密钥/正文脱敏检查;
|
|
553
|
+
- 未触碰 ID 7,未正式发布。
|