Skip to content

About

Local MiniMax Code token-usage dashboard: trend, per-call ledger, per-turn detail, cache hit rate and output speed. Unofficial, read-only, never leaves your machine.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

15 Commits

Folders and files

Repository files navigation

Token Meter Board

English | 简体中文

A near-real-time token-usage dashboard for MiniMax Code, running entirely on your own machine. It reads the runtime's own per-call ledger and observability logs, so the numbers it shows are the ones the client actually recorded — not an estimate reconstructed from transcript files.

Author: HaneWari · Version: 1.10.0 · License: Apache-2.0

Token Meter Board, synthetic data

The preview uses synthetic sessions and usage data. The app interface is currently in Chinese.

Important

This is an unofficial, community-built project. It is not built, reviewed, endorsed, or published by MiniMax, and nothing in it has passed the official Mini App review. It is distributed straight from this repository.

Read Data & access before you install it: this build reads your client's internal storage files (the runtime SQLite database, the observability logs, and config.yaml), and the client may change their layout in any update.

What you get

  • Usage trend — stacked bars per model, with the cache-hit-rate and output-speed curves drawn on top. Buckets adapt from minutes to hours to days as the window grows.
  • Five detail tables — per-model breakdown, daily totals, a 12-column per-call ledger, a conversation (turn) table, and a session ranking.
  • Conversation detail — one row per question you asked. The row shows your own message text, the outcome, wall-clock duration, how many model calls the turn took, the tokens, and the rate. A turn that is still running is marked as unfinished and grows live — you do not have to close the session first.
  • Per-call ledger — every single model call with its exact timestamp, model, all five token classes, output speed, whole-request speed, decode duration, request duration, and status code.
  • Time windows — last hour / today / last 7 days / last 30 days, or any custom start and end. The time bar is laid out in two rows so the native date picker can never cover the Apply button, and pressing Enter inside a time field applies the window too.
  • 5-second auto refresh with a manual refresh button. Switching a window never blanks the tables and never waits: the previous window stays on screen until the new values arrive, and a late response from an older window is discarded rather than overwriting the newer one.
  • Deleted sessions stay on the board. Deleting a conversation in the client removes its usage rows, its message rows and its transcript — there is nothing left to read them from. The board keeps its own copy of every call it has seen, in the writable directory the Host provides for the plugin, and serves deleted sessions back from that archive, marked 已删除. The runtime's own database is never written to.

Why this one

Most token boards answer "how much did I spend". This one also answers "where did it go, and is the number I am looking at the number the client shows?".

  • It reads the ledger, not the transcripts. Numbers come from the runtime's own per-call usage table plus the observability logs, not from reconstructing usage out of session transcript files. A transcript file carries one aggregate usage block per assistant message and no timing information at all, so a board that reads it cannot see the calls that are missing from the transcripts — and it can never show a per-call speed, because the transcript has no timings. Measured on a reference machine against a transcript-based board, that board reported about 48% fewer calls and 66% fewer cache-read tokens overall, and the gap swung between 0% and 98% from day to day. The cause is not a per-message aggregation rule: some sessions leave no transcript file at all, and a transcript-based board simply cannot see them.
  • The output speed matches the client. Output speed is Σ output tokens ÷ Σ decode duration — the decode phase only, from the first token to the end of the stream — which is the same basis the MiniMax Code interface uses. The whole-request speed is shown next to it as a comparison column, so you can see how much of each call was spent waiting for the first token. A speed computed over the whole request instead reports a number roughly 1.6–2.1× lower and does not line up with the interface.
  • Every call is matched to its own timings. The board pairs each usage row with the assistant message that carries that call's decode and request durations. It does not match by nearest timestamp and it does not match by position: both go wrong as soon as one turn contains an assistant row that is not a model call (context compaction writes one), which shifts the whole turn. The pairing rule used here was verified against a token-signature ground truth over 16,723 calls with zero mismatches.
  • Totals add up to five terms. total = input + output + reasoning + cacheRead + cacheWrite. Boards that stop at three terms silently drop cache writes and reasoning tokens.
  • Turn-level granularity. One question becomes one row, with the call count and the tokens that question actually cost. Nothing has to be closed or archived first.
  • No Python, no extra process, no dependency. Pure Node, no third-party runtime code except a vendored copy of Chart.js. Another board in this ecosystem requires Python 3.8+ and spawns a Python process per query.
  • Milliseconds, not seconds. After the caching rework in v1.7.0 a full page refresh went from ~8.5 s to tens of milliseconds. On a reference machine with a ~17,000-row message table, a full page refresh takes ~50 ms, switching to a 7-day window ~40 ms, and the first query in a fresh process ~1.3 s (it has to scan the message table once).
  • Read-only towards the runtime. The board never writes to the client's database, its logs or its configuration, sends nothing, and has no telemetry. Its only write is its own archive file, in the plugin's own data directory. No account, no API key, no configuration.

