codex_hook_notify 用来接收 Codex lifecycle hook 输入,并通过飞书自定义机器人发送提醒。当前提醒两个事件:
Stop:Codex 本轮完成或停止;PermissionRequest:Codex 等待用户批准命令或工具调用。
Stop 提醒会优先读取本机 Codex transcript,推送最后一轮 Codex 用户可见输出,也就是 CLI/App 里能看到的 assistant 文本消息。工具调用的原始 stdout/stderr 不会作为 Stop summary 推送。读取失败时,程序回退到 hook 输入里的 last_assistant_message。
提醒失败不会阻塞 Codex。程序会向 stderr 输出错误,并写入本地日志。
| 目标 | 处理方式 |
|---|---|
| 不阻塞 Codex | 读取、解析、发送和写日志失败都返回 0,只写 stderr 和日志 |
| 支持按事件路由 | 配置中用 routes[].events 匹配 hook event |
| Stop 内容更有用 | 优先从本机 transcript 提取最后一轮 assistant 可见输出 |
| 长消息可发送 | 飞书 text 内容过长时拆成多条消息 |
| 多机器可区分 | 每条通知都带 Machine: 字段 |
| 路径 | 作用 |
|---|---|
cli/codex_hook_notify/main.go |
CLI 入口,读取配置并调用 Run |
cli/codex_hook_notify/config.go |
配置结构、事件匹配、机器名解析 |
cli/codex_hook_notify/event.go |
Codex hook 输入解析和通知文本构造 |
cli/codex_hook_notify/transcript.go |
从 CODEX_HOME/sessions 查找 transcript 并提取 summary |
cli/codex_hook_notify/feishu.go |
飞书自定义机器人发送 |
cli/codex_hook_notify/logger.go |
本地日志路径和日志写入 |
cli/codex_hook_notify/run.go |
主流程编排和失败上报 |
flowchart TD
A["读取 --config"] --> B["解析 codex_hook_notify.json"]
B --> C["从 stdin 读取 Codex hook JSON"]
C --> D["解析 hook_event_name 和 session 信息"]
D --> E{"配置中是否匹配事件?"}
E -->|否| F["静默结束"]
E -->|是| G["解析 Machine 名称"]
G --> H{"是否 Stop 事件?"}
H -->|是| I["尝试读取 transcript summary"]
H -->|否| J["使用 hook 输入构造消息"]
I --> K["失败时回退 last_assistant_message"]
J --> L["按长度拆分飞书 text"]
K --> L
L --> M["发送到匹配的 webhook"]
M --> N["写入本地日志"]
| 内容 | Linux | macOS |
|---|---|---|
| 可执行文件 | /usr/local/bin/codex_hook_notify |
/usr/local/bin/codex_hook_notify |
| 配置文件 | /etc/life_tools/codex_hook_notify.json |
/etc/life_tools/codex_hook_notify.json |
| 日志目录 | /var/log/codex_hook_notify |
~/Library/Logs/codex_hook_notify |
| Codex hook 配置 | ~/.codex/hooks.json |
~/.codex/hooks.json |
其他系统的日志目录是 ~/.codex_hook_notify/logs。
先在飞书群里添加自定义机器人,拿到 webhook URL。不要把真实 webhook 提交到 git。
配置文件默认是:
/etc/life_tools/codex_hook_notify.json配置格式:
{
"machine_name": "home-nas",
"routes": [
{
"events": ["Stop"],
"feishu_custom_robot_urls": [
"https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
]
},
{
"events": ["PermissionRequest"],
"feishu_custom_robot_urls": [
"https://open.feishu.cn/open-apis/bot/v2/hook/yyy"
]
}
]
}一个事件可以配置多个 webhook。没有匹配 route 的事件会被静默跳过。
所有通知都会包含 Machine: 字段,用来区分是哪台机器触发了 hook。
机器名取值顺序:
- 配置文件顶层
machine_name,非空时直接使用; machine_name未配置或为空时,使用当前操作系统 hostname;- hostname 获取失败或为空时,显示
unknown。
示例:
{
"machine_name": "home-nas",
"routes": []
}machine_name 只影响通知展示,不参与 route 匹配、日志文件命名或 webhook 选择。
Stop 事件的 hook 输入不一定包含完整输出。为了补全最后一轮内容,程序会按下面顺序取 summary:
- 从
CODEX_HOME/sessions读取本地 transcript;如果CODEX_HOME未设置,默认使用~/.codex/sessions; - 先按 session id 匹配 transcript 文件名;匹配不到时,再读取
session_meta.payload.id做一次兜底匹配; - 只提取同一
turn_id下的 assistantmessage,并保留commentary和final_answer等用户可见文本; - 找不到 transcript、格式变化或解析失败时,回退到
last_assistant_message; - 仍然没有内容时,发送
No summary available.。
长 summary 会拆成多条飞书 text 消息,标题带 Codex Hook Reminder [1/N] 这样的分片标记。推送内容原样发送,不做敏感信息脱敏;如果最后一轮 Codex 输出里包含路径、配置片段、token 或其他敏感内容,它们会进入配置的飞书群。
在仓库根目录执行:
./install.sh --tool codex_hook_notify脚本会做这些事:
- 构建
output/codex_hook_notify; - 安装到
/usr/local/bin/codex_hook_notify; - 如果
/etc/life_tools/codex_hook_notify.json不存在,安装示例配置; - 创建
/var/log/codex_hook_notify,并把目录 owner 设置为当前用户。
填写真实 webhook 后,安装全局 Codex hook。默认只安装 Stop,也就是 Codex 完成或停止时提醒:
./install.sh --tool codex_hook_notify --install-codex-hookPermissionRequest 在 approval 模式下会非常频繁,默认不安装。默认安装命令也会移除本工具旧版本安装过的 PermissionRequest hook。如果你明确需要等待审批提醒,再执行:
./install.sh --tool codex_hook_notify --install-codex-hook --with-permission-request在仓库根目录执行:
./install.sh --tool codex_hook_notify脚本会把程序安装到 /usr/local/bin/codex_hook_notify,配置仍放在 /etc/life_tools/codex_hook_notify.json。日志目录会创建在:
~/Library/Logs/codex_hook_notify填写真实 webhook 后,安装全局 Codex hook。默认只安装 Stop,也就是 Codex 完成或停止时提醒:
./install.sh --tool codex_hook_notify --install-codex-hookPermissionRequest 在 approval 模式下会非常频繁,默认不安装。默认安装命令也会移除本工具旧版本安装过的 PermissionRequest hook。如果你明确需要等待审批提醒,再执行:
./install.sh --tool codex_hook_notify --install-codex-hook --with-permission-requestmacOS 上如果 /usr/local/bin 或 /etc/life_tools 需要管理员权限,脚本会通过 sudo 请求权限。
先做一次手工 Stop 事件测试:
printf '%s\n' '{"hook_event_name":"Stop","session_id":"manual-stop-test","cwd":"/tmp","model":"manual","last_assistant_message":"codex_hook_notify Stop test"}' \
| /usr/local/bin/codex_hook_notify --config /etc/life_tools/codex_hook_notify.json再做一次 PermissionRequest 事件测试:
printf '%s\n' '{"hook_event_name":"PermissionRequest","session_id":"manual-permission-test","cwd":"/tmp","tool_name":"manual","permission_request":{"reason":"codex_hook_notify PermissionRequest test","command":"manual command"}}' \
| /usr/local/bin/codex_hook_notify --config /etc/life_tools/codex_hook_notify.json收到飞书消息后,再检查日志。
Linux:
ls -lt /var/log/codex_hook_notifymacOS:
ls -lt ~/Library/Logs/codex_hook_notify最后启动一次 Codex,让它自然触发 Stop。如果 Codex 第一次提示是否信任这个 hook,确认信任后再测一次。
如果你没有使用 --with-permission-request,手工 PermissionRequest 测试可以验证程序本身能发送,但正常 Codex approval 不会触发飞书提醒。
如果只想停用提醒,编辑:
~/.codex/hooks.json删除 Stop 和可选 PermissionRequest 中命令为下面内容的 hook:
/usr/local/bin/codex_hook_notify --config /etc/life_tools/codex_hook_notify.json不要删除其他 hook,避免影响已有 Codex 集成。