npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xuzhiyang/ainotify

v0.2.16

Published

Monitor working status of codex / opencode / claude / gemini CLI sessions

Readme

AINotify

监控 codex / opencode / claude / gemini CLI 工作状态的服务,统一暴露为 HTTP JSON + SSE,并可选推送到 Webhook 和桌面通知。

状态模型

每个被观察到的会话(按 cwd 维度组织)映射到以下统一状态之一:

| status | 含义 | | ------------------- | ----------------------------------------------------- | | running | 正在工作(思考 / 工具调用 / 流式输出) | | completed | 已完成本轮任务(陈述性总结,无需用户输入) | | waiting_for_input | 需要用户回答 / 选择 / 授权(agent 主动询问) | | idle | 会话存活但无活跃任务 | | error | 报错 / 重试中 | | not_running | 该 CLI + cwd 没有活会话 |

completed vs waiting_for_input 的区分:当一个 agent turn 结束时,本服务读取其最后一条 assistant 消息文本,用启发式规则(src/adapters/heuristics.js)判断:

  • 含问号(且文本较短)/ 选择类关键词(请选择 / which / option)/ 授权类关键词(approve / 授权 / y/n)/ 等待类短语(waiting for / 请回复)→ waiting_for_input
  • 陈述总结 / 链接列表 / 无文本 → completed

该启发式应用于 codex / claude / gemini。opencode 的 SQLite 事件日志不持久化 assistant 文本内容(只存工具调用 part),因此无法区分,所有结束的 turn 都标记为 completed(即便 agent 实际上在询问用户)。要获得准确的 opencode waiting_for_input,需启用 OPENCODE_SERVER_URL 让 adapter 从 HTTP API 获取完整消息文本(未来扩展)。

快速开始

npm install
cp .env.example .env   # 按需编辑
npm start              # 监听 0.0.0.0:3000

前端面板

内置一个 React + Vite 构建的实时监控面板(终端风深色 UI),由 Express 静态托管,前后端同源、一个进程

构建

# 首次需安装前端依赖
npm run install:web

# 构建到 web/dist/ —— 之后 npm start 会自动托管
npm run build:web      # 或 npm run build
npm start
# 浏览器打开 http://localhost:3000

开发模式(热更新)

# 终端 A:启动后端(带 --watch 热重载)
npm run dev

# 终端 B:启动 Vite dev server(:5173,自动 proxy /status /events /health 到 :3000)
npm run dev:web
# 浏览器打开 http://localhost:5173 —— 前端改动秒级生效

面板功能

  • 会话卡片:每个 cli:cwd 一张卡片,左侧彩色竖条 + 状态点动效(脉冲/呼吸/闪烁),展示 cwd、pid、sessionId、置信度条;detail 可折叠展开
  • 实时事件流:右侧滚动列表,显示每次状态变化(前态 → 后态 + 相对时间),保留最近 500 条(localStorage 持久化)
  • 状态分布图:手写 SVG 堆叠面积图,最近 10 分钟按 5s 桶采样,hover 显示分项计数
  • 筛选:按 status / cli 多选、cwd 模糊搜索、是否显示已结束会话;顶部 StatsBar 的 chip 点击即可联动筛选
  • SSE 实时推送:状态变化秒级到达;断线自动指数退避重连(1s→2s→…→30s 上限),通过 Last-Event-ID 续传

HTTP API

| 方法/路径 | 说明 | | --------------------- | ------------------------------------------------------------- | | GET /health | 健康检查 | | GET /status | 全量快照(可加 ?cli=claude / ?status=running 过滤) | | GET /status/:cli | 单个 CLI 的快照 | | GET /events | SSE 流,每次状态变化推送一帧(支持 Last-Event-ID 断线续传)|

/status 响应示例

