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
5 changes: 5 additions & 0 deletions .changeset/540e02b2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@mynameistito/opencode-usage-limits": patch
---

Add DeepSeek balance usage provider
5 changes: 5 additions & 0 deletions .changeset/dee7d71f.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@mynameistito/opencode-plugins-docs": patch
---

Document the DeepSeek balance provider and add its logo to the Usage Limits guide.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Supported providers:
- [Alibaba Token Plan](https://www.alibabacloud.com/help/en/model-studio/token-plan-personal-overview)
- [ChatGPT](https://chatgpt.com/)
- [Command Code](https://commandcode.ai/)
- [DeepSeek](https://www.deepseek.com/)
- [OpenCode GO](https://opencode.ai/go)
- [MiniMax Token Plan](https://platform.minimax.io/subscribe/token-plan)
- [Synthetic](https://synthetic.ai/)
Expand Down
4 changes: 2 additions & 2 deletions apps/web/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ Both packages are published to npm and installed through the OpenCode CLI. They
composer hint.
</Card>
<Card title="Usage Limits" href="/usage-limits" icon="gauge">
Track Codex, Command Code, Z.AI, Synthetic, MiniMax, Qwen, Alibaba Token
Plan, and OpenCode GO windows.
Track Codex, DeepSeek, Command Code, Z.AI, Synthetic, MiniMax, Qwen, Alibaba
Token Plan, and OpenCode GO windows and balances.
</Card>
</CardGroup>

Expand Down
40 changes: 38 additions & 2 deletions apps/web/docs/usage-limits.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Usage Limits
description: Show provider quota windows in the OpenCode sidebar and prompt footer.
description: Show provider quota windows and account balances in the OpenCode sidebar and prompt footer.
sidebar:
label: Usage Limits
order: 3
Expand Down Expand Up @@ -105,6 +105,23 @@ The top-level `enabled` flag disables the plugin. Each provider's `enabled` flag
</svg>
<span class="provider-logo-name">Command Code</span>
</a>
<a href="#deepseek" aria-label="DeepSeek">
<svg
class="provider-logo-deepseek"
width="27"
height="20"
viewBox="0 0 27 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M26.3543 1.64471C26.0719 1.5067 25.9506 1.77006 25.7856 1.90346C25.7292 1.94659 25.6815 2.00294 25.6338 2.05469C25.2215 2.49516 24.7396 2.78439 24.1106 2.74989C23.1905 2.69814 22.4051 2.98737 21.7104 3.69119C21.5627 2.82349 21.0722 2.3054 20.3258 1.97304C19.9354 1.80054 19.5403 1.62746 19.2666 1.25197C19.0757 0.984593 19.0234 0.686733 18.9279 0.392893C18.867 0.215793 18.8066 0.0346628 18.6025 0.00418277C18.3811 -0.0303172 18.2943 0.155413 18.2074 0.310673C17.8601 0.945493 17.7256 1.64471 17.7388 2.35313C17.7693 3.9465 18.442 5.21556 19.779 6.11834C19.9307 6.22184 19.9699 6.32535 19.9221 6.47658C19.8307 6.78766 19.7226 7.08955 19.6272 7.40063C19.5662 7.59901 19.4753 7.64271 19.2626 7.55588C18.5289 7.2494 17.8951 6.79571 17.3351 6.24772C16.3846 5.32827 15.525 4.31336 14.4531 3.51869C14.2013 3.33296 13.9494 3.16045 13.689 2.996C12.5953 1.93394 13.8321 1.06164 14.1185 0.958143C14.4181 0.850033 14.2226 0.478573 13.2548 0.483173C12.2871 0.487203 11.4015 0.811513 10.2728 1.24335C10.1077 1.30832 9.93411 1.35547 9.75642 1.39457C8.73231 1.20022 7.66853 1.15709 6.5576 1.28245C4.46568 1.51533 2.79468 2.50436 1.56645 4.19261C0.0909569 6.22184 -0.256354 8.5277 0.168584 10.9324C0.615372 13.4671 1.90916 15.5653 3.89699 17.2058C5.95843 18.9067 8.33268 19.7405 11.0416 19.5806C12.6867 19.4858 14.5181 19.2655 16.5842 17.5169C17.1051 17.7762 17.652 17.8797 18.5588 17.9574C19.2574 18.0229 19.9302 17.9229 20.4512 17.8148C21.2671 17.6423 21.2108 16.8867 20.9158 16.7481C18.5243 15.6343 19.0493 16.0874 18.572 15.7206C19.787 14.283 21.6432 11.7276 22.2159 8.24821C22.2722 7.86409 22.3441 7.323 22.3355 7.01192C22.3309 6.82216 22.3746 6.74856 22.5914 6.72671C23.1905 6.65771 23.7719 6.49383 24.3061 6.19999C25.8557 5.35357 26.4808 3.96318 26.628 2.29678C26.6498 2.04204 26.6234 1.77869 26.3543 1.64471ZM12.8512 16.6446C10.5333 14.8224 9.40911 14.2226 8.94507 14.2485C8.51093 14.2744 8.58913 14.7712 8.68459 15.0949C8.78464 15.4146 8.91459 15.6349 9.09687 15.9155C9.2228 16.1012 9.30963 16.3772 8.97095 16.5848C8.22457 17.0465 6.92676 16.4296 6.86581 16.3991C5.35524 15.5095 4.0925 14.3353 3.20237 12.7293C2.34272 11.1837 1.84361 9.5253 1.76138 7.75542C1.73953 7.32818 1.86546 7.17695 2.29097 7.09932C2.85104 6.99582 3.42835 6.97397 3.98784 7.05619C6.35347 7.40178 8.36718 8.4592 10.0554 10.1348C11.0191 11.0888 11.7483 12.229 12.4992 13.3429C13.2979 14.5257 14.157 15.6527 15.2513 16.5768C15.6377 16.9005 15.9459 17.1466 16.2409 17.3283C15.3513 17.4278 13.8666 17.4491 12.8512 16.6458V16.6446ZM13.9621 9.4989C13.9621 9.3091 14.1139 9.1579 14.3048 9.1579C14.3479 9.1579 14.387 9.1665 14.4221 9.1792C14.4698 9.1964 14.5135 9.2223 14.548 9.2614C14.609 9.3218 14.6435 9.408 14.6435 9.4989C14.6435 9.6886 14.4917 9.8399 14.3008 9.8399C14.1099 9.8399 13.9621 9.6886 13.9621 9.4989ZM17.4128 11.2688C17.1914 11.3596 16.97 11.4373 16.7572 11.4459C16.4272 11.4631 16.0672 11.3291 15.8717 11.1653C15.5681 10.9105 15.3508 10.7679 15.2599 10.3234C15.2208 10.1337 15.2426 9.8399 15.2771 9.6714C15.3554 9.3085 15.2685 9.0757 15.0126 8.864C14.8045 8.6915 14.5394 8.6438 14.2484 8.6438C14.1398 8.6438 14.0403 8.5961 13.9661 8.5576C13.8448 8.4972 13.7447 8.346 13.8402 8.16023C13.8707 8.09985 14.0184 7.95322 14.0529 7.92734C14.448 7.70251 14.9034 7.77612 15.3249 7.9446C15.7153 8.10445 16.0109 8.3977 16.4358 8.8123C16.8699 9.3131 16.9481 9.4511 17.1954 9.8272C17.3909 10.121 17.5686 10.4229 17.6905 10.7685C17.7641 10.9841 17.6686 11.1607 17.4128 11.2688Z"
fill="black"
/>
</svg>
<span class="provider-logo-name">DeepSeek</span>
</a>
<a href="#minimax" aria-label="MiniMax">
<img src="https://cdn.simpleicons.org/minimax" alt="MiniMax" />
<span class="provider-logo-name">MiniMax</span>
Expand Down Expand Up @@ -207,6 +224,25 @@ Credentials are discovered from OpenCode auth for the `commandcode` provider. As
}
```

### DeepSeek

DeepSeek exposes a current account balance rather than a rolling or monthly usage quota. The provider reads the official [`GET /user/balance`](https://api-docs.deepseek.com/api/get-user-balance/) endpoint and displays each reported currency independently, for example `$12.34 remaining` for USD.

Enable it with the DeepSeek key already saved by OpenCode, or configure an explicit key:

```jsonc
{
"providers": {
"deepseek": {
"enabled": true,
"apiKey": "{env:DEEPSEEK_API_KEY}",
},
},
}
```

The adapter uses `total_balance` directly. It does not add `granted_balance` and `topped_up_balance`, combine currencies, or invent a percentage/reset time. OpenCode-discovered credentials are sent only to `https://api.deepseek.com`; a custom `baseUrl` requires an explicit `authPath` or `apiKey`.

### MiniMax

Token Plan rolling five-hour and weekly windows. Uses MiniMax auth or `OC_MINIMAX_TOKEN_PLAN_KEY`. See the [MiniMax documentation](https://platform.minimax.io/docs/token-plan/intro).
Expand All @@ -233,7 +269,7 @@ Z.AI Coding Plan token rolling window. Uses OpenCode `zai-coding-plan` or `zai`

## Display and window selection

Sidebar rows show provider labels, usage percentages or counts, reset times, and stale data when a refresh fails. The footer selects the active session's provider and renders a compact primary window.
Sidebar rows show provider labels, usage percentages, counts, or remaining balances, reset times, and stale data when a refresh fails. The footer selects the active session's provider and renders a compact primary window.

Supported `sidebarWindow` values are `all`, `rolling`, `daily`, `weekly`, `monthly`, `credits`, and `other`. `footerWindow` accepts `auto` or one of those values. An unavailable explicit choice falls back to the provider's automatic selection, then its first available window.

Expand Down
9 changes: 9 additions & 0 deletions apps/web/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@
text-align: center;
}

.provider-logo-deepseek {
width: 2.25rem !important;
height: 1.67rem !important;
}

:root[data-theme="dark"] .provider-logo-deepseek {
filter: invert(1);
}

.provider-logo-opencode-light {
display: none;
}
Expand Down
18 changes: 16 additions & 2 deletions packages/opencode-usage-limits/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# @mynameistito/opencode-usage-limits

OpenCode TUI plugin that shows Codex, Command Code, OpenCode GO, ZAI, Synthetic, MiniMax Token Plan, Qwen, and Alibaba Token Plan usage limits in the sidebar and prompt footer.
OpenCode TUI plugin that shows Codex, DeepSeek, Command Code, OpenCode GO, ZAI, Synthetic, MiniMax Token Plan, Qwen, and Alibaba Token Plan usage limits in the sidebar and prompt footer.

## Features

- Adds a `Usage Limits` block under the sidebar `Context` section.
- Shows current Codex usage windows from OpenAI/Codex auth.
- Shows current DeepSeek currency balances from the official balance API.
- Shows current ZAI quota windows from ZAI Coding Plan auth.
- Shows current Synthetic rolling 5-hour and weekly windows.
- Shows current MiniMax Token Plan rolling 5-hour and weekly windows.
- Shows current Qwen Token Plan windows from the local `qwencloud` CLI.
- Shows current Alibaba Token Plan 5-hour and weekly windows from the local `bl` CLI.
- Shows current OpenCode GO rolling, weekly, and monthly windows.
- Displays current Command Code 5-hour, weekly, and derived monthly credit usage.
- Adds compact prompt-footer usage when the current session uses an OpenAI, Command Code, OpenCode GO, ZAI Coding Plan, Synthetic, MiniMax Token Plan, or Qwen Token Plan model.
- Adds compact prompt-footer usage when the current session uses an OpenAI, DeepSeek, Command Code, OpenCode GO, ZAI Coding Plan, Synthetic, MiniMax Token Plan, or Qwen Token Plan model.
- Providers are toggled from `~/.config/opencode/usage-limits.jsonc`.
- Reads OpenCode-connected credentials first, then falls back to explicit config/env credentials.

Expand Down Expand Up @@ -152,6 +153,7 @@ The response contract follows the official CLI's [`usage/token-plan.ts`](https:/
| Provider ID | Service | Env var | Auth header | Default base URL |
| --- | --- | --- | --- | --- |
| `codex` | ChatGPT Codex usage | — | Bearer | `https://chatgpt.com/backend-api` |
| `deepseek` | DeepSeek balances | `DEEPSEEK_API_KEY` | Bearer | `https://api.deepseek.com` |
| `zai` | Z.AI Coding Plan quota | `OC_ZAI_API_KEY` | raw / Bearer | `https://api.z.ai` |
| `synthetic` | Synthetic quotas | `OC_SYNTHETIC_API_KEY` | Bearer | `https://api.synthetic.new` |
| `minimax` | MiniMax Token Plan | `OC_MINIMAX_TOKEN_PLAN_KEY` | Bearer | `https://www.minimax.io` |
Expand All @@ -164,6 +166,8 @@ Qwen usage requires the local `qwencloud` CLI to be installed and authenticated

Synthetic always uses `Bearer` auth and ignores `authorizationScheme`.

DeepSeek reads `GET https://api.deepseek.com/user/balance`. Each reported currency is displayed as an independent `credits` balance using `total_balance`; the component balances are not summed. OpenCode auth is used only for the exact official DeepSeek origin. A custom `baseUrl` requires an explicit `authPath` or `apiKey`, including `{env:DEEPSEEK_API_KEY}`.

Set `baseUrl` on `minimax` to `https://api.minimaxi.com` when using the mainland-China region. MiniMax always uses `Bearer` auth and ignores `authorizationScheme`.

## Credential Lookup
Expand Down Expand Up @@ -196,6 +200,12 @@ MiniMax Token Plan lookup order:
2. OpenCode auth at `~/.local/share/opencode/auth.json`, provider `minimax-coding-plan`, `minimax`, or `minimax-token-plan`.
3. Config `apiKey`, including `{env:OC_MINIMAX_TOKEN_PLAN_KEY}` references.

DeepSeek lookup order:

1. Config `authPath` JSON file (`{ "key": "..." }` / `{ "apiKey": "..." }` / `{ "deepseek": { "key": "..." } }`).
2. OpenCode auth at `~/.local/share/opencode/auth.json`, provider `deepseek`.
3. Config `apiKey`, including `{env:DEEPSEEK_API_KEY}` references.

Command Code lookup order:

1. Config `authPath` JSON file (`{ "key": "..." }` / `{ "apiKey": "..." }` / `{ "commandcode": { "key": "..." } }`).
Expand All @@ -221,6 +231,9 @@ Synthetic
weekly: 11% used resets 7m
MiniMax
5h: 10% used resets 2h 56m
DeepSeek
USD: $12.50 remaining
CNY: ¥0.00 remaining
```

Prompt footer shows compact usage when the current session model belongs to a supported provider:
Expand All @@ -234,6 +247,7 @@ Command Code sessions use the rolling 5-hour window in the prompt footer.
Provider mapping:

- OpenCode provider `openai` -> Codex usage.
- OpenCode provider `deepseek` -> DeepSeek balance usage.
- OpenCode provider `zai-coding-plan` -> ZAI token usage.
- OpenCode provider `synthetic` -> Synthetic usage.
- OpenCode provider `minimax-coding-plan` -> MiniMax Token Plan usage (prompt footer); `minimax` is also accepted as an alias.
Expand Down
64 changes: 63 additions & 1 deletion packages/opencode-usage-limits/__tests__/components.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import type {
ProviderUsage,
UsageWindow,
} from "@/types.ts";
import { parseUsagePercentage, percentageQuota } from "@/usage.ts";
import {
balanceQuota,
parseUsageBalance,
parseUsagePercentage,
percentageQuota,
} from "@/usage.ts";

const color = RGBA.fromValues(1, 2, 3, 255);

Expand Down Expand Up @@ -191,6 +196,37 @@ describe(UsageLimitsPanel, () => {
expect(text).toContain("[█████░░░░░░░]");
});

it("renders a remaining balance without a percentage or progress bar", async () => {
const text = await renderPanelText(
[
{
data: usage({
windows: [
usageWindow({
label: "USD balance",
quota: balanceQuota(
Result.getOrThrow(parseUsageBalance(12.34)),
"USD"
),
resetsAt: null,
}),
],
}),
id: "codex",
label: "Codex",
stale: false,
status: "ready",
},
],
true
);

expect(text).toContain("USD balance");
expect(text).toContain("$12.34 remaining");
expect(text).not.toContain("%");
expect(text).not.toContain("[");
});

it("filters windows by the provider sidebar window", async () => {
const text = await renderPanelText(
[
Expand Down Expand Up @@ -424,4 +460,30 @@ describe(UsageLimitsPanel, () => {
setup.renderer.destroy();
}
});

it("renders footer balances without a percentage or progress bar", async () => {
const setup = await testRender(
() => (
<BottomUsage
showBar
theme={theme}
window={usageWindow({
label: "CNY balance",
quota: balanceQuota(Result.getOrThrow(parseUsageBalance(0)), "CNY"),
resetsAt: null,
})}
/>
),
{ height: 4, width: 80 }
);
try {
await setup.flush();
const text = setup.captureCharFrame();
expect(text).toContain("CNY balance ¥0.00 remaining");
expect(text).not.toContain("%");
expect(text).not.toContain("[");
} finally {
setup.renderer.destroy();
}
});
});
Loading
Loading