How this compares with the other token boards

The MiniMax Code community lists two other token boards. They are good at what they do, and they solve a slightly different problem:

Token Meter Board (this one) mcode-token-usage-board mcode-usage-monitor
Source of truth Runtime usage table + observability logs Session transcript files (messages.jsonl) Runtime database, read through Python 3.8+
Per-call timings Yes — decode, request, status code per call No — transcripts carry no timings Partly — per-request speed
Per-call ledger Yes, 12 columns No Recent requests
Conversation (turn) table with your own words Yes No No
Needs Python No No Yes
Extra process per query No No Yes
Auto refresh 5 s 60 s 5 s / 10 s / 30 s
Layout Fixed, dense Fixed Draggable, user-editable card layout

The practical difference: if you want to know where the tokens went in a specific conversation, or to audit one specific call (its decode duration, its request duration, its HTTP status), only this board shows it — and it can do so without re-deriving anything from transcript files. If you prefer a freely rearrangeable card layout, mcode-usage-monitor is the one to look at.

Install

Warning

Mini App code runs on your machine under your own user account, and the Host does not apply an OS-level sandbox. Install only packages whose source you trust — and read Data & access first, so you know exactly what this one touches.

There are three routes. Pick whichever fits you.

A. Download the ZIP from Releases and hand it to your Agent

Go to Releases, download token-meter-board-v1.9.0.zip, and unpack it. Then give your MiniMax Code Agent either the unpacked folder or the ZIP itself and ask it to publish it as a finished package (成品包流程). The Agent copies it into <dataDir>/plugins/token-meter-board/ for you.

Note

The ZIP root contains .minimax-plugin/plugin.json directly. Unpack it as it is — do not add another folder level around it.

B. Copy the directory yourself

Clone or download this repository, then copy the whole plugin directory — including the hidden .minimax-plugin folder — into:

<dataDir>/plugins/token-meter-board/
System Target path
Windows C:\Users\<you>\.minimax\plugins\token-meter-board
macOS /Users/<you>/.minimax/plugins/token-meter-board
Linux /home/<you>/.minimax/plugins/token-meter-board

<dataDir> is the active MiniMax Code data directory — .minimax in your home folder by default, or whatever MINIMAX_DATA_DIR points at.

If you cloned this repository, the plugin directory is the repository root, so copy the repo root into plugins/token-meter-board/. Two mistakes to avoid: ending up with plugins/token-meter-board/token-meter-board/, and keeping an extra plugins/<your-github-name>/ layer in between.

C. Clone straight into the plugins folder

git clone https://github.com/HaneWari/mcode-token-meter.git ~/.minimax/plugins/token-meter-board

Then

Restart MiniMax Code, confirm the plugin is listed and enabled, then open Token 仪表盘 (Token Meter Board) — or just ask the Agent to open it.

Note

The desktop client's "import plugin from GitHub" flow does not support Mini App packages. Every Mini App has to be installed by one of the three routes above.

Use it

Pick a window (preset or custom), and read the trend, the KPI strip, and the five tables below it. Model chips filter the charts and tables; the sessions ranking and the daily table have their own independent range selectors, so changing the main window never disturbs them. The page refreshes every 5 seconds, and the refresh button forces an immediate reload.

