Skip to content

Show subscription usage in Tokscale - #493

Merged
junhoyeo merged 34 commits into
junhoyeo:mainfrom
shidevil:main
May 24, 2026
Merged

Show subscription usage in Tokscale#493
junhoyeo merged 34 commits into
junhoyeo:mainfrom
shidevil:main

Conversation

@shidevil

@shidevil shidevil commented May 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a tokscale usage CLI command and TUI Usage tab that displays live subscription quota and remaining usage for AI coding assistants.

Quick start

tokscale usage           # light-mode card output
tokscale usage --json    # JSON for scripting
tokscale tui             # switch to Usage tab (2nd tab)

Supported Providers (7)

Provider Auth Source Metrics
Claude ~/.claude/.credentials.json / macOS Keychain Session (5h), Weekly (7d), Opus (7d)
Codex CODEX_HOME/auth.json, ~/.config/codex/auth.json, ~/.codex/auth.json, macOS Keychain Session (5h), Weekly (7d)
Z.ai ZAI_API_KEY / GLM_API_KEY env var Session, Weekly, Web Search
Amp ~/.local/share/amp/secrets.json Free tier ($remaining/$total), Credits
GitHub Copilot macOS Keychain gh:github.com, hosts.yml (respects GH_CONFIG_DIR) Premium, Chat, Completions (paid + free)
Kimi Code ~/.kimi/credentials/kimi-code.json Session, Weekly
MiniMax MINIMAX_API_KEY / MINIMAX_API_TOKEN env var Session (prompts)

Only providers with valid credentials are queried β€” the rest are silently skipped.

Architecture

Refactored commands/usage.rs into a commands/usage/ module directory:

commands/usage/
β”œβ”€β”€ mod.rs          # Shared types, fetch_all(), disk cache, CLI rendering
β”œβ”€β”€ helpers.rs      # capitalize(), format_reset_time(), read_keychain(), render_ascii_bar()
β”œβ”€β”€ claude.rs       # Claude OAuth provider
β”œβ”€β”€ codex.rs        # Codex/OpenAI provider
β”œβ”€β”€ zai.rs          # Z.ai provider
β”œβ”€β”€ amp.rs          # Amp provider
β”œβ”€β”€ copilot.rs      # GitHub Copilot provider
β”œβ”€β”€ kimi.rs         # Kimi Code provider
└── minimax.rs      # MiniMax provider

Each provider exports has_credentials() -> bool and fetch() -> Result<UsageOutput>.

Performance

  • Credential pre-check: Fast local file/env checks skip providers without credentials entirely (no network calls)
  • Parallel fetching: Active providers run concurrently via std::thread::scope
  • Disk cache: Data cached to ~/.cache/tokscale/subscription-usage-cache.json with 5-minute TTL β€” the Usage tab loads instantly on startup like other tabs
  • No new dependencies: Uses only crates already in the workspace (reqwest, serde, serde_json, chrono, anyhow, dirs, tokio)

Bug fixes included

  • MiniMax: current_interval_usage_count is a remaining count despite its name β€” now handled correctly with current_interval_used_count preferred when available
  • Kimi: OAuth refresh tokens (which rotate) are now persisted back to disk after each refresh, preventing stale tokens on next run
  • Codex: Credential lookup now requires tokens.access_token to be present (not just the tokens object), supports CODEX_HOME env var and macOS Keychain fallback
  • Copilot: Respects GH_CONFIG_DIR env var for hosts.yml path; YAML parser correctly handles other fields appearing before oauth_token under github.com:
  • OAuth payloads: Kimi and Codex refresh payloads use reqwest .form() for proper URL encoding of tokens that may contain reserved characters
  • Platform compat: read_keychain() returns a clean error on non-macOS instead of spawning a missing binary
  • TUI: Empty usage state shows a proper message instead of the loading prompt; tab navigation tests updated for 7-tab layout
  • Z.ai: Metrics ordered as Session β†’ Weekly β†’ Web Search regardless of API response order; renamed Monthlyβ†’Weekly, Web Searchesβ†’Web Search

