Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
954ad5c
docs: design Codex WeChat observer
kdnsna Jul 19, 2026
4ece937
docs: plan Codex WeChat observer implementation
kdnsna Jul 19, 2026
9f2ac0b
feat: define observer jobs and run reports
kdnsna Jul 19, 2026
59d7310
fix: harden observer report sanitization
kdnsna Jul 19, 2026
5211508
fix: redact quoted characters in home paths
kdnsna Jul 19, 2026
434b308
fix: redact home paths with nested quotes
kdnsna Jul 19, 2026
b0490e6
fix: preserve text after quoted home paths
kdnsna Jul 19, 2026
18447aa
fix: scan ambiguous quoted home paths
kdnsna Jul 19, 2026
1d06ed4
fix: fail closed on unterminated home paths
kdnsna Jul 19, 2026
0075dbb
fix: fail closed on quoted home path lines
kdnsna Jul 19, 2026
61ae409
fix: fail closed on all home path lines
kdnsna Jul 19, 2026
be9ab9d
fix: close observer path sanitization gaps
kdnsna Jul 19, 2026
5362a8f
fix: sanitize reconstructed report text atomically
kdnsna Jul 19, 2026
77a2d87
fix: redact reconstructed secrets atomically
kdnsna Jul 19, 2026
fdd51f3
feat: journal observer runs and recover interruptions
kdnsna Jul 19, 2026
33a7366
fix: fsync observer journal directories
kdnsna Jul 19, 2026
79f8305
feat: render deterministic Codex run cards
kdnsna Jul 19, 2026
53b63b8
fix: harden report card rendering
kdnsna Jul 19, 2026
407d219
fix: constrain report card text width
kdnsna Jul 19, 2026
710c112
feat: send audited WeChat report images
kdnsna Jul 19, 2026
2330931
fix: close WeChat media host override
kdnsna Jul 19, 2026
1540076
fix: harden WeChat media boundaries
kdnsna Jul 19, 2026
0bbb6c7
feat: deliver run reports without duplicate sends
kdnsna Jul 19, 2026
a69b164
fix: close run report delivery edge states
kdnsna Jul 19, 2026
84d1072
fix: harden run delivery locking
kdnsna Jul 19, 2026
3ab4304
fix: reject nonregular delivery locks
kdnsna Jul 19, 2026
5bcfb76
feat: run fixed Codex jobs under observation
kdnsna Jul 19, 2026
c4ae5d3
fix: restore scheduled adapter contract
kdnsna Jul 19, 2026
94bfe11
fix: keep observer jobs serialized through exit
kdnsna Jul 19, 2026
b021321
fix: clean observer lock owner failures
kdnsna Jul 19, 2026
1ffa094
fix: harden observer lock directories
kdnsna Jul 19, 2026
1f2a2f7
feat: orchestrate observed Codex jobs
kdnsna Jul 19, 2026
4952c15
feat: operate the Codex WeChat observer
kdnsna Jul 19, 2026
c653d18
fix: complete observer health coverage
kdnsna Jul 19, 2026
9f1449e
fix: harden observer operations
kdnsna Jul 19, 2026
a0ce66f
fix: serialize test-card delivery
kdnsna Jul 19, 2026
7cb4b0f
fix: initialize private observer locks
kdnsna Jul 19, 2026
aaf24e0
test: make scheduled gates deterministic
kdnsna Jul 19, 2026
3783d39
fix: close observer recovery gaps
kdnsna Jul 19, 2026
e8e3572
docs: design Codex Image 2 editorial reports
kdnsna Jul 19, 2026
d898bf5
docs: plan Codex Image 2 editorial reports
kdnsna Jul 20, 2026
585e377
feat: define the Codex image-only boundary
kdnsna Jul 20, 2026
51d8e24
feat: produce verified editorial digest data
kdnsna Jul 20, 2026
afdeb4a
feat: carry editorial data into run reports
kdnsna Jul 20, 2026
6756829
feat: assign stable editorial report numbers
kdnsna Jul 20, 2026
569eb4a
feat: generate audited Codex report heroes
kdnsna Jul 20, 2026
67a40fb
feat: persist editorial visual generation
kdnsna Jul 20, 2026
04ddeca
feat: render detailed editorial report images
kdnsna Jul 20, 2026
5da2bca
fix: separate fallback hero labels
kdnsna Jul 20, 2026
2ea9269
feat: orchestrate numbered Image 2 reports
kdnsna Jul 20, 2026
1935b4d
feat: verify editorial image readiness
kdnsna Jul 20, 2026
6defd3b
test: align managed job editorial result
kdnsna Jul 20, 2026
72023cf
docs: operate Codex editorial image reports
kdnsna Jul 20, 2026
ff504ff
fix: outlive WeChat QR long polling
kdnsna Jul 20, 2026
7f4fb41
fix: fill status reports with audit evidence
kdnsna Jul 20, 2026
9fbf4fc
Merge remote-tracking branch 'origin/main' into codex/daily-tech-digest
kdnsna Jul 20, 2026
c5f80aa
test: isolate observer process identities
kdnsna Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ jobs:
- name: Install root dependencies
run: npm ci