Example prompts, and what you get back:

  • "打开 Token 仪表盘" → the dashboard opens on the current window.
  • "查看我的模型用量趋势" → the trend chart with the per-model stacked bars, the cache-hit-rate curve and the output-speed curve.
  • "哪个自定义模型的缓存命中率最高?" → the per-model table with the cache hit rate per provider and model.

The board also offers 让 Agent 分析此窗口 (let the Agent analyse this window): it appends the current window's totals, call count, cache hit rate and output speed to the chat input so you can ask follow-up questions about the same numbers you were just looking at.

Data & access

Important

This build reads your client's internal storage, not a public API. It opens the runtime's SQLite database, the observability log files, and config.yaml directly. Those paths, their table layout, and their field names are the client's private implementation — they can change in any client update, and when they do this board stops working until someone updates it. The official Mini App package review does not cover this build, because it is not distributed through the official channel; you are the one deciding whether that trade-off is acceptable for you.

It also writes one file of its own — a local archive of the calls it has seen — because the client deletes a session's usage rows for good. See What is written below for exactly what that file is and where it lives.

Files read (read-only, never modified):

Path Used for
<dataDir>/v2/sqlite/runtime-state.sqlite The per-call usage ledger and the assistant/user message rows (decode timings, finish reason, message text). Opened with node:sqlite in read-only mode.
<dataDir>/v2/observability/logs/runtime-*.log Provider, model, and response status, correlated per turn. The usage table itself does not store the model name, so the logs are what make custom (BYOK) providers show up as themselves instead of "unknown".
<dataDir>/config.yaml Provider and model display names, so the tables read "MiniMax-M3.1-Flash" rather than a raw id.

The location is resolved from the Host-provided context.dataDir (the active MiniMax Code data directory), falling back to ~/.minimax. These paths and the column layout of the runtime database are not a published API — they are the client's internal storage, so a future client release may change them. When that happens the board shows an explicit error instead of an empty dashboard.

