@bitio/opencode-provider-balance
v1.0.2
Published
OpenCode TUI plugin that renders a provider balance panel in the sidebar
Maintainers
Readme
opencode-provider-balance

Documentation: docs/00-index.md · Troubleshooting · Releasing
An opencode TUI plugin that shows your API-provider balance in the sidebar. It
fetches balances on session start and re-fetches them on an interval, rendering
a provider header (icon + name, e.g. 🐋 DeepSeek) with one line per currency
showing the total balance, plus emoji indicators (💰 for the panel, 🕓 when
stale, ⚠️ for low-balance warnings). The last-known balance is cached in
opencode's KV store (state/kv.json), so it survives restarts and is marked
🕓 when the API is unreachable. A ⚠️ prefix warns when a currency's
total is below the configured threshold. Optionally, each line can also show the
granted and topped-up split.
First provider: DeepSeek, fetched with DEEPSEEK_API_KEY using Bearer auth (no
OAuth). The provider list is extensible for future providers.
Install
Requires opencode with TUI plugin support (opencode >= 1.17).
Install from npm and register it with opencode's plugin flow:
opencode plugin @bitio/opencode-provider-balanceor add the package to the plugin array of tui.json (the TUI config file):
{
"plugin": ["@bitio/opencode-provider-balance"]
}Set the API key:
export DEEPSEEK_API_KEY=sk-...If the key is missing, the panel shows API key not configured and Set
DEEPSEEK_API_KEY.
Configuration
Options go in the second element of a [spec, options] tuple in the plugin
array:
{
"plugin": [
[
"@bitio/opencode-provider-balance",
{ "threshold": 20, "currency": "USD", "refreshIntervalMinutes": 15, "fields": "total", "providers": ["deepseek"] }
]
]
}| Option | Type | Default | Description |
| --- | --- | --- | --- |
| threshold | number, optional | null (off) | Prefix ! when a currency's total balance is strictly below this value. |
| currency | string, optional | none | Restrict threshold evaluation to this currency. Display is unaffected. |
| refreshIntervalMinutes | number, optional | 15 (min 1) | How often to re-fetch balances. |
| fields | "total" | "split", optional | "total" | Show only totals, or also granted/topped-up breakdown. |
| providers | string[] or string, optional | [] | List of provider ids to enable (e.g. ["deepseek"]); empty = panel hidden, but the commands stay in the palette. |
| keybind | string, optional | <leader>shift+b | Keybind that toggles the panel; "none" disables it. |
| refreshKeybind | string, optional | none | Keybind that refreshes balances; "none" disables it. |
All options are optional; invalid values fall back to defaults. Threshold
comparison is strict (<); omit threshold or set it to null to disable
warnings.
Only configured providers are fetched and shown; unconfigured providers produce no messages.
Toggle and commands
balance.toggle— show/hide the panel. Default binding<leader>shift+b(leader, then shift+b; the leader key defaults toctrl+x). Plain<leader>bis opencode's built-in sidebar toggle, so the panel toggle uses shift+b and doesn't collide with it.balance.refresh— fetch balances now. No default binding; run it from the command palette.
Both commands appear in the command palette (command_list, default ctrl+p)
regardless of provider configuration — even with an empty providers
array (panel hidden), balance.toggle and balance.refresh are registered
and runnable; refresh is a no-op and toggle toggles a hidden panel.
Custom keybinds are set via PLUGIN OPTIONS, not tui.json keybinds. The host's
keybinds accepts only built-in keybind names and silently ignores plugin
commands, so overrides live in the plugin tuple:
{
"plugin": [
[
"@bitio/opencode-provider-balance",
{ "keybind": "ctrl+b", "refreshKeybind": "f5" }
]
]
}Pass "none" to disable a binding (e.g. "keybind": "none").
Warning: avoid keys already used by opencode's built-in keybinds — built-in bindings take precedence over plugin bindings, so the plugin never fires. For example,
ctrl+ris bound to rename session,<leader>btoggles the sidebar, and<leader>rredoes. Check the built-in list in opencode's keybinds docs before picking a binding (e.g.f5is unbound and works well for refresh).
Logs
Refresh outcomes are logged through opencode's app log under service:
balance-panel (info on success, warn/error on failures). Enable the debug
console with a built-in keybind in tui.json, e.g.
{ "keybinds": { "app_console": "f9" } }, then press f9 to view.
Disable
Disable the plugin without removing it. plugin_enabled is keyed by the plugin
id (balance.panel), not the package name:
{
"plugin_enabled": { "balance.panel": false }
}Troubleshooting
The balance panel doesn't show up
Check that plugin_enabled isn't false for the balance.panel plugin id and
that the package is listed in the plugin array of tui.json. An empty
providers option ([]) also hides the panel. If the config looks right, check
opencode's logs — see Logs — or run opencode --log-level debug.
I installed a new version but opencode still behaves like the old one
opencode caches installed plugin packages. Clear its plugin cache and restart opencode to pick up the new version.
The panel shows API key not configured
The DEEPSEEK_API_KEY environment variable is missing or wasn't exported in the
shell opencode was started from. Set it and restart opencode.
The balance is stale (🕓)
The API is unreachable, so the last-known cached balance is shown. Check your
network connection and that DEEPSEEK_API_KEY is still valid, then run
balance.refresh to fetch again.
Development
bun install
bun run typecheck
bun run test
bun run buildbun run build emits dist/, the npm-published artifact (exports["./tui"]).
Contributions are welcome — see CONTRIBUTING.md and our Code of Conduct.
