hungnv26/claude-cockpit

GitHub: hungnv26/claude-cockpit

一个将旧 iPad/iPhone 变为 Claude Code 实时状态监控与远程遥控面板的 Web 应用,支持用量追踪、会话管理和工具调用审批。

Stars: 3 | Forks: 0

# Claude Cockpit ![平台](https://img.shields.io/badge/platform-macOS-8a8a93) ![专为构建](https://img.shields.io/badge/built%20for-iPad%20Mini%204%20%C2%B7%20iPhone%2011-d97757) ![React](https://img.shields.io/badge/React-18-149eca?logo=react&logoColor=white) ![Fastify](https://img.shields.io/badge/Fastify-5-000000?logo=fastify&logoColor=white) ![Tailwind CSS](https://img.shields.io/badge/Tailwind-v3-38bdf8?logo=tailwindcss&logoColor=white) ![TypeScript](https://img.shields.io/badge/TypeScript-5-3178c6?logo=typescript&logoColor=white) ![许可证](https://img.shields.io/badge/license-MIT-8a8a93) **一个用于挂墙展示的 [Claude Code](https://claude.com/claude-code) 状态面板。** 在书桌旁闲置的 iPad 或 iPhone 上运行,一眼就能看出您已消耗了多少使用量限制、当前的实时会话正在进行什么操作,以及您是否即将达到限制。它不仅可以*监视* Claude Code 会话,还能*驱动*它们。 ![iPad Mini 4 横屏上的 Claude Cockpit](https://static.pigsec.cn/wp-content/uploads/repos/cas/a1/a15da95a1a47a63f5fac9f87d3c7629b8a010385e248635d451116ff47378fba.png)

Claude Cockpit on an iPhone 11, portrait

一个面板,两种布局 —— iPad 上为横屏,手机上为竖屏。