Files changed

  • New: commands/usage/ directory (9 provider + helper files, ~1700 lines)
  • New: tui/ui/usage.rs (TUI rendering for Usage tab)
  • Modified: tui/app.rs (Usage tab, disk cache, updated tab tests)
  • Modified: main.rs (Usage subcommand + --home rejection)
  • Modified: README.md (provider docs, corrected tab count)
  • Unchanged: Cargo.lock (no new dependencies)

shidevil and others added 4 commits May 1, 2026 13:22
…viders

Adds tokscale usage CLI command and TUI tab for monitoring subscription
quotas across Claude, Codex, and Z.ai. Supports --json and --light output
modes, token refresh, and keychain credential fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
feat: add subscription usage command with Claude, Codex, and Z.ai pro…
…odule directory

Refactor usage.rs into commands/usage/ directory with per-provider modules
for better maintainability. Add four new subscription quota providers:

- Amp: reads API key from ~/.local/share/amp/secrets.json, parses
  displayText from ampcode.com/api/internal
- GitHub Copilot: reads token from macOS keychain or ~/.config/gh/hosts.yml,
  fetches quota from api.github.com/copilot_internal/user
- Kimi: reads OAuth credentials from ~/.kimi/credentials/kimi-code.json,
  fetches usage from api.kimi.com/coding/v1/usages with token refresh
- MiniMax: reads API key from MINIMAX_API_KEY env var, fetches from
  api.minimax.io/v1/api/openplatform/coding_plan/remains

Update README.md with subscription usage documentation and provider table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
feat(usage): add Amp, Copilot, Kimi, MiniMax providers; refactor to m…
@vercel

vercel Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tokscale Ready Ready Preview, Comment May 24, 2026 3:04pm

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="README.md">

<violation number="1" location="README.md:246">
P3: README says there are 6 views and that `1-6` switches them, but the TUI actually has 7 tabs and does not implement numeric tab shortcuts.</violation>
</file>

<file name="crates/tokscale-cli/src/main.rs">

<violation number="1" location="crates/tokscale-cli/src/main.rs:590">
P2: `usage` accepts the global `--home` flag but neither rejects it nor passes it through, so the override is silently ignored.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/codex.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/codex.rs:59">
P2: `read_credentials()` accepts the first file with any `tokens` value, even if it lacks `access_token`, so a stale primary auth file can prevent falling back to a valid secondary one.</violation>
</file>

<file name="crates/tokscale-cli/src/tui/app.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/app.rs:48">
P3: Tab tests are now stale: adding `Usage` changes the expected count/order, so the existing unit tests will fail.</violation>
</file>

<file name="crates/tokscale-cli/src/tui/ui/usage.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/ui/usage.rs:54">
P2: Loaded-but-empty subscription usage is rendered as the loading/prompt state, so a successful no-data fetch is hidden instead of showing an empty-state message.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/minimax.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/minimax.rs:134">
P1: `current_interval_usage_count` is treated as used-count and inverted, but MiniMax returns it as remaining-count, so usage/remaining numbers are reported backwards when the explicit remaining field is missing.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/helpers.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/helpers.rs:13">
P2: Unconditionally spawning the macOS `security` binary makes keychain lookup fail on non-macOS platforms.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/kimi.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/kimi.rs:87">
P2: Refresh token is inserted into an x-www-form-urlencoded payload without URL encoding, which can break refresh for valid tokens containing reserved characters.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/kimi.rs:151">
P1: Refresh updates only the in-memory access token; the refreshed refresh token/expires-at are never saved back to disk, so a rotated refresh token will go stale on the next run.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/copilot.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/copilot.rs:48">
P1: Hardcoded gh hosts path ignores gh config resolution, so Copilot auth lookup fails for users whose gh config lives outside ~/.config/gh.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/copilot.rs:67">
P2: `hosts.yml` parsing assumes `oauth_token` appears before any other key under `github.com`, so valid configs with other fields first are rejected.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/minimax.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs
Comment thread crates/tokscale-cli/src/commands/usage/copilot.rs Outdated
Comment thread crates/tokscale-cli/src/main.rs
Comment thread crates/tokscale-cli/src/commands/usage/codex.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/helpers.rs
Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/copilot.rs Outdated
Comment thread README.md Outdated
Comment thread crates/tokscale-cli/src/tui/app.rs
@shidevil
shidevil marked this pull request as draft May 2, 2026 12:33
shidevil and others added 10 commits May 2, 2026 12:47
current_interval_usage_count is a remaining count despite its name.
Prefer explicit current_interval_used_count when available, and treat
usage_count as remaining in the fallback path. Also adds:
- remains_time fallback for reset timestamps
- data-level plan fields (data.current_subscribe_title, data.plan_name)
- Plan inference that divides total by MODEL_CALLS_PER_PROMPT (15)
- Separate is_auth_error() detection (status 1004)
- epoch_to_ms() helper for proper timestamp normalization
- normalize_plan_name() that strips "MiniMax Coding Plan" prefix

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The OAuth server rotates refresh tokens on each refresh, but the old
code only updated the in-memory access_token and discarded the new
refresh_token/expires_in from the response. This meant the on-disk
refresh token would go stale after one rotation, forcing re-login.