- name: Build pinned WeChat adapter
- name: Build and patch pinned WeChat adapter
run: |
npm --prefix vendor/cli-in-wechat ci
npm --prefix vendor/cli-in-wechat run build
npm run vendor:build

- name: Verify project
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ qrcode*
*.qr.png
*.qr.svg
.codex/
.superpowers/
*.codex-output.json
*.codex-output.md

Expand Down
49 changes: 46 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ WeChatPilot 是一个运行在本机的微信 AI 自动化平台。当前内置
> [!IMPORTANT]
> WeChatPilot 是独立、非微信或腾讯官方项目,与微信及腾讯不存在隶属、授权或背书关系。

项目不会从微信执行命令,不会启动 `cli-in-wechat` 的 Router、Codex 适配器或远程控制入口,也不会使用 `--dangerously-bypass-approvals-and-sandbox`。微信凭据和 context token 只保存在本项目被 Git 忽略的 `.state/wechat/`,目录权限为 `0700`、文件权限为 `0600`;命令输出和日志只报告就绪状态,不显示具体值、用户 ID 或令牌。
项目不会从微信执行命令,不会启动 `cli-in-wechat` 的 Router、Codex 适配器或远程控制入口,也不会使用 `--dangerously-bypass-approvals-and-sandbox`。微信不能启动、批准、恢复、停止或修改 Codex 任务;它在本项目中始终只是单向投递终点。微信凭据和 context token 只保存在本项目被 Git 忽略的 `.state/wechat/`,目录权限为 `0700`、文件权限为 `0600`;命令输出和日志只报告就绪状态,不显示具体值、用户 ID 或令牌。

## 产品方向

Expand All @@ -18,7 +18,7 @@ WeChatPilot 将科技日报作为首个经过真实运行验证的内置工作
- macOS 当前用户权限,不需要 `sudo`。
- 固定运行时:`/opt/homebrew/opt/node@22/bin/node`(已验证为 Node 22.23.1)。
- Codex:由 `config/digest.yaml` 固定为检测到的 Node 可执行文件与官方 CLI JavaScript 入口。
- 微信依赖:`vendor/cli-in-wechat` Git submodule,固定提交 `75d7db9338e133080fef9914b92ceb5e3ebd0dfc`,不跟随任何分支。
- 微信依赖:`vendor/cli-in-wechat` Git submodule,固定提交 `75d7db9338e133080fef9914b92ceb5e3ebd0dfc`,不跟随任何分支。构建后仅对未跟踪的 QR 运行时产物应用已审计的 45 秒长轮询补丁,上游源码和 submodule 指针保持不变。

不要运行 vendor 内的 `npm start`、`npm run dev` 或其主入口。项目只校验并动态加载审计过的 QR、HTTP 和加密辅助模块;构建产物必须同时通过 `config/vendor-wechat-artifacts.json` 的大小和 SHA-256 清单。

Expand All @@ -43,6 +43,8 @@ cd '/Users/kdnsna/Documents/微信 chat'
./scripts/health-check.sh
```

输出中的 `observer` 只有配置/渲染器/Image 2 就绪布尔值,以及待恢复、待发送、不确定投递、待生成视觉、固定背景视觉和无效状态记录的安全计数,不包含 run ID、期号、提示词或路径。待处理计数会把整体状态标为 `pending`,不代表必需环境损坏;无效配置、编号/视觉账本损坏、渲染器不可用或 Image 2 能力标记不匹配则返回 `failed`。健康检查不会为自证就绪而现场生成图片。若微信登录过期,重新运行 `./scripts/init-wechat.sh` 扫码初始化,不要手工编辑私有状态文件。

只准备 Codex 输入、检查安全参数而不调用 Codex:

```sh
Expand Down Expand Up @@ -79,6 +81,14 @@ GitHub Token 是可选项;不设置时使用公开 API 降级运行。若临
./scripts/send-test.sh
```

显式发送一张固定值守员测试卡(会真实向已绑定本人微信发送文字和 PNG):

```sh
./scripts/send-run-report-test.sh
```

测试卡没有收件人、命令、附件、模型、提示词或消息覆盖参数。Codex 内置 Image 2 只生成不含文字的主视觉;所有中文、期号、信号、行动和审计字段由本地确定性模板渲染。

发送指定日期的完整日报:

```sh
Expand All @@ -87,6 +97,37 @@ GitHub Token 是可选项;不设置时使用公开 API 降级运行。若临

