Coucou的Hook+Socket中继机制剖析:如何做到永不阻塞Claude Code
Coucou的HookSocket中继机制剖析如何做到永不阻塞Claude Code【免费下载链接】coucouA tiny friend that lives in your notch (macOS) or at the top of your screen (Windows, Linux) and keeps an eye on your coding agents: Claude Code, Gemini CLI, Antigravity and more.项目地址: https://gitcode.com/gh_mirrors/co/coucouCoucou 是一款常驻在 Mac 刘海Windows 则在屏幕顶部的开源小工具它通过一套Hook Socket 中继机制实时盯着你的 Claude Code 会话还能在刘海里直接审批权限。这套机制最较真的一条设计原则就是——永不阻塞 Claude Code无论 Coucou 是否启动、是否崩溃、界面是否卡死Claude Code 都会像没装它一样照常运行。本文就来拆解它到底是怎么做到的。一、前提Claude Code 自带的 Hook 机制要理解中继得先知道 Claude Code 本身提供了Hooks钩子能力。你可以在配置文件~/.claude/settings.json里为一系列生命周期事件注册一条要执行的命令。比如会话开始、提交提示词、使用工具前后、请求权限、会话结束等SessionStart/SessionEnd会话开始与结束UserPromptSubmit你提交了一段提示词PreToolUse/PostToolUse工具调用前后PermissionRequest请求执行敏感操作需要人工批准Stop/StopFailure一次任务完成或失败每当这些事件触发Claude Code 就会把一段 JSON 通过标准输入stdin喂给你注册的命令并等待命令结束。Coucou 要做的事情就是注册一条中继命令把这段 JSON 转发给自己的主程序。⚠️ 关键点Claude Code 的耐心是有限的——每个 hook 都有一个超时多数事件 10 秒PermissionRequest是 120 秒。超时的后果是命令被放弃、Claude Code 继续走。所以中继脚本必须又快又稳。二、中继脚本把 stdin 转成一条 Socket 消息中继脚本是夹在 Claude Code 和 Coucou 主程序之间的轻量角色。它负责两件事读取 stdin 的 JSON、通过本地 Socket 发出去。macOSnb-hooknb-hook.pyUnix 域套接字在 macOS 上Coucou 会往~/Library/Application Support/NotchBuddy/写两个脚本nb-hookShell 外壳由 Claude Code 直接调用。它内部调用 Python 中继脚本并且无论发生什么都exit 0——这是永不阻塞的第一道保险。nb-hook.pyPython 中继把 JSON 发往本地Unix 域套接字nb.sockApp Store 沙盒版本则发往容器内的路径。Windowscoucou-hook.exe命名管道Windows 上对应的是一个用 Rust 编译的原生小工具coucou-hook.exe。它读取 stdin、补充一点终端上下文比如TERM_PROGRAM再把 JSON 通过命名管道\\.\pipe\coucou-sid发给主程序。管道名里带了当前用户的 SID避免同一台机器上不同账号撞到同一条管道。三、服务端HookServer 与命名管道服务器中继发出去的消息由 Coucou 主程序里的服务器接收再把事件翻译成界面状态。macOSHookServerSwift原生 Darwin SocketHookServer.swift 在后台线程上监听nb.sock每来一个连接就开一个独立线程处理绝不占用主线程。它有几条防守只接受与当前用户同一 UID的连接getpeereid校验并发连接上限32 个单条消息上限1 MB收包超时5 秒套接字权限0600、目录权限0700别人读不到。Windowspipe.rsRust tokiopipe.rs 用 tokio 的命名管道实现同样的逻辑。除PermissionRequest外其它事件都是转发即断开把事件emit给岛窗口、然后立刻disconnect。四、关键设计为什么永远不会阻塞 Claude Code这是整篇文章的题眼。Coucou 用五层兜底确保 Claude Code 永远不会被它卡住。1. 外壳脚本永远 exit 0macOS 的nb-hook脚本最后一行就是exit 0。哪怕 Python 中继出错、超时、崩溃外壳都会安静地以 0 退出——对 Claude Code 而言这就等于命令正常执行完了。2. 极短的连接超时 发后即忘对绝大多数事件非权限请求中继采用fire-and-forget发后即忘macOS 的 Python 中继只给自己0.3 秒去连接Windows 的coucou-hook给连接300 毫秒整个发完就走的事件给了2 秒预算。如果 Coucou 没开、Socket/管道不存在中继在几百毫秒内就放弃、空着 stdout 退出。结果就是Claude Code 完全感受不到它的存在会话照常推进。3. 主线程预算制管道卡死也甩得掉Windows 的 main.rs 把真正会阻塞的读写全丢到一个工作线程里主线程则拿着一个截止时间。一旦工作线程超时普通事件 2 秒主线程直接退出进程——进程一死管道句柄也随之释放绝无可能把 Claude Code 卡死。4. 只有PermissionRequest会等而且要先确认卡片真的可见权限审批是唯一需要等人点按钮的事件但它也做了精心的两段式等待见 pipe.rsACK 段约 800 毫秒岛窗口必须先回报审批卡片已经显示在屏幕上。如果界面被暂停、被别的东西挡住、甚至 webview 没在监听这一步就失败——代价只是几百毫秒而不是两分钟决策段最长 108 秒只有卡片确实在人眼前才开始等用户点允许 / 拒绝。macOS 上则是 HookServer.swift 把这条连接的文件描述符挂起保留最长 115 秒。5. 硬超时兜底最终把决定权交回终端不管上面哪一步失败、超时、Coucou 彻底无响应最终都收敛到同一件事中继不往 stdout 写任何东西。而Claude Code 收到 hook 命令却没输出的默认行为就是——回到终端里照常询问。也就是说最坏情况等价于根本没装 Coucou绝不会变成一次错误或死锁。五、顺带看几个稳健性细节除了不阻塞这套机制在不乱改用户配置上也很有分寸见 hooks.rs先备份再写写settings.json前先做一份带时间戳的备份只合并、不清空只增删 Coucou 自己的条目别人的 hook 原样保留先看 diff 再落盘把变更差异给用户看且用文件指纹校验你看到的和要写入的是同一份中间被人改过就中止原子写入先写到临时文件再重命名覆盖磁盘写一半也不会留下半个坏文件。六、关键文件索引想自己翻代码验证的话重点看这几处macOS 中继与套接字服务器NotchBuddy/Sources/App/HookServer.swiftWindows 中继可执行文件windows/hook/src/main.rsWindows 管道服务器windows/src-tauri/src/pipe.rshook 安装与 settings.json 安全写入windows/src-tauri/src/hooks.rs项目行为与设计原则CLAUDE.md、README.md一句话总结Coucou 把 Claude Code 的 Hook 命令当成一次性的、短命的、可以随时放弃的中继主程序才是真正长驻的服务端。无论这一端断了、卡了还是没人看兜底逻辑都保证 Claude Code 最终会回到终端继续干活——这正是它敢承诺永不阻塞的底气所在。【免费下载链接】coucouA tiny friend that lives in your notch (macOS) or at the top of your screen (Windows, Linux) and keeps an eye on your coding agents: Claude Code, Gemini CLI, Antigravity and more.项目地址: https://gitcode.com/gh_mirrors/co/coucou创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考