Add save_credentials() that writes the full credential JSON back to
~/.kimi/credentials/kimi-code.json after a successful token refresh.
Both the proactive (near-expiry) and reactive (401 fallback) refresh
paths now persist the updated tokens.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The gh CLI supports GH_CONFIG_DIR to override its config directory,
but read_token_from_hosts() hardcoded ~/.config/gh/hosts.yml. Users
with a custom GH_CONFIG_DIR would fail to find the token.

Extract gh_config_dir() that checks GH_CONFIG_DIR first, then falls
back to ~/.config/gh β€” matching the gh CLI own resolution logic.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ng it

The usage command accepted the global --home flag but did nothing with
it, silently ignoring the override. Add reject_unsupported_home_override()
to match the pattern used by other commands that don't support --home.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…OME/keychain

read_credentials() accepted the first file with any tokens value, even
if access_token was null inside. This meant a stale primary auth file
(Codex nulls out access_token when switching to keyring storage) would
prevent falling back to a valid secondary one.

Now only accepts a credential source if tokens.access_token is present.
Also adds:
- CODEX_HOME env var as first path to check (matches Codex CLI)
- macOS keychain fallback for service "Codex Auth" (matches openusage)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1. README: correct tab count from 6 to 7, remove non-existent 1-6
   numeric shortcut claim

2. app.rs tests: update tab assertions for 7 tabs (Usage added at
   index 1), fixing test_tab_all/next/prev expectations

3. usage.rs: add empty-state rendering so a successful no-data fetch
   shows "No subscription data available" instead of the loading prompt

4. helpers.rs: guard read_keychain with cfg!(not(target_os = "macos"))
   so it fails cleanly on non-macOS instead of spawning a missing binary

5. kimi.rs + codex.rs: use reqwest .form() for OAuth refresh payloads
   instead of manual string formatting, ensuring proper URL encoding of
   refresh tokens that may contain reserved characters

6. copilot.rs: fix hosts.yml parsing to handle other fields (user,
   git_protocol) appearing before oauth_token under github.com: β€” only
   exit the section on a non-indented top-level key

All 451 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previously fetch_all() tried all 7 providers sequentially, causing slow
tab switches and noisy error messages for providers the user does not
have credentials for.

Add has_credentials() fast local checks to each provider (file exists,
env var set, keychain lookup) that skip providers entirely when no
credentials are on disk. Active providers now run in parallel via
std::thread::scope, so multiple providers fetch simultaneously.

Also fix remaining tab navigation tests (backtab, left/right) that
missed the Usage tab insertion.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Subscription usage now persists to the same cache directory as other
TUI data (~/.cache/tokscale/subscription-usage-cache.json). On startup
the cached data is loaded instantly, making the Usage tab appear
immediately on first switch. A fresh fetch is triggered when the cache
is older than 5 minutes or when the user presses u/r to refresh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…column