长文本遵循固定上游逻辑:每段最多 2000 个 JavaScript 字符单元,依次优先在段落、换行、空格处分段,最后才硬切,段间等待 300ms。主动发送 POST 不做未经证明的网络重放;响应丢失或部分分段成功会记为 `indeterminate`,当天停止自动重发,避免重复。完整日报使用原子锁和每日 ledger;成功后同一天任何再次调用都会被拒绝。确定失败最多允许初次加两次重试。

## 编辑部情报长图与 Image 2

每个新的最终 Observer 运行报告会获得一个连续期号,例如 `WP-000021`,并对应唯一的视觉账本、主视觉和最终 PNG。成功、跳过、失败、超时、取消、中断和恢复报告共用同一条期号序列。同一 run 重试时复用原期号、主视觉、报告内容和最终长图,不再分配新期号或重复生成。

长图固定为 1080 × 2400 PNG,从上到下包含五个区域:

1. 无文字主视觉、报告身份、期号、状态与时间。
2. `EXECUTIVE BRIEF` 今日结论。
3. 最多五项带 `01`–`05` 编号的重点信号、为何重要、来源数、置信度和影响。
4. 三项可执行的下一步行动。
5. Codex 执行与交付审计,包含候选/入选数、来源覆盖、耗时、视觉模式和短 run ID。

Image 2 使用本机已登录的 Codex 内置认证,项目不读取、保存或要求 OpenAI API key。图片子进程仅接收经脱敏的视觉摘要,从 stdin 输入,且固定为临时、非交互、项目根目录、`read-only`、`approval never`。它只启用 `image_generation`,显式关闭 shell、unified exec、browser、computer use、Apps、plugins、hooks、多代理和额外目录;不接收微信消息、收件人、凭据、源 URL、完整日报、本机私有路径或可执行指令。

先验证能力,再生成本地预览,最后才实发:

```sh
npm run report:image-probe
npm run health
npm run report:preview
npm run report:test
```

`report:image-probe` 会调用一次相同的生产图片路径,完成后只保存 Codex 版本、样式版本、成功时间和产物哈希,并删除经文件身份核对的项目内探测图。`report:preview` 生成固定长图但不加载任何微信发送器。`report:test` 生成五项固定信号与三项行动,获得真实期号,然后向已绑定本人发送一次文字和长图。

主视觉最多尝试三次;三次都失败后生成确定性品牌背景,图上如实标注 `AI 主视觉暂不可用`,详细正文仍可投递。如果最终合成失败,文字和图片都不发送,最终报告与视觉账本保留供恢复;只有微信明确拒绝才在次数上限内重试,响应丢失则记为 `indeterminate` 且永不自动重放。

图片在微信发送之前已完成渲染,因此它只能声明 `报告图:已生成`,不得声明自己未来的微信投递已经成功。真实投递结果只由图片之外的私有 delivery ledger 确认。

紧急回滚无需更换代码或破坏新账本:只将 `config/report-visual.yaml` 中的 `mode: editorial` 改为 `mode: legacy`,再重新加载同一份 schema-compatible Observer 构建。legacy 模式仍会分配期号,但跳过 Image 2 并使用旧报告卡渲染器。保留所有最终报告、期号、视觉账本和不确定投递记录,不要重置状态或自动补发。

## 数据源与降级

