Skip to content

Releases: Bencibr/tokenme

v0.1.8

Choose a tag to compare

@github-actions github-actions released this 13 Oct 10:05

[0.1.8] — 2026-10-13

Changed

  • 统一发布版本元数据,并将公开镜像迁移与发布校验纳入本次维护版本。

Full Changelog: https://github.com/Bencibr/tokenme/commits/v0.1.8

v0.1.6

Choose a tag to compare

@github-actions github-actions released this 10 Oct 12:00

本次更新(0.1.6 发布后的全部工作)

Added

  • 每日签到,三家风(Trae CN / Qoder / MiniMax Code) — 一个注册表(CHECKIN_TOOLS)同时驱动三处:引擎的宿主退出豁免(高级 → 自动签到,开启后关着 IDE 也不错过当天——领取是对账号的 HTTPS 调用,不是读宿主状态)、面板的工具头按钮、底栏的控件渲染;按钮只把真实领取变成"已签到"徽标,排队中、未到点都保持按钮并把原因写明。Qoder 的读取按客户端实测定案:请求头带无横线的 Cosy-ClientType 才返回活动列表(横线版服务端答空列表,曾把"有活动"误读成"无活动");每日行 10:00 开门,开门前按钮置灰写明时刻;领取有客户端设备指纹门槛(第三方面部伪造应答 503 SAME_PERSON),TokenMe 把状态读对、把"需在 Qoder 内领取"说清楚——用户在 Qoder 里领完,下一次轮询面板即亮已签到。MiniMax Code 的协议取自厂商自己的开源客户端(MiniMax-AI/minimax-code)逐字实现:7 天面板的 status 2 即可领日、claim 幂等,请求带厂商公开定义为 wire 常量的归因签名头(授权仍是面板本就代为续期的 bearer token),账号 id 按客户端同款三级阶梯解析(运行时快照 → 指纹钉住的 CLI 身份文件 → 账号端点)。Trae CN 的积分包把已用份额真正送进仪表(原先恒 0%),签到包 id 带日期作用域——上周的积分包不再把"今日已签到"点亮;匿名积分包各持唯一 id,不再互相串状态。
  • CodeBuddy CN 桌面版 加入适配器注册表——会话流与配额照注册表契约一并接入。
  • Windows 宠物皮肤可配置 — 桌面宠物从内置注册表选择模型与皮肤(PetModel 组件 + 皮肤注册表),配独立的一致性校验脚本进构建门禁。
  • Windows 面板与托盘的稳定化 — "第一次点击就有页面":WebView2 控制器的可见性在每次显示时重申(Runtime 154 起不再免费合成不可见控制器)、显示先于锚定、无托盘锚点改读光标;托盘面板可见性恢复;托盘字形按任务栏实际绘制尺寸取图;应用图标铺满 Windows 期望的画布。

