商业产业 Hacker News AI 2026-08-10T22:07 正文 ok 1209 words

Show HN:AI Pulse——macOS Dock 旁显示 Agent 状态的虚拟 LED 灯带

英文标题:🔶 Show HN: AI Pulse a fake LED strip beside the macOS Dock that shows agent status

AI Pulse 是一款面向 macOS 的本地应用,在 Dock 旁以 8 颗虚拟 LED 灯汇总显示 AI 编程 Agent 的运行、等待输入、失败、完成和空闲状态。应用不嵌入 Dock,而是通过公开的 macOS 屏幕几何 API 定位无边框 `NSPanel`,不使用私有 API,也不要求辅助功能或屏幕录制权限。项目提供本地 HTTP 事件服务、`aipulse` CLI 和 Claude Code Hook 集成,实测从 CLI 到界面灯带的端到端状态更新约为 150 毫秒。对开发者而言,它提供了一个不读取提示词、终端或编辑器内容的本地 Agent 状态聚合界面,也为接入更多 Agent 工具提供了事件 API 与扩展框架。
打开原文相关 3 · 机会 1
media835.8 KB media49.6 KB media154.3 KB media20.3 KB media8.6 KB media256.2 KB media5.5 KB media101.2 KB

中文

AI Pulse:由八颗 LED 组成的灯带,循环展示其 Agent 状态动画

这是一款面向 AI 编程 Agent 的 macOS 环境灯带,灵感来自 SidePulse 硬件设备——但它以应用形式实现。由八颗虚拟 LED 构成的纤细灯带悬浮在 Dock 旁未使用的空间中,并通过单一聚合信号、不区分具体模型,显示是否有任务正在运行、等待你的操作、已经完成或发生故障:

工作中:青色彗星动画
工作中——当 Agent 运行时,一颗青色彗星掠过 | 需要你操作:橙色呼吸动画
需要你操作——当 Agent 等待输入或批准时,灯带呈橙色呼吸效果 | 失败:红色双闪动画
失败——当会话中断时,灯带红色双闪
---|---|---
已完成:纯绿色 LED
已完成——八颗灯全部稳定显示绿色 | 空闲:缓慢的蓝绿色极光动画
空闲——会话处于静默状态时,缓慢的极光效果漂移 | 关闭:熄灭的 LED
关闭——没有任何连接时保持熄灭

“减少动态效果”(Reduce Motion)会将每种动画替换为静态呈现。原有的按 Agent 显示图标胶囊样式仍可通过“设置 → 外观 → 指示器样式”启用。

AI Pulse 并非嵌入在 Dock 中——macOS 没有提供 Dock 附件的公开 API。它是一个贴近 Dock 的、无边框且不会激活的 NSPanel,基于公开的屏幕几何信息(NSScreen.frame 与 visibleFrame)进行定位。不使用私有 API,不需要辅助功能或屏幕录制权限,也不进行注入。

从最新发布版本下载 AI-Pulse-<version>.zip,解压后将 AI Pulse.app 移至 /Applications 并启动。目前发布版本尚未经过公证,因此首次启动时请右键点击应用 → 打开(在 macOS 15 及更高版本中,还需在系统设置 → 隐私与安全性 → 仍要打开中允许)。aipulse CLI 随应用包提供,路径为 AI Pulse.app/Contents/Helpers/aipulse。

当 PR 合并时会自动创建发布版本:默认递增补丁版本;当 PR 带有 release:minor 或 release:major 标签时,则递增次版本或主版本。

要求(从源代码构建)

  • macOS 14+

  • Xcode 16+ / Swift 6 工具链

    swift build # 构建全部内容(应用、CLI、kit) swift test # 单元测试 + 集成测试(AIPulseKit) swift run AIPulseApp # 启动胶囊指示器(辅助应用:没有 Dock 图标) swift run AIPulseApp --snapshot DIR # 无头渲染胶囊指示器和悬停卡片 PNG,然后退出 swift run aipulse health # CLI:检查本地事件服务 ./Scripts/make-app.sh # 组装并签名 dist/AI Pulse.app ./Scripts/make-gifs.sh # 从无头帧重新生成 docs/ GIF(需要 ffmpeg)

