在这里插入图片描述

仓库:https://github.com/yuanmomoya/harbor-light

现在是 0.1.0,macOS 12+、Windows 10/11(x64 / ARM64)都能装。程序是 Rust 写的,不依赖 Python 或 Swift 运行时。

平时把活丢给 Codex 或 Cursor 之后,我经常切去干别的。过几分钟再切回来,有时已经写完了,有时卡在权限确认,有时其实还在跑。来回切窗口挺烦的,就做了这个小工具:桌面上挂一盏红绿灯,🟡 在干活,🔴 在等你,🟢 是刚做完。

源码和安装包都在上面那个仓库里。Releases 页可以直接下:

https://github.com/yuanmomoya/harbor-light/releases


灯怎么看

状态含义效果
🟡 黄模型在思考或在跑工具呼吸
🟢 绿这一轮结束了亮一下,大概 3 秒回到空闲
🔴 红要你点允许,或者报错了闪得比较急
🔴🟡 红 + 黄有一个在等你,另外还有任务在跑两盏一起闪
⚪ 三灯都暗没在干活低亮

窗口是个黑色胶囊,拖到副屏也行,位置会存下来。显示器拔掉之后如果窗口跑到屏幕外面,会回到主屏右上角。Mac 点菜单栏图标,Windows 右键托盘,可以看状态、重装 Hooks、退出。


一个状态文件不够用

最早只给 Codex 用,状态写在 ~/.codex-status.json 里。一个文件记一个状态,后写的覆盖先写的。后来要接 Cursor,这个就撑不住了。

比如 Codex 那边项目 A 还在跑,灯是 🟡。Cursor 那边项目 B 刚好跑完,往同一个文件里写了 🟢。界面就会显示绿灯,Codex 其实还没停。

现在改成每个软件、每个对话各写一份 json:

~/.harbor-light/activities/<provider>/<activity-id>.json

字段大概是这些:

provider
activity_id
conversation_id
generation_id   // 可选
workspace
state           // working | waiting | done
event
updated_at

主键用 provider + conversation_id + generation_id。只按进程或者项目路径分会漏:同一个仓库里也能同时开两个对话。

悬浮窗还是只有一组灯,颜色按所有活动算,优先级是 waiting > working > done > idle。有 waiting 就 🔴,没有 waiting 但有 working 就 🟡,只剩刚结束的 done 就 🟢 一下。已经结束的绿灯,只要别处还有 waiting / working,就不参与显示。过期和心跳也是按单条活动算的,不会因为其中一个还在跳,把别的一起续上。

Cursor 退了只清 Cursor 的文件,Codex 还在跑的不会被带走。要是 Hook 没把结束事件发出来,这条活动会在 30 分钟没更新之后自己掉回空闲,不然 🟡 会一直挂着。


怎么拿到状态

走官方 Hooks。Codex 和 Cursor 各自把事件打到 harbor-light hook --provider ...,程序写成活动文件,再聚合成灯。对话正文不读,也不往外传。

Codex Hooks ─┐
             ├→ 适配器 → ~/.harbor-light/activities/<provider>/*.json ─┐
Cursor Hooks ┘                                                         ├→ 聚合 → 灯
Codex rollout JSONL(兜底)─────────────────────────────────────────────┘

macOS 用 FSEvents 盯活动目录,大概 2 秒再扫一遍。Windows 大概 1 秒轮询一次进程、活动文件和 Codex 的 rollout。Codex 同一条会话可能同时出现在 Hook、旧的 ~/.codex-status.json 和 rollout 里,聚合前按 provider + 会话 ID 去重,Hook 的结果优先。

Codex

事件
SessionStart / UserPromptSubmit / PreToolUse / PostToolUse🟡
PermissionRequest🔴
Stop / SessionEnd🟢,大约 3 秒后空闲

Hook 没点信任、漏触发、或者还是旧安装的时候,会再扫 ~/.codex/sessions/**/rollout-*.jsonl,里面的 exec_approval_requestapply_patch_approval_requestrequest_permissions 这些也能把 🔴 点起来。

Cursor

配置在 ~/.cursor/hooks.json,项目级是 <项目>/.cursor/hooks.json。官方文档:https://prod.cursor.com/docs/hooks

事件处理
beforeSubmitPrompt🟡,当一轮开始
preToolUse / afterAgentThought续期,不新建任务
approval_required=truepermission=ask 这类字段🔴
afterShellExecution / afterMCPExecution已有活动改回 🟡;新开一条容易被迟到事件重新点亮,所以不新建
stop(completed/aborted)🟢
stop(error)🔴
sessionEnd清掉这条对话
sessionStart只登记,不亮 🟡。开了个 Composer 不等于开始干活