Rename Monthly to Weekly and Web Searches to Web Search. Collect
metrics into named variables instead of pushing in API order, so the
output is always Session β†’ Weekly β†’ Web Search regardless of API
response ordering. Widen label column to 14 chars for cleaner bar
alignment across all providers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/tui/app.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/app.rs:499">
P2: Usage refresh is performed synchronously on the UI thread, so slow provider fetches can freeze the TUI during refresh.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/claude.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/claude.rs:42">
P2: Refresh responses drop rotated `refresh_token` values and never persist refreshed credentials, so a rotated token would become stale on the next run.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/copilot.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/copilot.rs:167">
P2: Copilot credentials detection is too broad: it treats any existing `hosts.yml` as valid, so providers are queried even when no `github.com` oauth token is present.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/kimi.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/kimi.rs:83">
P2: Refreshed Kimi credentials are written with the result discarded, so disk write failures silently leave stale tokens in place.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/kimi.rs:83">
P1: Kimi credentials are written non-atomically with default permissions, which can expose sensitive tokens; use an atomic write with 0o600 permissions instead.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated
Comment thread crates/tokscale-cli/src/tui/app.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/claude.rs
Comment thread crates/tokscale-cli/src/commands/usage/copilot.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/commands/usage/amp.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/amp.rs:63">
P2: Percentage calculations can produce non-finite values here because parsed `f64`s are not validated with `is_finite()`, so malformed Amp text can leak `NaN`/`inf` into usage output.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:488">
P3: README instructs users to press `2` for the Usage tab even though numeric tab shortcuts are not supported in the TUI.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/codex.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/codex.rs:45">
P1: Refresh results are not persisted, so rotated refresh tokens or renewed access tokens are lost and future refreshes can fail.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/mod.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/mod.rs:100">
P2: Provider fetch failures are printed with `eprintln!` from worker threads used by the TUI, which can interleave raw output with the ratatui screen.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/mod.rs:123">
P2: The card rows can overflow the fixed border width because the formatted fields are not truncated and the account/plan rows are already wider than the border.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/codex.rs
Comment thread crates/tokscale-cli/src/commands/usage/amp.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/mod.rs
Comment thread crates/tokscale-cli/src/commands/usage/mod.rs Outdated
Comment thread README.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/commands/usage/codex.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/codex.rs:45">
P2: Refresh results are not persisted, so a rotated refresh token will be lost and future runs can fail after the access token expires.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/codex.rs

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

13 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/commands/usage/kimi.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/kimi.rs:83">
P2: Credential persistence errors are swallowed here, so refreshed Kimi tokens may never be written to disk and subsequent runs can keep using stale credentials.</violation>
</file>

<file name="crates/tokscale-cli/src/tui/ui/usage.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/ui/usage.rs:8">
P2: Duplicate helper implementations already exist in `commands/usage/helpers.rs`; this copies logic instead of reusing the shared helpers.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/mod.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/mod.rs:117">
P2: Light-mode cards never display `output.provider`, so multiple provider results become ambiguous when email/plan are absent.</violation>
</file>

<file name="README.md">

<violation number="1" location="README.md:488">
P3: README documents an unsupported `2` shortcut for the Usage tab; the TUI only supports arrow/Tab navigation.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/minimax.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/minimax.rs:101">
P2: The seconds/milliseconds heuristic is wrong for current Unix timestamps and can render reset times as 1970 or inflate duration-based resets.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/claude.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/claude.rs:42">
P2: Refresh handling ignores rotated refresh tokens, so a new refresh_token is never persisted for later runs.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/copilot.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/copilot.rs:51">
P2: Hardcoded `~/.config/gh` fallback misses GitHub CLI’s Windows config directory, so Copilot credentials can’t be discovered on Windows unless `GH_CONFIG_DIR` is set.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/copilot.rs:167">
P2: `has_credentials()` treats any existing `hosts.yml` as valid Copilot credentials, even though `fetch()` requires an actual `github.com` token. This can make Copilot appear active and then fail at runtime instead of being skipped.</violation>

<violation number="3" location="crates/tokscale-cli/src/commands/usage/copilot.rs:191">
P2: Parsing `percent_remaining` as an integer drops valid fractional quota values and can misreport Copilot usage as 0% used.</violation>
</file>

<file name="crates/tokscale-cli/src/tui/app.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/app.rs:288">
P2: Unit tests now read the user's real subscription-usage cache during app construction, so tab-navigation tests are not hermetic.</violation>