docs/pulse-social.gif 是同一状态循环的更大尺寸、带说明文字版本,适合分享。

日常使用时,请运行打包后的应用,而非裸二进制文件:该脚本会使用你的 Apple Development 身份对其签名,从而提供稳定的代码签名,让 Keychain 能在多次重新构建后持续信任该应用(裸 swift run 二进制文件采用临时签名,每次重新构建后都会触发 Keychain 同意提示——这也是开发循环中存在 AIPULSE_DEV_EPHEMERAL_TOKEN=1 的原因)。CLI 内嵌于 AI Pulse.app/Contents/Helpers/aipulse。

(应用产品名称为 AIPulseApp,因为 AIPulse 与 aipulse CLI 会在不区分大小写的文件系统中发生冲突。)

AI Pulse 监听 127.0.0.1:7455(可在设置中配置)。启动时,它会将 Bearer Token 存储在 Keychain 中,并写入 ~/Library/Application Support/AIPulse/cli.json(权限模式为 0600),以便 aipulse CLI 在认证时无需让 Token 出现在 Shell 命令中:

aipulse agent upsert \
  --id "claude-code:$PWD:$SESSION_ID" \
  --name "Claude Code" --provider anthropic \
  --instance "$(basename "$PWD")" \
  --state working --message "Implementing Dock placement"

aipulse agent update \
  --id "claude-code:$PWD:$SESSION_ID" \
  --state waitingForInput --message "Waiting for permission" --sequence 2

aipulse agent remove --id "claude-code:$PWD:$SESSION_ID"
aipulse agents        # 列出胶囊指示器已知的全部内容

端点包括:POST /v1/agents/upsert、POST /v1/agents/{id}/event、DELETE /v1/agents/{id}、GET /v1/agents、GET /v1/health(health 是唯一不需要认证的路由)。服务器仅绑定回环接口,限制请求大小,验证每个载荷(AgentReducer + RequestValidator),拒绝不安全的 URL Scheme,并且绝不会执行事件中接收到的任何内容。

开发说明:重新构建应用会改变其临时代码签名,因此每次构建都需要重新提示 Keychain 读取授权;开发期间可使用 AIPULSE_DEV_EPHEMERAL_TOKEN=1 启动,以跳过 Keychain 并改用每次运行独立的 Token。

可通过胶囊指示器的右键菜单 → 退出 AI Pulse 退出(或直接终止进程)。

  • Sources/AIPulseKit —— 不依赖 AppKit、经过完整单元测试的核心:
    • Domain/ —— Agent、AgentState、AgentAction(类型安全的操作 + URL Scheme 白名单)、AgentEventPayload(线缆模型)、AgentIntegrationLevel、StatusPriority(紧急程度排序)。
    • Store/AgentStore —— 唯一事实来源;所有界面均从中渲染。
    • Placement/ —— ScreenSnapshot、尽力推断的 DockGeometry、纯粹的 PlacementPolicy(间隙 → 相邻 → 角落回退,以及边界限制)。
  • Sources/AIPulse —— 应用本体:
    • Presentation/Pill/ —— 不激活的 AIPulsePanel、SwiftUI 胶囊界面、各状态图标(字形 + 徽章 + 圆环,绝不只依赖颜色)、悬停卡片。
    • Presentation/AgentList/、Presentation/Settings/ —— 常规的键盘无障碍窗口。
    • Placement/DockPlacementController —— 带防抖的屏幕变化监听;不轮询。