preToolUse 不能当 🔴。自动批准的工具也会走这个事件,误报很多。

beforeShellExecution(sandbox=false) 也没拿来当「在等你点允许」。Cursor 现在没有单独的 PermissionRequest,字段也分不清「弹窗等你」和「马上要执行」。如果非沙箱命令一律亮 🔴,winget、装依赖都会误报。所以现在只有事件里写明要审批,或者执行报错,才走红灯。IDE 里那种 Plan 审批,Hooks 目前看不出来。

用户级 Hook 只管本地 Agent。Cloud Agent 不跑用户级 Hook,得往每个仓库装项目级的,这版没自动配。

软件崩了或者直接关掉时,靠进程兜底:

  • macOS:com.openai.codexcom.todesktop.230313mzl4w4u92
  • Windows:chatgpt.exe / codex.execursor.exe

正常结束还是以 Hook 为准,进程退出只负责把对应 provider 的活动清掉。


TRAE 还没接

TRAE 3.5.66 加了 Hooks,设置里能配。但公开资料比较少,这几件事还不清楚:

  • 事件完整列表,stdin 里的 JSON 长什么样
  • 有没有稳定的会话 / 轮次 / 工作区 ID
  • 正常结束、手动停止、报错能不能分开
  • 有没有权限确认这种事件
  • 普通对话、SOLO、子代理是不是同一套

所以没直接套 Cursor 的事件名。也没去读它的 SQLite 或 globalStorage,那些文件格式会变,里面经常有完整提示词,而且「正在跑」和「历史刚写进去」不好分。

后面打算先挂一个只记事件名和结构的 Hook,把提交、完成、手动停、工具确认、报错、关对话、两个项目同时跑、SOLO 这些场景过一遍。协议够用再接到现在的 provider 层;会话 ID 或结束事件不稳的话,最多做成有限支持,不会靠读内部库或者模拟点击窗口来硬做。

项目以前叫 Codex Light,后来改成 Harbor Light,就是因为后面不打算只接 Codex。


安装

https://github.com/yuanmomoya/harbor-light/releases

Windows

HarborLight-0.1.0-windows-x64-setup.exe,ARM 机器用 arm64-setup.exe。装到 %LOCALAPPDATA%\HarborLight,当前用户权限就行,会合并 Codex / Cursor 的用户级 Hooks,并加登录自启动。安装包没签名,SmartScreen 拦了就「更多信息 → 仍要运行」。

如果下的是 zip,解压后先跑一次:

.\HarborLight.exe install

只双击 exe 不会写 Hooks。

macOS

双击 HarborLight.pkg。没签名的话,按住 Control 再点打开。源码安装:

make install

安装是往现有 hooks.json 里合并,不会把你自己的条目盖掉。卸载也只删这个工具写进去的那几行。

手动配的话命令得写绝对路径,比如:

# macOS
/Applications/HarborLight.app/Contents/MacOS/harbor-light hook --provider cursor

# Windows
%LOCALAPPDATA%\HarborLight\HarborLight.exe hook --provider cursor

Cursor 这边 timeout 给了 8 秒,思考内容太长时 stdin 容易把默认超时撑爆。Codex 3 秒够用。

装完新开一轮对话,看 ~/.harbor-light/activities/codex/cursor/ 下面有没有 json,或者翻 ~/.harbor-light.log


没做的几件事

对话 transcript 默认不读。

没用辅助功能去扫权限窗口,标题和控件树换个版本就对不上,还得要额外权限。

辅助功能、屏幕录制这些权限默认都不申请。

托盘里尽量显示项目名,完整路径不往外摊。

这些主要是图省事、少出幺蛾子。对方改内部目录或者改个窗口标题,这种方案最先挂。


要自己接别的 Agent 的话

先把多活动存储、聚合、按 provider 清理做完,再写适配器。🔴 只绑明确的「等用户」事件,工具调用不要拿来猜。安装器合并配置,卸载只删自己的条目。Windows 路径带空格、macOS 在 App Bundle 里的绝对路径,升级的时候都容易出问题。

能跑通这几条基本就够用了:两个软件同时跑时不会因为其中一个结束就显示空闲;手动停止后 🟡 会灭;退出 Cursor 不影响 Codex;两个项目能在托盘里分开看;过期按单条清,不定时把全部状态重置掉。

仓库再放一次:https://github.com/yuanmomoya/harbor-light

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