## 显示内容 **监视器**(monitor)视图是一个只读的一览面板: - **使用量仪表盘** —— 显示滚动的 **5 小时** 和 **7 天** 套餐限制,以及基于模型范围的 **Fable** 仪表盘,每个都带有实时重置倒计时。当您接近上限时,颜色会变为琥珀色。 - **实时会话** —— 每个正在运行的 Claude Code 会话(包括由桌面应用启动的会话),及其模型和状态。 - **活动流** —— 实时推送的会话事件持续日志(对话轮次完成、需要批准、进入空闲等)。 - **状态页脚** —— 持久显示的 `LIVE / OFFLINE` 指示器、当前聚焦会话的模型和 effort,以及显示当前上下文饱满程度的全宽 **Context Window**(上下文窗口)进度条。 **控制台**(console)视图将其变成了一个遥控器: - 启动由仪表盘接管的 Claude Code 会话,并从平板电脑对其进行提示。 - 实时切换**模型**,更改 **effort**(保留对话),中断或关闭。 - 当会话需要权限时,直接在 iPad 上**批准或拒绝工具调用**。 ### 两种布局,一键切换 页眉处的控件可在两种设备布局之间切换。它默认匹配视口的纵横比——因此手机会采用竖屏,而平板电脑会采用横屏——随后会记住您的选择。 | | iPad(横屏) | iPhone(竖屏) | |---|---|---| | 仪表盘 | 全尺寸,排成一行 | 较小,三列并排 | | 主体 | 会话 ∣ 活动流,并排显示 | 堆叠显示:先是会话,然后是活动流 | | 会话 | 显示所有会话 | 仅显示最近的 2 个,以保持活动流留在屏幕上 | | 宽度 | 占满整个屏幕 | 手机宽度的列,在更宽的屏幕上居中显示 | 两者都遵循 iOS 的安全区域 insets,因此页眉会避开刘海,页脚会避开主屏幕指示器。 ## 技术栈 | 层级 | 选择 | 原因 | |---|---|---| | 前端 | **React 18 + TypeScript**,使用 **Vite 5** 构建 | 开发循环快;体积小且自包含的 bundle(Gzip 压缩后约 50 KB)。 | | 样式 | **Tailwind CSS v3** | **不是 v4** —— v4 需要 Safari 16.4,否则在 iPad Mini 4 上会渲染出无样式的内容。此固定版本是核心支撑。 | | 后端 | **Fastify 5** + **`ws`** (WebSocket) | 一个长期运行的进程,用于 tail 文件、监视会话并推送更新 —— 这与 Next.js 等请求/响应框架的运行逻辑完全相反。 | | 文件监视 | **chokidar** | 监视 `~/.claude` 以获取会话和记录更改。 | | 运行环境 | 通过 **tsx** 运行的 **Node.js**(直接运行 TypeScript) | 服务器无需单独的构建步骤。 | | 进程管理 | **launchd** (macOS LaunchAgent) | 登录时自动启动,崩溃时重启,可在重启后继续运行。 | | 传输层 | 单个 WebSocket,更新**合并至 250 毫秒** | iPad 的 2015 年 A8 芯片(2 GB 内存)会因为每个 token 触发一次 socket 而不堪重负。 | 在生产环境中,所有内容都通过**单个端口 (5200)** 提供服务 —— Vite 构建的 SPA 和 API/WebSocket 共享同一个 Fastify 服务器。 ## 工作原理 ### 观察层(只读,适用于所有会话) - **会话注册表** —— 监视 `~/.claude/sessions/*.json` 以获取实时会话(pid、cwd、模型、版本),并通过 `process.kill(pid, 0)` 确认其存活状态。 - **记录 Tailer** —— 从字节偏移量开始,增量 tail `~/.claude/projects/**/.jsonl`,并读取最新的助手对话轮次以获取模型和 token 使用情况。Sub-agent 轮次会被跳过,因此上下文仅反映主线程。 - **套餐使用量** —— 使用 macOS Keychain 中的 OAuth token,从 Claude 使用量 API 获取您的限制,并**自动刷新**,确保面板永不过期(详见设计说明)。 - **上下文窗口** —— 当前上下文与模型实际窗口的对比,直接从 Models API(`max_input_tokens`)实时拉取,而不是硬编码的数字。 ### 控制层(驱动应用接管的会话) 桌面会话由 Claude Code 应用通过 stdio 管道接管,无法从外部驱动——因此控制台视图通过 `claude -p --input-format stream-json --output-format stream-json` **启动自己的**会话并完全接管该进程。这些会话可以被提示、切换、进行权限控制,并且也会像任何其他会话一样显示在监视器上。 ### 通知 一组 Claude Code **hooks**(`Notification`、`Stop`、`UserPromptSubmit`)会向服务器发送 POST 请求,从而驱动活动流、“需要您关注”的指示器以及页面内的声音提醒。(由于 Web Push 在 Safari 15 中不可用,因此提醒在页面内进行。) ## 入门指南 **前置条件:** macOS、Node.js 20+、[pnpm](https://pnpm.io) 以及已安装的 Claude Code。使用量面板需要执行一次性的 `claude auth login`。 ``` pnpm install pnpm dev # Vite on :5199, API + WebSocket on :5200 ``` 打开服务器打印的 URL(其中包含一个访问 token): ``` http://:5200/?t= ``` 在 iPad 上,使用 Safari 打开该 URL 一次,然后点击 **Share → Add to Home Screen** 以获得全屏 kiosk 模式。将 **Auto-Lock → Never**(自动锁定 → 从不)并保持充电状态。 ### 永久运行 (macOS) 作为用户级 LaunchAgent 安装 —— 登录时启动,崩溃时重启,并在系统重启后继续运行。生产环境构建,所有内容均在 **:5200** 上运行。`service/install.sh` 会使用您检出代码的绝对路径生成 plist 文件并加载它。 ``` pnpm build sh service/install.sh ``` | 任务 | 命令 | |---|---| | 重启(在 `pnpm build` 之后) | `launchctl kickstart -k gui/$(id -u)/com.cockpit.server` | | 停止 | `launchctl bootout gui/$(id -u)/com.cockpit.server` | | 日志 | `tail -f service/cockpit.log` | ## 项目结构 ``` server/ Fastify + WebSocket backend (TypeScript, run via tsx) hub.ts aggregates all state, coalesces updates, broadcasts sessions.ts watches ~/.claude/sessions registry transcript.ts incremental JSONL tailer for model + token usage limits.ts plan-usage gauges (5h / 7d / Fable) from the usage API credential.ts Keychain read + self-refreshing OAuth token models.ts live context-window sizes from the Models API agent.ts / agents.ts spawns and drives owned Claude Code sessions permissions.ts holds tool calls for iPad approval index.ts HTTP/WS server, token gate, control endpoints src/ React + Tailwind frontend App.tsx layout + view switching components/ LimitsHero, StatusBar, SessionCard, Feed, Console, … ws.ts WebSocket hook + shared types hooks/ Claude Code hooks that POST to the server notify.mjs Notification / Stop / UserPromptSubmit → activity feed permission.mjs PreToolUse → iPad allow/deny service/ launchd launcher (run.sh) + logs ``` ## 设计说明 这些有趣的决策大多是由于 Safari 15 的目标限制,或者是 Claude Code 的实际行为所迫(每一项都对照真实的 CLI 进行了验证,而非主观假设): - **是 Tailwind v3,而不是 v4。** v4 需要 Safari 16.4;而 iPad Mini 4 最高只能升级到 iPadOS 15.8。Vite 的 `build.target` 也被固定为 `['es2020','safari15']`。 - **触摸目标使用绝对像素的 44 px,而不是 `rem`。** 指尖不会随字体大小控件缩放,经过密度缩放的按钮根本无法点击。 - **更新在 250 毫秒处合并**,并且 DOM 数量受到限制 —— 2015 年的 A8 芯片无法承受每个 token 都触发一次 socket。 - **使用量 token 会自动刷新。** 它的 TTL 为 8 小时,并且 refresh token 在每次使用时都会*轮换*,因此应用会将新 token 写回到共享的 Keychain 中(应用私有的副本会导致无论是应用还是 CLI,谁第二个刷新就会被登出)。该 Keychain 条目还包含不相关的 MCP token,因此写回操作仅合并账户字段。 - **上下文窗口是实时拉取的,绝不硬编码。** 几乎所有当前的模型都是 **1M** tokens,而不是 200k —— 如果硬编码 200k,会导致 Opus 的可用度被少报 5 倍。 - **Effort 无法实时更改**(`set_model` 作为控制请求有效,但 `set_effort` 根本不存在),因此 effort 控件会使用 `--resume` 重启会话,这会保留对话状态。 - **权限批准依赖于 `PreToolUse` hook**,而不是 `can_use_tool` —— 因为后者在 `-p` 模式下永远不会触发。返回 `permissionDecision: allow` 可以覆盖 CLI 的静默自动拒绝。 - **故障安全 hooks。** 通知和权限 hooks 在每个对话轮次都会运行,因此每个失败路径(服务器宕机、JSON 错误)都会干净地退出,并交由 Claude Code 的正常行为处理 —— 它们绝不会阻塞会话或伪造批准。 ## 限制 - **仅限 macOS** —— 使用量 token 存储在 macOS Keychain 中,且该服务是一个 launchd 代理。要支持 Linux/Windows 需要进行代码移植。 - **监视器上的 effort 显示为 `—`** —— 针对桌面会话,它是在启动时设置的,从不写入磁盘,因此观察者确实无法知晓。在控制台中它是实时的(因为是由应用做出的选择)。 - **Mac 必须保持唤醒且处于局域网中**,iPad 才能连接到它。 ## 许可证 [MIT](LICENSE) © Hung Ngo
标签:Claude Code, Fastify, iPad, MITM代理, React, Syscalls, 使用量监控, 状态看板, 自动化攻击, 远程控制