首期配置位于 `config/sources.yaml`:
Expand Down Expand Up @@ -198,6 +239,8 @@ PATH=/opt/homebrew/opt/node@22/bin:/usr/bin:/bin \
./scripts/run-scheduled.sh
```

该脚本现在由 Observer 启动受审计的 `daily-tech-digest` 任务:日报投递完成后,再发送对应的文字运行报告和 PNG 卡片。如果上次 Observer 在任务运行期间被主机或进程中断,`interrupted` 报告只会在下一次 Observer 成功启动并完成恢复检查后上报。投递状态为 `indeterminate` 时永不自动重放,必须由运维人员核查,避免向微信重复发送。

新环境默认不会自动加载 LaunchAgent。安装脚本必须收到显式确认参数,否则退出且不改动 LaunchAgent:

```sh
Expand All @@ -211,7 +254,7 @@ PATH=/opt/homebrew/opt/node@22/bin:/usr/bin:/bin \
./scripts/health-check.sh
```

卸载只停止任务并删除用户 `LaunchAgents` 中这一份精确 plist,不删除项目、日报、日志或微信状态:
要停用已存在的 LaunchAgent,使用专用卸载脚本。它只停止任务并删除用户 `LaunchAgents` 中这一份精确 plist,不删除项目、日报、日志或微信状态:

```sh
./scripts/uninstall-launchd.sh
Expand Down
58 changes: 57 additions & 1 deletion config/digest-output.schema.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "object",
"additionalProperties": false,
"required": ["title", "date", "digest", "sourceUrls"],
"required": ["title", "date", "digest", "sourceUrls", "editorial"],
"properties": {
"title": {
"type": "string"
Expand All @@ -17,6 +17,62 @@
"items": {
"type": "string"
}
},
"editorial": {
"type": "object",
"additionalProperties": false,
"required": ["executiveSummary", "highlights", "actions"],
"properties": {
"executiveSummary": {
"type": "string",
"minLength": 80,
"maxLength": 140
},
"highlights": {
"type": "array",
"minItems": 1,
"maxItems": 5,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["title", "whyItMatters", "sourceUrls", "impact"],
"properties": {
"title": {
"type": "string",
"minLength": 8,
"maxLength": 36
},
"whyItMatters": {
"type": "string",
"minLength": 40,
"maxLength": 100
},
"sourceUrls": {
"type": "array",
"minItems": 1,
"maxItems": 6,
"items": {
"type": "string"
}
},
"impact": {
"type": "string",
"enum": ["high", "medium-high", "medium", "exploratory"]
}
}
}
},
"actions": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": {
"type": "string",
"minLength": 12,
"maxLength": 80
}
}
}
}
}
}
15 changes: 15 additions & 0 deletions config/observer-jobs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: 1
jobs:
- id: daily-tech-digest
name: 每日科技情报
projectLabel: WeChatPilot
executable: /opt/homebrew/opt/node@22/bin/node
entrypoint: dist/src/cli/scheduled.js
arguments:
- --project-root
- "{projectRoot}"
workingDirectory: .
timeoutMs: 1200000
resultAdapter: scheduled-digest-json-v1
allowedArtifactRoot: output
notify: always
16 changes: 16 additions & 0 deletions config/report-image-output.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"type": "object",
"additionalProperties": false,
"required": ["status", "imagePath"],
"properties": {
"status": {
"type": "string",
"enum": ["generated"]
},
"imagePath": {
"type": "string",
"minLength": 1,
"maxLength": 1024
}
}
}
70 changes: 70 additions & 0 deletions config/report-visual.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
version: 1
mode: editorial
canvas:
width: 1080
height: 2400
maximumBytes: 5242880
generation:
maximumAttempts: 3
timeoutMs: 180000
style:
version: editorial-v1
heroHeight: 480
codex:
version: 0.144.4
nodeExecutable: /opt/homebrew/opt/node@22/bin/node
javascriptEntrypoint: /opt/homebrew/lib/node_modules/@openai/codex/bin/codex.js
globalArgs:
- --sandbox
- read-only
- --ask-for-approval
- never
- --cd
- "{projectRoot}"
- --enable
- image_generation
- --disable
- apps
- --disable
- plugins
- --disable
- hooks
- --disable
- browser_use
- --disable
- computer_use
- --disable
- in_app_browser
- --disable
- shell_tool
- --disable
- unified_exec
- --disable
- shell_snapshot
- --disable
- multi_agent
- --disable
- goals
- --disable
- code_mode_host
- --disable
- workspace_dependencies
- --disable
- chronicle
- --disable
- memories
- --disable
- tool_suggest
- --disable
- auth_elicitation
command: exec
execArgs:
- --ephemeral
- --ignore-user-config
- --ignore-rules
- --color
- never
- --json
- --output-schema
- "{projectRoot}/config/report-image-output.schema.json"
- "-"
7 changes: 6 additions & 1 deletion config/vendor-wechat-artifacts.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
{
"path": "ilink/auth.js",
"bytes": 3140,
"sha256": "0743aeb829d774a3f67b277df757db234750d6b3ce9347eac4465d29691da707"
"sha256": "fb7953688a0393443a7847082b9aa36e04f4ddfcac13708e425e832e3b83a094"
},
{
"path": "ilink/client.js",
"bytes": 29893,
"sha256": "d4374ca607458c44321c58a4d2a9af7e040fbd21f055319533a89ce33d4c171b"
},
{
"path": "utils/http.js",
Expand Down
Loading