{
  "generatedAt": 1782388287370,
  "count": 2,
  "sessions": [
    {
      "cli": "claude",
      "cwd": "/root/AINotify",
      "sessionId": "71286e0b-...",
      "pid": 135036,
      "status": "running",
      "statusLabel": "工作中",
      "confidence": 1,
      "since": 1782388290000,
      "updatedAt": 1782388300000,
      "detail": {}
    },
    {
      "cli": "gemini",
      "cwd": "/root/foo",
      "status": "waiting_for_input",
      "statusLabel": "等待答复",
      "confidence": 0.6,
      "detail": { "source": "heuristic" }
    }
  ]
}

SSE 帧格式

id: 1782388300000
event: change
data: {"cli":"claude","cwd":"/root/AINotify","before":{...},"after":{...},"ts":1782388300000}

各 CLI 监控机制

| CLI | 主通道 | 可靠度 | | --------- | --------------------------------------------------- | --------------- | | codex | tail ~/.codex/sessions/**/rollout-*.jsonlfs.watch push)+ pgrep 存活检测 | ⭐⭐⭐⭐ 高(turn 边界明确)| | opencode | 只读轮询 SQLite ~/.local/share/opencode/opencode.dbevent + session 表)+ pgrep 存活过滤;HTTP SSE 可选加速 | ⭐⭐⭐⭐⭐ 高(turn 边界明确)| | claude | fs.watch ~/.claude/sessions/*.json + 解析 transcript 末行 stop_reason | ⭐⭐⭐⭐ 高 | | gemini | pgrep + tail transcript + mtime 启发式 | ⭐⭐ 低(见下方)|

关于 codex 的重要说明

VSCode/code-server 扩展启动的 codex 走 stdio 模式codex app-server,无 --listen),其 JSON-RPC 的 stdin/stdout 被扩展宿主独占,外部进程无法挂载。系统里看到的 /tmp/codex-ipc/ipc.sock 其实归 code-server 所有,连上去会被 ECONNRESET。因此默认走 rollout JSONL 尾读方案——可靠且无侵入。

判定逻辑(按优先级):

  1. TUI/CLI 会话(有 rollout 文件)——读取 ~/.codex/sessions/**/rollout-*.jsonl 末尾事件:
    • task_started / turn_startedrunning(conf 0.95)
    • task_complete / turn_completewaiting_for_input(conf 0.85)
    • errorerrorturn_abortedwaiting_for_input
  2. VSCode 扩展拉起的 app-server(无 rollout)——仅能通过 pgrep 确认进程存活,无法判断 turn 内状态 → idle(conf 0.7,detail 标注 state unknown)。这是真实限制,非实现缺陷:app-server 的 JSON-RPC 走 stdio 被扩展宿主独占。
  3. 进程消失 → not_running

历史会话(>24h)不进入监控;/status 默认也排除 not_running,需 ?includeEnded=1 查看。

高保真可选方案(Plan B):若要拿到 thread/status/changed 等原生推送(含 waitingOnApproval 等子状态,conf 1.0),自行启动 codex 并设置 CODEX_IPC_SOCKET

codex app-server --listen unix:///tmp/codex-monitor.sock
# .env: CODEX_IPC_SOCKET=/tmp/codex-monitor.sock

关于 opencode 的重要说明

opencode 的 TUI 模式(opencode [project],默认调用方式)不会启动 HTTP server——只有 opencode serve / opencode web 才监听端口。因此本服务不依赖 HTTP API,而是直接只读轮询 opencode 的 SQLite 数据库(~/.local/share/opencode/opencode.db,WAL 模式),通过 Node.js 内置的 node:sqlite(v22+)零依赖访问,不会锁竞争。

判定逻辑:

  1. 查询最近 2 小时内 time_updated 有变化的 session
  2. 取每个 session 最后一条 role=assistantmessage.updated 事件(注意:不看 user message,因为 user 消息的 seq 可能更大从而掩盖正在生成的 assistant turn)
  3. 该事件的 info.finish 字段决定状态:
    • null"tool-calls"(且无 time.completed)→ 正在生成 → running(conf 0.95)
    • "stop" → turn 正常结束 → completed(conf 0.9)
    • time.completed 存在但 finish 缺失(异常)→ 保持 running(等待 finish 字段写入)
  4. pgrep opencode + /proc/<pid>/cwd 过滤,只报告有活进程的 cwd 下的 session

