news 2026/10/15 17:47:03

tokentab 源码解析④:一个不装任何框架、断网也能打开的本地Web仪表盘是怎么实现的

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
tokentab 源码解析④:一个不装任何框架、断网也能打开的本地Web仪表盘是怎么实现的

【免费下载链接】tokentab

A CLI that reads Claude Code, Codex, and Gemini CLI session logs and works out how much they cost, by model, project, and day.

项目地址:https://gitcode.com/gh_mirrors/tok/tokentab
点击查看免费下载

tokentab 是一个读取 Claude Code、Codex 与 Gemini CLI 本地会话日志,按模型、项目、天数统计 AI 编码开销的工具。本文拆解它的本地 Web 仪表盘源码:不引入任何 Web 框架、不依赖 CDN,断网也能在浏览器里打开一张漂亮的"AI 用量账单"。

💡 为什么"离线"是最硬的约束

这是一个纯本地工具:数据都在你自己电脑上,仪表盘自然也不能依赖任何网络资源。可很多小工具的仪表盘,一跑起来就"破功"——字体要拉 CDN、JS 要拉框架、页面要npm构建,任何一环都需要联网。

tokentab 的解法是两个很小的文件:

文件职责
tokentab/web/server.py标准库 HTTP 服务器,只监听 127.0.0.1
tokentab/web/page.py一段自包含的 HTML 字符串:样式 + 标记 + 原生 JS
tokentab/report/serialize.py把报告数据转成 camelCase JSON

page.py 开头的一句注释就把设计目标说清了:no build, no framework, no network——拔掉网线也能打开。

🌐 服务端:只用标准库,一个 Web 服务器最少要写多少行

整个服务器就是ThreadingHTTPServer+ 一个自定义的BaseHTTPRequestHandler,一行框架代码都没有。do_GET 里的路由只有两条:

  • GET /→ 返回render_page()生成的整页 HTML
  • GET /api/report?period=week→ 重新读日志、聚合,返回 JSON

每次请求都重新读盘

server.py 顶部注释解释了取舍:数据很小,没有任何理由去缓存,而缓存恰恰意味着可能给你看一份过期的数字。所以"每次请求都从磁盘重读"在这里是特性,不是缺陷。

4 个值得抄的细节

  1. 只绑定 localhost:L65 写死127.0.0.1,账单数据不会离开这台机器。
  2. 端口被占用自动换:指定的端口拿不到时,用0让操作系统挑一个空闲端口(L64-L67)。
  3. 安静且抗错:覆写log_message关掉请求日志(L26-L27);每个请求包在try/except里,一次坏请求只会得到 500,不会杀死整个服务(L47-L48)。
  4. 自动开浏览器:启动 0.4 秒后用threading.Timer打开浏览器,失败也只是静默吞掉——"锦上添花,不是刚需"(L74-L75)。

📄 前端:一整个 HTML 字符串画出来的"账单"

整页就是一个原始字符串PAGE_HTML(page.py L11),CSS、标记、JS 全部内联,零外部资源。"离线"这件事,主要靠它保证:

  • 只用系统字体:L28-L30 的注释特意强调——只用系统里现成的衬线/等宽字体栈,不碰字体 CDN。
  • 零构建:render_page()(L374-L375)直接返回字符串,没有 node_modules,没有打包器。
  • JS 只干一件事:fetch("/api/report?period=" + p)然后画图,见 load()。顶部 Today / 7 days / 30 days / Month / All 几个按钮,只是切换请求里的period参数,时间范围由 range_for() 在服务端算好。

视觉上也刻意做成一张纸质账单:米色纸底、点状引导线、带边框的"total spend"总额块、底部一枚斜着的"LOCAL COPY"印章——是文档,不是终端(设计说明见 L6-L8)。

🔗 数据链路:从会话日志到一行 JSON