<violation number="2" location="crates/tokscale-cli/src/tui/app.rs:499">
P2: Synchronous usage refresh blocks the main TUI event loop and can freeze the UI during network fetches.</violation>

<violation number="3" location="crates/tokscale-cli/src/tui/app.rs:500">
P2: Subscription usage cache is not cleared when `fetch_all()` returns empty, so stale data can reappear on relaunch.</violation>

<violation number="4" location="crates/tokscale-cli/src/tui/app.rs:610">
P1: Switching to the Usage tab can now trigger a live subscription fetch and cache write from ordinary navigation tests.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/tui/app.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated
Comment thread crates/tokscale-cli/src/tui/ui/usage.rs
Comment thread crates/tokscale-cli/src/commands/usage/mod.rs
Comment thread crates/tokscale-cli/src/commands/usage/minimax.rs
Comment thread crates/tokscale-cli/src/commands/usage/copilot.rs Outdated
Comment thread crates/tokscale-cli/src/tui/app.rs Outdated
Comment thread crates/tokscale-cli/src/tui/app.rs Outdated
Comment thread crates/tokscale-cli/src/tui/app.rs Outdated
Comment thread README.md Outdated
shidevil and others added 4 commits May 3, 2026 00:31
- Persist OAuth refresh tokens to disk (Claude, Codex, Kimi)
- Atomic credential writes with 0o600 permissions
- Fix copilot has_credentials() to properly parse hosts.yml
- Fix copilot percent_remaining to use f64 not i64
- Fix minimax epoch_to_ms() heuristic for 2026 timestamps
- Add is_finite() guards on f64 arithmetic (amp.rs)
- Make helpers module public for TUI import
- Remove duplicate helpers from tui/ui/usage.rs
- Remove eprintln! error spam from worker threads
- Add provider name header + truncation to light-mode cards
- Fix README Usage tab shortcut instruction
- Make TUI tests hermetic (skip real cache in test builds)
- Remove auto-fetch on tab switch (cache + u key)
- Clear cache when fetch returns empty results
- Fix codex CredentialSource Copy derive (PathBuf not Copy)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(usage): batch reliability and security fixes
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(copilot): support Windows and XDG config directories for gh hosts
@shidevil

shidevil commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai

@cubic-dev-ai

cubic-dev-ai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai

@shidevil I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/commands/usage/kimi.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/kimi.rs:104">
P1: Secrets are created before restrictive permissions are applied, leaving a brief window where the temp credentials file may be more permissive than intended on Unix.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/amp.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/amp.rs:145">
P2: Ignores Amp's application-level error flag and silently converts failed responses into empty usage.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/claude.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/claude.rs:121">
P1: Secret temp file permissions are applied after creation, leaving a window where the credential temp file may be readable before it is tightened to 0o600.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/claude.rs:125">
P2: Atomic secret writes should fall back when `rename` fails; this helper only renames the temp file, so refreshed Claude credentials may not be saved on rename failures.</violation>
</file>

<file name="crates/tokscale-cli/src/tui/ui/usage.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/ui/usage.rs:20">
P2: Empty usage results are rendered as loading/prompt state instead of the empty state.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/codex.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/codex.rs:139">
P1: Temp credential file permissions are tightened after creation; set the Unix mode at open time to avoid a world-readable window.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/codex.rs:147">
P2: Atomic credential writes abort on rename failure without a copy-then-replace fallback or temp-file cleanup, so a transient save error can leave stale `.tmp` files and block future credential updates.</violation>

<violation number="3" location="crates/tokscale-cli/src/commands/usage/codex.rs:234">
P2: Refreshed access tokens are not persisted unless a new refresh token is also returned, so the cache stays stale and forces unnecessary refreshes on later runs.</violation>
</file>

