Merge upstream and add generic Codex notifications
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
| **CLI 子命令** | `notify save\|notify\|input\|cleanup` | 否(纯互操作 / 落盘) | 即起即退(~100ms) |
|
||||
| **Host** | `notify` 或 `notify host` | 是 | 常驻(单例,无主窗口) |
|
||||
|
||||
这样设计的原因:hook 在你每次发消息时都会被拉起,必须**极快返回、绝不阻塞 Claude Code**;而真正画 UI 的 Avalonia 较重,放在一个**只初始化一次**的常驻进程里。
|
||||
hook 需要快速返回。CLI 只写入状态和通知队列,Avalonia UI 由常驻进程加载一次。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
@@ -25,15 +25,15 @@ flowchart TD
|
||||
RUN --> APP[App:托盘 + SpoolWatcher + ToastManager]
|
||||
```
|
||||
|
||||
- **单例**:`RunHost` 用命名互斥量 `ClaudeCodeNotifyHost` 保证只有一个 Host;第二个实例直接退出。
|
||||
- **单例**:`RunHost` 用命名互斥量 `NotifyHost` 保证只有一个 Host;第二个实例直接退出。
|
||||
- **保活**:`ShutdownMode.OnExplicitShutdown`,没有主窗口也不退,只有托盘"退出"才结束。
|
||||
|
||||
## 两条数据通道
|
||||
|
||||
1. **状态文件**(每会话)`%TEMP%\claude-notify-{session_id}.json`
|
||||
1. **状态文件**(每会话)`%TEMP%\notify-{session_id}.json`
|
||||
- `save` 写入:前台窗口句柄、prompt、WT 标签 RuntimeId、调用方 exe 路径。
|
||||
- `notify`/`input` 读取,拼成通知;`cleanup` 删除。
|
||||
2. **spool 队列** `%TEMP%\claude-notify-spool\*.json`
|
||||
2. **spool 队列** `%TEMP%\notify-spool\*.json`
|
||||
- `notify`/`input` 把一条 `NotifyMessage` 原子落盘,Host 用 `FileSystemWatcher` 消费。
|
||||
- 取代命名管道,**让 CLI 写完即走、不等 Host**(见 README 的「非阻塞投递」时序)。
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# 构建与安装
|
||||
|
||||
## 安装(用户)
|
||||
## Codex 接入示例
|
||||
|
||||
```bash
|
||||
claude plugin marketplace add https://git.pchuan.top/cc-tools/notify.git
|
||||
claude plugin install claude-code-notify@claude-code-notify
|
||||
在用户级 `~/.codex/config.toml` 中加入:
|
||||
|
||||
```toml
|
||||
notify = ["E:\\notify\\scripts\\notify.cmd", "codex"]
|
||||
```
|
||||
|
||||
重启 Claude Code 后生效。插件的 `hooks/hooks.json` 指向 `${CLAUDE_PLUGIN_ROOT}/scripts/notify.cmd`,首次触发钩子时该脚本会从 Release 下载单文件 `notify.exe` 到 `bin/`,之后常驻。
|
||||
`notify` 必须放在用户级配置中,项目目录内的 `.codex/config.toml` 不会覆盖它。
|
||||
|
||||
引导脚本(`scripts/notify.cmd`、`scripts/notify.sh`)顶部的 `DOWNLOAD_URL` 决定从哪拉取 exe;下载用临时文件 + 原子改名 + mkdir 锁,并发触发不会重复下载。
|
||||
|
||||
@@ -30,7 +31,7 @@ NativeAOT 静态链接 Skia / HarfBuzz / ANGLE,产出**单个无依赖 exe**
|
||||
```bash
|
||||
# 从 "Developer Command Prompt for VS" 运行,或用脚本(自动用 vswhere 配 vcvars)
|
||||
scripts\build.bat
|
||||
# 产物:bin\notify.exe(单文件,~40MB)
|
||||
# 产物:bin\notify.exe(未压缩单文件,约 40 MB)
|
||||
```
|
||||
|
||||
然后把它作为 Release 资产发布,并确保引导脚本的 `DOWNLOAD_URL` 指向它:
|
||||
@@ -41,7 +42,7 @@ gh release create v0.1.0 bin/notify.exe
|
||||
|
||||
> AOT 配置在 `Notify/Notify.csproj`(`PublishAot` 条件块 + `CoreUtils.*.Static` 静态库包 + 发布后清理)。源生成 COM / UIAutomation 与静态渲染需真机运行验证,详见 [interop.md](interop.md)。
|
||||
|
||||
## 不接 Claude 的手动烟雾测试
|
||||
## 手动烟雾测试
|
||||
|
||||
```bash
|
||||
notify host & # 起 Host(托盘出现)
|
||||
|
||||
+18
-8
@@ -1,20 +1,30 @@
|
||||
# Hook 与 CLI
|
||||
|
||||
## Hook → 子命令映射
|
||||
## Codex notify
|
||||
|
||||
| Claude Code 事件 | 子命令 | 作用 |
|
||||
|------------------|--------|------|
|
||||
在用户级 `~/.codex/config.toml` 中配置:
|
||||
|
||||
```toml
|
||||
notify = ["E:\\notify\\scripts\\notify.cmd", "codex"]
|
||||
```
|
||||
|
||||
Codex 调用 `notify codex <JSON>`。程序处理 `agent-turn-complete` 事件,使用 `thread-id` 或 `turn-id` 标识通知,以 `last-assistant-message` 作为正文。
|
||||
|
||||
## Hook 与子命令映射
|
||||
|
||||
| 事件 | 子命令 | 作用 |
|
||||
|------|--------|------|
|
||||
| `UserPromptSubmit` | `notify save` | 记录前台窗口、prompt、WT 标签、调用方图标路径 |
|
||||
| `Stop` | `notify notify` | 弹"任务完成"通知(自动消失,聚焦时更短) |
|
||||
| `Notification` | `notify input` | 弹"需要输入"通知(常驻),按类型分标题 |
|
||||
| `PreToolUse`(`AskUserQuestion`/`ExitPlanMode`) | `notify input` | 提问 / 出 Plan 时弹常驻通知 |
|
||||
| `SessionEnd` | `notify cleanup` | 删除该会话状态文件 |
|
||||
|
||||
`hooks/hooks.json`(插件形式)里命令为 `${CLAUDE_PLUGIN_ROOT}/bin/notify.exe <子命令>`;直连 `settings.json` 时可写 `notify <子命令>` 或绝对路径。
|
||||
客户端可调用 `notify.exe <子命令> [来源]`。来源为 `codex` 时固定显示 Codex 图标。
|
||||
|
||||
## stdin JSON
|
||||
|
||||
Claude Code 通过 **stdin** 把事件数据以 JSON 传入。`HookInput` 关心这几个字段:
|
||||
外部 hook 通过 **stdin** 把事件数据以 JSON 传入。`HookInput` 使用以下字段:
|
||||
|
||||
| 字段 | 用途 |
|
||||
|------|------|
|
||||
@@ -30,10 +40,10 @@ Claude Code 通过 **stdin** 把事件数据以 JSON 传入。`HookInput` 关心
|
||||
|
||||
| 条件 | 标题 |
|
||||
|------|------|
|
||||
| `tool_name == AskUserQuestion` | Claude is Asking |
|
||||
| `tool_name == AskUserQuestion` | Input Required |
|
||||
| `tool_name == ExitPlanMode` | Plan Ready for Approval |
|
||||
| `notification_type == permission_prompt` | Permission Required |
|
||||
| `notification_type == idle_prompt` | Claude is Waiting |
|
||||
| `notification_type == idle_prompt` | Application is Waiting |
|
||||
| `notification_type == elicitation_dialog` | MCP Asks |
|
||||
| 其它 | Input Required |
|
||||
|
||||
@@ -41,7 +51,7 @@ Claude Code 通过 **stdin** 把事件数据以 JSON 传入。`HookInput` 关心
|
||||
|
||||
## 状态文件
|
||||
|
||||
路径:`%TEMP%\claude-notify-{session_id}.json`(`session_id` 做了文件名安全过滤)。
|
||||
路径:`%TEMP%\notify-{session_id}.json`(`session_id` 做了文件名安全过滤)。
|
||||
|
||||
```jsonc
|
||||
{
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ Windows 限制后台进程抢焦点。组合技:还原最小化 → 模拟一
|
||||
|
||||
## 取图标(AppIcon)
|
||||
|
||||
`ExtractIconEx` 拿 HICON → `GetIconInfo` 取彩色位图 → `GetDIBits` 以 32bpp 自上而下读出 BGRA → 构造 `Avalonia.Media.Imaging.Bitmap`。老图标无 alpha(全 0)时补成不透明,避免整块透明。取不到则回退默认 Claude 图标。
|
||||
`ExtractIconEx` 拿 HICON → `GetIconInfo` 取彩色位图 → `GetDIBits` 以 32bpp 自上而下读出 BGRA → 构造 `Avalonia.Media.Imaging.Bitmap`。老图标无 alpha(全 0)时补成不透明。取不到图标时隐藏图标区域。
|
||||
|
||||
## AOT
|
||||
|
||||
|
||||
Reference in New Issue
Block a user