Repository navigation
v1.5.0
新增
-
AI 审批台接管 Claude Code 原生确认框:新注册
PermissionRequesthook(install.py
重跑会自动补上这一条,已有的两条不动)。以前审批台只镜像规则表里confirm的操作,
Claude Code 自己要弹的 "Do you want to proceed?"(没命中任何规则的那些)在网页上完全
看不到——PreToolUse阶段根本不知道它接下来会不会弹。现在这类询问以kind='permission'
出现在审批台,按钮跟 confirm 一样(允许/拒绝/10、30 分钟/一直允许,"一直允许"按
session + 工具名记),网页或终端给了答案就通过hookSpecificOutput.decision.behavior
替用户答掉;没人答(90s 超时)或终端里敲回车,hook 静默退出、原生确认框照常弹出。
审批历史新增 "已转回原生确认框" 状态。 -
macOS 系统层探针(网络部分):新增
cc_monitor/probe_darwin.py,bin/CC-Monitor-probe
在 macOS 上自动切过去。用系统自带的nettop -d -L 0每 2 秒采样 claude 进程树(claude- 子进程,每次采样重新算)每条连接的远端 IP:port 和上传/下载字节增量,写进跟 Linux
探针同一张network_traffic表和同一种os_net事件,网络流量页/世界地图/AI 轨迹在
Mac 上不再永远是空的。不需要 root。没有execve观测(verify的绕过检测仍是 Linux
独有),域名只能靠反向 DNS 兜底。SIGTERM 时会把 nettop 子进程一起收掉。
Web UI 上网络页的几条提示文案改成会告诉你"去跑 bin/CC-Monitor-probe",并提到走本地
代理时远端全是 127.0.0.1、地图上不会有点这种情况。
- 子进程,每次采样重新算)每条连接的远端 IP:port 和上传/下载字节增量,写进跟 Linux
-
install.sh自动安装并接线 ccstatusline:新增第 1 步,检测系统上有没有
ccstatusline(command -v ccstatusline),
没有就npm install -g ccstatusline;装好后如果~/.claude/settings.json里还没有
statusLine配置,install.py的configure_statusline()会自动写入一份(command: "ccstatusline",padding: 0,refreshInterval: 10)。两个条件都要满足才会真的写——
没装就不接(接了也是空跑),已经有statusLine(不管是不是 ccstatusline、不管什么参数)
就绝不覆盖,尊重用户已有的定制。--skip-ccstatusline/CC_MONITOR_SKIP_CCSTATUSLINE=1
能把安装和接线两件事一起跳过(内部会转成install.py --skip-statusline透传下去)。
GeoIP 数据库下载顺延成第 5 步。 -
install.sh第 5 步自动下载 GeoIP 数据库:DB-IP Lite(CC BY 4.0,约 60MB)下到
~/.cc-monitor/dbip-city.mmdb(尊重CC_MONITOR_HOME)。已有任何.mmdb就跳过;
先下到.part再改名、小于 1MB 视为失败并删掉,不会留半截文件让 geoip.js 静默打不开;
失败只提示不中断。--skip-geoip/CC_MONITOR_SKIP_GEOIP=1跳过,CC_MONITOR_GEOIP_URL
换镜像。 -
system_package_install规则覆盖 macOS 的包管理器:brew install/reinstall/uninstall/ remove/rm/upgrade/tap/untap/bundle(含brew cask …)和 MacPorts 的port [-flags] install/uninstall/upgrade/activate/deactivate/selfupdate(port installed、lsof -i :port
这类不会误命中)。首页"软件安装统计"的"系统包管理器"卡片标签同步。顺手修了pacman -Syu不命中的问题(原正则要求 S/R 是最后一个字母)。已有用户的~/.cc-monitor/rules.json
是首次运行时拷的副本,不会自动更新——想要新规则的话删掉它让它重新生成,或者手动改。
修复
- 桌面版(Electron)在 macOS 上 AI 审批台完全没有提醒:页面里走的是浏览器 Notification
API,Electron 渲染进程里Notification.permission永远是 "granted"、new Notification()
也不报错,但底层 UNUserNotificationCenter 对未正式签名的 app(npm run electron用的
node_modules 里那个 Electron.app 只有 ad-hoc 签名)直接拒绝,实测主进程侧是 failed 事件
UNErrorDomain error 1(NotificationsNotAllowed),渲染进程侧静默失败。改为主进程自己
每 2 秒轮询pending_approvals:新请求 →shell.beep()+ Dock 图标跳动 + 角标数字(这
三样不需要任何授权),能弹系统通知就一并弹(点击跳到审批台),弹失败只警告一次不再重试。
页面按 UA 识别 Electron 后关掉浏览器那条路,按钮改成说明文字,避免两边重复。 - Web 终端"新建会话"直接 500(
posix_spawnp failed.):node-pty 靠
prebuilds/<平台>/spawn-helper这个小程序起 pty,npm 解包时(实测 macOS + npm 11)
会丢掉它的可执行位。新增webui/scripts/fix-node-pty-perms.js,作为postinstall
跑一次,lib/sessions.js加载时再幂等地跑一次(打包后没有 postinstall)。 - macOS 上额度/套餐永远显示"没找到 Claude Code 的登录凭证":Claude Code 在 macOS 上
不写~/.claude/.credentials.json,OAuth 凭证存在登录钥匙串里(service
Claude Code-credentials,内容是同一份 JSON)。全新的 Mac 必现。新增
webui/lib/credentials.js:先读文件,没有再security find-generic-password -s "Claude Code-credentials" -w读钥匙串,usage.js/account.js共用。跟有没有装
ccstatusline 无关——我们不调用它,只是复刻它的读取逻辑(它在 macOS 上走的正是钥匙串)。 - macOS 上终端里永远看不到 CC-Monitor 的
[y/N]确认提示:notify.py用文本模式
open("/dev/tty", "r+"),这会套一层要求可 seek 的 BufferedRandom;Linux 上对 tty
lseek返回 0 所以没事,macOS 返回ESPIPE,open()直接抛io.UnsupportedOperation
(OSError子类)被静默吞掉,tty 一直是 None,只剩网页一条路。改成"r+b", buffering=0
的裸 FileIO。 - 终端处于 raw 模式时敲 y/N 可能把 hook 卡死:Claude Code 的 TUI 让终端处于 raw 模式,
回车发过来的是\r不是\n,原来的readline()会一直等那个不来的换行,连网页那条
路的轮询也一起卡住。改为直接读当前可读的字节、看首字符。
English
Added
-
AI Approvals now covers Claude Code's native permission dialog: a new
PermissionRequesthook is registered (re-runninginstall.pyadds it; the existing two
entries are left alone). Previously the approvals page only mirrored operations our rule
table flags asconfirm; Claude Code's own "Do you want to proceed?" prompts (calls that
matched no rule) never showed up — atPreToolUsetime there is no way to know whether a
prompt is coming. These now appear askind='permission'with the same buttons as confirm
(allow / deny / 10 or 30 min / always — "always" is remembered persession + tool name).
An answer from the web page or the terminal is returned through
hookSpecificOutput.decision.behavior; if nobody answers (90s timeout) or Enter is
pressed at the terminal, the hook exits silently and the native dialog appears as usual.
Approval history gains a "Handed back to native dialog" status. -
macOS system-layer probe (network part): new
cc_monitor/probe_darwin.py;
bin/CC-Monitor-probeswitches to it automatically on macOS. It samples the claude process
tree (claude + descendants, recomputed each sample) every 2s with the built-in
nettop -d -L 0, recording each connection's remote IP:port and upload/download byte
deltas into the samenetwork_traffictable andos_netevents the Linux probe uses, so
the traffic page / world map / AI trajectory are no longer permanently empty on a Mac. No
root required. Noexecveobservation (the bypass detection behindverifystays
Linux-only); hostnames fall back to reverse DNS. SIGTERM also tears down the nettop child.
The network page hints now tell you to runbin/CC-Monitor-probe, and mention that with a
local proxy every remote is 127.0.0.1 so the map has nothing to plot. -
install.shnow installs and wires up ccstatusline: new step 1 checks whether
ccstatusline is already onPATH
(command -v ccstatusline) and runsnpm install -g ccstatuslineif not; once it's
available,install.py'sconfigure_statusline()writes astatusLineentry into
~/.claude/settings.jsonif one isn't already there (command: "ccstatusline",
padding: 0,refreshInterval: 10). Both conditions have to hold before anything is
written — not installed means nothing to wire up, and an existingstatusLine(whether
it's ccstatusline or something else, with any settings) is never overwritten, respecting
whatever the user already configured.--skip-ccstatusline/
CC_MONITOR_SKIP_CCSTATUSLINE=1skips both the install and the wiring (internally passed
through asinstall.py --skip-statusline). The GeoIP download step is renumbered to 5. -
install.shstep 5 downloads the GeoIP database: DB-IP Lite (CC BY 4.0, ~60MB) to
~/.cc-monitor/dbip-city.mmdb(honoursCC_MONITOR_HOME). Skipped if any.mmdbis
already present; downloads to a.partfile first and treats anything under 1MB as a
failure (deleted), so a truncated file can never make geoip.js fail silently; a failed
download warns without aborting.--skip-geoip/CC_MONITOR_SKIP_GEOIP=1skips it,
CC_MONITOR_GEOIP_URLpoints at a mirror. -
system_package_installrule now covers macOS package managers:brew install/reinstall/uninstall/remove/rm/upgrade/tap/untap/bundle(incl.brew cask …) and
MacPortsport [-flags] install/uninstall/upgrade/activate/deactivate/selfupdate(things
likeport installedorlsof -i :portdon't match). The "system package manager" card on
the home page is relabelled accordingly. Also fixedpacman -Syunot matching (the old
regex required S/R to be the last flag letter). Note that an existing
~/.cc-monitor/rules.jsonis a copy made on first run and is not updated automatically —
delete it to regenerate, or edit it by hand.
Fixed
- Desktop app (Electron) on macOS gave no alert at all for AI Approvals: the page used
the browser Notification API; in an Electron rendererNotification.permissionis always
"granted" andnew Notification()doesn't throw, but the underlying
UNUserNotificationCenter refuses apps that aren't properly signed (npm run electronruns
the ad-hoc-signed Electron.app from node_modules) — the main-process side gets afailed
event withUNErrorDomain error 1(NotificationsNotAllowed), the renderer side fails
silently. The main process now pollspending_approvalsevery 2s itself: a new request →
shell.beep()+ Dock bounce + badge count (none of which need permission), plus a system
notification when possible (click → approvals tab); a failed notification warns once and
isn't retried. The page detects Electron via UA, disables the browser path and turns the
button into an explanatory label so the two never double-notify. - Web terminal "new session" returned 500 (
posix_spawnp failed.): node-pty spawns
the pty through its bundledprebuilds/<platform>/spawn-helperbinary, and npm dropped
its executable bit when unpacking (seen on macOS + npm 11). New
webui/scripts/fix-node-pty-perms.jsruns aspostinstalland again, idempotently, when
lib/sessions.jsloads (packaged builds have no postinstall step). - macOS always showed "Claude Code credentials not found" for usage/plan: on macOS
Claude Code does not write~/.claude/.credentials.json; the OAuth credential lives in
the login Keychain (serviceClaude Code-credentials, same JSON payload). Every fresh Mac
hit this. Newwebui/lib/credentials.jsreads the file first and falls back to
security find-generic-password -s "Claude Code-credentials" -w; shared byusage.js
andaccount.js. Unrelated to whether ccstatusline is installed — we never invoke it, we
only replicate its lookup (which uses the Keychain on macOS too). - macOS never showed CC-Monitor's
[y/N]prompt in the terminal:notify.pyopened
/dev/ttyin text mode"r+", which wraps aBufferedRandomthat requires a seekable
stream;lseekon a tty returns 0 on Linux butESPIPEon macOS, soopen()raised
io.UnsupportedOperation(anOSErrorsubclass), was silently swallowed, and the tty
path was permanently disabled — only the web path worked. Now opens as a rawFileIO
("r+b", buffering=0). - Typing y/N while the terminal is in raw mode could hang the hook: Claude Code's TUI
keeps the terminal in raw mode, so Enter arrives as\rrather than\n; the old
readline()waited forever for a newline and stalled the web-side polling too. Now reads
whatever bytes are available and inspects the first character.
完整更新日志见 CHANGELOG.md (English)。