重要限制:opencode SQLite 的 message.part.updated.1 事件表只持久化工具调用 part(type:"tool"),不写入 assistant 的文本内容。因此本服务无法对 opencode 应用询问检测启发式,所有 finish=stop 的 turn 都标记为 completed——即使用户实际被 agent 问了问题。这是 opencode 本身日志设计的固有限制。需要准确的 waiting_for_input 判定时,请启用 OPENCODE_SERVER_URL 并使用 opencode serve 模式(后续版本可能从 HTTP API 补全文本)。

可选加速:若检测到 opencode serve(:4096)或设置了 OPENCODE_SERVER_URL,会额外订阅其 SSE /event 流,收到 message.updated / session.updated 时立即触发一次 SQLite 轮询,实现亚秒级延迟。TUI 模式下纯靠 1s 轮询。

关于 gemini 的重要说明

Gemini CLI 没有任何原生的状态暴露面(无 socket、无 pid 文件、transcript 里也没有 status 字段)。本服务通过 pgrep 进程检测 + transcript 尾部行类型 + mtime 启发式判定状态:

  1. pgrep -af gemini 发现进程 → 读 /proc/<pid>/cwd 获取工作目录 → 从 ~/.gemini/projects.jsonprojects 映射查 slug → 定位 ~/.gemini/tmp/<slug>/chats/session-*.jsonl
  2. transcript 最后有意义的行类型:
    • user(刚提交 prompt)→ running(conf 0.9)
    • gemini + 文件 mtime 在阈值内(仍在增长)→ running(conf 0.9)
    • gemini + 文件 mtime 超过阈值(默认 4s 无增长)→ waiting_for_input(conf 0.6)
  3. 进程消失 → not_running(conf 1.0)

已知局限:gemini 在长时间思考(网络等待、复杂推理)时 transcript 不会写入,这与"用户正在思考输入"的停顿无法区分,可能导致长时间运行的 turn 被误判为 waiting_for_input。这是 gemini 本身无状态暴露的固有限制,可通过调高 GEMINI_IDLE_THRESHOLD_MS 缓解,但无法根治。因此 gemini 的 waiting_for_input 置信度固定 0.6。

配置覆盖

所有发现路径都支持环境变量覆盖(见 .env.example):

  • CODEX_IPC_SOCKET:codex IPC socket 路径(默认空;自行 --listen unix:// 启动时才填)
  • OPENCODE_SERVER_URL:opencode HTTP server URL(可选加速通道;TUI 模式无需设置)
  • OPENCODE_DB_PATH:opencode SQLite 数据库路径(默认 ~/.local/share/opencode/opencode.db
  • CLAUDE_HOME / GEMINI_HOME:自定义 home 目录
  • GEMINI_IDLE_THRESHOLD_MS:gemini "等待输入"判定阈值(默认 4000ms)

输出通道

  1. HTTP JSON + SSE:见上
  2. Webhook:状态变化时 POST 到 WEBHOOK_URL,body 为 {before, after, ts};配置了 WEBHOOK_SECRET 时附带 X-AINotify-Signature: sha256=<hmac>
  3. 桌面通知:调用 notify-send(Linux),标题 [<cli>] <cwd>,内容为中文状态标签

架构

CLI 进程 ──┐
           ├─→ Adapter ──→ Store (内存态, 按 cli:cwd 维度)
CLI 进程 ──┘                    │
                                ├─→ HTTP /status  (pull)
                                ├─→ HTTP /events  (SSE push)
                                ├─→ Webhook       (POST push)
                                └─→ notify-send   (桌面)

每个 Adapter 是独立的探测器,只负责把观测到的状态 emitStatus({...}) 到 Store,Store 自动去重后通过 Notifier 分发到所有输出通道。新增 CLI 只需实现一个新 adapter。

测试

目前没有单元测试 fixture;验证方式:

npm start &
# 在另一终端运行任意 CLI,然后:
curl http://localhost:3000/status
curl -N http://localhost:3000/events