里程碑 1–3 已完成:

  • M1 —— 不激活面板、胶囊 UI、模拟 Agent、悬停卡片、上下文菜单。

  • M2 —— 底部/左侧/右侧 Dock 定位、通过“设置”中的显示器选择器进行多显示器选择、带防抖的屏幕/空间变化监听、屏幕外位置限制、可选的自动隐藏 Dock 跟随功能(尽力而为:读取 Dock 的公开偏好设置域,仅只读,不与 Dock 交互)。

  • M3 —— AgentReducer 事件规范化(版本控制、ID 验证、序列号/时间戳排序、重复拒绝、安全操作映射)、集中式 StalenessPolicy(工作中的 Agent 在可配置的静默时间后被标记为过期,并在更长时间后降级为已断开;其后端进程已经退出的 Agent 会通过 PID 存活检查在一次扫描内降级;已完成 Agent 会在可配置延迟后过期;等待、审批和失败状态不会因计时器而过期),以及持久化到 ~/Library/Application Support/AIPulse/agents.json 并支持重启恢复:尚未解决的需注意状态会保持原样恢复,仅实时有效的状态会恢复为 disconnected,直至其来源再次上报。

  • M4 —— 仅限回环的 HTTP 事件服务(LocalHTTPServer)、面向 CLI 的 Keychain Bearer Token + 0600 握手文件、传输验证、aipulse 发布者 CLI,以及端到端集成测试。已验证实时链路:CLI → HTTP → reducer → store → 胶囊指示器,往返时间约为 150 毫秒。

  • M5 —— Claude Code 适配器(ClaudeCodeAdapter + aipulse claude-hook),基于文档化的 Hook 生命周期构建:SessionStart→idle、UserPromptSubmit/PreToolUse→working、PermissionRequest 和权限提示 Notifications→approvalRequired、空闲提示 Notifications→waitingForInput、Stop→completed、StopFailure→failed、SessionEnd→removed。每个项目的每个会话对应一个胶囊指示器条目。绝不会解码提示词文本、工具输入和助手输出,因此它们绝不会被发布。该 Hook 始终以退出码 0 退出且不输出任何内容。

  • M6 —— 可选动态 Dock 图标(默认关闭;“设置 → 外观”)。通过 DockTileAggregator 聚合同一份供胶囊指示器使用的 AgentStore 快照:失败 > 需注意 > 工作中 > 中性;使用 NSDockTile 自定义内容视图呈现状态,并通过 badgeLabel 显示未确认的紧急 Agent 数量。悬浮胶囊指示器和 Dock 图标可分别独立开关;图标绝不会弹跳。

全部六个 MVP 里程碑均已完成,随后转向以灯光为优先的呈现方式(LightAggregator + LightStripView),同时保留图标胶囊样式作为选项。

该仓库附带 .claude/settings.json,通过调试构建路径为相关 Hook 事件注册 aipulse claude-hook,因此只要应用正在运行,本仓库中的 Claude Code 会话就会自动显示在胶囊指示器中。若要在全系统范围内使用:

swift build -c release
sudo cp .build/release/aipulse /usr/local/bin/

然后在 ~/.claude/settings.json 中注册同样的 Hook,并将命令替换为普通的 aipulse claude-hook。Claude Code 在首次加载项目 Hook 时会要求你批准。

AI Pulse 显示由本地 Agent 集成发送的状态。它不会读取提示词、终端内容、编辑器内容或应用窗口。

欢迎贡献——请参阅 CONTRIBUTING.md,了解项目的设计原则、测试预期以及如何添加新的 Agent 集成。docs/DEBUGGING.md 介绍了调试界面:环境变量、无头快照、日志流、磁盘状态,以及常见故障模式的修复方法。

MIT

English / 原文

AI Pulse: a strip of eight LEDs cycling through its agent-status animations

An ambient macOS light strip for AI coding agents, inspired by the SidePulse hardware gadget — but as an app. A slim strip of eight virtual LEDs floats in the unused space beside the Dock and shows, with a single aggregate signal and no per-model distinction, whether anything is running, waiting for you, finished, or broken:

