@leo000001/opencode-quota-sidebar 1.13.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,484 +1,482 @@
1
- # opencode-quota-sidebar
2
-
3
- [![npm version](https://img.shields.io/npm/v/@leo000001/opencode-quota-sidebar.svg)](https://www.npmjs.com/package/@leo000001/opencode-quota-sidebar)
4
- [![license](https://img.shields.io/npm/l/@leo000001/opencode-quota-sidebar.svg)](https://github.com/xihuai18/opencode-quota-sidebar/blob/main/LICENSE)
5
-
6
- OpenCode plugin: show token usage and subscription quota in the session sidebar title.
7
-
8
- ![Example sidebar title with usage and quota](./assets/OpenCode-Quota-Sidebar.png)
9
-
10
- ## Install
11
-
12
- Add the package name to `plugin` in your `opencode.json`. OpenCode uses Bun to install it automatically on startup:
13
-
14
- ```json
15
- {
16
- "plugin": ["@leo000001/opencode-quota-sidebar@1.13.2"]
17
- }
18
- ```
19
-
20
- Note for OpenCode `>=1.2.15`: TUI settings (`theme`/`keybinds`/`tui`) moved to `tui.json`, but plugin loading still stays in `opencode.json` (`plugin: []`).
21
- This plugin also accepts both `config.providers` and older `provider.list` runtime shapes when discovering provider options.
22
-
23
- If you prefer automatic upgrades, you can still use `@latest`, but pinning an exact version makes behavior easier to reproduce when debugging.
24
-
25
- ## Development (build from source)
26
-
27
- ```bash
28
- npm install
29
- npm run build
30
- ```
31
-
32
- Add the built file to your `opencode.json`:
33
-
34
- ```json
35
- {
36
- "plugin": ["file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/index.js"]
37
- }
38
- ```
39
-
40
- On Windows, use forward slashes: `"file:///D:/Lab/opencode-quota-sidebar/dist/index.js"`
41
-
42
- ## Supported quota providers
43
-
44
- | Provider | Endpoint | Auth | Status |
45
- | -------------- | -------------------------------------- | --------------- | --------------------------------------- |
46
- | OpenAI Codex | `chatgpt.com/backend-api/wham/usage` | OAuth (ChatGPT) | Multi-window (short-term + weekly) |
47
- | GitHub Copilot | `api.github.com/copilot_internal/user` | OAuth | Monthly quota |
48
- | RightCode | `www.right.codes/account/summary` | API key | Subscription or balance (by prefix) |
49
- | Buzz | `buzzai.cc/v1/dashboard/billing/*` | API key | Balance only (computed from total-used) |
50
- | Anthropic | `api.anthropic.com/api/oauth/usage` | OAuth | Multi-window (5h + weekly / plan-based) |
51
-
52
- Want to add support for another provider (Google Antigravity, Zhipu AI, Firmware AI, etc.)? See [CONTRIBUTING.md](CONTRIBUTING.md).
53
-
54
- ## Features
55
-
1
+ # opencode-quota-sidebar
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@leo000001/opencode-quota-sidebar.svg)](https://www.npmjs.com/package/@leo000001/opencode-quota-sidebar)
4
+ [![license](https://img.shields.io/npm/l/@leo000001/opencode-quota-sidebar.svg)](https://github.com/xihuai18/opencode-quota-sidebar/blob/main/LICENSE)
5
+
6
+ OpenCode plugin: show token usage and subscription quota in the session sidebar title.
7
+
8
+ ![Example sidebar title with usage and quota](./assets/OpenCode-Quota-Sidebar.png)
9
+
10
+ ## Install
11
+
12
+ Add the package name to `plugin` in your `opencode.json`. OpenCode uses Bun to install it automatically on startup:
13
+
14
+ ```json
15
+ {
16
+ "plugin": ["@leo000001/opencode-quota-sidebar@1.13.2"]
17
+ }
18
+ ```
19
+
20
+ Note for OpenCode `>=1.2.15`: TUI settings (`theme`/`keybinds`/`tui`) moved to `tui.json`, but plugin loading still stays in `opencode.json` (`plugin: []`).
21
+ This plugin also accepts both `config.providers` and older `provider.list` runtime shapes when discovering provider options.
22
+
23
+ If you prefer automatic upgrades, you can still use `@latest`, but pinning an exact version makes behavior easier to reproduce when debugging.
24
+
25
+ ## Development (build from source)
26
+
27
+ ```bash
28
+ npm install
29
+ npm run build
30
+ ```
31
+
32
+ Add the built file to your `opencode.json`:
33
+
34
+ ```json
35
+ {
36
+ "plugin": ["file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/index.js"]
37
+ }
38
+ ```
39
+
40
+ On Windows, use forward slashes: `"file:///D:/Lab/opencode-quota-sidebar/dist/index.js"`
41
+
42
+ ## Supported quota providers
43
+
44
+ | Provider | Endpoint | Auth | Status |
45
+ | -------------- | -------------------------------------- | --------------- | --------------------------------------- |
46
+ | OpenAI Codex | `chatgpt.com/backend-api/wham/usage` | OAuth (ChatGPT) | Multi-window (short-term + weekly) |
47
+ | GitHub Copilot | `api.github.com/copilot_internal/user` | OAuth | Monthly quota |
48
+ | RightCode | `www.right.codes/account/summary` | API key | Subscription or balance (by prefix) |
49
+ | Buzz | `buzzai.cc/v1/dashboard/billing/*` | API key | Balance only (computed from total-used) |
50
+ | Anthropic | `api.anthropic.com/api/oauth/usage` | OAuth | Multi-window (5h + weekly / plan-based) |
51
+
52
+ Want to add support for another provider (Google Antigravity, Zhipu AI, Firmware AI, etc.)? See [CONTRIBUTING.md](CONTRIBUTING.md).
53
+
54
+ ## Features
55
+
56
56
  - Session title becomes multiline in sidebar:
57
57
  - line 1: original session title
58
58
  - line 2: Input/Output tokens
59
59
  - line 3: Cache Read tokens (only if non-zero)
60
60
  - line 4: Cache Write tokens (only if non-zero)
61
- - line 5: `$X.XX as API cost` (equivalent API billing for subscription-auth providers)
61
+ - next lines: `Cache Coverage` (read/write cache models) and `Cache Read Coverage` (read-only cache models) when enough cache telemetry is available; mixed sessions can show both
62
+ - next line: `$X.XX as API cost` (equivalent API billing for subscription-auth providers)
62
63
  - quota lines: quota text like `OpenAI 5h 80% Rst 16:20`; short windows (`5h`, `1d`, `Daily`) show `HH:MM` on same-day resets and `MM-DD HH:MM` when crossing days, while longer windows continue to show `MM-DD`
63
- - RightCode daily quota shows `$remaining/$dailyTotal` + expiry (e.g. `RC Daily $105/$60 Exp 02-27`, without trailing percent) and also shows balance on the next indented line when available; `Exp` remains date-only
64
- - Session-scoped usage/quota can include descendant subagent sessions (enabled by default via `sidebar.includeChildren=true`). Traversal is bounded by `childrenMaxDepth` (default 6), `childrenMaxSessions` (default 128), and `childrenConcurrency` (default 5); truncation is logged when `OPENCODE_QUOTA_DEBUG=1`. Day/week/month ranges never merge children — only session scope does.
64
+ - RightCode daily quota shows `$remaining/$dailyTotal` + expiry (e.g. `RC Daily $105/$60 Exp 02-27`, without trailing percent) and also shows balance on the next indented line when available; `Exp` remains date-only
65
+ - Session-scoped usage/quota can include descendant subagent sessions (enabled by default via `sidebar.includeChildren=true`). Traversal is bounded by `childrenMaxDepth` (default 6), `childrenMaxSessions` (default 128), and `childrenConcurrency` (default 5); truncation is logged when `OPENCODE_QUOTA_DEBUG=1`. Day/week/month ranges never merge children — only session scope does.
65
66
  - Toast message includes three sections: `Token Usage`, `Cost as API` (per provider), and `Quota`
66
- - OpenAI priority API-cost detection uses two fallbacks in order:
67
- - message metadata: `openai.serviceTier` / `openai.service_tier`
68
- - model defaults from `provider.list`: `models[modelID].options.serviceTier`
69
- - provider aliases with `npm: "@ai-sdk/openai"` are treated as OpenAI for this billing rule
70
- - Quota snapshots are de-duplicated before rendering to avoid repeated provider lines
71
- - Custom tools:
72
- - `quota_summary` generate usage report for session/day/week/month (markdown + toast)
73
- - `quota_show` toggle sidebar title display on/off (state persists across sessions)
74
- - Quota connectors:
75
- - OpenAI Codex OAuth (`/backend-api/wham/usage`)
76
- - GitHub Copilot OAuth (`/copilot_internal/user`)
77
- - RightCode API key (`/account/summary`)
78
- - Buzz API key (`/v1/dashboard/billing/subscription` + `/v1/dashboard/billing/usage`)
79
- - Anthropic Claude OAuth (`/api/oauth/usage`, with beta header)
80
- - OpenAI OAuth quota checks auto-refresh expired access token (using refresh token)
81
- - API key providers still show usage aggregation (quota only applies to subscription providers)
82
- - Incremental usage aggregation — only processes new messages since last cursor
83
- - Sidebar token units are adaptive (`k`/`m` with one decimal where applicable)
84
-
85
- ## Storage layout
86
-
87
- The plugin stores lightweight global state and date-partitioned session chunks.
88
-
89
- - Global metadata: `<opencode-data>/quota-sidebar.state.json`
90
- - `titleEnabled`
91
- - `sessionDateMap` (sessionID -> `YYYY-MM-DD`)
92
- - `quotaCache`
93
- - Session chunks: `<opencode-data>/quota-sidebar-sessions/YYYY/MM/DD.json`
94
- - per-session title state (`baseTitle`, `lastAppliedTitle`)
95
- - `createdAt`
96
- - `parentID` (when the session is a subagent child session)
97
- - cached usage summary used by `quota_summary`
98
- - incremental aggregation cursor
99
-
100
- Example tree:
101
-
102
- ```text
103
- ~/.local/share/opencode/
104
- quota-sidebar.state.json
105
- quota-sidebar-sessions/
106
- 2026/
107
- 02/
108
- 23.json
109
- 24.json
110
- ```
111
-
112
- Sessions older than `retentionDays` (default 730 days / 2 years) are evicted from
113
- memory on startup. Chunk files remain on disk for historical range scans.
114
-
115
- ## Compatibility
116
-
117
- - Node.js: >= 18 (for `fetch` + `AbortController`)
118
- - OpenCode: plugin SDK `@opencode-ai/plugin` ^1.2.10
119
- - OpenCode config split: if you are on `>=1.2.15`, keep this plugin in `opencode.json` and keep TUI-only keys in `tui.json`.
120
-
121
- ## Force refresh after npm update
122
-
123
- If `npm view @leo000001/opencode-quota-sidebar version` shows a newer version but OpenCode still behaves like an older release, OpenCode/Bun is usually reusing an older installed copy.
124
-
125
- Recommended recovery steps:
126
-
127
- 1. Pin the target plugin version in `opencode.json`.
128
- 2. Fully exit OpenCode.
129
- 3. Delete any cached installed copies of the plugin.
130
- 4. Start OpenCode again so it reinstalls the package.
131
- 5. Verify the actual installed `package.json` version under the plugin directory.
132
-
133
- Common install/cache locations:
134
-
135
- - `~/.cache/opencode/node_modules/@leo000001/opencode-quota-sidebar`
136
- - `~/node_modules/@leo000001/opencode-quota-sidebar`
137
-
138
- Windows PowerShell example:
139
-
140
- ```powershell
141
- Remove-Item -Recurse -Force "$HOME\.cache\opencode\node_modules\@leo000001\opencode-quota-sidebar" -ErrorAction SilentlyContinue
142
- Remove-Item -Recurse -Force "$HOME\node_modules\@leo000001\opencode-quota-sidebar" -ErrorAction SilentlyContinue
143
- ```
144
-
145
- macOS / Linux example:
146
-
147
- ```bash
148
- rm -rf ~/.cache/opencode/node_modules/@leo000001/opencode-quota-sidebar
149
- rm -rf ~/node_modules/@leo000001/opencode-quota-sidebar
150
- ```
151
-
152
- ## Optional commands
153
-
154
- You can add these command templates in `opencode.json` so you can run `/qday`, `/qweek`, `/qmonth`, `/qtoggle`:
155
-
156
- ```json
157
- {
158
- "command": {
159
- "qday": {
160
- "description": "Show today's usage and quota",
161
- "template": "Call tool quota_summary with period=day and toast=true."
162
- },
163
- "qweek": {
164
- "description": "Show this week's usage and quota",
165
- "template": "Call tool quota_summary with period=week and toast=true."
166
- },
167
- "qmonth": {
168
- "description": "Show this month's usage and quota",
169
- "template": "Call tool quota_summary with period=month and toast=true."
170
- },
171
- "qtoggle": {
172
- "description": "Toggle sidebar usage display on/off",
173
- "template": "Call tool quota_show (no arguments, it toggles)."
174
- }
175
- }
176
- }
177
- ```
178
-
179
- ## Configuration files
180
-
181
- Recommended global config:
182
-
183
- - `~/.config/opencode/quota-sidebar.config.json`
184
-
185
- Optional project overrides:
186
-
187
- - `<worktree>/quota-sidebar.config.json`
188
- - `<directory>/quota-sidebar.config.json` (when different from `worktree`)
189
- - `<worktree>/.opencode/quota-sidebar.config.json`
190
- - `<directory>/.opencode/quota-sidebar.config.json` (when different from `worktree`)
191
-
192
- Optional explicit override:
193
-
194
- - `OPENCODE_QUOTA_CONFIG=/absolute/path/to/config.json`
195
-
196
- Optional config-home override:
197
-
198
- - `OPENCODE_QUOTA_CONFIG_HOME=/absolute/path/to/config-home`
199
-
200
- Resolution order (low -> high):
201
-
202
- 1. Global config (`~/.config/opencode/...`)
203
- 2. `<worktree>/quota-sidebar.config.json`
204
- 3. `<directory>/quota-sidebar.config.json`
205
- 4. `<worktree>/.opencode/quota-sidebar.config.json`
206
- 5. `<directory>/.opencode/quota-sidebar.config.json`
207
- 6. `OPENCODE_QUOTA_CONFIG`
208
-
209
- Values are layered; later sources override earlier ones.
210
-
211
- ## Configuration
212
-
213
- If you do not provide any config file, the plugin uses the built-in defaults below.
214
-
215
- ### Built-in defaults
216
-
217
- Sidebar defaults:
218
-
219
- - `sidebar.enabled`: `true`
220
- - `sidebar.width`: `36` (clamped to `20`-`60`)
221
- - `sidebar.multilineTitle`: `true`
222
- - `sidebar.showCost`: `true`
223
- - `sidebar.showQuota`: `true`
224
- - `sidebar.wrapQuotaLines`: `true`
225
- - `sidebar.includeChildren`: `true`
226
- - `sidebar.childrenMaxDepth`: `6` (clamped to `1`-`32`)
227
- - `sidebar.childrenMaxSessions`: `128` (clamped to `0`-`2000`)
228
- - `sidebar.childrenConcurrency`: `5` (clamped to `1`-`10`)
229
-
230
- Quota defaults:
231
-
232
- - `quota.refreshMs`: `300000` (clamped to `>=30000`)
233
- - `quota.includeOpenAI`: `true`
234
- - `quota.includeCopilot`: `true`
235
- - `quota.includeAnthropic`: `true`
236
- - `quota.providers`: `{}` (per-adapter switches, for example `rightcode.enabled` or `buzz.enabled`)
237
- - `quota.refreshAccessToken`: `false`
238
- - `quota.requestTimeoutMs`: `8000` (clamped to `>=1000`)
239
-
240
- Other defaults:
241
-
242
- - `toast.durationMs`: `12000` (clamped to `>=1000`)
243
- - `retentionDays`: `730`
244
-
245
- ### Full example config
246
-
247
- ```json
248
- {
249
- "sidebar": {
250
- "enabled": true,
251
- "width": 36,
252
- "multilineTitle": true,
253
- "showCost": true,
254
- "showQuota": true,
255
- "wrapQuotaLines": true,
256
- "includeChildren": true,
257
- "childrenMaxDepth": 6,
258
- "childrenMaxSessions": 128,
259
- "childrenConcurrency": 5
260
- },
261
- "quota": {
262
- "refreshMs": 300000,
263
- "includeOpenAI": true,
264
- "includeCopilot": true,
265
- "includeAnthropic": true,
266
- "providers": {
267
- "buzz": {
268
- "enabled": true
269
- },
270
- "rightcode": {
271
- "enabled": true
272
- }
273
- },
274
- "refreshAccessToken": false,
275
- "requestTimeoutMs": 8000
276
- },
277
- "toast": {
278
- "durationMs": 12000
279
- },
280
- "retentionDays": 730
281
- }
282
- ```
283
-
284
- ### Notes
285
-
286
- - `sidebar.showCost` controls API-cost visibility in sidebar title, `quota_summary` markdown report, and toast message.
287
- - `quota_summary` follows the same reset compaction rules for short windows in its subscription section (`5h` / `1d` / `Daily` show time, long windows show date, RightCode `Exp` stays date-only).
288
- - `sidebar.width` is measured in terminal cells. CJK/emoji truncation is best-effort to avoid sidebar overflow.
289
- - `sidebar.multilineTitle` controls multi-line sidebar layout (default: `true`). Set `false` for compact single-line title.
290
- - `sidebar.wrapQuotaLines` controls quota line wrapping and continuation indentation (default: `true`).
291
- - `sidebar.includeChildren` controls whether session-scoped usage/quota includes descendant subagent sessions (default: `true`).
292
- - `sidebar.childrenMaxDepth` limits how many levels of nested subagents are traversed (default: `6`, clamped 1–32).
293
- - `sidebar.childrenMaxSessions` caps the total number of descendant sessions aggregated (default: `128`, clamped 0–2000).
294
- - `sidebar.childrenConcurrency` controls parallel fetches for descendant session messages (default: `5`, clamped 1–10).
295
- - `output` includes reasoning tokens (`output = tokens.output + tokens.reasoning`). Reasoning is not rendered as a separate line.
296
- - API cost bills reasoning tokens at the output rate (same as completion tokens).
297
- - API cost is computed from OpenCode model pricing metadata, not from `message.cost`. This keeps subscription-backed providers such as OpenAI OAuth usable for API-equivalent cost estimation even when OpenCode's measured cost is `0`.
298
- - When OpenCode exposes a long-context tier like `context_over_200k`, the plugin uses that premium rate for the whole request once `input > 200000`, matching OpenCode's current pricing schema.
299
- - `quota.providers` is the extensible per-adapter switch map.
300
- - If API Cost is `$0.00`, it usually means the model/provider has no pricing mapping in OpenCode at the moment, so equivalent API cost cannot be estimated.
301
- - Usage chunks cache both measured `cost` and computed `apiCost`. `quota_summary` (`/qday`, `/qweek`, `/qmonth`) usually reads those cached aggregates first, but a billing-cache version bump or missing/legacy API-cost data will trigger a rescan and persist refreshed values.
302
-
303
- ### Buzz provider example
304
-
305
- Buzz matching is based on the provider `baseURL`, similar to RightCode. Any OpenAI-compatible provider that points at `https://buzzai.cc` will be recognized by the Buzz adapter and rendered as a balance-only quota source.
306
-
307
- Provider options example:
308
-
309
- ```json
310
- {
311
- "id": "openai",
312
- "options": {
313
- "baseURL": "https://buzzai.cc",
314
- "apiKey": "sk-..."
315
- }
316
- }
317
- ```
318
-
319
- The adapter also tolerates `https://buzzai.cc/v1`, but `https://buzzai.cc` is the recommended example.
320
-
321
- With that setup, the sidebar/toast quota line will look like:
322
-
323
- ```text
324
- Buzz Balance CNY 10.17
325
- ```
326
-
327
- ## Rendering examples
328
-
329
- These examples show the quota block portion of the sidebar title.
330
-
331
- ### `sidebar.multilineTitle=true`
332
-
333
- 0 providers (no quota data):
334
-
335
- ```text
336
- (no quota block)
337
- ```
338
-
339
- 1 provider, 1 window (fits):
340
-
341
- ```text
342
- Copilot Monthly 78% Rst 04-01
343
- ```
344
-
345
- 1 provider, multi-window (for example OpenAI 5h + Weekly):
346
-
347
- ```text
348
- OpenAI
349
- 5h 78% Rst 05:05
350
- Weekly 73% Rst 03-12
351
- ```
352
-
353
- 1 provider, short window crossing into the next day:
354
-
355
- ```text
356
- Anthropic
357
- 5h 0% Rst 03-10 01:00
358
- Weekly 46% Rst 03-15
359
- ```
360
-
361
- 2+ providers (even if each provider is single-window):
362
-
363
- ```text
364
- OpenAI
365
- 5h 78% Rst 05:05
366
- Copilot
367
- Monthly 78% Rst 04-01
368
- ```
369
-
370
- 2+ providers mixed (multi-window + single-window):
371
-
372
- ```text
373
- OpenAI
374
- 5h 78% Rst 05:05
375
- Weekly 73% Rst 03-12
376
- Copilot
377
- Monthly 78% Rst 04-01
378
- ```
379
-
380
- 2+ providers mixed (window providers + Buzz balance):
381
-
382
- ```text
383
- OpenAI
384
- 5h 78% Rst 05:05
385
- Copilot
386
- Monthly 78% Rst 04-01
387
- Buzz Balance CNY 10.2
388
- ```
389
-
390
- Balance-style quota:
391
-
392
- ```text
393
- RC Balance $260
394
- ```
395
-
396
- Buzz balance quota:
397
-
398
- ```text
399
- Buzz Balance CNY 10.17
400
- ```
401
-
402
- Multi-detail quota (window + balance):
403
-
404
- ```text
405
- RC
406
- Daily $88.9/$60 Exp 02-27
407
- Balance $260
408
- ```
409
-
410
- Provider status / quota (examples):
411
-
412
- ```text
413
- Anthropic 5h 80%+
414
- Copilot unavailable
415
- OpenAI Remaining ?
416
- ```
417
-
418
- ### `sidebar.multilineTitle=false`
419
-
420
- Quota is rendered inline as part of a single-line title:
421
-
422
- ```text
423
- <base> | Input ... | Output ... | OpenAI 5h 78%+ | Copilot Monthly 78% | ...
424
- ```
425
-
426
- Mixed with Buzz balance:
427
-
428
- ```text
429
- <base> | Input ... | Output ... | OpenAI 5h 78%+ | Copilot Monthly 78% | Buzz Balance CNY 10.2
430
- ```
431
-
432
- `quota_summary` also supports an optional `includeChildren` flag (only effective for `period=session`) to override the config per call. For `day`/`week`/`month` periods, children are never merged — each session is counted independently.
433
-
434
- ## Billing cache behavior
435
-
436
- - Cached per-session usage stores token totals, measured `cost`, computed `apiCost`, provider breakdowns, and the incremental cursor.
437
- - Session-scoped sidebar aggregation can merge descendant subagents when `sidebar.includeChildren=true` (default). Measured `cost` stays aligned with the root session's OpenCode `message.cost`, while API-equivalent cost still includes descendant usage.
438
- - Range tools such as `/qday`, `/qweek`, and `/qmonth` do not merge children. They aggregate each session independently across the selected time window.
439
- - When API-cost logic changes, the plugin bumps an internal billing-cache version so historical range reports are recomputed with the new rules the next time they are queried.
440
-
441
- ## Debug logging
442
-
443
- Set `OPENCODE_QUOTA_DEBUG=1` to enable debug logging to stderr. This logs:
444
-
445
- - Chunk I/O operations
446
- - Auth refresh attempts and failures
447
- - Session eviction counts
448
- - Symlink write refusals
449
-
450
- ## Security & privacy notes
451
-
452
- - The plugin reads OpenCode credentials from `<opencode-data>/auth.json`.
453
- - If enabled, quota checks call external endpoints:
454
- - OpenAI Codex: `https://chatgpt.com/backend-api/wham/usage`
455
- - GitHub Copilot: `https://api.github.com/copilot_internal/user`
456
- - RightCode: `https://www.right.codes/account/summary`
457
- - Buzz: `https://buzzai.cc/v1/dashboard/billing/subscription` and `https://buzzai.cc/v1/dashboard/billing/usage`
458
- - Anthropic: `https://api.anthropic.com/api/oauth/usage`
459
- - **Screen-sharing warning**: Session titles and toasts surface usage/quota
460
- information. If you are screen-sharing or recording, consider toggling the
461
- sidebar display off (`/qtoggle` or `quota_show` tool) to avoid leaking
462
- subscription details.
463
- - State is persisted under `<opencode-data>/quota-sidebar.state.json` and
464
- `<opencode-data>/quota-sidebar-sessions/` (see Storage layout).
465
- - OpenAI OAuth token refresh is disabled by default; set
466
- `quota.refreshAccessToken=true` if you want the plugin to refresh access
467
- tokens when expired.
468
- - Anthropic quota currently uses a beta/internal-style OAuth usage endpoint and
469
- request header; response fields may change without notice.
470
- - State/chunk file writes refuse to write through symlinked targets (best-effort defense-in-depth).
471
- - The `OPENCODE_QUOTA_DATA_HOME` env var overrides the OpenCode data directory
472
- path (for testing); do not set this in production.
473
- - The `OPENCODE_QUOTA_CONFIG_HOME` env var overrides global config directory
474
- lookup (`<config-home>/opencode`).
475
- - The `OPENCODE_QUOTA_CONFIG` env var points to an explicit config file and
476
- applies as the highest-priority override.
477
-
478
- ## Contributing
479
-
480
- Contributions are welcome — especially new quota provider connectors. See [CONTRIBUTING.md](CONTRIBUTING.md) for a step-by-step guide on adding support for a new provider.
481
-
482
- ## License
483
-
484
- MIT. See `LICENSE`.
67
+ - `quota_summary` markdown / toast also include `Cache Coverage` and `Cache Read Coverage` summary lines when available
68
+ - Quota snapshots are de-duplicated before rendering to avoid repeated provider lines
69
+ - Custom tools:
70
+ - `quota_summary` generate usage report for session/day/week/month (markdown + toast)
71
+ - `quota_show` toggle sidebar title display on/off (state persists across sessions)
72
+ - Quota connectors:
73
+ - OpenAI Codex OAuth (`/backend-api/wham/usage`)
74
+ - GitHub Copilot OAuth (`/copilot_internal/user`)
75
+ - RightCode API key (`/account/summary`)
76
+ - Buzz API key (`/v1/dashboard/billing/subscription` + `/v1/dashboard/billing/usage`)
77
+ - Anthropic Claude OAuth (`/api/oauth/usage`, with beta header)
78
+ - OpenAI OAuth quota checks auto-refresh expired access token (using refresh token)
79
+ - API key providers still show usage aggregation (quota only applies to subscription providers)
80
+ - Incremental usage aggregation only processes new messages since last cursor
81
+ - Sidebar token units are adaptive (`k`/`m` with one decimal where applicable)
82
+
83
+ ## Storage layout
84
+
85
+ The plugin stores lightweight global state and date-partitioned session chunks.
86
+
87
+ - Global metadata: `<opencode-data>/quota-sidebar.state.json`
88
+ - `titleEnabled`
89
+ - `sessionDateMap` (sessionID -> `YYYY-MM-DD`)
90
+ - `quotaCache`
91
+ - Session chunks: `<opencode-data>/quota-sidebar-sessions/YYYY/MM/DD.json`
92
+ - per-session title state (`baseTitle`, `lastAppliedTitle`)
93
+ - `createdAt`
94
+ - `parentID` (when the session is a subagent child session)
95
+ - cached usage summary used by `quota_summary`
96
+ - incremental aggregation cursor
97
+
98
+ Example tree:
99
+
100
+ ```text
101
+ ~/.local/share/opencode/
102
+ quota-sidebar.state.json
103
+ quota-sidebar-sessions/
104
+ 2026/
105
+ 02/
106
+ 23.json
107
+ 24.json
108
+ ```
109
+
110
+ Sessions older than `retentionDays` (default 730 days / 2 years) are evicted from
111
+ memory on startup. Chunk files remain on disk for historical range scans.
112
+
113
+ ## Compatibility
114
+
115
+ - Node.js: >= 18 (for `fetch` + `AbortController`)
116
+ - OpenCode: plugin SDK `@opencode-ai/plugin` ^1.2.10
117
+ - OpenCode config split: if you are on `>=1.2.15`, keep this plugin in `opencode.json` and keep TUI-only keys in `tui.json`.
118
+
119
+ ## Force refresh after npm update
120
+
121
+ If `npm view @leo000001/opencode-quota-sidebar version` shows a newer version but OpenCode still behaves like an older release, OpenCode/Bun is usually reusing an older installed copy.
122
+
123
+ Recommended recovery steps:
124
+
125
+ 1. Pin the target plugin version in `opencode.json`.
126
+ 2. Fully exit OpenCode.
127
+ 3. Delete any cached installed copies of the plugin.
128
+ 4. Start OpenCode again so it reinstalls the package.
129
+ 5. Verify the actual installed `package.json` version under the plugin directory.
130
+
131
+ Common install/cache locations:
132
+
133
+ - `~/.cache/opencode/node_modules/@leo000001/opencode-quota-sidebar`
134
+ - `~/node_modules/@leo000001/opencode-quota-sidebar`
135
+
136
+ Windows PowerShell example:
137
+
138
+ ```powershell
139
+ Remove-Item -Recurse -Force "$HOME\.cache\opencode\node_modules\@leo000001\opencode-quota-sidebar" -ErrorAction SilentlyContinue
140
+ Remove-Item -Recurse -Force "$HOME\node_modules\@leo000001\opencode-quota-sidebar" -ErrorAction SilentlyContinue
141
+ ```
142
+
143
+ macOS / Linux example:
144
+
145
+ ```bash
146
+ rm -rf ~/.cache/opencode/node_modules/@leo000001/opencode-quota-sidebar
147
+ rm -rf ~/node_modules/@leo000001/opencode-quota-sidebar
148
+ ```
149
+
150
+ ## Optional commands
151
+
152
+ You can add these command templates in `opencode.json` so you can run `/qday`, `/qweek`, `/qmonth`, `/qtoggle`:
153
+
154
+ ```json
155
+ {
156
+ "command": {
157
+ "qday": {
158
+ "description": "Show today's usage and quota",
159
+ "template": "Call tool quota_summary with period=day and toast=true."
160
+ },
161
+ "qweek": {
162
+ "description": "Show this week's usage and quota",
163
+ "template": "Call tool quota_summary with period=week and toast=true."
164
+ },
165
+ "qmonth": {
166
+ "description": "Show this month's usage and quota",
167
+ "template": "Call tool quota_summary with period=month and toast=true."
168
+ },
169
+ "qtoggle": {
170
+ "description": "Toggle sidebar usage display on/off",
171
+ "template": "Call tool quota_show (no arguments, it toggles)."
172
+ }
173
+ }
174
+ }
175
+ ```
176
+
177
+ ## Configuration files
178
+
179
+ Recommended global config:
180
+
181
+ - `~/.config/opencode/quota-sidebar.config.json`
182
+
183
+ Optional project overrides:
184
+
185
+ - `<worktree>/quota-sidebar.config.json`
186
+ - `<directory>/quota-sidebar.config.json` (when different from `worktree`)
187
+ - `<worktree>/.opencode/quota-sidebar.config.json`
188
+ - `<directory>/.opencode/quota-sidebar.config.json` (when different from `worktree`)
189
+
190
+ Optional explicit override:
191
+
192
+ - `OPENCODE_QUOTA_CONFIG=/absolute/path/to/config.json`
193
+
194
+ Optional config-home override:
195
+
196
+ - `OPENCODE_QUOTA_CONFIG_HOME=/absolute/path/to/config-home`
197
+
198
+ Resolution order (low -> high):
199
+
200
+ 1. Global config (`~/.config/opencode/...`)
201
+ 2. `<worktree>/quota-sidebar.config.json`
202
+ 3. `<directory>/quota-sidebar.config.json`
203
+ 4. `<worktree>/.opencode/quota-sidebar.config.json`
204
+ 5. `<directory>/.opencode/quota-sidebar.config.json`
205
+ 6. `OPENCODE_QUOTA_CONFIG`
206
+
207
+ Values are layered; later sources override earlier ones.
208
+
209
+ ## Configuration
210
+
211
+ If you do not provide any config file, the plugin uses the built-in defaults below.
212
+
213
+ ### Built-in defaults
214
+
215
+ Sidebar defaults:
216
+
217
+ - `sidebar.enabled`: `true`
218
+ - `sidebar.width`: `36` (clamped to `20`-`60`)
219
+ - `sidebar.multilineTitle`: `true`
220
+ - `sidebar.showCost`: `true`
221
+ - `sidebar.showQuota`: `true`
222
+ - `sidebar.wrapQuotaLines`: `true`
223
+ - `sidebar.includeChildren`: `true`
224
+ - `sidebar.childrenMaxDepth`: `6` (clamped to `1`-`32`)
225
+ - `sidebar.childrenMaxSessions`: `128` (clamped to `0`-`2000`)
226
+ - `sidebar.childrenConcurrency`: `5` (clamped to `1`-`10`)
227
+
228
+ Quota defaults:
229
+
230
+ - `quota.refreshMs`: `300000` (clamped to `>=30000`)
231
+ - `quota.includeOpenAI`: `true`
232
+ - `quota.includeCopilot`: `true`
233
+ - `quota.includeAnthropic`: `true`
234
+ - `quota.providers`: `{}` (per-adapter switches, for example `rightcode.enabled` or `buzz.enabled`)
235
+ - `quota.refreshAccessToken`: `false`
236
+ - `quota.requestTimeoutMs`: `8000` (clamped to `>=1000`)
237
+
238
+ Other defaults:
239
+
240
+ - `toast.durationMs`: `12000` (clamped to `>=1000`)
241
+ - `retentionDays`: `730`
242
+
243
+ ### Full example config
244
+
245
+ ```json
246
+ {
247
+ "sidebar": {
248
+ "enabled": true,
249
+ "width": 36,
250
+ "multilineTitle": true,
251
+ "showCost": true,
252
+ "showQuota": true,
253
+ "wrapQuotaLines": true,
254
+ "includeChildren": true,
255
+ "childrenMaxDepth": 6,
256
+ "childrenMaxSessions": 128,
257
+ "childrenConcurrency": 5
258
+ },
259
+ "quota": {
260
+ "refreshMs": 300000,
261
+ "includeOpenAI": true,
262
+ "includeCopilot": true,
263
+ "includeAnthropic": true,
264
+ "providers": {
265
+ "buzz": {
266
+ "enabled": true
267
+ },
268
+ "rightcode": {
269
+ "enabled": true
270
+ }
271
+ },
272
+ "refreshAccessToken": false,
273
+ "requestTimeoutMs": 8000
274
+ },
275
+ "toast": {
276
+ "durationMs": 12000
277
+ },
278
+ "retentionDays": 730
279
+ }
280
+ ```
281
+
282
+ ### Notes
283
+
284
+ - `sidebar.showCost` controls API-cost visibility in sidebar title, `quota_summary` markdown report, and toast message.
285
+ - `quota_summary` follows the same reset compaction rules for short windows in its subscription section (`5h` / `1d` / `Daily` show time, long windows show date, RightCode `Exp` stays date-only).
286
+ - `sidebar.width` is measured in terminal cells. CJK/emoji truncation is best-effort to avoid sidebar overflow.
287
+ - `sidebar.multilineTitle` controls multi-line sidebar layout (default: `true`). Set `false` for compact single-line title.
288
+ - `sidebar.wrapQuotaLines` controls quota line wrapping and continuation indentation (default: `true`).
289
+ - `sidebar.includeChildren` controls whether session-scoped usage/quota includes descendant subagent sessions (default: `true`).
290
+ - `sidebar.childrenMaxDepth` limits how many levels of nested subagents are traversed (default: `6`, clamped 1–32).
291
+ - `sidebar.childrenMaxSessions` caps the total number of descendant sessions aggregated (default: `128`, clamped 0–2000).
292
+ - `sidebar.childrenConcurrency` controls parallel fetches for descendant session messages (default: `5`, clamped 1–10).
293
+ - `output` includes reasoning tokens (`output = tokens.output + tokens.reasoning`). Reasoning is not rendered as a separate line.
294
+ - API cost bills reasoning tokens at the output rate (same as completion tokens).
295
+ - API cost is computed from OpenCode model pricing metadata, not from `message.cost`. This keeps subscription-backed providers such as OpenAI OAuth usable for API-equivalent cost estimation even when OpenCode's measured cost is `0`.
296
+ - When OpenCode exposes a long-context tier like `context_over_200k`, the plugin uses that premium rate for the whole request once `input > 200000`, matching OpenCode's current pricing schema.
297
+ - `quota.providers` is the extensible per-adapter switch map.
298
+ - If API Cost is `$0.00`, it usually means the model/provider has no pricing mapping in OpenCode at the moment, so equivalent API cost cannot be estimated.
299
+ - Usage chunks cache both measured `cost` and computed `apiCost`. `quota_summary` (`/qday`, `/qweek`, `/qmonth`) usually reads those cached aggregates first, but a billing-cache version bump or missing/legacy API-cost data will trigger a rescan and persist refreshed values.
300
+
301
+ ### Buzz provider example
302
+
303
+ Buzz matching is based on the provider `baseURL`, similar to RightCode. Any OpenAI-compatible provider that points at `https://buzzai.cc` will be recognized by the Buzz adapter and rendered as a balance-only quota source.
304
+
305
+ Provider options example:
306
+
307
+ ```json
308
+ {
309
+ "id": "openai",
310
+ "options": {
311
+ "baseURL": "https://buzzai.cc",
312
+ "apiKey": "sk-..."
313
+ }
314
+ }
315
+ ```
316
+
317
+ The adapter also tolerates `https://buzzai.cc/v1`, but `https://buzzai.cc` is the recommended example.
318
+
319
+ With that setup, the sidebar/toast quota line will look like:
320
+
321
+ ```text
322
+ Buzz Balance CNY 10.17
323
+ ```
324
+
325
+ ## Rendering examples
326
+
327
+ These examples show the quota block portion of the sidebar title.
328
+
329
+ ### `sidebar.multilineTitle=true`
330
+
331
+ 0 providers (no quota data):
332
+
333
+ ```text
334
+ (no quota block)
335
+ ```
336
+
337
+ 1 provider, 1 window (fits):
338
+
339
+ ```text
340
+ Copilot Monthly 78% Rst 04-01
341
+ ```
342
+
343
+ 1 provider, multi-window (for example OpenAI 5h + Weekly):
344
+
345
+ ```text
346
+ OpenAI
347
+ 5h 78% Rst 05:05
348
+ Weekly 73% Rst 03-12
349
+ ```
350
+
351
+ 1 provider, short window crossing into the next day:
352
+
353
+ ```text
354
+ Anthropic
355
+ 5h 0% Rst 03-10 01:00
356
+ Weekly 46% Rst 03-15
357
+ ```
358
+
359
+ 2+ providers (even if each provider is single-window):
360
+
361
+ ```text
362
+ OpenAI
363
+ 5h 78% Rst 05:05
364
+ Copilot
365
+ Monthly 78% Rst 04-01
366
+ ```
367
+
368
+ 2+ providers mixed (multi-window + single-window):
369
+
370
+ ```text
371
+ OpenAI
372
+ 5h 78% Rst 05:05
373
+ Weekly 73% Rst 03-12
374
+ Copilot
375
+ Monthly 78% Rst 04-01
376
+ ```
377
+
378
+ 2+ providers mixed (window providers + Buzz balance):
379
+
380
+ ```text
381
+ OpenAI
382
+ 5h 78% Rst 05:05
383
+ Copilot
384
+ Monthly 78% Rst 04-01
385
+ Buzz Balance CNY 10.2
386
+ ```
387
+
388
+ Balance-style quota:
389
+
390
+ ```text
391
+ RC Balance $260
392
+ ```
393
+
394
+ Buzz balance quota:
395
+
396
+ ```text
397
+ Buzz Balance CNY 10.17
398
+ ```
399
+
400
+ Multi-detail quota (window + balance):
401
+
402
+ ```text
403
+ RC
404
+ Daily $88.9/$60 Exp 02-27
405
+ Balance $260
406
+ ```
407
+
408
+ Provider status / quota (examples):
409
+
410
+ ```text
411
+ Anthropic 5h 80%+
412
+ Copilot unavailable
413
+ OpenAI Remaining ?
414
+ ```
415
+
416
+ ### `sidebar.multilineTitle=false`
417
+
418
+ Quota is rendered inline as part of a single-line title:
419
+
420
+ ```text
421
+ <base> | Input ... | Output ... | OpenAI 5h 78%+ | Copilot Monthly 78% | ...
422
+ ```
423
+
424
+ Mixed with Buzz balance:
425
+
426
+ ```text
427
+ <base> | Input ... | Output ... | OpenAI 5h 78%+ | Copilot Monthly 78% | Buzz Balance CNY 10.2
428
+ ```
429
+
430
+ `quota_summary` also supports an optional `includeChildren` flag (only effective for `period=session`) to override the config per call. For `day`/`week`/`month` periods, children are never merged — each session is counted independently.
431
+
432
+ ## Billing cache behavior
433
+
434
+ - Cached per-session usage stores token totals, measured `cost`, computed `apiCost`, provider breakdowns, and the incremental cursor.
435
+ - Session-scoped sidebar aggregation can merge descendant subagents when `sidebar.includeChildren=true` (default). Measured `cost` stays aligned with the root session's OpenCode `message.cost`, while API-equivalent cost still includes descendant usage.
436
+ - Range tools such as `/qday`, `/qweek`, and `/qmonth` do not merge children. They aggregate each session independently across the selected time window.
437
+ - When API-cost logic changes, the plugin bumps an internal billing-cache version so historical range reports are recomputed with the new rules the next time they are queried.
438
+
439
+ ## Debug logging
440
+
441
+ Set `OPENCODE_QUOTA_DEBUG=1` to enable debug logging to stderr. This logs:
442
+
443
+ - Chunk I/O operations
444
+ - Auth refresh attempts and failures
445
+ - Session eviction counts
446
+ - Symlink write refusals
447
+
448
+ ## Security & privacy notes
449
+
450
+ - The plugin reads OpenCode credentials from `<opencode-data>/auth.json`.
451
+ - If enabled, quota checks call external endpoints:
452
+ - OpenAI Codex: `https://chatgpt.com/backend-api/wham/usage`
453
+ - GitHub Copilot: `https://api.github.com/copilot_internal/user`
454
+ - RightCode: `https://www.right.codes/account/summary`
455
+ - Buzz: `https://buzzai.cc/v1/dashboard/billing/subscription` and `https://buzzai.cc/v1/dashboard/billing/usage`
456
+ - Anthropic: `https://api.anthropic.com/api/oauth/usage`
457
+ - **Screen-sharing warning**: Session titles and toasts surface usage/quota
458
+ information. If you are screen-sharing or recording, consider toggling the
459
+ sidebar display off (`/qtoggle` or `quota_show` tool) to avoid leaking
460
+ subscription details.
461
+ - State is persisted under `<opencode-data>/quota-sidebar.state.json` and
462
+ `<opencode-data>/quota-sidebar-sessions/` (see Storage layout).
463
+ - OpenAI OAuth token refresh is disabled by default; set
464
+ `quota.refreshAccessToken=true` if you want the plugin to refresh access
465
+ tokens when expired.
466
+ - Anthropic quota currently uses a beta/internal-style OAuth usage endpoint and
467
+ request header; response fields may change without notice.
468
+ - State/chunk file writes refuse to write through symlinked targets (best-effort defense-in-depth).
469
+ - The `OPENCODE_QUOTA_DATA_HOME` env var overrides the OpenCode data directory
470
+ path (for testing); do not set this in production.
471
+ - The `OPENCODE_QUOTA_CONFIG_HOME` env var overrides global config directory
472
+ lookup (`<config-home>/opencode`).
473
+ - The `OPENCODE_QUOTA_CONFIG` env var points to an explicit config file and
474
+ applies as the highest-priority override.
475
+
476
+ ## Contributing
477
+
478
+ Contributions are welcome — especially new quota provider connectors. See [CONTRIBUTING.md](CONTRIBUTING.md) for a step-by-step guide on adding support for a new provider.
479
+
480
+ ## License
481
+
482
+ MIT. See `LICENSE`.