Repository navigation
v0.1.5
[0.1.5] — 2026-10-07
Added
- Kimi Code — usage from the CLI's
wire.jsonl(v1 and v2) and from the desktop app's embedded runtime home, plus live plan windows (5-hour / weekly / monthly) from the vendor's own/usages. Credentials are read-only: the probe never refreshes or stands in for your login. (Superseded within this release — see Changed: the probe now rotates the access token and persists the pair.) - MiniMax Code — usage from the dated session streams under
~/.minimax, plus the plan's 5-hour / weekly windows and both credit wallets (purchased and check-in). The ~1-hour access token is renewed in place from the app's own stored refresh token and written back atomically in the app's own format, so the app and the panel stay signed in together. - Qoder desktop quota — the probe reads the 0.4 desktop app's own auth record (
auth.v1.datbeside its Chromium profile, keychain-paired on macOS, DPAPI on Windows) when the IDE's encrypted snapshot is not available. - Homebrew delivery —
brew install --cask tokenmefor the panel andbrew install tokenme-clifor the CLI, via theBencibr/homebrew-tokenmetap that self-bumps from each release's ownsha256.txt. - Supervised engine and the "Updates stopped" badge — the panel restarts its engine loop after a panic or an unexpected exit (capped backoff, reason in
panel.log), and the header states the report's age once it goes stale, so frozen numbers no longer look identical to quiet ones. - The Windows installer wears the brand and asks whose machine it is on — the NSIS bundle had shipped Tauri's stock look: a default icon (an empty
INSTALLERICONdefine letmakensisfall back to its own), an English-only wizard, and a hardcoded per-user install into%LOCALAPPDATA%.installerIcon/uninstallerIconnow point at the app's own icon —ExtractAssociatedIconhashes the setup exe and the installed app to the same PNG — the wizard lists SimpChinese before English with its language selector on, andinstallMode: bothoffers 所有用户 (Program Files) against 只为我 on every interactive run.bothrides MultiUser'shighestAvailable, so an admin sees one UAC consent at launch even on the per-user path, and the quiet upgrade grows the/currentuserflag that/Salone no longer implies; logs, the price cache and the index all resolve through per-user data dirs, so a Program Files install still writes where the user can. - The Linux collector, end to end —
tokenme exportships a window of the index as a gzipped JSONL bundle plus a sha256 manifest (tmp+rename,0600on unix);tokenme importvalidates hash, sums and schema version and merges in one all-or-nothing transaction, so re-importing the same file changes nothing. The panel engine merges~/tokenme-syncbefore every publish decision.scripts/build-linux.shcross-builds fully static musl binaries andscripts/install-linux.shwires a systemd--usertimer (cron fallback when the user manager is unreachable) with--every,--pushand--uninstall. - Machine scope —
event_rollupgained an origin column, so every period, breakdown, call series and session list folds under 全部机器, 本机 or any single remote by name from a header dropdown that re-summarizes without re-ingesting, and hides itself entirely while nothing has been imported. The session list'sLIMITis applied in SQL, so a busy local machine can no longer starve a remote's list empty. - In-panel SSH pull — the footer's 服务器 surface probes a host, pins its SSH fingerprint on first connect (TOFU), provisions a dedicated key, uploads the static collector and fetches bundles on a per-server schedule; removing a server cleans up both ends. The password authenticates that one setup connection and is never stored.
- Filled form fields grow a trailing clear control — every server-sheet field (host, user, port, name, password, key path, passphrase) renders through a
ClearableInput: the × shows only when there is something to clear, sits inside the field's right edge, and clearing keeps the field focused — the button swallows its own mousedown so clicking it never blurs the input first, and the parent's change handler travels as text so clearing is an ordinary empty value. The wrapper reserves the field width whether or not the control is showing, and the label speaks both languages (清除 / Clear). - Cline desktop sub-agents get their project back — the desktop app writes an agent's transcript inside the parent session directory but its meta into a sibling
<session>__agent_<uuid>/, so 9,238,243 prompt tokens of real sub-agent work reached the tool column unattributed. The label now walks a candidate chain (own sibling meta → the agent dir's meta → the containing session's meta), and the accuracy gate scores our stage totals against the app's ownmetadata.usage/aggregateUsageledger per settled session. - A repair path for derived labels — an event whose stored totals equal the fresh reading now takes a newer
project/modelin place (totals still never move backwards), the touched days' rollup buckets are rebuilt in the same pass, andIngestReportcounts those asrelabeled. scripts/check-theme-parity.py— parses the three colour blocks (light:root, theprefers-color-schemeblock, the 深色 pin) and fails on any name, order or value drift between the two dark ones.build-macos.shruns it before the cargo gate, and it runs even with--fast. It now also bindsindex.html's two boot-shell colours to--ink-3/--accent, because that copy of the palette lives outside theme.css by necessity.- The cold start now has something to say at every stage, measured frame by frame.
index.html's pre-paint shell used the app's own class names, so on the frame it exists for it had no stylesheet: text in the corner and two arcs with no stroke. It carries its own critical CSS now, bound to the real palette by the parity gate, and the recording shows it painting — brand ring and 正在索引本机用量… centred — around +3 s into a cold start. The stretch before that is not a page problem: from the moment the window is on screen (~+1.4 s) until the page paints, every pixel is the native window backing, and no markup can reach it. So the note is drawn by AppKit as well — a system spinner above one teal line, centred as a group, 10 pt apart like the page's own card — and its retirement moved off bundle execution onto the first real report, because taking it away atbootleft the sheet bare again from +3.2 s to +8.1 s, which is exactly what the recording caught. The spinner is not decoration: a static line on an empty sheet was reported as a dead panel on 2026-10-07, and motion is the only thing that layer has to say "working" with. The two milestones are now logged separately (the page booted …/the page reported content …) because the difference between them is where the wait lives. scripts/verify-cold-start.py— the instrument that measures the panel's first composited frame: it quits the app, records the display, cold-starts it with the panel opening itself, then finds the panel by pixels (its configured 400×660 size, hanging off the menu bar) and reports when it first appears, when it first draws anything, and where it landed. Three traps are built into it: a still screenshot and a video frame are encoded differently, so the baseline must come from the recording itself; another window of a similar size is not the panel; and behind a lock or a sleeping display the recorder writes nothing, which is exit code 2 and never a verdict.- Two gates on the version's homes —
the_version_lives_in_four_homes_and_they_all_agreebinds rootCargo.toml, the panel'sCargo.toml,tauri.conf.jsonandpackage.jsonto the compiled version, andthe_release_manifest_never_lies_about_its_own_assetsallowslatest.jsonto lag but never to advertise an installer it does not name. All 24 adapter crates now inherit the workspace version, sorelease.shreaches every one of them (two were pinned at0.1.0and moved with no release). - The panel's first figures now come off disk, not off the scan. Every publish writes the finished report to
report.jsonnext to the index (tmp + fsync + rename,0600), and a launch reads it back before it has detected a source, opened a price table or run a query — 3–22 ms measured, against the 1.1–5.6 s the same first publish costs in the same rounds (and 6.8–24.9 s across this machine's earlier runs). Interleaved A/B on this machine's 170 MB index, three rounds each, figures-on-screen (the page reported content): 6,691 / 1,572 / 4,682 ms before, 1,284 / 905 / 1,126 ms after; re-measured with the display awake, 2,449 and 3,172 ms. What that leaves, named honestly: the remaining term is no longer the report at all but the webview's own page boot (the page booted, 0.8–3.1 s in these runs, and 19.4 s once immediately after the bundle was replaced on disk, with every cache cold) — the engine now answers in 2–3 ms and waits for the page. What it restores is exactly what the user last saw, so the footer says so: a 上次扫描的数据 chip with the age the report itself carries (from the last scan · N min ago), and the quota strip goes back to 探测中 because a vendor window sampled an hour ago is not this run's answer. The restore is refused — and the old wait returns unchanged — when the cached report was folded under a different machine scope, on a different local day (its 今日 would be yesterday's totals under today's label), more than an hour ago, or by a clock that has since moved backwards; and a restored report is never written back, so a snapshot cannot chain. While one is on screen the engine-death warning (数据已停止更新) is suppressed: a scanning engine is not a dead one. - Cold-start telemetry — the boot path logs its phases (
restore / detect / pricing / index open / has-events / sources / report) and the panel logs when the page first reports content, so a slow open is attributed with numbers instead of guessed. - QA pins —
?qpending=1reaches the quota section's wait state,?restored=<min>the restored-first-frame chip at any age,?pending=<s>the footer's busy readout that many seconds after the click,?stale=<min>the frozen line and?sync=<hours>the sync badge; none of them are reachable from a hand-authored fixture, which always answers as if everything had settled.
Changed
- Kimi Code's quota probe renews its own expired access token — through the vendor's refresh contract, persisting the rotated
refresh_tokenback into~/.kimi-code/credentials/<name>.json. This supersedes the read-only claim in this release's own Kimi Code entry. Three probes now write a rotated credential back — MiniMax Code already renewed in place, and Cline's gateway token does the same — each into the tool's own store, atomically, and only where that tool's own login is what the probe is reading; the remaining probes stay read-only. - The footer's left corner stopped describing the price table and started describing the numbers on screen. The event count left first (it only repeated the details page's 数据源 line), then the provenance went onto a badge's tooltip, and now the badge itself is gone —
models.devwith its 4,213 keys was the state on almost every machine at almost every minute, which is chrome, not information. What stays in that corner is the panel's own condition: an always-on 最后刷新 clock saying when the figures were last recomputed — seconds for its first minute, so a manual refresh visibly restarts it at 刚刚, and a 刷新中 spin in place of the age while the pass that click started has not published yet — the 上次扫描的数据 chip while a restored report is on screen, the amber 数据已停止更新 chip with the last pass's age once the engine goes silent (the clock and this chip are the same fact pointing opposite ways, so they never show together), and the amber 陈旧 warning for a price table over 24 h old with no fetch behind it. Saying plainly what that removes: which listing the costs were priced from (models.dev, the local cache, the snapshot compiled into the app, nothing) and how many keys it holds are no longer displayed anywhere — the panel was their only surface,tokenme pricing explainanswers per model rather than per table, and?psource=went with the badge it drove. - Two waits got their own lines — the Linux-sync badge moved off the figures line to a row of its own, and the quota section's 探测中 spinner is centred in the area its list will occupy instead of a strip under the section label.
- The whole-file integrity check left the panel's first render —
PRAGMA quick_checkreads every page (0.4 s warm, 3.6 s cold on a 170 MB index) and used to run insideIndex::open, in front of the first report. It is nowIndex::verify_integrity(), called by the panel after the first publish and before the first write, and by the CLI straight after open: every launch still verifies and still heals a damaged image, but measured engine startup before the panel could render went from 6,402 ms to 378–2,340 ms. - The accuracy gate (
scripts/verify-totals.py) learns Kimi Code's and MiniMax Code's stages — including MiniMax's own vendor-side usage table as a third opinion — plus an identity check for antigravity's retry boxes. - READMEs list the two new tools (22 built-in adapters); the user guide gained the Kimi / MiniMax log paths, the in-place token-renewal note, and the frozen-badge troubleshooting row.
Fixed
- antigravity retry billing — only retry boxes carrying their own
responseIdcount as their own call. Copies of the parent's stream (81,260 of the 81,727 boxes on the machine measured) and id-less boxes no longer bill twice, recovering the 17 genuinely distinct attempts they were hiding. - OpenCode v2 sessions — the newer
session_messagetable (role in thetypecolumn) is read alongsidemessage, and rows updated in place are no longer skipped by the resume cursor. - Qoder app lookup off macOS — the auth-record directory search gained the Windows
%APPDATA%fallback instead of assuming macOS paths. - The Windows process reads
TokenMe.exeagain —b668ec7lowercased the binary while dropping the-barsuffix, and Task Manager has showntokenme.exeon every install since;mainBinaryNamereturns to the product's own casing. The upgrade path survives it untouched: the uninstaller-first flow keys onproductName(never changed), the installer deletes a previous binary name that differs from its own — so the lowercase-eratokenme.exeis removed on upgrade — and Windows folds%LOCALAPPDATA%\TokenMeonto the directory the old installs already use. - The server sheet accepts typing on Windows — the tray panel is non-activating by design (opening must not pull focus from whatever you were editing), and on Windows
set_focusable(false)maps toWS_EX_NOACTIVATE, so WebView2 received no key events at all: every text field in the server wizard rendered but swallowed every keystroke (macOS never showed this, because an NSPanel routes keys without activating). A field that takes focus now borrows a keyboard session — the frontend watches focusin/focusout over inputs and textareas and the Rust side flips focusability for its duration — and a fresh open resets the flag, because the session can outlive the hide path and tao'sMARKER_DONT_FOCUSis one-shot: left set, every show after the first would foreground the panel. - 跟随系统 + a dark OS lost the modal scrim — the media-query block never declared
--today/--scrim, so the light defaults fell through and dimmed the sheet with a dark veil on a pure-black panel; the 深色 pin also kept a stale pink on--cat-18, so one tool drew two colours on the two theme paths. - The blank card on a cold start is gone, and what is left is named with numbers. Two causes were measured and closed: the engine's first publish used to wait on the whole-file integrity read (6,402 ms of startup before the panel could render;
Index::verify_integrity()now runs after the first publish), and the window's first frames belonged to no layer the page could reach — they are the native backing, which is why three builds of markup inindex.htmlchanged nothing.panel::show_boot_notedraws the line in AppKit instead, and four recordings of the installed app now measure no bare frame at all: the panel's first frame on screen (~+1.3–1.7 s) already carries it. What remained was not a blank frame but a wait — the first report cost 10–27 s on a 170 MB index whiledetect / pricing / sources / reportwere all in front of it. That wait is now answered at the source rather than painted over: the first frame is the previous run's own report read off disk (3–22 ms, labelled), and the scan's fold lands behind it as the second frame. - The panel opened in the bottom-left corner when nothing supplied a tray rectangle (a relaunch, a QA open, the single-instance hand-off): the fallback only clamped whatever position the window server had chosen. It now anchors under the right end of the menu bar, and says so in
panel.logwhen the session exposes no monitors at all — which is the one case that genuinely cannot be placed. - A stale fingerprint froze Cline's history — a desktop sub-agent's transcript stops growing when its agent exits while the meta that names its workspace can land later, so 226 rows stayed unlabelled even after the labelling fix; the fingerprint now carries the label's freshness, and both discovery paths agree on it.
- Fixtures stopped naming a person's machine, and the antigravity port test now drives the shipped loopback filter instead of re-implementing it — with a dual-stack
127.0.0.1/::1pair that must count once. - The release gate never compiled the shipped app's own crate —
apps/tokenme-baris excluded from the root workspace, socargo test --workspacecould not see the panel's 37 tests, including the anchor and engine-loop ones.build-macos.shnow runs the panel's tests as part of the same gate. - Documentation that the code contradicted — the READMEs,
docs/COMMANDS.mdanddocs/internal/ADAPTERS_DESIGN.mdcalled the quota probes read-only, which three of them (MiniMax Code, Kimi Code, Cline) have not been since they started renewing short-lived tokens in place; the Gemini CLI's real blockers were named instead of a vague boundary; the AgnesCode token file was documented at a Linux path on a macOS-first app; the cache was described as per-vendor TTLs where the project has one shared 5-minute TTL; and the design notes had no section at all for the two newest tools.
Full Changelog: https://github.com/Bencibr/tokenme/commits/v0.1.5