Working: cyan comet animation
Working — a cyan comet streaks while an agent runs | Needs you: breathing orange animation
Needs you — breathes orange when an agent waits for input or approval | Failed: double-blinking red animation
Failed — double-blinks red when a session breaks
---|---|---
Finished: solid green LEDs
Finished — all eight settle to solid green | Idle: slow blue-teal aurora animation
Idle — a slow aurora drifts while sessions sit quiet | Off: dark LEDs
Off — dark when nothing is connected

Reduce Motion replaces every animation with a static treatment. The original per-agent icon pill remains available via Settings → Appearance → Indicator style.

AI Pulse is not embedded in the Dock — macOS exposes no public API for Dock accessories. It is a Dock-adjacent, borderless, nonactivating NSPanel positioned from public screen geometry (NSScreen.frame vs visibleFrame). No private APIs, no Accessibility/Screen Recording permissions, no injection.

Grab AI-Pulse-<version>.zip from the latest release, unzip, move AI Pulse.app to /Applications, and launch. Releases are not yet notarized, so on first launch right-click the app → Open (on macOS 15+, also allow it under System Settings → Privacy & Security → Open Anyway). The aipulse CLI ships inside the bundle at AI Pulse.app/Contents/Helpers/aipulse.

Releases are cut automatically when a PR merges: patch by default, minor or major when the PR carries a release:minor / release:major label.

Requirements (building from source)

  • macOS 14+

  • Xcode 16+ / Swift 6 toolchain

    swift build # build everything (app, CLI, kit) swift test # unit + integration tests (AIPulseKit) swift run AIPulseApp # launch the pill (accessory app: no Dock icon) swift run AIPulseApp --snapshot DIR # render pill + hover card PNGs headlessly and exit swift run aipulse health # CLI: check the local event service ./Scripts/make-app.sh # assemble + sign dist/AI Pulse.app ./Scripts/make-gifs.sh # regenerate docs/ GIFs from headless frames (needs ffmpeg)

docs/pulse-social.gif is a larger, captioned cut of the same state cycle, made for sharing.

For day-to-day use, run the bundled app rather than the bare binary: the script signs it with your Apple Development identity, giving a stable code signature so the Keychain trusts the app across rebuilds (bare swift run binaries are ad-hoc signed and trigger a Keychain consent prompt after every rebuild — that is also why AIPULSE_DEV_EPHEMERAL_TOKEN=1 exists for dev loops). The CLI is embedded at AI Pulse.app/Contents/Helpers/aipulse.

(The app product is AIPulseApp because AIPulse and the aipulse CLI would collide on case-insensitive filesystems.)

AI Pulse listens on 127.0.0.1:7455 (configurable in Settings). On launch it stores a bearer token in the Keychain and writes ~/Library/Application Support/AIPulse/cli.json (mode 0600) so the aipulse CLI authenticates without tokens ever appearing in shell commands:

aipulse agent upsert \
  --id "claude-code:$PWD:$SESSION_ID" \
  --name "Claude Code" --provider anthropic \
  --instance "$(basename "$PWD")" \
  --state working --message "Implementing Dock placement"

aipulse agent update \
  --id "claude-code:$PWD:$SESSION_ID" \
  --state waitingForInput --message "Waiting for permission" --sequence 2

aipulse agent remove --id "claude-code:$PWD:$SESSION_ID"
aipulse agents        # list everything the pill knows

Endpoints: POST /v1/agents/upsert, POST /v1/agents/{id}/event, DELETE /v1/agents/{id}, GET /v1/agents, GET /v1/health (health is the only unauthenticated route). The server binds to the loopback interface only, caps request sizes, validates every payload (AgentReducer + RequestValidator), rejects unsafe URL schemes, and never executes anything received in an event.

Dev note: rebuilding the app changes its ad-hoc code signature, so Keychain reads re-prompt per build; launch with AIPULSE_DEV_EPHEMERAL_TOKEN=1 during development to skip the Keychain and use a per-run token instead.

