Repository navigation
Conversation
A local, read-only token-usage dashboard for MiniMax Code. It reads the runtime's own per-call usage table and observability logs, so it shows the numbers the client actually recorded rather than an estimate reconstructed from session transcript files. - Usage trend with per-model stacked bars and cache-hit-rate / output-speed curves; five detail tables (per-model, daily, a 12-column per-call ledger, a turn-level conversation table, session ranking). - Output speed uses the same basis as the client interface: Sigma output tokens / Sigma decode duration, time-weighted, with P50/P90. - Every call is paired with its own decode and request durations. The pairing rule was verified against a token-signature ground truth over 16,723 calls with zero mismatches. - No Python, no extra process in the normal path, no third-party runtime code except a vendored copy of Chart.js (MIT, full licence text included). - Strictly read-only: no files written, no network, no telemetry. Query parameters are an explicit per-endpoint allow-list. Includes the English README with the required "## Tested environment" and "## Data & access" sections, a Chinese README, an Apache-2.0 LICENSE, a synthetic-data preview image, and a validation record.
MyPrototypeWhat
left a comment
There was a problem hiding this comment.
Hi @HaneWari,欢迎第一次向本仓库投稿!逐次调用台账、turn 级对话明细、以及和客户端同口径的输出速率,方向很清楚;文档也写得很完整(Data & access、与另外两块看板的对照、ENTRY_STDOUT 的说明、合成数据预览)。在 0414d527b9d9d53a2701ed7ab7153643e9588286 上本地看过:范围只有 plugins/hanewari/token-meter-board/ 和两个根 README 各一行;Apache-2.0 + 内置 Chart.js 的 MIT 文本齐全;代码路径上确认是 DatabaseSync(..., { readOnly: true }),没有对宿主文件的写入;npm run check 对本包是 0 errors, 1 warning(就是你说明过的 db-child.mjs stdout)。
不过有一项仓库红线,这一版没法合入。
必须修改
- 请去掉对 MiniMax Code 内部数据库的直接访问(只读也不行)
社区插件不能直接打开 MiniMax Code 的内部数据库文件,读、写都不允许。PR / README 里写的数据来源,和代码行为是一致的,并不是「文档写了、代码其实没开」:
miniapp/node/server.mjs:24-34:从context.dataDir向上最多 8 层,专门查找宿主数据目录里的内部数据库文件,再落到resolveDataDir()。miniapp/node/lib/common.mjs:39-55:dbPath/openDb用node:sqlite的DatabaseSync打开该库(readOnly: true)。- 查询侧实际读到的表包括用量台账、消息行、会话行:
common.mjs:74-78(local_runtime_token_usage)、facts.mjs:46-53(local_runtime_message_rows,并在 SQL 里substr出用户消息前 200 字)、queries.mjs:67(local_runtime_sessions的title/workspace_dir)。 - 同一条数据路径还顺带读了宿主观测日志与配置:
common.mjs:304(v2/observability/logs)、common.mjs:137-158(config.yaml,整文件读入再解析显示名)。
因此「只读」「不出网」「参数白名单挡住 dataDir」这些加固都成立,也改变不了:插件在打开并查询宿主内部库。请整条数据路径拿掉(数据库、为关联模型/状态而扫的观测日志、以及为显示名去读的宿主 config.yaml)。没有这条路径之后,看板需要改成不依赖宿主内部库的数据来源(例如画廊里已有、基于会话记录文件的做法),或先收缩到不碰宿主内部库的功能;README 的 Data & access 也请一并改到与真实行为一致。
页面实际会带出的内容(在去掉上述访问之前,也请心里有数——这些都来自那座库):对话表明细的 userPreview(用户消息前 200 字,facts.mjs:36 / queries.mjs:308)、会话标题与 id、会话排行 tooltip 里的绝对工作区路径(queries.mjs:392)。用户若把密钥或路径贴进对话,前 200 字会出现在本机页面的 JSON 响应里。
建议(不阻塞;当前红线未解决前可先放着)
/api/health与查询meta带绝对dataDir(server.mjs:173,queries.mjs:98):成功路径也会把宿主数据目录绝对路径送给页面。若以后有不依赖内部库的版本,建议改成 basename,或干脆不返回。- 报错文案里的绝对路径(例如
common.mjs:52的not found at ${file}):同样建议脱敏。 - Host / Origin:入口目前只校验 GET。可参考仓库里已有插件对 loopback
Host(以及写操作时的Origin)的检查,降低 DNS rebinding / 跨站误打到本机端口的风险。 ENTRY_STDOUT:你对db-child.mjs的说明和minimax-photo的处理方式一致,这条 warning 可以保留;若整条子进程读库路径随红线一起删掉,warning 也会自然消失。
谢谢投稿。去掉对宿主内部数据库(以及同路径上的观测日志 / 宿主配置)的访问之后,很乐意再看一版。
| if (envDir && fs.existsSync(envDir)) return path.resolve(envDir); | ||
| let dir = contextDataDir ? path.resolve(contextDataDir) : null; | ||
| for (let i = 0; dir && i < 8; i += 1) { | ||
| if (fs.existsSync(path.join(dir, 'v2', 'sqlite', 'runtime-state.sqlite'))) return dir; |
There was a problem hiding this comment.
必改: 这里从插件 dataDir 向上查找并定位 MiniMax Code 的内部数据库文件。社区插件不能直接打开宿主内部数据库(只读也不行),请去掉整条数据路径。
| return { db: null, error: `runtime state database not found at ${file} — is this a MiniMax Code data directory with recorded usage?` }; | ||
| } | ||
| try { | ||
| return { db: new sqlite.DatabaseSync(file, { readOnly: true }), error: null }; |
There was a problem hiding this comment.
必改: DatabaseSync 直接打开了 MiniMax Code 的内部数据库。请删除 openDb / dbPath 以及对宿主内部库的查询;只读模式不能作为例外。
| ${jsonField('$.usage.request_duration_ms', "IN ('integer','real')")} AS request_ms, | ||
| ${jsonField('$.finish_reason', "= 'text'")} AS finish_reason, | ||
| substr(${jsonField('$.msg_content', "= 'text'")}, 1, ${USER_PREVIEW_CHARS}) AS msg_preview | ||
| FROM local_runtime_message_rows |
There was a problem hiding this comment.
必改: 这里全表扫描宿主内部库的消息行,并取出用户消息前 200 字。请不要读宿主内部库;对话预览也不能再依赖这条路径。
| function loadSessionTitles(db) { | ||
| const titles = new Map(); | ||
| try { | ||
| for (const r of db.prepare('SELECT session_id, title, agent_name, workspace_dir FROM local_runtime_sessions').all()) { |
There was a problem hiding this comment.
必改: 从宿主内部库的会话表读取 title / workspace_dir。请去掉对宿主内部库的访问;绝对工作区路径也不应再经插件 API 带出。
| const maxBytesPerFile = options.maxBytesPerFile ?? 12 * 1024 * 1024; | ||
| const now = options.now ?? Date.now(); | ||
| const map = new Map(); | ||
| const dir = path.join(dataDir, 'v2', 'observability', 'logs'); |
There was a problem hiding this comment.
必改: 观测日志属于宿主内部存储,且是为补齐内部库里没有的模型名而扫的。请与内部库访问一并移除。
| let catalogCache = null; | ||
|
|
||
| export function loadCatalog(dataDir) { | ||
| const file = path.join(dataDir, 'config.yaml'); |
There was a problem hiding this comment.
必改: 这里整文件读取宿主 config.yaml(可能含自定义 provider 配置)。请不要读宿主配置文件;显示名需改用不依赖宿主内部文件的方式,或先去掉。
| sqliteInProcess, | ||
| queryMode: childMode === true ? 'child' : 'in-process', | ||
| }, | ||
| dataDir, |
There was a problem hiding this comment.
建议:/api/health 返回了绝对 dataDir。若以后还有健康检查,请只返回 basename,或省略该字段。
中文摘要
提交 Token 仪表盘(插件 ID
token-meter-board):本机 MiniMax Code 的实时 token 用量看板。直读运行时自己的逐次调用台账与观测日志,展示的是客户端真实记录的数字;输出速率采用与产品内界面一致的口径(Σ输出 ÷ Σdecode,时间加权)。纯 Node,无需 Python,全程只读、不出网、无遥测。界面为中文,README 提供英文与中文两版。预览图为合成数据实拍,不含任何真实会话内容。
What it does
A local, read-only token-usage dashboard for MiniMax Code. It reads the runtime's own per-call usage table (
local_runtime_token_usage) and the observability logs, so the numbers it shows are the ones the client actually recorded — not an estimate reconstructed from session transcript files.How it differs from the two token boards already in the gallery
Both existing boards are good; they solve a slightly different problem.
mcode-token-usage-boardmcode-usage-monitormessages.jsonl)The practical difference: if you want to know where the tokens went in one specific conversation, or to audit one specific call (its decode duration, its request duration, its HTTP status), only this board can show it — without re-deriving anything from transcript files. If you prefer a freely rearrangeable card layout,
mcode-usage-monitoris the one to look at.Measured on a reference machine against a transcript-based board, that board reported about 48% fewer calls and 66% fewer cache-read tokens overall, with the gap swinging between 0% and 98% from day to day — because some sessions leave no transcript file at all, so a transcript-based board cannot see them.
Correctness notes
The headline rate uses the same basis as the client interface:
Σ output tokens ÷ Σ decode duration, time-weighted, with P50/P90 shown next to it.Each usage row is paired with the assistant message carrying that call's decode/request durations. The rule is "the first message of the turn written at or after the call started" — not index position and not nearest timestamp, because the runtime also writes assistant rows that are not model calls (context compaction). On this machine 490 of 17,214 assistant messages carry no usage block; counting them as calls shifted a whole turn by one, so 22.5% of the calls reported the previous call's duration and rate. The rule above was verified against a token-signature ground truth over 16,723 calls with zero mismatches, and it stays correct when the query window starts in the middle of a turn. Details in the package's
docs/validation.md.Install and open
Copy
plugins/hanewari/token-meter-board/into your MiniMax Code data directory (by default~/.minimax/plugins/token-meter-board/), keep the hidden.minimax-plugindirectory, restart MiniMax Code, confirm the plugin is enabled, and open Token 仪表盘 — or just ask the Agent to open it. Full instructions are in the package README.Data & access
Files read (read-only, never modified):
<dataDir>/v2/sqlite/runtime-state.sqlitenode:sqlitein read-only mode.<dataDir>/v2/observability/logs/runtime-*.log<dataDir>/config.yamlThese paths and the runtime database layout are not a published API — they are the client's internal storage. This is stated in the README; if a future client changes them the board shows an explicit error instead of an empty dashboard.
What the page displays (so screenshots can carry more than you might expect): the first 200 characters of each turn's own user message (truncated inside the database query, so the full text never enters memory or the browser), session titles and ids, the absolute workspace path of each session (session-ranking tooltip), and model/provider names including your own custom providers.
Written: nothing. No files are created or modified, no data leaves the machine, no telemetry. The only cache is in the running process's memory. Browser storage holds exactly one thing: the theme choice, under the key
tmb-theme.Network: none. The Mini App serves one page on
127.0.0.1and answers its own API routes. Query parameters are an explicit per-endpoint allow-list, so a crafted query string cannot reach the query layer with an unexpected key.Subprocess: normally none. If the host's Node runtime does not expose
node:sqlite, the identical read-only queries are re-run in a child process. It first triesnodefromPATHand only falls back to the runtime's own executable withELECTRON_RUN_AS_NODE=1; the query mode and parameters are passed as an argv array, never through a shell, and the child prints one JSON document on stdout and exits. No user input is placed in a command line — the mode is from a fixed set and the parameters are the same allow-listed values the in-process path receives.Node.js: the in-process path needs
node:sqlite, unflagged from Node.js 23.4. The runtime used for testing is Node.js v24.18.0.Tested environment
node:sqliteavailablecontext.dataDir— but please treat the other platforms as untested.Validation results
npm run validate→plugins/hanewari/token-meter-board: 0 errors, 1 warning, and the repository-level check reportsrepository: OK(the plugin ID is unique across the repository, and both root READMEs now link to it).npm test→ 67 of 69 pass. The two failures are not caused by this package: they are the repository's owntest/files.test.mjscases that create a symbolic link in a temporary fixture to prove the validator rejects one. On this Windows accountfs.symlinkreturnsEPERM(noSeCreateSymbolicLinkPrivilege, i.e. Developer Mode is off), so the fixture cannot be created — reproduced independently of this package with a standalonefs.symlinkcall in a temp directory. CI runs onubuntu-latest, where symlinks are available.About the
ENTRY_STDOUTwarningThe warning points at
miniapp/node/lib/db-child.mjs, not at the Host entryminiapp/node/server.mjs. That file is the child-process executor we spawn ourselves when the host runtime lacksnode:sqlite; it writes one JSON document to stdout, which the parent that spawned it reads back over that pipe — it is parent/child IPC, not the Host process's output channel. The Host entry never writes to stdout (the checker treats that as an error, and it did not fire).The rule is a static scan —
/\b(?:console\.(?:log|info|debug|dir|table)|process\.stdout\.write)\s*\(/u— and cannot tell "the Host entry's stdout" from "the stdout of a child process we own". Following the same approach the repository already accepts forplugins/webguhui/minimax-photo, the warning is left in place and explained here rather than worked around: the checker is not modified, no third-party or first-party code is hidden, and the implementation is not contorted into a file channel — which would contradict the "writes nothing" disclosure above. This code path does not run at all on a host withnode:sqlite.Third-party components
Chart.js v4.4.3 — vendored at
miniapp/client/vendor/chart.umd.min.jsfor offline use. MIT License, © Chart.js Contributors. The full licence text ships with the package atminiapp/client/vendor/LICENSE-chartjs.txt.License
Apache-2.0. The vendored Chart.js copy keeps its own MIT licence, reproduced in
miniapp/client/vendor/LICENSE-chartjs.txt.Need help on this PR? Tag
@codesmith-botwith what you need.