Fixed

  • DSH 的跨天会话不再把历史用量搬进今天 — v4 会话原先按"终身累计 + 最后使用时刻"计账:昨天花 12M 的会话今天继续用 1M,今天就读成 13M、昨天清零(用户实测报告)。实拆 v4 流发现逐次计费事件俱在(152 次调用之和与投影累计分毫不差),现在 v3/v4 一律逐次计费、各归各的一天;投影缓存退役为精度审计的厂商侧台账;一次性迁移把存量累计行退役、全部流按日真归因重读(总量分毫不差,只挪归属)。
  • 配额门禁的承诺在无缓存时也成立 — 被关闭或宿主暂停的探测,在缓存目录缺失时不再漏出去真连厂商;对签到工具这意味着不会替用户已关闭的工具偷偷发领取请求。
  • 启动 Loading 的双环终于转起来了,颜色也跟随主题 — 守卫询问的两个 selector 名(一个拼错、一个张冠李戴)自始就不存在于 AppKit,静默降级让双环从上线首日起就只画一条字;现在换上真 selector、拒绝按名落日志,文字与双环按设置钉住/系统外观解析主题色(浅色 #66748a/#0d8f74 · 深色 #8a99ad/#30d9aa,与页面加载卡同源),浅色底不再把字和内环洗没。
  • 额度横幅的两次误响 — 100% 横幅每个窗口实例只发一次(不再随样本抖动反复横跳),重发加冷却;配额缓存带版本换挡,同类窗口的碰撞按版本作废而不是互相污染。
  • 签到标记行不再冒充数据 — 今日已签到 / 未到签到时间 / 需在客户端内领取是状态不是窗口,不再作为一行插在真实仪表之间;徽标与按钮仍从原始报告读取它们。
  • 全平台 lint 干净,scripts/check-no-window-spawns.py 等门禁随之覆盖新代码面。

Changed

  • 公开交付成为一条命令 — scripts/public-deliver.sh:全量门禁(CLEAN 否则不动)→ github 分支跟随 main 并在 forge 落台 → 新 tag 先行(release 工作流必须先于 main 启动)→ 公共远端 main 以 force-with-lease 收尾;README hero 横幅随树自动重渲(版本号、工具数、徽章布局、双主题 2x PNG)。
  • CI 的三个打包 job 各自带齐 collector — macos / macos-universal / windows-panel 在构建前从 cli-linux 工件取两枚 musl 二进制(资源是未跟踪的构建输入,镜像检出没有它们)。
  • README 的 Windows 更新段与适配器表落到与实物一致;scan-public.py 喂 cat-file --batch 改为独立线程(范围过千 blob 时同管道先写后读会两头死锁)。

[0.1.6] — 2026-10-08

Added

  • Quota banners at 80 % and 100 % — the panel now posts a native notification the moment a vendor-metered window first crosses 80 % used ("已用 83%(剩余 17%)", titled Qoder · 5 小时) and once more when it runs out ("额度已用完(100%)"; the language follows the panel's). The real-quota sources get banners — the strip's windows and credit wallets — while §5 budgets stay the panel's own business. Edge-triggered, not level-triggered: each line fires once per window instance and the fact is persisted beside the index (notify-state.json, 0600, tmp + fsync + rename, 30-day retention), so a restart replays nothing and the 60 s quota-pass TTL can re-publish the same sample all it likes — the machine is idempotent. A posted banner is what advances the tier: a delivery the system refused is retried on a later pass (one attempt per window per 60 s). A window renewal — its reset instant jumped past the few seconds of probe jitter (> 5 min) — re-baselines silently; a tier that falls back re-arms without a word (the path that also covers credit top-ups, whose resets_at never moves); and a window first seen already past 80 % is baselined quietly, because the banner marks the crossing, not the state — only an already-exhausted first sight announces itself. The permission prompt is raised lazily, by the first banner actually due (once per run, on its own thread, so a prompt nobody answers cannot wedge the worker) — a user who never crosses a line is never asked — and a denial logs the System Settings path on first occurrence instead of silently swallowing banners.
  • Delivery is notify-rust's UNUserNotificationCenter backend; the Tauri notification plugin was tried and dropped — on desktop that plugin's permission API is an empty stub (permission_state answers Granted unconditionally), its show posts through the deprecated NSUserNotification path and discards the delivery error in a fire-and-forget task, and in dev it would post as com.apple.Terminal. The threshold machine needs the real results at both ends — the permission verdict and whether a banner actually posted — so delivery goes through notify-rust with its preview-macos-un feature, the UNC path the spike proved posts from an ad-hoc-signed bundle; its Windows side rides the same crate's WinRT toast backend under the app's AppUserModelID once installed. TOKENME_TEST_NOTIFY=1 posts a test banner through the same gate (permission request included) for QA of the permission flow, and the Windows backend is cross-checked compiling for x86_64-pc-windows-gnu. The honest boundary: a bare cargo run has no bundle identifier, so desktop banners are off there by nature and the worker says so in the log — the .app is the real surface.
  • The pointer to the OS toggle lives in the panel — while the system would not deliver a banner (permission denied, or never asked) and there is a vendor window to warn about, the quota section carries a one-line guide above its list: 开启系统通知后,额度跨过 80% 或耗尽时会自动弹出提醒 — with an 开启通知 button when the permission is undecided (it raises the system prompt, which is macOS's own decision UI; the state is polled afterwards so the row retires the moment the answer lands) or 去开启 after a denial (opens System Settings' notification pane, deep-linked straight to TokenMe's row). It is dismissible (忽略) and the dismissal is remembered per state — observing the permission change re-arms it, so a grant now never suppresses a later denial — and it renders nothing at all once banners can post, so a granted machine never sees it. The status is a new read-only pair of commands (notify_status / notify_enable), QA-pinned by TOKENME_NOTIFY_STATE and ?notify=. In the same batch: the TOKENME_TEST_NOTIFY test post now fires after the run loop is up — fired at setup it could not deliver ("Mainthread not running"), a failure a denial had been masking.
  • Settings became a four-tab sheet, and every probe is now something you can turn off — the one long scroll is now 通用 / 提醒 / 高级 / 关于 at a fixed 65 % of the panel height, with the body scrolling inside the selected tab and 版本 · 检查更新 · 退出 pinned in the footer so the escape hatch never scrolls away; tabs carry no entry counts. Two rows open a list inside the same sheet (‹ 返回 back, › a page here, ↗ another surface — 服务器与同步 borrows the existing server sheet rather than reimplementing it). What the old sheet could not do was stop polling: 高级 → 自动探测配额 is the master gate and 逐工具探测 the per-vendor list, wired through usage_quota::collect_gated(budget, gate). A gated probe sends no request but its cache is still read, so the row keeps its last answer on screen instead of going blank, and while the gate is closed the quota section says 已停止探测 · 数据停在 N 分钟前 — quiet numbers and stale numbers must not look identical. Moving either switch deliberately does not fire a refresh: a forced pass would delete the answer the user just chose to keep, and the panel's own 60 s pass picks it back up. The list defaults to the probes this machine has actually answered and offers 显示全部 for the ones it never has; the row set comes from usage_quota::inventory() — the registry describing itself (which probes exist, which have a host-process gate, which have answered here) rather than a front-end copy that drifts — pinned by a test that names the probes no host gate can reach. The four with no mapping (Copilot, Gemini CLI, Kimi Code, MiniMax Code) are tagged 无退出门, because that list is the only way to stop them. 提醒 grew the choice the banners were missing: 提醒档位 (80 % 与 100 % / 仅 100 % / 关闭) and 静默工具, which mutes selected windows while their numbers keep refreshing — notify::Gate filters delivery and still advances the window's recorded tier, because a tier left frozen at its old value would post a stale banner the instant the user changed their mind. A list under a closed gate or a denied notification permission greys its switches and says why instead of showing toggles that look live, and 退出后暂停 moved into the per-tool page where the host gate belongs — which is also what keeps all four tabs inside the 65 % box. The tray display-mode chip and 桌面气泡 (Windows-only) keep their places; the quota section's guide line is unchanged.
  • scripts/check-i18n-parity.py — the two dictionaries and their call sites are now gated, not eyeballed — TypeScript pins the key sets against each other, but it cannot see a placeholder that diverges between the two sides ({n} in zh against {count} in en renders the raw token), a call site handing t() the wrong name ({ lab } against a string that says {label} typechecks and prints {label} on screen), or dead copy left behind when a control is deleted — and t() answers undefined for a miss, so every one of those symptoms is a single blank or raw row. The gate checks the key sets, the placeholder sets per key, that each literal t("key", { … }) hands exactly the names its string asks for, and that no key in the dictionary is unreachable; it refuses to skip a dictionary line it cannot parse, and it is wired into build-macos.sh beside the theme-parity gate so it runs even with --fast. Controls, because a gate nobody has falsified is decoration: four broken copies (an English key removed, a placeholder renamed on one side, an orphan key added, a value written unquoted) each fail, and a call site renamed to { lab } fails with the file and line. The first run that read the file correctly (two parser holes earlier: a value written across two lines, and a key reached through a `set.tab.${k}` template) found six keys nothing referenced — read.hour.tail, set.quit.btn, `srv.a...
Read more

v0.1.5

Choose a tag to compare

@github-actions github-actions released this 07 Oct 11:40

[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.dat beside its Chromium profile, keychain-paired on macOS, DPAPI on Windows) when the IDE's encrypted snapshot is not available.
  • Homebrew delivery — brew install --cask tokenme for the panel and brew install tokenme-cli for the CLI, via the Bencibr/homebrew-tokenme tap that self-bumps from each release's own sha256.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 INSTALLERICON define let makensis fall back to its own), an English-only wizard, and a hardcoded per-user install into %LOCALAPPDATA%. installerIcon / uninstallerIcon now point at the app's own icon — ExtractAssociatedIcon hashes the setup exe and the installed app to the same PNG — the wizard lists SimpChinese before English with its language selector on, and installMode: both offers 所有用户 (Program Files) against 只为我 on every interactive run. both rides MultiUser's highestAvailable, so an admin sees one UAC consent at launch even on the per-user path, and the quiet upgrade grows the /currentuser flag that /S alone 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 export ships a window of the index as a gzipped JSONL bundle plus a sha256 manifest (tmp+rename, 0600 on unix); tokenme import validates 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-sync before every publish decision. scripts/build-linux.sh cross-builds fully static musl binaries and scripts/install-linux.sh wires a systemd --user timer (cron fallback when the user manager is unreachable) with --every, --push and --uninstall.
  • Machine scope — event_rollup gained 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's LIMIT is 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 own metadata.usage / aggregateUsage ledger per settled session.
  • A repair path for derived labels — an event whose stored totals equal the fresh reading now takes a newer project/model in place (totals still never move backwards), the touched days' rollup buckets are rebuilt in the same pass, and IngestReport counts those as relabeled.
  • scripts/check-theme-parity.py — parses the three colour blocks (light :root, the prefers-color-scheme block, the 深色 pin) and fails on any name, order or value drift between the two dark ones. build-macos.sh runs it before the cargo gate, and it runs even with --fast. It now also binds index.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 at boot left 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_agree binds root Cargo.toml, the panel's Cargo.toml, tauri.conf.json and package.json to the compiled version, and the_release_manifest_never_lies_about_its_own_assets allows latest.json to lag but never to advertise an installer it does not name. All 24 adapter crates now inherit the workspace version, so release.sh reaches every one of them (two were pinned at 0.1.0 and 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.json next 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 ...
Read more

v0.1.4

Choose a tag to compare

@github-actions github-actions released this 01 Oct 04:00

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 29 Sep 18:02

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 29 Sep 07:49

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 06:33