Quit via the pill's right-click menu → Quit AI Pulse (or kill the process).

  • Sources/AIPulseKit — AppKit-free, fully unit-tested core:
    • Domain/ — Agent, AgentState, AgentAction (typed safe actions + URL scheme allowlist), AgentEventPayload (wire model), AgentIntegrationLevel, StatusPriority (urgency sort).
    • Store/AgentStore — single source of truth; all surfaces render from it.
    • Placement/ — ScreenSnapshot, best-effort DockGeometry inference, pure PlacementPolicy (gutter → adjacent → corner fallbacks, clamping).
  • Sources/AIPulse — the app:
    • Presentation/Pill/ — nonactivating AIPulsePanel, SwiftUI capsule, per-state icons (glyph + badge + ring, never color alone), hover card.
    • Presentation/AgentList/, Presentation/Settings/ — conventional keyboard-accessible windows.
    • Placement/DockPlacementController — debounced screen-change observation; no polling.

Milestones 1–3 are complete:

  • M1 — nonactivating panel, pill UI, mock agents, hover card, context menu.

  • M2 — bottom/left/right Dock placement, multi-display selection with a Settings display picker, debounced screen/space-change observation, off-screen clamping, opt-in auto-hidden-Dock following (best-effort, from the Dock's public preference domain — read-only, no Dock interaction).

  • M3 — AgentReducer event normalization (versioning, ID validation, sequence/timestamp ordering, duplicate rejection, safe-action mapping), centralized StalenessPolicy (working agents stale after a configurable silence, then demoted to disconnected after a longer one; agents whose backing process has exited are demoted within one sweep via a PID liveness check; completed agents expire after a configurable delay; waiting, approval, and failed states never expire on timers), and persistence to ~/Library/Application Support/AIPulse/agents.json with restart restore: unresolved attention states come back as-is, live-only states come back as disconnected until their source reports again.

  • M4 — loopback-only HTTP event service (LocalHTTPServer), Keychain bearer token + 0600 handshake file for the CLI, transport validation, the aipulse publisher CLI, and end-to-end integration tests. Verified live: CLI → HTTP → reducer → store → pill in ~150 ms round-trip.

  • M5 — Claude Code adapter (ClaudeCodeAdapter + aipulse claude-hook), built against the documented hook lifecycle: SessionStart→idle, UserPromptSubmit/PreToolUse→working, PermissionRequest and permission-prompt Notifications→approvalRequired, idle-prompt Notifications→waitingForInput, Stop→completed, StopFailure→failed, SessionEnd→removed. One pill entry per session per project. Prompt text, tool inputs, and assistant output are never decoded, so they can never be published. The hook always exits 0 and prints nothing.

  • M6 — optional dynamic Dock icon (off by default; Settings → Appearance). Aggregates the same AgentStore snapshot as the pill via DockTileAggregator: failure > attention > working > neutral, with an NSDockTile custom content view for the state treatment and badgeLabel showing the count of unacknowledged urgent agents. The floating pill and the Dock icon are independently toggleable; the icon never bounces.

All six MVP milestones are complete, followed by the pivot to the lights-first presentation (LightAggregator + LightStripView), keeping the icon pill as an option.

This repository ships .claude/settings.json registering aipulse claude-hook for the relevant hook events (via the debug build path), so Claude Code sessions in this repo appear in the pill automatically once the app is running. For system-wide use:

swift build -c release
sudo cp .build/release/aipulse /usr/local/bin/

then register the same hooks in ~/.claude/settings.json, replacing the command with plain aipulse claude-hook. Claude Code asks you to approve project hooks the first time it loads them.

AI Pulse displays status sent by local agent integrations. It does not read prompts, terminal contents, editor contents, or application windows.

Contributions are welcome — see CONTRIBUTING.md for the project's design principles, testing expectations, and how to add a new agent integration. docs/DEBUGGING.md covers the debugging surface: environment variables, headless snapshots, log streaming, on-disk state, and fixes for the common failure modes.

MIT