<file name="crates/tokscale-cli/src/tui/app.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/app.rs:490">
P2: Usage refresh runs synchronously in the TUI key handler and can freeze the UI while provider fetches complete.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/mod.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/mod.rs:123">
P3: truncate() can return a string longer than the declared max_len, which overflows the fixed-width terminal column and breaks alignment.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/mod.rs:132">
P3: Light-mode card rows use inconsistent inner widths, so the right border shifts between lines and the CLI box renders jagged.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/copilot.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/copilot.rs:70">
P3: `has_credentials()` duplicates the `hosts.yml` parsing logic from `read_token_from_hosts()` instead of reusing it, which can cause the two credential checks to drift.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/claude.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/codex.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/amp.rs
Comment thread crates/tokscale-cli/src/commands/usage/claude.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/codex.rs Outdated
Comment thread crates/tokscale-cli/src/tui/app.rs
Comment thread crates/tokscale-cli/src/commands/usage/mod.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/mod.rs
Comment thread crates/tokscale-cli/src/commands/usage/copilot.rs
shidevil and others added 2 commits May 3, 2026 01:01
- Move atomic_write_secret to shared helpers with mode(0o600) at open time (P1)
- Add temp file cleanup on rename failure
- Check Amp ok field instead of silently returning empty metrics
- Persist Codex access token even when no new refresh token returned
- Fix TUI empty state to distinguish never-fetched from fetched-empty
- Fix truncate() off-by-one with ellipsis
- Fix light-mode card width consistency across header/metrics/border
- Deduplicate copilot hosts.yml parsing into shared helper

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(usage): batch 2 β€” security, correctness, and UX fixes

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/commands/usage/copilot.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/copilot.rs:185">
P2: Creating a fresh multi-threaded Tokio runtime inside each provider fetch can explode thread count when multiple providers are fetched concurrently; use a current-thread runtime or shared runtime instead.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/claude.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/claude.rs:66">
P2: A stale or partial `~/.claude/.credentials.json` can block valid keychain credentials because file existence is treated as authoritative and no fallback occurs on missing fields or parse failure.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/claude.rs:187">
P1: Refreshed Claude credentials are not persisted for keychain-backed logins, so rotated refresh tokens can become stale and break future auth.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/helpers.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/helpers.rs:62">
P2: atomic_write_secret uses a fixed temp path and unconditionally removes it on create_new() failure, so stale temp files or concurrent writers can make secret writes fail and interfere with each other.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/claude.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/copilot.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/claude.rs
Comment thread crates/tokscale-cli/src/commands/usage/helpers.rs Outdated
shidevil and others added 2 commits May 3, 2026 04:54
- Switch all 7 providers to current-thread Tokio runtime to reduce
  thread count when fetched in parallel
- Claude: fallback from stale/corrupt credentials file to keychain
- Claude: always persist refreshed tokens (not just file-sourced)
- atomic_write_secret: use PID-scoped temp file to avoid concurrent
  write collisions

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(usage): runtime, credential fallback, and concurrency fixes
@shidevil
shidevil marked this pull request as ready for review May 3, 2026 11:25

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/commands/usage/kimi.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/kimi.rs:166">
P2: Proactive refresh can rotate the refresh token, but the retry path still uses the original cached token instead of the freshly returned one.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/minimax.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/minimax.rs:206">
P2: Displays raw request counts as "prompts left", which misstates the remaining quota.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/minimax.rs
shidevil and others added 2 commits May 3, 2026 11:43
The reactive retry path was using the original cached refresh token
instead of the freshly rotated one from the proactive refresh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(kimi): update stored refresh token after proactive refresh
@shidevil
shidevil marked this pull request as draft May 3, 2026 12:16
@shidevil
shidevil marked this pull request as ready for review May 3, 2026 12:16

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid β€” if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/commands/usage/kimi.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/kimi.rs:223">
P2: Labels `limits` entries by index instead of the API’s window metadata, so any reordering or new limit window will mislabel session vs weekly usage.</violation>

<violation number="2" location="crates/tokscale-cli/src/commands/usage/kimi.rs:225">
P2: Deduplication collapses distinct Kimi quota windows when they share the same usage values, because the key ignores `label` and `resets_at`.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/helpers.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/helpers.rs:85">
P2: `atomic_write_secret` can lose credential updates because it deletes the temp file and returns an error when `rename` fails, with no fallback to persist the new secret.</violation>
</file>

<file name="crates/tokscale-cli/src/tui/app.rs">

<violation number="1" location="crates/tokscale-cli/src/tui/app.rs:383">
P2: Missing `TryRecvError::Disconnected` handling leaves the usage fetch stuck forever if the background thread exits without sending a result, preventing retries until restart.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/copilot.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/copilot.rs:207">
P2: Paid-tier usage falls back to 100% remaining when `percent_remaining` is missing, so it can report zero usage even when `remaining`/`entitlement` show consumption.</violation>
</file>

<file name="crates/tokscale-cli/src/commands/usage/mod.rs">

<violation number="1" location="crates/tokscale-cli/src/commands/usage/mod.rs:137">
P3: Metric labels are not truncated before rendering, so labels longer than 14 characters can overflow the fixed-width card layout.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/kimi.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/helpers.rs
Comment thread crates/tokscale-cli/src/tui/app.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/copilot.rs Outdated
Comment thread crates/tokscale-cli/src/commands/usage/mod.rs Outdated
shidevil and others added 2 commits May 3, 2026 12:33
- Kimi: use window duration metadata instead of index for labels
- Kimi: include label in dedup key to avoid collapsing distinct windows
- TUI: handle Disconnected from background fetch thread (prevents stuck spinner)
- Copilot: compute pct from remaining/entitlement when percent_remaining missing
- Light-mode: truncate metric labels to prevent column overflow

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(usage): kimi labels, dedup, disconnected handling, copilot fallback
@shidevil

shidevil commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai

@cubic-dev-ai

cubic-dev-ai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai

@shidevil I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 17 files

@junhoyeo junhoyeo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM β€” cleanly modularized per-provider, real provider quirks addressed. Follow-up: remove committed PR_BODY.md and add a vendor-reported disclaimer in README.

# Conflicts:
#	Cargo.lock
#	README.md
#	crates/tokscale-cli/src/main.rs
#	crates/tokscale-cli/src/tui/app.rs
@junhoyeo
junhoyeo merged commit 1d4dacd into junhoyeo:main May 24, 2026
3 of 4 checks passed
junhoyeo added a commit that referenced this pull request May 25, 2026
Folds in the low-risk fold-in items captured in
`pr_followups_2026_05_24.md` after the 19-PR batch review/merge:

- Delete `PR_BODY.md` from the repo root (slipped in via #493).
- Fix `EmbedContributionDay` test mocks in `renderIsometric3DSvg.test.ts`
  and `renderProfileEmbedSvg.test.ts` β€” both were missing the
  `totalTokens`/`totalCost` fields that #584 added to the type, so
  `bunx tsc --noEmit` had been red on main since the embed-templates PR.
  Both files type-check clean now.
- Add a precedence-rule comment above `userHasToggledSort` in
  `LeaderboardClient.tsx` so the URL-`sortBy` β†’ user-click handoff
  semantics survive future refactors. Each SortOption.onClick relies on
  flipping this ref; removing those lines silently regresses the URL
  param into a permanent override.
- Document the `schemaVersion: 2` value introduced in `getSubmitDevice`
  with a comment listing all three known schema versions and the
  `GREATEST()` invariant on the submissions row.
- Add a "vendor-reported" note to the README's Trae section explaining
  that cost figures come from Trae's `dollar_float` rather than
  tokscale's pricing engine, and a parallel disclaimer in the
  Subscription Usage section for the same reason.

Constraint: keep the PR scope to safe, behavior-preserving changes
Rejected: bundle env-overridable thresholds + ms/s normalization | both require design discussion and were left as separate followups
Rejected: add last-owner-cannot-be-demoted unit test | needs a non-trivial mocking surface for the role route; production code is already correct on main
Confidence: high
Scope-risk: narrow
Directive: do NOT remove the `userHasToggledSort.current = true;` lines in any SortOption.onClick β€” the comment now explains why
Not-tested: README rendering (markdown only)
@junhoyeo

Copy link
Copy Markdown
Owner

@shidevil this has been merged to v3.0.0: https://github.com/junhoyeo/tokscale/releases/tag/v3.0.0 thanks for the contribution!

@shidevil

Copy link
Copy Markdown
Contributor Author

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants