把Claude用量放进macOS菜单栏:一个可以完整读完的小工具

发布时间:2026/8/29 1:38:45
把Claude用量放进macOS菜单栏:一个可以完整读完的小工具 在 Claude API 和 Claude Code 的使用逐渐进入日常开发后一个高频问题开始浮现想看当前用量总要先打开浏览器、登录控制台、找到用量页面再等图表加载。如果只是每周看一眼还好一旦每天大量调用 API 或长时间使用 Claude Code这种“打开网页才能看到用量”的方式就会明显打断节奏。这个标题为 “Show HN: A Claude usage menu bar small enough to read before you run it” 的项目正是针对这个场景出现的把 Claude 用量放到 macOS 菜单栏让开发者随时瞄一眼就知道今天的输入输出 token、请求数大概处于什么水平。更值得注意的不是“菜单栏展示”这个结果而是标题后半句 “small enough to read before you run it”。这句话的意思是这个工具足够小你可以在运行它之前把代码完整读一遍。这是一个非常重要的工程信号意味着项目追求代码量小、行为透明、没有黑盒逻辑。本文不讨论某个闭源成品而是把这类工具的完整实现思路拆开讲清楚并提供一个可以自行跑通的最小版本包含数据层、菜单栏展示层、常见报错和上线前的检查清单。1. 先理解这类菜单栏工具到底在解决什么问题1.1 用量查看的现状为什么菜单栏更有优势Claude 用量信息通常散落在不同地方。如果用官方 API响应里会返回input_tokens、output_tokens、cache_creation、cache_read等字段如果用 Claude Code会话过程中也会消耗 token如果想看账号级别的聚合数据则需要到控制台的用量页面或通过管理类接口获取。问题在于这些数据没有一个“统一入口”能让开发者日常零成本查看。浏览器的控制台页面适合做周报但不适合高频查看。菜单栏应用的优势是常驻、轻量、无需打开浏览器鼠标移过去就能看到数据。对写代码的人来说这是把“监控”从主动查询变成被动显示减少了上下文切换。1.2 “small enough to read before you run it” 到底指什么这句标题不是单纯强调代码行数少它背后有三层含义第一层是代码可读性。一个工具如果只有几十到几百行开发者可以快速理解它的入口、数据源、刷新逻辑和退出方式。第二层是安全透明。工具需要读取 API Key 或本地凭据如果代码量小用户就能确认数据发往哪里、有没有其他外部请求避免闭源工具偷偷上传信息。第三层是维护成本低。小工具出问题时日志和堆栈一眼就能看完不需要借助复杂的调试链路。这也是开源小工具最常见的价值体现它不追求功能大而全而是把“一个场景、一个入口、一个显示”做到足够清晰。1.3 适合哪些人以及使用前提这个工具适合三类人使用 Anthropic API 做应用的开发者需要关注 token 消耗和成本。高频使用 Claude Code 的开发者希望快速估算当天消耗。管理多个工作区或账号的团队成员希望用一个统一视图观察用量趋势。前提是需要有一个可以读取用量数据的来源。没有数据源菜单栏 UI 就只是一个空壳。所以在实现之前第一步不是写界面而是确认“数据从哪来、认证怎么解决、刷新频率怎么控制”。2. 环境准备先把 Claude 本机环境跑通2.1 学习环境与生产环境的最低要求如果你只是想把菜单栏工具在自己的机器上跑起来环境要求并不高项目学习环境要求生产环境建议操作系统macOS 13 或更高macOS 13 或更高且固定发布版本Claude 凭据有 Anthropic API Key 或 Claude Code 已登录使用权限受限的只读 Key开发工具Xcode 15 或 Node.js 18建议使用稳定版 Xcode 和 Node.js LTS网络能正常访问 Anthropic API需要稳定的网络和监控告警在普通开发机上先不要追求复杂架构。用本地 JSON 文件作为临时数据源先让菜单栏 UI 跑通再逐步把真实数据接入。2.2 安装 Claude Code 的常见检查点虽然菜单栏工具不一定依赖 Claude Code但如果你计划统计 Claude Code 的消耗建议先把 Claude Code 本机命令安装正确。官方最常用的安装命令是npm install -g anthropic-ai/claude-code安装完成后用以下命令验证claude --version如果命令能正常输出版本号说明全局路径已经配置好。首次使用时还需要完成账号登录登录后 Claude Code 会在本机保存凭据后续调用会复用这套身份。2.3 安装过程中最常见的四类报错基于大量用户反馈和我自己的排查经验以下四类报错出现频率最高错误现象常见原因检查方式处理建议claude : 无法将“claude”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。或claude 不是内部或外部命令npm 全局 bin 目录没有加入 PATH或安装未成功执行npm config get prefix查看全局安装路径把%AppData%\npm或$(npm prefix)/bin加入 PATH重新打开终端error: claude native binary not installed. either postinstall did not runnpm 安装时 postinstall 脚本未执行常见于权限不足或安装源异常查看 npm 安装日志确认 install 脚本是否运行清理 node_modules 缓存后重新安装确认 npm 没有关闭ignore-scriptsunfortunately, claude is not available to new users right now当前账号或所在区域不在服务开放范围内查看官方服务的区域和账号说明以官方当前开放状态为准不要在受限环境下寻找规避方案connection dropped (econnreset) · retrying in 3s · attempt 4/1网络连接不稳定或本地网络环境干扰长连接用ping和普通 curl 测试基础连通性调整网络环境后重试若持续失败检查本地网络配置注意安装报错必须先看完整日志再看命令返回码。很多问题并不是命令写错而是路径、权限或网络导致安装脚本中途失败。2.4 获得用量数据的前置条件要显示用量至少需要一种数据来源方式一使用 Anthropic API 的调用记录在每次 API 响应中提取usage字段并累加。方式二使用控制台页面或管理类接口提供的聚合用量。方式三使用 Claude Code 的日志和本地缓存来估算 token 消耗。如果你是独立开发者方式一最可控因为数据完全由自己的请求产生。方式二适合查看账号级汇总但具体接口结构会随官方版本变化落地前需要确认当前文档。方式三的优点是无需额外鉴权但字段和日志路径可能随 Claude Code 版本变化适合做估算而不是精确统计。3. 设计一个清晰的用量读取与展示流程3.1 数据层与 UI 层要解耦菜单栏小工具最容易犯的错误是把网络请求、数据解析、界面刷新全部写在一个文件里。项目称为 “small enough to read”恰恰说明它应该保持职责分离。推荐的分层方式数据采集脚本负责从 API 或日志读取数据输出统一格式的 JSON 文件。菜单栏应用只读取 JSON 文件负责展示和交互。配置文件控制刷新间隔、API 地址、显示格式等。这样做的好处很明显数据采集脚本可以单独测试菜单栏 UI 可以用样例数据先跑通真实环境切换时不需要改 UI 代码。例如统一输出文件路径可以设为~/claude-usage.json结构如下{ updatedAt: 2025-06-01T10:30:00Z, today: { inputTokens: 1250000, outputTokens: 380000, cacheReadTokens: 900000, cacheCreationTokens: 12000, requests: 327 } }这个 JSON 既包含更新时间又包含当天累计数据。菜单栏 UI 只需要读取、解码、显示三步逻辑非常容易理解。3.2 两种数据源的选型建议需求推荐数据源理由只看当前项目的成本趋势本地统计 API 响应中的 usage 字段精确到每次请求数据完全可控查看账号每日汇总控制台或管理类接口适合做整体报表但需要额外鉴权统计 Claude Code 消耗Claude Code 日志与本地记录无需自己调用 API但字段可能随版本变化做成团队共享面板管理类接口 服务端缓存避免每台机器频繁请求上游接口选型时还要考虑一个现实问题API 响应中的 usage 字段只能统计本机发出的请求如果你有多台机器或多个用户单机统计会漏数据。账号级聚合更适合这种场景。3.3 菜单栏显示信息的设计原则菜单栏空间很有限不能把大段文字直接塞进去。合理的显示方式是这样菜单栏主标题只显示最核心的两个数字例如C 1.25M / 0.38M表示输入和输出 token。下拉窗口显示更多细节包括请求数、缓存命中、更新时间。提供“刷新”“打开控制台”“退出”三个操作。在 UI 设计上要避免菜单栏标题太长。如果单日输入 token 达到百万级别可以用缩写格式例如1.2M而不是1250000。3.4 刷新策略轮询频率不能太激进菜单栏工具的刷新频率需要平衡实时性和资源占用。常见的策略是初始化时立即刷新一次。之后每 60 秒读取一次本地 JSON 文件。如果数据源是远程 API不要放在 UI 主线程同步请求建议在后台任务中异步完成。请求失败时保留上一次成功数据并在 UI 上标记 “stale”而不是直接清空显示。这种策略能保证用户在任何时刻打开菜单栏都有数据可看同时避免高频请求触发限流。4. 最小实现第一步用脚本把数据整理成 JSON4.1 一个可读性极高的 Node 脚本示例在没有拿到原项目源码的情况下这里给出一个通用实现思路。数据采集脚本的职责是读取真实用量数据整理成统一 JSON 文件。下面这个脚本先演示如何生成结构#!/usr/bin/env node // fetch-claude-usage.js const fs require(fs); const path require(path); function buildUsageRecord() { // 在真实项目中这里应该从 Claude API 或日志读取数据。 // 此处使用示例值主要演示数据结构。 const inputTokens 1250000; const outputTokens 380000; const requests 327; return { updatedAt: new Date().toISOString(), today: { inputTokens, outputTokens, requests } }; } const outPath path.join(process.env.HOME, claude-usage.json); fs.writeFileSync(outPath, JSON.stringify(buildUsageRecord(), null, 2)); console.log(written to, outPath);运行方式node fetch-claude-usage.js运行后会在用户根目录生成claude-usage.json。后面的菜单栏 UI 不需要关心这个脚本内部如何取数只需要读取 JSON。这种解耦方式让整个工具保持“小到可以读完”的状态。4.2 接入真实 API 时应该注意什么如果要接入真实 API代码结构可以扩展为async function fetchUsage() { const apiKey process.env.ANTHROPIC_API_KEY; const url process.env.CLAUDE_USAGE_API_URL; if (!apiKey || !url) { throw new Error(请先设置 ANTHROPIC_API_KEY 和 CLAUDE_USAGE_API_URL); } const res await fetch(url, { headers: { Authorization: Bearer ${apiKey} } }); if (!res.ok) { throw new Error(usage API error: ${res.status}); } return await res.json(); }这段代码不包含具体端点地址因为不同账号和版本的接口结构变化较大。落地前要先确认官方文档中的认证方式、请求路径和返回字段然后把返回结果映射成上面统一的 JSON 结构。注意不要把 API Key 写死在脚本里。建议从环境变量读取或者使用系统钥匙串。菜单栏应用本身要展示数据但不应该成为泄漏密钥的入口。4.3 用样例数据验证 UI 的最佳姿势在没有真实 API Key 之前可以直接手动创建~/claude-usage.json放入几组数字。这样菜单栏应用在开发阶段也能完整跑通不会因为拿不到真实数据而卡住。cat ~/claude-usage.json EOF { updatedAt: 2025-06-01T10:30:00Z, today: { inputTokens: 1250000, outputTokens: 380000, cacheReadTokens: 900000, cacheCreationTokens: 12000, requests: 327 } } EOF这个文件就是整个菜单栏 UI 的“测试桩”。等真实采集脚本写好后只需要把同样的 JSON 结构输出到同一个路径UI 代码完全不用改。5. 用 SwiftUI 实现菜单栏展示层5.1 为什么选择 SwiftUI 的 MenuBarExtra在 macOS 13 及以上版本SwiftUI 提供了原生的MenuBarExtra场景用于创建菜单栏常驻应用。它比传统 AppKit 的NSStatusBar写法更简洁也比 Electron 方案轻量得多。对一个 “small enough to read before you run it” 项目来说用 SwiftUI 能最大程度控制代码量。创建 Xcode 项目时选择 macOS App然后在App入口中把主场景替换为MenuBarExtra。下面是完整的最小示例import SwiftUI import AppKit main struct ClaudeUsageMenuBarApp: App { var body: some Scene { MenuBarExtra { UsageView() } label: { Label(C, systemImage: chart.bar) } .menuBarExtraStyle(.window) } }这段代码声明了一个菜单栏应用点击菜单栏图标后会弹出一个小窗口窗口内容是UsageView。5.2 展示用量信息的主视图UsageView负责展示数据并提供刷新、打开控制台、退出三个操作struct UsageView: View { StateObject private var viewModel UsageViewModel() var body: some View { VStack(alignment: .leading, spacing: 8) { Text(Claude 用量) .font(.headline) Divider() Text(今日请求数\(viewModel.usage?.requests ?? 0)) Text(输入 tokens\(viewModel.usage?.inputTokens ?? 0)) Text(输出 tokens\(viewModel.usage?.outputTokens ?? 0)) Divider() HStack { Button(刷新) { viewModel.refresh() } Button(打开控制台) { viewModel.openConsole() } Spacer() Button(退出) { NSApplication.shared.terminate(nil) } } } .padding() .frame(width: 280) } }这里用StateObject管理 ViewModel视图只做声明式展示。如果后续要增加图表、颜色标识或告警状态只需要在视图层扩展。5.3 读取 JSON 与定时刷新UsageViewModel是整个 UI 层的核心读文件、解析、定时刷新都在这里struct UsageRecord: Decodable { let updatedAt: String let today: DailyUsage } struct DailyUsage: Decodable { let inputTokens: Int let outputTokens: Int let cacheReadTokens: Int? let cacheCreationTokens: Int? let requests: Int } final class UsageViewModel: ObservableObject { Published var usage: UsageRecord? private let usageURL URL(fileURLWithPath: NSString(~/claude-usage.json).expandingTildeInPath) init() { refresh() Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in self.refresh() } } func refresh() { guard let data try? Data(contentsOf: usageURL) else { return } let decoder JSONDecoder() if let record try? decoder.decode(UsageRecord.self, from: data) { DispatchQueue.main.async { self.usage record } } } func openConsole() { // 控制台地址以实际账号所在环境为准 if let url URL(string: https://console.anthropic.com/) { NSWorkspace.shared.open(url) } } }这个 ViewModel 在 60 秒间隔内读取本地 JSON 文件解析成功后更新Published属性。由于读取的是本地文件操作非常快不会阻塞主线程。5.4 运行与验证方法在 Xcode 中运行项目后菜单栏会立即出现一个柱状图标。点击后弹出窗口应该能看到~/claude-usage.json中的数据。验证路径可以按顺序检查菜单栏是否出现图标。点击图标后窗口是否正常弹出。窗口里的数字是否和 JSON 文件一致。修改 JSON 文件中的数字等待最多 60 秒确认界面会自动更新。删除 JSON 文件确认应用不会崩溃而是继续显示上一次缓存数据。如果第 3 步数据不一致优先检查 JSON 字段名是否与DailyUsage完全匹配。Swift 的Decodable对字段名大小写敏感错一个字段就会导致整次解析失败。6. 更轻量的替代方案xbar 脚本式菜单栏6.1 xbar 的思路菜单栏只显示脚本输出如果不想为这么小的工具维护一个 Xcode 工程可以考虑 xbar 这类菜单栏脚本工具。xbar 的做法是把一个可执行脚本放在固定插件目录脚本的stdout会直接变成菜单栏文字。脚本每运行一次菜单栏就刷新一次。这个方案极其适合 Claude usage 场景因为整个“工具”可以只用一个 Python 脚本实现完全满足 “small enough to read before you run it”。6.2 一个 Python 脚本示例把下面的脚本保存为claude-usage.10s.py放到 xbar 插件目录并赋予可执行权限#!/usr/bin/env python3 import json import os from pathlib import Path usage_file Path(os.path.expanduser(~/claude-usage.json)) if not usage_file.exists(): print(C: no data) print(---) print(未找到用量文件) raise SystemExit(0) data json.loads(usage_file.read_text()) today data.get(today, {}) input_tokens today.get(inputTokens, 0) output_tokens today.get(outputTokens, 0) print(fC {input_tokens / 1000:.1f}k / {output_tokens / 1000:.1f}k) print(---) print(f今日请求数: {today.get(requests, 0)}) print(f更新时间: {data.get(updatedAt, unknown)})文件名中的.10s.py是 xbar 的刷新约定10s表示每 10 秒运行一次py表示用 Python 执行。运行之后菜单栏会直接显示类似C 1250.0k / 380.0k的文本点击后展开详情。6.3 方案对比SwiftUI、xbar 与 Electron 类方案方案代码量构建成本依赖适用场景SwiftUI MenuBarExtra中等需要 Xcode 编译macOS 13想做成正式菜单栏应用xbar 脚本很少无编译直接放脚本xbar、Python快速验证、个人监控Electron/Tauri多高依赖 Node/Rust跨平台框架需要复杂 UI 或跨平台发布从“可读性优先”的角度看xbar 方式最贴近项目标题的描述一个脚本就是全部逻辑用户可以在运行前完整读完。SwiftUI 方案适合后续扩展比如要做通知、图表、多账号切换。7. 常见问题与排查路径7.1 菜单栏图标不出现或点击无反应问题现象常见原因检查方式处理建议运行后菜单栏没有图标macOS 版本低于 13MenuBarExtra 不可用打开“关于本机”查看系统版本升级系统或改用 xbar 方案点击图标后窗口一闪而过.window 样式下窗口未正确加载查看 Xcode 控制台日志检查 View 是否包含强制解包逻辑运行时直接被系统拦截自编译应用未签名查看系统弹窗在系统设置中允许运行或进行开发者签名7.2 数据显示为 0 或完全不刷新这类问题集中在数据层。排查顺序是先确认 JSON 文件存在再确认字段名一致最后确认刷新逻辑执行。ls -l ~/claude-usage.json cat ~/claude-usage.json如果文件存在但 UI 仍为 0很可能是Decodable解析失败。建议在 ViewModel 里加一行打印把解码错误输出到日志do { let record try decoder.decode(UsageRecord.self, from: data) DispatchQueue.main.async { self.usage record } } catch { print(decode error: \(error)) }如果文件不存在说明数据采集脚本没有执行成功。先单独运行脚本确认脚本能输出 JSON 后再回头排查 UI。7.3 数据采集脚本或 API 返回认证错误如果使用真实 API401 Unauthorized是最常见的错误。这时要检查三个点环境变量ANTHROPIC_API_KEY是否设置正确。当前 Key 是否拥有读取用量数据的权限。请求地址是否与账号所在区域匹配。如果使用 Claude Code 的本地身份则要检查本机登录状态是否过期。可以重新执行 Claude Code 的登录流程然后再运行采集脚本。7.4 Claude Code 安装阶段残留问题在菜单栏工具之前很多用户会先被 Claude Code 的安装问题卡住。这里把最典型的场景列全问题现象常见原因处理建议claude : 无法将“claude”项识别为 cmdletnpm 全局路径不在 PATH找到 npm 全局 bin 目录并加入 PATHerror: claude native binary not installedpostinstall 脚本未执行清理缓存后重装并检查 prefer-offline 等 npm 配置your organization has disabled claude subscription access组织策略限制联系组织管理员确认订阅权限deepseek-v4-pro is not a model this version of claude code recognizes配置了当前版本不认识的模型名检查模型名和 Claude Code 版本是否匹配排查时不要只看错误码还要看错误发生的阶段。安装阶段错误优先检查 npm 和网络登录阶段错误优先检查账号权限运行阶段错误优先检查模型配置和网络连接。7.5 本地网络不稳定导致请求重试收集到ECONNRESET或connection dropped时Claude Code 会显示重试提示。此时不要反复重装先确认网络基础连通性curl -I https://api.anthropic.com如果连通失败应从网络设备、DNS、本地防火墙等方向排查。如果只是偶发的长连接断开正常重试即可。8. 最佳实践与扩展方向8.1 发布前检查清单把一个菜单栏小工具给其他人使用之前建议按这份清单逐项检查[ ] API Key 没有硬编码在源码或配置文件里。[ ] 应用只发起当前数据源必需的网络请求。[ ] 本地 JSON 文件的权限不是全局可读。[ ] 刷新失败时保留上次数据并显示更新时间。[ ] 日志输出不包含完整 Key 和敏感请求体。[ ] 已确认目标 macOS 版本兼容性。[ ] 退出按钮会真正结束进程而不是隐藏到后台。[ ] 没有依赖会随时间失效的硬编码路径。其中最关键的一条是“失败时保留上次数据”。菜单栏工具的典型使用场景是长时间挂着如果某次刷新生效瞬时网络异常就把界面清空体验会非常差。8.2 生产环境还需要考虑什么如果只是个人使用一个 SwiftUI 应用或 xbar 脚本已经足够。但当你想让团队一起使用时就要补上几件事配置外置化API 地址、刷新间隔、显示格式不要写在代码里用配置文件或环境变量维护。日志与监控记录刷新成功、失败、耗时方便远端排查。权限分级尽量使用只读 Key不要把写权限和账号管理权限暴露给本机脚本。多用户兼容不同用户的家目录路径不同不要写死绝对路径。自动更新如果分发为 App需要签名、公证和更新通道如果只是脚本建议在脚本里加入版本自检。8.3 可以继续扩展的方向这个工具的扩展空间很大但每次扩展都要回到“small enough to read”的原则不要一次性堆叠太多功能。值得做的方向包括预算告警当今日 token 消耗超过阈值时用系统通知提醒。多账号支持在菜单栏切换不同账号的用量。按项目统计结合 Claude Code 的会话记录拆分不同目录的消耗。历史趋势在本地保存最近 30 天的用量生成简单趋势图。导出报表把月度用量导出成 CSV方便和财务对账。这些方向里预算告警的价值最高。因为多数开发者不是不知道用量而是忘记去看。菜单栏展示解决了“看”的便利性通知则解决“忘”的问题。8.4 给新手的实践建议如果你想亲手实现一个类似工具建议按这个顺序推进先用脚本读一次 API 响应打印出usage字段理解数据长什么样。把数据整理成统一 JSON 文件确保脚本能独立运行。用样例 JSON 跑通菜单栏 UI。接入真实数据源替换脚本中的模拟数据。加入定时刷新、失败保留、日志输出。最后再加通知、图表等扩展功能。不要在第一步就直接去写菜单栏 UI。先解决“数据从哪来”再解决“数据怎么显示”整个开发过程会顺利很多。保持代码量小不是偷懒而是让工具在面对不确定的 API 变化和系统环境时仍然可以被快速理解和修复。对这类菜单栏小工具来说可读性本身就是一种维护性和安全性。