仪表盘本身不含任何业务逻辑,每次请求都走同一条三步流水线,与 CLI 完全一致:

  1. 采集:collect_all() 读取各工具的本机日志,摊平成带单价的PricedRecord列表;
  2. 聚合:build_report() 按工具、模型、项目、活动、天分组;
  3. 序列化:report_to_dict() 转成 camelCase 字典——--json输出和/api/report接口共用同一种形状(L1-L5)。

这也是为什么"新增一个工具"只是在 tokentab/providers/ 下加一个模块:定价、分组、仪表盘全都只认那一种共享数据形状。

✅ 动手跑起来:3 步启动本地仪表盘

按 README 的说法:

git clone https://gitcode.com/gh_mirrors/tok/tokentab cd tokentab pip install .

然后执行python cli.py -web,终端会打印http://localhost:4747并自动打开浏览器;端口想自定义用--port,不想被抢走浏览器就加--no-open(README L56-L64)。

📦 值得抄走的 3 个实现要点

  1. 标准库优先:整个项目唯一的第三方依赖是 rich,Web 服务器、JSON、URL 解析全来自标准库——安装零负担。
  2. 新鲜优于缓存:数据小的时候,"每次请求都重读磁盘"比一套缓存层更诚实。
  3. 离线保证在前端:零 CDN、零框架文件、系统字体——"断网能打开"的最后一公里,往往不在服务器,而在页面悄悄加载的那些资源。

一句话总结:本地仪表盘不需要重型方案,标准库服务器 + 一个自包含 HTML,就已经把"断网可用"这件事做成了。

【免费下载链接】tokentab

A CLI that reads Claude Code, Codex, and Gemini CLI session logs and works out how much they cost, by model, project, and day.

项目地址:https://gitcode.com/gh_mirrors/tok/tokentab
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 17:47:03

万耦开发板FlashDB四层栈移植:W25Q64+SFUD+FAL实战指南

简介:本资源是一套面向嵌入式开发者的完整 FlashDB 轻量级键值数据库移植与实战例程,适用于 STM32 平台(特别是中移物联万耦开发板)的 W25Q64 Flash 存储扩展场景,解决嵌入式设备中 Flash 手动管理复杂、擦写寿命短、数…

作者头像 李华
网站建设 2026/10/15 17:46:42

CAsyncSocket实战:MFC异步Socket事件机制与避坑指南

简介:一个涵盖客户端与服务端的CAsyncSocket示例项目,完整演示了在MFC框架下构建基于TCP/IP的异步通信过程,适合正在学习Windows网络编程、希望掌握套接字事件驱动模型的开发者。客户端与服务端双工程对照展示,从Create创建套接字…

作者头像 李华
网站建设 2026/10/15 17:45:36

C++从零手搓CNN:MNIST实战与嵌入式部署指南

简介:这份资源面向深度学习初学者与具备一定C基础的开发者,用纯C从零实现卷积神经网络与全连接神经网络,并在MNIST手写数字数据集上完成训练与精度验证,帮助读者绕开框架黑盒,真正理解卷积、池化、反向传播与梯度下降的…

作者头像 李华
网站建设 2026/10/15 17:45:36

浏览器扫码实战:html5-qrcode 同时识别条形码与二维码的落地指南

简介:这是一份面向前端开发者的网页扫码功能插件资源,基于 HTML5 与 JavaScript 实现,可在浏览器中调用摄像头识别条形码与二维码,适用于电商商品查询、物联网设备配对、移动支付验证等需要便捷数据录入的场景。压缩包共 88 个文件…

作者头像 李华
网站建设 2026/10/15 17:45:23

并查集实战:无向迷宫与有向树的判定逻辑差异

1. 这两道题不是“树”的判断题,而是并查集的临床诊断手册刚接触 HDU 1773 和 HDU 1325/POJ 1308 的时候,我下意识地翻开了《算法导论》里关于“树”的定义:连通、无环、边数 顶点数 - 1。结果交了七次,WA(Wrong Answ…

作者头像 李华