What the page displays. The dashboard renders real local data, which means a screenshot of it can contain:

  • Your own message text — the conversation table shows the first 200 characters of each turn's user message. The full text is never loaded: it is truncated inside the database query, so it never enters the app's memory and never reaches the browser.
  • Session titles and session ids, and the absolute workspace path of each session (shown in the session ranking's tooltip).
  • Model and provider names, including the names of your own custom (BYOK) providers.

Everything stays on the machine and on the page. If you plan to share a screenshot, remember that your own questions and your local folder names are in it.

What is written — one file, in the plugin's own directory. Deleting a session in the client is a hard delete: the Host's own SessionArtifactService.deleteSession drops that session's usage rows, its message rows and its transcript directory together, so afterwards there is nothing left to read them from. The board therefore keeps a copy of every model call it has seen, in the writable directory the Host provides for the plugin:

<dataDir>/v2/plugin-data/liveboards/token-meter-board/archive.sqlite
Written Where What it holds
archive.sqlite the plugin's own data directory (Host-provided, removed with the plugin) one row per model call — tokens, timestamp, session, turn, and the model / decode timing / HTTP status it was displayed with — plus the session titles and the first 200 characters of each user message the board showed

The runtime's own database, its observability logs and config.yaml are only ever read. The archive is a copy, not a replacement: every session the runtime still lists is served from the runtime, and archived rows are merged in only for calls the runtime no longer has. If the archive cannot be written (a read-only directory, for instance), the board still works — deleted sessions simply cannot be restored.

Beyond that one file, nothing is persisted: no data leaves the machine, and there is no telemetry. The page stores exactly one thing in your browser's localStorage: the theme choice, under the key tmb-theme.

Network: none. The Mini App serves one page on 127.0.0.1 and answers its own API routes. It never contacts a remote host, and it makes no request other than to its own origin. The query parameters it accepts are an explicit allow-list per endpoint — anything else, including anything that looks like a data-directory override, is dropped before the query runs.

Subprocess: normally none. If the host's Node runtime does not expose node:sqlite, the Mini App re-runs the identical read-only queries in a child process. It first tries node from PATH, and only if that executable does not exist does it fall back to the runtime's own executable launched with ELECTRON_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.

Node.js: the in-process path needs node:sqlite, which is unflagged from Node.js 23.4. The bundled runtime used for testing is Node.js v24.18.0. If neither the in-process runtime nor the child process has it, the board reports the requirement instead of failing silently.

Metrics

Metric Definition
Total tokens input + output + reasoning + cacheRead + cacheWrite
Cache hit rate cacheRead / (cacheRead + input) — the share of the prompt that came from cache. Cache writes are not in the denominator.
Output speed Σ output tokens ÷ Σ decode duration, weighted by time. Single call: output tokens ÷ decode duration (first token → end of stream). P50 and P90 are shown alongside.
Whole-request speed output tokens ÷ request_duration_ms (request sent → end of stream). The difference against output speed is the first-token wait and queueing.
Conversation (turn) One user message plus however many model calls the agent needed before answering. Duration is wall-clock span and is shown for information only; it does not enter the rate calculation.

Calls whose decode duration exceeds the request duration are excluded from the speed aggregate (that combination means the first token arrived before the request was sent — a timestamp artefact that would otherwise inflate the denominator). They are still listed individually in the ledger. A turn that is still in flight is marked unfinished and its rate is computed from what has arrived so far.

The client interface shows its own figure over its own live session. The board uses the same formula on a window you choose, so once the windows are not identical the two can differ by a fraction of a percent (0.3%–3.0% measured on a reference machine).

Tested environment

  • MiniMax Code Desktop 3.1.4 (build 186), Windows 11 (10.0.22631, x64)
  • Bundled Node.js v24.18.0, node:sqlite available
  • Verified in the desktop client: plugin install, window switching, custom start/end windows, per-call ledger, live turn growth, auto refresh, light and dark themes, and the error path when the runtime database is missing.
  • macOS and Linux are not verified. Nothing in the code is platform-specific — the data paths are resolved from context.dataDir — but treat the other platforms as untested.

The full validation record for this release — package checker result, the per-call pairing regression numbers, the injection test, and a same-machine performance A/B — is in docs/validation.md.

Limitations

  • This is a local observation tool, not an invoice. Billing and quota are the product's own "usage" page, which is computed server-side and may catch up later.
  • Numbers cover what the client recorded locally. Usage on another machine, or usage from a client that was never running locally, is not visible here.
  • The runtime database and the observability log format are internal to the client and can change without notice.
  • The client writes one aggregate usage block per assistant message into its transcript files, but the per-call usage table is the authoritative record used here. If the runtime ever stops writing a usage row for a call, that call cannot be shown.
  • The archive can only protect what it has already seen. A session that is deleted before the board has ever run over it is gone for good — the runtime removes it without a trace, and the board was not there to copy it. The first run of the board archives the whole history it can still see, so the window in which history is at risk is the one before you ever open the board.

Third-party components

  • Chart.js v4.4.3 — vendored at miniapp/client/vendor/chart.umd.min.js for offline use. MIT License, © Chart.js Contributors. The full licence text ships with the package at miniapp/client/vendor/LICENSE-chartjs.txt.

Source layout

miniapp/
  client/index.html              The whole page: markup, styles, and the client script
  client/vendor/                 Chart.js (MIT), vendored for offline use
  client/vendor/LICENSE-chartjs.txt  Full MIT licence text for the vendored copy
  node/server.mjs                HTTP server, routing, and the subprocess fallback
  node/lib/common.mjs            Data-dir resolution, config catalog, log enrichment, metrics
  node/lib/facts.mjs             Incremental message-fact cache (SQL-side JSON extraction)
  node/lib/ledger.mjs            One scan of the runtime + the local archive merge
  node/lib/archive.mjs           The local archive: schema, incremental sync, restore
  node/lib/queries.mjs           Window, summary, session, daily, call, and turn queries
  node/lib/trend.mjs             Window resolution and bucketing
  node/lib/db-child.mjs          Query runner for the subprocess fallback

No build step is required: the page and the Node service are used as they are.

License

Apache-2.0 — see the file for the full text. The vendored Chart.js copy keeps its own MIT licence, reproduced in miniapp/client/vendor/LICENSE-chartjs.txt.

About

Local MiniMax Code token-usage dashboard: trend, per-call ledger, per-turn detail, cache hit rate and output speed. Unofficial, read-only, never leaves your machine.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages