Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ body:
- OpenRouter
- Warp
- z.ai
- Kimi K2
- Kimrel
- Ollama
- Multiple / All
validations:
Expand Down
19 changes: 12 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,18 @@ exhaustive provider coverage. Restore a dropped topic via
Stream Deck UI. The SVG owns the value, glyph, and ratio fill; the
title bar owns the label text. Send labels in UPPERCASE
(`SESSION`, `WEEKLY`, …) to match the title font's expected look.
- Provider button glyphs live in `internal/icons/`. Most come from
lobehub/lobe-icons (MIT) — `internal/icons/lobe_generated.go` is
produced by `go run scripts/sync-lobe-icons.go`. Edit the mapping
table in that script and re-run to add a provider, change a variant
(`mono` vs wordmark `text`), or refresh after upstream changes. The
remaining hand-drawn marks (warp, factory, abacus, augment,
jetbrains, kiro, opencodego, synthetic) live in their own
- Provider button glyphs live in `internal/icons/`. **Source of truth
for new provider glyphs is lobehub/lobe-icons (MIT).** Add the entry
to `scripts/sync-lobe-icons.go` and re-run `go run
scripts/sync-lobe-icons.go` to regenerate `internal/icons/lobe_generated.go`.
Do not hand-draw a new mark when a Lobe icon exists for that brand,
and do not borrow another provider's mark when one doesn't (instead
use a neutral letterform — see `internal/icons/kimrel.go`). Action-
picker SVGs in `io.github.anthonybaldwin.UsageButtons.sdPlugin/assets/`
must use the same path data as the in-Go glyph so the SD store icon
matches what the button renders. The remaining hand-drawn marks
(warp, factory, abacus, augment, jetbrains, kiro, opencodego,
synthetic) predate the lobe-icons sync and stay in their own
`<provider>.go` files alongside the trimmed `icons.go` literal.

## Browser fetch bridge (Usage Buttons Helper extension)
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Runs on **Windows and macOS**.

## Settings

Each provider is its own action — drag **Claude Code**, **Codex**, **Gemini**,
Each provider is its own action — drag **Claude**, **Codex**, **Gemini**,
**Vertex AI**, etc. onto a key and configure the metric, colors, and
thresholds from the Property Inspector.

Expand Down Expand Up @@ -65,7 +65,7 @@ UsageButtons/
│ │ ├── amp/ # Amp (browser)
│ │ ├── antigravity/ # Antigravity (local language server)
│ │ ├── augment/ # Augment (CLI/browser)
│ │ ├── claude/ # Claude Code (Claude OAuth + browser web API)
│ │ ├── claude/ # Claude (Claude OAuth + browser web API)
│ │ ├── codex/ # Codex (OAuth)
│ │ ├── cookieaux/ # cookie-gated provider messaging helpers
│ │ ├── copilot/ # GitHub Copilot
Expand All @@ -78,8 +78,8 @@ UsageButtons/
│ │ ├── nousresearch/ # Nous Research portal (Hermes / Nous Chat, browser)
│ │ ├── jetbrains/ # JetBrains AI
│ │ ├── kilo/ # Kilo
│ │ ├── kimi/ # Kimi (browser)
│ │ ├── kimik2/ # Kimi K2 (API key)
│ │ ├── kimi/ # Kimi (browser, OAuth fallback)
│ │ ├── kimrel/ # Kimrel — third-party Kimi K2 reseller (API key)
│ │ ├── kiro/ # Kiro
│ │ ├── minimax/ # MiniMax (browser/API key)
│ │ ├── mistral/ # Mistral (browser)
Expand Down Expand Up @@ -119,7 +119,7 @@ Short version:
**UsageButtons-Helper-unpacked.zip** from the same release, unzip
it, and **Load unpacked** in `chrome://extensions`. The plugin
auto-registers — nothing to configure.
3. Drag a provider (**Claude Code**, **Codex**, **Copilot**, etc.) onto a
3. Drag a provider (**Claude**, **Codex**, **Copilot**, etc.) onto a
Stream Deck key and pick a metric from the Property Inspector.

## Build from source
Expand Down Expand Up @@ -176,7 +176,7 @@ through your real browser session; cookies never leave Chrome.
`chrome://extensions`, done.
- **Providers that don't need it keep working unchanged** — Gemini,
Vertex AI, Copilot, OpenRouter, DeepSeek, Moonshot, Warp, z.ai,
Kimi K2, Synthetic, Kilo, Kiro, JetBrains AI, Anthropic, OpenAI,
Kimrel, Synthetic, Kilo, Kiro, JetBrains AI, Anthropic, OpenAI,
and Antigravity never require the extension.
- **Waits patiently on cold start.** Cookie-gated buttons stay in a
quiet "needs browser extension" state until the extension
Expand Down
2 changes: 1 addition & 1 deletion chrome-extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Usage Buttons Helper",
"short_name": "UsageButtons",
"version": "0.8.1",
"version": "0.8.0",
"description": "Companion extension for the Usage Buttons Stream Deck plugin. Proxies a narrow allowlist of AI usage APIs (abacus.ai, alibabacloud.com, aliyun.com, claude.ai, cursor.com, factory.ai, ollama.com, chatgpt.com, augmentcode.com, ampcode.com, perplexity.ai, grok.com, nousresearch.com, opencode.ai, kimi.com, minimax.io, minimaxi.com, mistral.ai, deepseek.com) using your logged-in browser session — credentials stay in Chrome.",
"author": "Anthony Baldwin",
"minimum_chrome_version": "127",
Expand Down
50 changes: 50 additions & 0 deletions chrome-extension/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,46 @@ async function readDeepSeekPlatformToken() {
return null;
}

// readKimiAccessToken reads localStorage["access_token"] from any open
// kimi.com tab. Kimi migrated off cookie-based auth — the kimi-auth
// JWT cookie is no longer set, and apiv2 endpoints reject cookie-only
// requests with `REASON_INVALID_AUTH_TOKEN`. The session bearer token
// now lives in localStorage and the page's own client passes it via
// `Authorization: Bearer <tok>` on every API call.
//
// Returns null when no kimi.com tab is open or the user is signed out;
// the Go side then falls back to OAuth credentials placed by the
// `kimi login` CLI.
async function readKimiAccessToken() {
let tabs;
try {
tabs = await chrome.tabs.query({ url: "*://*.kimi.com/*" });
} catch (_e) {
return null;
}
if (!tabs || tabs.length === 0) return null;
for (const t of tabs) {
try {
const results = await chrome.scripting.executeScript({
target: { tabId: t.id },
// MAIN world reaches the page's own localStorage rather than
// the isolated-world copy a content script would see.
world: "MAIN",
func: () => localStorage.getItem("access_token") || "",
});
const raw = results && results[0] && results[0].result;
if (raw && typeof raw === "string" && raw.length > 0) {
return raw;
}
} catch (_e) {
// executeScript can fail if the tab is loading or in a special state;
// try the next one.
continue;
}
}
return null;
}

// augmentHeadersForOrigin attaches site-specific auth/version headers
// for hosts whose internal APIs require explicit non-cookie auth.
// Returns the final headers object to pass to fetch(). For everything
Expand All @@ -252,6 +292,16 @@ async function augmentHeadersForOrigin(url, callerHeaders) {
headers["x-app-version"] = DEEPSEEK_PLATFORM_APP_VERSION;
}
}

if (host === "kimi.com" || host === "www.kimi.com" || host.endsWith(".kimi.com")) {
if (!hasHeader(headers, "authorization")) {
const tok = await readKimiAccessToken();
if (tok) {
headers["Authorization"] = "Bearer " + tok;
}
}
}

return headers;
}

Expand Down
3 changes: 2 additions & 1 deletion cmd/genkeys/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ var providerColors = map[string]string{
"vertexai": "#4285f4",
"warp": "#938bb4",
"zai": "#e85a6a",
"kimi-k2": "#4c00ff",
"kimi-k2": "#64748b",
"moonshot": "#0a84ff",
}

func main() {
Expand Down
26 changes: 25 additions & 1 deletion cmd/plugin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/anthonybaldwin/UsageButtons/internal/settings"
"github.com/anthonybaldwin/UsageButtons/internal/streamdeck"
"github.com/anthonybaldwin/UsageButtons/internal/update"
"github.com/anthonybaldwin/UsageButtons/internal/wsl"

// Register all providers via init().
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/abacus"
Expand All @@ -45,7 +46,7 @@ import (
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/jetbrains"
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/kilo"
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/kimi"
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/kimik2"
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/kimrel"
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/kiro"
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/minimax"
_ "github.com/anthonybaldwin/UsageButtons/internal/providers/mistral"
Expand Down Expand Up @@ -741,9 +742,32 @@ func handleSendToPlugin(conn *streamdeck.Connection, ev streamdeck.Event) {
go replyUnregisterCookieHost(conn, ev.Context, ev.Action)
case "getProviderStatus":
go replyProviderStatus(conn, ev.Context, ev.Action)
case "getWSLDistros":
go replyWSLDistros(conn, ev.Context, ev.Action)
}
}

// replyWSLDistros tells the PI which WSL distributions are currently
// running. The PI uses the response to inject extra "WSL: <distro>"
// entries into the metric dropdown for cost-tile-bearing providers.
//
// On non-Windows builds wsl.Sources() returns nil and the PI sees an
// empty list, so nothing changes in the UI.
func replyWSLDistros(conn *streamdeck.Connection, ctxStr, action string) {
sources := wsl.Sources()
distros := make([]map[string]string, 0, len(sources))
for _, s := range sources {
distros = append(distros, map[string]string{
"key": s.Key,
"label": s.Label,
})
}
conn.SendToPropertyInspector(ctxStr, action, map[string]any{
"action": "wslDistros",
"distros": distros,
})
}

// cookieHostPayload is the PI → plugin shape for registerCookieHost.
func replyCookieStatus(conn *streamdeck.Connection, ctxStr, action string) {
pctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
Expand Down
6 changes: 3 additions & 3 deletions docs/PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ account or API response includes that quota lane.
| Nous Research | Usage Buttons Helper from `portal.nousresearch.com`. | Subscription credits ($, Hermes Agent + Nous Chat pool), API credits balance ($), all-time totals (spend $, requests, tokens, input/output/cache-read/cache-write tokens) — combined or split by allowance (api / sub). |
| JetBrains AI | Local JetBrains IDE quota files. Optional overrides: `CODEXBAR_JETBRAINS_IDE_BASE_PATH` or `JETBRAINS_QUOTA_FILE`. | Current credits remaining %. |
| Kilo | Kilo API key from the Provider tab, `KILO_API_KEY`, or `~/.local/share/kilo/auth.json`. | Credits remaining %, Kilo Pass remaining %. |
| Kimi | Usage Buttons Helper from `kimi.com`. | Weekly coding quota remaining %, 5-hour rate limit remaining %. |
| Kimi K2 | Kimi K2 API key from the Provider tab or `KIMI_K2_API_KEY`. | Credits remaining. |
| Kimi | Usage Buttons Helper from `kimi.com` (preferred). Falls back to OAuth credentials placed by `kimi login` at `~/.kimi/credentials/kimi-code.json` when the Helper isn't connected; refresh tokens are exchanged against `auth.kimi.com`. | Weekly coding quota remaining %, 5-hour rate limit remaining %. |
| Kimrel | Kimrel API key from the Provider tab or `KIMREL_API_KEY` (older `KIMI_K2_API_KEY` / `KIMI_API_KEY` / `KIMI_KEY` still resolve). Kimrel (kimrel.com, formerly kimi-k2.ai) is an **independent third-party reseller** of Kimi K2 model access — not affiliated with, endorsed by, or sponsored by Moonshot AI. Use the Moonshot provider for the official Moonshot dev platform. | Credits remaining. |
| Kiro | `kiro-cli`; run `kiro-cli login` first. | Monthly credits remaining %, bonus credits remaining %. |
| MiniMax | MiniMax API key from the Provider tab / `MINIMAX_API_KEY`, or Usage Buttons Helper from `minimax.io`. Optional region override: `MINIMAX_REGION`. | Coding prompts remaining %. |
| Mistral | Usage Buttons Helper from `admin.mistral.ai`. | Monthly billing usage. |
| Moonshot (Kimi platform) | Moonshot API key from the Provider tab or `MOONSHOT_API_KEY` / `KIMI_PLATFORM_API_KEY`. Optional China-region host override: `MOONSHOT_API_HOST=https://api.moonshot.cn`. | Available balance ($), voucher balance ($), cash balance ($). Distinct from the Kimi (chat) provider — Moonshot is the paid developer API platform. |
| Moonshot (Kimi platform) | Moonshot API key from the Provider tab or `MOONSHOT_API_KEY` / `KIMI_PLATFORM_API_KEY`. Optional China-region host override: `MOONSHOT_API_HOST=https://api.moonshot.cn`. | Available balance ($), voucher balance ($), cash balance ($). Distinct from the Kimi provider (per-user CLI quotas) and Kimrel (third-party reseller credits) — Moonshot is the org-wide paid developer API balance. |
| Ollama | Usage Buttons Helper from the signed-in Ollama web session. | Session usage remaining %, session pace (burn rate), weekly usage remaining %, weekly pace (burn rate). |
| OpenAI | OpenAI admin API key (`sk-admin-…`) from the Provider tab or `OPENAI_ADMIN_API_KEY` (kept namespaced so it doesn't shadow the SDK-standard `OPENAI_API_KEY`). Org admins only — personal `sk-` keys are rejected by the admin endpoints. | Org spend today (UTC, $), yesterday ($), last 7 days ($), month-to-date ($), last 30 days ($), 7-day burn rate ($/day), projected month total ($). Distinct from the Codex provider (per-user session/weekly window from ChatGPT OAuth) — this is the org-wide cost view. |
| OpenCode | Usage Buttons Helper from `opencode.ai`. Optional workspace override: `CODEXBAR_OPENCODE_WORKSPACE_ID`. | 5-hour usage remaining %, weekly usage remaining %. |
Expand Down
6 changes: 3 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,9 @@ <h3>Standalone native binary</h3>
</div>

<h2 style="margin-top: 56px">Supported providers</h2>
<p class="sub">35 providers are live, spanning hosted AI assistants (Claude Code, Codex, Cursor, Copilot, Gemini, Grok), self-hosted gateways (OpenClaw, Hermes Agent, Ollama), portal accounts (Nous Research, Perplexity, Mistral, Kimi, MiniMax, Abacus, Alibaba, Augment, Amp, Droid, OpenCode, JetBrains AI, Antigravity, Kilo, Kiro), and direct API keys (OpenRouter, DeepSeek, Moonshot, Anthropic, OpenAI, Vertex AI, Synthetic, Warp, z.ai, Kimi K2).</p>
<p class="sub">35 providers are live, spanning hosted AI assistants (Claude, Codex, Cursor, Copilot, Gemini, Grok), self-hosted gateways (OpenClaw, Hermes Agent, Ollama), portal accounts (Nous Research, Perplexity, Mistral, Kimi, MiniMax, Abacus, Alibaba, Augment, Amp, Droid, OpenCode, JetBrains AI, Antigravity, Kilo, Kiro), and direct API keys (OpenRouter, DeepSeek, Moonshot, Anthropic, OpenAI, Vertex AI, Synthetic, Warp, z.ai, Kimrel).</p>
<div class="providers-row">
<span class="pill active"><img src="provider-icons/claude.svg" alt="">Claude Code</span>
<span class="pill active"><img src="provider-icons/claude.svg" alt="">Claude</span>
<span class="pill active"><img src="provider-icons/anthropic.svg" alt="">Anthropic</span>
<span class="pill active"><img src="provider-icons/codex.svg" alt="">Codex</span>
<span class="pill active"><img src="provider-icons/openai.svg" alt="">OpenAI</span>
Expand Down Expand Up @@ -513,7 +513,7 @@ <h2 style="margin-top: 56px">Supported providers</h2>
<span class="pill active"><img src="provider-icons/synthetic.svg" alt="">Synthetic</span>
<span class="pill active"><img src="provider-icons/warp.svg" alt="">Warp</span>
<span class="pill active"><img src="provider-icons/zai.svg" alt="">z.ai</span>
<span class="pill active"><img src="provider-icons/kimi-k2.svg" alt="">Kimi K2</span>
<span class="pill active"><img src="provider-icons/kimi-k2.svg" alt="">Kimrel</span>
<span class="pill active"><img src="provider-icons/ollama.svg" alt="">Ollama</span>
<span class="pill active"><img src="provider-icons/kiro.svg" alt="">Kiro</span>
</div>
Expand Down
4 changes: 2 additions & 2 deletions docs/provider-icons/kimi-k2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading