@leo000001/opencode-quota-sidebar 4.0.5 → 4.0.11

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,446 +1,504 @@
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
- [简体中文](./README.zh-CN.md)
7
-
8
- OpenCode plugin that shows token usage and provider quota in the TUI sidebar, while keeping the shared session title compact and readable across Desktop, Web, and TUI.
9
-
10
- ![Example sidebar title with usage and quota](./assets/OpenCode-Quota-Sidebar.png)
11
-
12
- The screenshot above comes from [`./assets/OpenCode-Quota-Sidebar.png`](./assets/OpenCode-Quota-Sidebar.png) and shows the actual TUI sidebar layout this plugin renders.
13
-
14
- ## What It Does
15
-
16
- - Renders dedicated `TITLE`, `USAGE`, and `QUOTA` blocks in the TUI sidebar
17
- - Keeps the shared `session.title` on a compact single line instead of pushing multiline telemetry into every client
18
- - Aggregates usage for `session`, `day`, `week`, and `month`
19
- - Supports provider quota/balance fetchers for OpenAI, Copilot, Anthropic, Kimi, Zhipu, MiniMax, and RightCode
20
- - Can include descendant subagent sessions in session-scoped usage/quota totals
21
- - Exposes `quota_summary` and `quota_show` tools for reports and title toggling
22
-
23
- ## Architecture Overview
24
-
25
- This repository is a pure plugin implementation. It does not modify OpenCode core.
26
-
27
- - Server layer: aggregates usage, fetches quota snapshots, stores state, manages title refresh, and exposes tools
28
- - TUI layer: renders the sidebar panel UI and reads persisted sidebar-panel payloads
29
- - Persistence layer: stores global state plus day-partitioned session chunks so historical sessions can render without full rescans
30
- - Provider adapter layer: maps each provider to a common `QuotaSnapshot` shape through a registry of adapters
31
-
32
- The implementation is built on top of `@opencode-ai/plugin` and `@opencode-ai/sdk`.
33
-
34
- ## How It Works
35
-
36
- This plugin has two display layers:
37
-
38
- - TUI sidebar panel: renders structured `TITLE`, `USAGE`, and `QUOTA` blocks
39
- - Shared session title: stays compact so Desktop, Web, and TUI can all read the same title cleanly
40
-
41
- In `sidebar.titleMode="auto"`, the shared title remains a compact single line. The richer multiline layout is handled by the dedicated TUI plugin instead of being written into `session.title` for every client.
42
-
43
- Session-scoped aggregation can include descendant subagent sessions when `sidebar.includeChildren=true`. Day/week/month summaries do not merge descendants.
44
-
45
- ## Supported Providers
46
-
47
- Built-in quota adapters:
48
-
49
- | Provider | Endpoint family | Auth | Quota shape | Notes |
50
- | ------------------- | ---------------------------------------------------------- | ------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
51
- | OpenAI Codex | `chatgpt.com/backend-api/wham/usage` | OAuth | Multi-window subscription | Reads ChatGPT usage windows such as short-term + weekly; Pro plans may also expose Codex Spark limits (`additional_rate_limits`) |
52
- | GitHub Copilot | `api.github.com/copilot_internal/user` | OAuth | Monthly subscription | Uses the Copilot internal user endpoint |
53
- | Anthropic | `api.anthropic.com/api/oauth/usage` | OAuth | Multi-window subscription | Supports plan-based usage windows |
54
- | Kimi For Coding | `api.kimi.com/coding/v1/usages` | API key | Multi-window subscription | Typically `5h` + weekly windows |
55
- | Zhipu Coding Plan | `bigmodel.cn/api/monitor/usage/quota/limit` | API key | Token quota | Coding-plan style quota window |
56
- | MiniMax Coding Plan | `www.minimaxi.com/v1/api/openplatform/coding_plan/remains` | API key | Multi-window subscription | Typically `5h` + weekly windows |
57
- | RightCode | `www.right.codes/account/summary` | API key | Daily quota and/or balance | Prefix-based subscription matching, with balance fallback |
58
-
59
- Generic providers without a built-in quota endpoint can still contribute usage totals, but they will not show quota/balance unless an adapter exists.
60
-
61
- Provider notes:
62
-
63
- - OpenAI, Copilot, and Anthropic quota support is based on OAuth/session auth, not generic API-key billing endpoints
64
- - **OpenAI Codex Spark**: OpenAI Pro subscriptions may expose additional per-feature windows (e.g. `GPT-5.3-Codex-Spark`) in the `additional_rate_limits` field of the `wham/usage` response. When present, the plugin automatically parses and renders these as extra windows under the OpenAI quota line. No extra config is required. Code review quota (`code_review_rate_limit`) is not displayed yet.
65
- - RightCode can show both a daily allowance line and a balance line
66
- - Copilot quota is supported, but API-equivalent cost is intentionally not shown because runtime pricing is not reliable enough
67
-
68
- ## Display Rules
69
-
70
- - Sidebar quota lines show providers actually used in the current session and recognized by an adapter
71
- - `quota_summary` fetches default quota providers even if they were not used in the current session
72
- - The TUI sidebar reads persisted `sidebarPanel` / usage state first, so historical sessions can render quickly on open or resume
73
- - Compact-title quota parsing is only a fallback path; the TUI panel prefers persisted structured data
74
- - `quota_show` toggles title decoration on or off, but the TUI panel remains the main rich display path
75
-
76
- ## Install
77
-
78
- OpenCode loads the server plugin from `opencode.json` and the TUI plugin from `tui.json`.
79
-
80
- `opencode.json`
81
-
82
- ```json
83
- {
84
- "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
85
- }
86
- ```
87
-
88
- `tui.json`
89
-
90
- ```json
91
- {
92
- "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
93
- }
94
- ```
95
-
96
- For OpenCode `>=1.2.15`, keep server plugins in `opencode.json` and TUI plugins in `tui.json`.
97
-
98
- ## Sidebar Demo
99
-
100
- Typical TUI sidebar layout (with Codex Spark windows):
101
-
102
- ```text
103
- TITLE
104
- Fix quota adapter matching
105
- USAGE
106
- R184 I189k O53.2k
107
- CR31.4k CW3.2k Cd66%
108
- Est $12.8
109
- QUOTA
110
- OAI 5h80 R3h20m
111
- W70 R2D04h
112
- Sk5h100 R1h00m
113
- SkW100 R3D04h
114
- Cop M78 R12D00h
115
- RC D$88.9/$60 E6D00h
116
- B260
117
- ```
118
-
119
- Compact shared title example:
120
-
121
- ```text
122
- Fix quota adapter matching | OAI 5h80 R3h20m W70 R2D04h | RC D$88.9/$60 B260 | Cd66% | Est$12.8
123
- ```
124
-
125
- ## Tool Report Demo
126
-
127
- Example historical `quota_summary` markdown output shape:
128
-
129
- ```md
130
- ## Quota History - Daily since 2026-02-18
131
-
132
- ### Quota Status
133
-
134
- - OpenAI: 5h | 80.0% | reset 3h20m; Weekly | 70.0% | reset 2D04h
135
- - Copilot: Monthly | 78.0% | reset 12D00h
136
- - RightCode: Daily $88.9/$60 | reset 6D00h
137
-
138
- ### Totals
139
-
140
- | Metric | Total | Avg/Period |
141
- | ------------ | ----: | ---------: |
142
- | Requests | 184 | 26.3 |
143
- | Total Tokens | 277k | 39.6k |
144
- | Cache Hit | 63.1% | 58.4% |
145
- | API Cost | $12.8 | $1.83 |
146
-
147
- ### Provider Breakdown
148
-
149
- | Provider | Req | Input | Output | Total | Share | Cache Hit | API Cost |
150
- | --------- | --: | ----: | -----: | ----: | ----: | --------: | -------: |
151
- | OpenAI | 140 | 160k | 61k | 221k | 79.8% | 66.2% | $10.4 |
152
- | Anthropic | 44 | 29k | 27.1k | 56.1k | 20.2% | 51.3% | $2.34 |
153
-
154
- ### Period Detail
155
-
156
- | Period | Requests | Input | Output | Cache | Cache Hit | Total | API Cost |
157
- | ------------ | -------: | ----: | -----: | ----: | --------: | ----: | -------: |
158
- | 2026-02-18 | 12 | 18.3k | 4.2k | 8.9k | 32.7% | 31.4k | $1.12 |
159
- | 2026-02-24\* | 17 | 8.1k | 2.0k | 3.4k | 66.0% | 13.5k | $0.88 |
160
- ```
161
-
162
- The tool already returns full markdown. Clients should display that report directly instead of replacing it with a short summary.
163
-
164
- Sidebar output uses compact tokens. Toasts and markdown reports keep fuller human-readable wording.
165
-
166
- ## Why The TUI Panel Exists
167
-
168
- OpenCode renders the sidebar title as plain text inside a single styled title field. That means the shared title is great for compact telemetry, but not ideal for a rich multi-section layout.
169
-
170
- This plugin therefore uses:
171
-
172
- - shared compact titles for cross-client compatibility
173
- - a dedicated TUI sidebar panel for the detailed block layout
174
-
175
- That split avoids polluting Desktop/Web titles while still giving TUI users a readable quota dashboard.
176
-
177
- ## Abbreviations
178
-
179
- Usage tokens:
180
-
181
- - `R`: requests
182
- - `I`: input tokens
183
- - `O`: output tokens, including reasoning tokens
184
- - `CR`: cache read tokens
185
- - `CW`: cache write tokens
186
- - `Cd`: cached ratio / cache coverage
187
- - `Est`: API-equivalent cost estimate
188
-
189
- Quota tokens:
190
-
191
- - `OAI`: OpenAI
192
- - `Cop`: GitHub Copilot
193
- - `Ant`: Anthropic
194
- - `RC`: RightCode
195
- - `B`: balance
196
- - `D`: daily window
197
- - `W`: weekly window
198
- - `M`: monthly window
199
- - `Sk5h`: OpenAI Codex Spark short window (e.g. 5h)
200
- - `SkW`: OpenAI Codex Spark weekly window
201
- - `R3h20m`: resets in `3h20m`
202
- - `R2D04h`: resets in `2D04h`
203
- - `E6D00h`: expires in `6D00h`
204
-
205
- Example compact quota fragments:
206
-
207
- - `OAI 5h80 R3h20m`: OpenAI short window, 80% remaining, resets in `3h20m`
208
- - `OAI Sk5h100 R1h00m`: OpenAI Codex Spark 5h window, 100% remaining, resets in `1h00m`
209
- - `OAI SkW100 R3D04h`: OpenAI Codex Spark weekly window, 100% remaining, resets in `3D04h`
210
- - `Cop M78 R12D00h`: Copilot monthly quota, 78% remaining, resets in `12D00h`
211
- - `RC D$88.9/$60 E6D00h B260`: RightCode daily quota plus balance
212
-
213
- ## Config
214
-
215
- Recommended config file locations:
216
-
217
- - `~/.config/opencode/quota-sidebar.config.json`
218
- - `<worktree>/quota-sidebar.config.json`
219
- - `<worktree>/.opencode/quota-sidebar.config.json`
220
- - `OPENCODE_QUOTA_CONFIG=/absolute/path/to/config.json`
221
-
222
- Minimal example:
223
-
224
- ```json
225
- {
226
- "sidebar": {
227
- "enabled": true,
228
- "titleMode": "auto",
229
- "showCost": true,
230
- "showQuota": true,
231
- "includeChildren": true
232
- }
233
- }
234
- ```
235
-
236
- See [`quota-sidebar.config.example.json`](./quota-sidebar.config.example.json) for a fuller config example.
237
-
238
- Important config notes:
239
-
240
- - `sidebar.titleMode`: `auto`, `compact`, or `multiline`
241
- - `sidebar.showCost`: controls API-equivalent cost in sidebar, title, markdown report, toast, and CLI output
242
- - `sidebar.wrapQuotaLines`: wraps long quota lines with indentation instead of dropping fields
243
- - `sidebar.includeChildren`: includes descendant subagent sessions for `period=session`
244
- Config is layered. The later source overrides the earlier one:
245
-
246
- 1. global config
247
- 2. worktree config
248
- 3. directory config
249
- 4. worktree `.opencode` config
250
- 5. directory `.opencode` config
251
- 6. `OPENCODE_QUOTA_CONFIG`
252
-
253
- ## Persistence And Aggregation
254
-
255
- The plugin stores:
256
-
257
- - global state in `<opencode-data>/quota-sidebar.state.json`
258
- - session chunks in `<opencode-data>/quota-sidebar-sessions/YYYY/MM/DD.json`
259
-
260
- Those persisted chunks keep title state, cached usage, sidebar-panel payloads, and quota cache data. This lets the TUI sidebar render from stored structured state on session open/resume instead of depending entirely on live message scans.
261
-
262
- Usage aggregation is incremental. The plugin tracks a cursor per session and processes only new messages when possible. If message history changes in a way that invalidates the incremental view, it can rescan and refresh persisted usage.
263
-
264
- ## Tools
265
-
266
- - `quota_summary`: shows usage and quota for `session`, `day`, `week`, or `month`
267
- - `quota_show`: toggles title decoration on or off
268
-
269
- Behavior notes:
270
-
271
- - `quota_summary` returns the full markdown report body
272
- - `quota_summary` can show a toast in addition to returning markdown
273
- - `quota_summary` accepts `period`, `since`, `last`, `toast`, and `includeChildren`
274
- - `quota_summary(includeChildren=true)` only changes `period=session`
275
- - `day/week/month` scans all sessions in the selected time range, so child sessions are included when they have activity in that range
276
- - `day/week/month` does not do parent-tree rollup; child sessions are counted as independent sessions, not merged through `includeChildren`
277
- - `since` and `last` are mutually exclusive
278
- - `period=session` does not accept `since` or `last`
279
- - `quota_show(enabled=true|false)` can explicitly force a state instead of toggling
280
- - Historical reports support both absolute `since` and relative `last`
281
- - `since` accepts `YYYY-MM` or `YYYY-MM-DD`
282
- - `last` accepts a positive integer and is relative to the current period: `day=7`, `week=8`, `month=6`
283
- - Empty `period=day|week|month` means the current natural day/week/month
284
-
285
- Example command aliases:
286
-
287
- For direct in-chat history output, define command aliases that call `quota_summary`.
288
- The old TUI history popup path was removed; history now goes through the tool report directly.
289
- These aliases are still OpenCode command templates, so they expand into prompt text before the model/tool chain runs. For the cleanest direct output path, prefer the standalone CLI.
290
-
291
- Examples:
292
-
293
- - `quota_summary(period=day)` -> today
294
- - `quota_summary(period=week)` -> this week
295
- - `quota_summary(period=month)` -> this month
296
- - `quota_summary(period=day,last=7)` -> last 7 days
297
- - `quota_summary(period=week,last=8)` -> last 8 weeks
298
- - `quota_summary(period=month,last=6)` -> last 6 months
299
- - `quota_summary(period=month,since=2026-01)` -> since Jan 2026
300
-
301
- ```json
302
- {
303
- "command": {
304
- "qday": {
305
- "description": "Today / last N days / since date",
306
- "template": "Run /qday for opencode-quota-sidebar. Call tool quota_summary exactly once and return its full report directly. If `$ARGUMENTS` is empty: period=day, toast=true. If `$ARGUMENTS` is a positive integer: period=day, last=<that integer>, toast=true. If `$ARGUMENTS` matches YYYY-MM-DD: period=day, since=<that date>, toast=true. Otherwise briefly explain: empty, positive integer, or YYYY-MM-DD."
307
- },
308
- "qweek": {
309
- "description": "This week / last N weeks / since date",
310
- "template": "Run /qweek for opencode-quota-sidebar. Call tool quota_summary exactly once and return its full report directly. If `$ARGUMENTS` is empty: period=week, toast=true. If `$ARGUMENTS` is a positive integer: period=week, last=<that integer>, toast=true. If `$ARGUMENTS` matches YYYY-MM-DD: period=week, since=<that date>, toast=true. Otherwise briefly explain: empty, positive integer, or YYYY-MM-DD."
311
- },
312
- "qmonth": {
313
- "description": "This month / last N months / since month",
314
- "template": "Run /qmonth for opencode-quota-sidebar. Call tool quota_summary exactly once and return its full report directly. If `$ARGUMENTS` is empty: period=month, toast=true. If `$ARGUMENTS` is a positive integer: period=month, last=<that integer>, toast=true. If `$ARGUMENTS` matches YYYY-MM: period=month, since=<that month>, toast=true. Otherwise briefly explain: empty, positive integer, or YYYY-MM."
315
- },
316
- "qtoggle": {
317
- "description": "Toggle sidebar usage display on/off",
318
- "template": "Call tool quota_show (no arguments, it toggles)."
319
- }
320
- }
321
- }
322
- ```
323
-
324
- ## CLI
325
-
326
- The package exposes a standalone CLI dashboard. After installing globally or making the `bin` available:
327
-
328
- ```bash
329
- npm install -g @leo000001/opencode-quota-sidebar
330
- ```
331
-
332
- If you prefer not to install globally, you can also run it with `npx @leo000001/opencode-quota-sidebar <args>`.
333
-
334
- ```bash
335
- # Current period (single snapshot)
336
- opencode-quota day # today
337
- opencode-quota week # this week (Monday-based)
338
- opencode-quota month # this month
339
-
340
- # Multi-period history
341
- opencode-quota day 7 # last 7 days
342
- opencode-quota week 8 # last 8 weeks
343
- opencode-quota month 6 # last 6 months
344
-
345
- # Absolute start date
346
- opencode-quota day --since 2026-04-01
347
- opencode-quota week --since 2026-04-01
348
- opencode-quota month --since 2026-01
349
-
350
- # Also accepted as positional (equivalent to --since)
351
- opencode-quota day 2026-04-01
352
- opencode-quota month 2026-01
353
- ```
354
-
355
- When called without `last` or `--since`, the CLI renders a single-period snapshot (`QUOTA + TOTALS + PROVIDERS`). When called with `last` or `--since`, it renders multi-period history with a larger multi-line `TREND` block.
356
-
357
- ### CLI semantics
358
-
359
- - `day` = current natural day; `week` = current natural week (Monday-based); `month` = current natural month
360
- - A positional integer maps to `last=<N>` (number of periods back from now)
361
- - A positional date string maps to `--since` (`YYYY-MM-DD` for day/week, `YYYY-MM` for month)
362
- - `--since` and `--last` are mutually exclusive
363
- - `last` is limited to 90 for day, reasonable ranges for week/month
364
-
365
- ### Trend section
366
-
367
- The `TREND` block appears only in multi-period mode. Each metric (`Requests`, `Tokens`, `Cache`, `Cost`) is rendered as a small multi-line bar chart:
368
-
369
- - one summary line: the current value only
370
- - one bar row per visible period (latest 8 periods max), ordered oldest-to-newest
371
- - the current period is marked with `*`
372
-
373
- Interpretation example:
374
-
375
- ```text
376
- Requests 12.3k
377
- 04-08 | ███░░░░░░░░░░░░░░░ | 4.1k
378
- 04-09 | ██████░░░░░░░░░░░░ | 8.2k
379
- 04-10* | █████████████░░░░░ | 12.3k
380
- ```
381
-
382
- This means the current bucket has `12.3k` requests, and the bar rows below show the relative size of each visible bucket from oldest to newest.
383
-
384
- ### Connection behavior
385
-
386
- - The CLI talks to the local OpenCode API at `http://localhost:4096` by default
387
- - Set `OPENCODE_BASE_URL` to override (e.g. `http://192.168.1.10:4096`)
388
- - If no server is running and `OPENCODE_BASE_URL` is not set, the CLI attempts to start one:
389
- - **Linux/macOS**: runs `opencode serve --hostname=127.0.0.1 --port=4096`
390
- - **Windows**: tries `opencode.cmd`, then `opencode` via `shell: true`, then `bash -lc opencode`
391
- - The auto-start waits up to 10 seconds for the server to print `opencode server listening on <url>`
392
- - If auto-start fails, check that `opencode` is in your `PATH`
393
- - On Windows, the `shell: true` path is usually the most reliable when `opencode.cmd` is not directly spawnable from Node
394
-
395
- ### Platform notes
396
-
397
- - **Terminal encoding**: the dashboard uses Unicode box-drawing and block elements (`█░`). Requires a UTF-8 capable terminal. Windows users should use Windows Terminal, PowerShell 7+, or a terminal that supports UTF-8. Classic cmd.exe with legacy codepages (CP437/CP850) may render garbled characters.
398
- - **Alignment**: weekly/monthly trend labels can still be truncated when the visible label is very long (for example long absolute week ranges). This is a known presentation tradeoff in the current terminal renderer.
399
- - **Windows PATH**: the CLI tries multiple command forms to find `opencode`. If none work, ensure `opencode` or `opencode.cmd` is on your PATH, or start the server manually and set `OPENCODE_BASE_URL`.
400
- - **Node.js**: requires `>=18`
401
-
402
- ### Environment variables
403
-
404
- | Variable | Default | Purpose |
405
- | ---------------------------- | ------------------------- | -------------------------------------------------------------------------------- |
406
- | `OPENCODE_BASE_URL` | `http://localhost:4096` | OpenCode API endpoint; set this if the server is remote or on a non-default port |
407
- | `OPENCODE_QUOTA_CONFIG_HOME` | `~/.config/opencode` | Global config directory override |
408
- | `OPENCODE_QUOTA_DATA_HOME` | `~/.local/share/opencode` | Global data directory override |
409
-
410
- ## Development
411
-
412
- ```bash
413
- npm install
414
- npm run build
415
- npm test
416
- ```
417
-
418
- For local development, load:
419
-
420
- - `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/index.js` in `opencode.json`
421
- - `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/tui.tsx` in `tui.json`
422
-
423
- On Windows, use forward slashes in `file:///` URLs.
424
-
425
- ## Compatibility And Caveats
426
-
427
- - Node.js `>=18`
428
- - OpenCode plugin SDK `@opencode-ai/plugin` / `@opencode-ai/sdk` `^1.3.5`
429
- - For OpenCode `>=1.2.15`, TUI config belongs in `tui.json`
430
- - The shared title is still one `session.title` value for all clients
431
- - The plugin avoids ANSI styling in sidebar titles to keep resize behavior stable
432
- - Some providers expose true quota windows, others only expose balance data
433
-
434
- ## Contributing
435
-
436
- - Changelog: [CHANGELOG.md](./CHANGELOG.md)
437
- - Adapter and architecture notes: [CONTRIBUTING.md](./CONTRIBUTING.md)
438
- - Security policy: [SECURITY.md](./SECURITY.md)
439
-
440
- ## Documentation Navigation
441
-
442
- - English README: [`README.md`](./README.md)
443
- - Simplified Chinese README: [`README.zh-CN.md`](./README.zh-CN.md)
444
- - Changelog: [`CHANGELOG.md`](./CHANGELOG.md)
445
- - Contributing guide: [`CONTRIBUTING.md`](./CONTRIBUTING.md)
446
- - Security policy: [`SECURITY.md`](./SECURITY.md)
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
+ [简体中文](./README.zh-CN.md)
7
+
8
+ OpenCode plugin that shows token usage and provider quota in the TUI sidebar, while keeping the shared session title compact and readable across Desktop, Web, and TUI.
9
+
10
+ ![Example sidebar title with usage and quota](./assets/OpenCode-Quota-Sidebar.png)
11
+
12
+ The screenshot above comes from [`./assets/OpenCode-Quota-Sidebar.png`](./assets/OpenCode-Quota-Sidebar.png) and shows the actual TUI sidebar layout this plugin renders.
13
+
14
+ ## What It Does
15
+
16
+ - Renders dedicated `TITLE`, `USAGE`, and `QUOTA` blocks in the TUI sidebar
17
+ - Keeps the shared `session.title` on a compact single line instead of pushing multiline telemetry into every client
18
+ - Aggregates usage for `session`, `day`, `week`, and `month`
19
+ - Supports provider quota/balance fetchers for OpenAI, Copilot, Anthropic, Kimi, Zhipu, MiniMax, and RightCode
20
+ - Can include descendant subagent sessions in session-scoped usage/quota totals
21
+ - Exposes `quota_summary` and `quota_show` tools for reports and title toggling
22
+
23
+ ## Architecture Overview
24
+
25
+ This repository is a pure plugin implementation. It does not modify OpenCode core.
26
+
27
+ - Server layer: aggregates usage, fetches quota snapshots, stores state, manages title refresh, and exposes tools
28
+ - TUI layer: renders the sidebar panel UI and reads persisted sidebar-panel payloads
29
+ - Persistence layer: stores global state plus day-partitioned session chunks so historical sessions can render without full rescans
30
+ - Provider adapter layer: maps each provider to a common `QuotaSnapshot` shape through a registry of adapters
31
+
32
+ The implementation is built on top of `@opencode-ai/plugin` and `@opencode-ai/sdk`.
33
+
34
+ ## How It Works
35
+
36
+ This plugin has two display layers:
37
+
38
+ - TUI sidebar panel: renders structured `TITLE`, `USAGE`, and `QUOTA` blocks
39
+ - Shared session title: stays compact so Desktop, Web, and TUI can all read the same title cleanly
40
+
41
+ In `sidebar.titleMode="auto"`, the shared title remains a compact single line. The richer multiline layout is handled by the dedicated TUI plugin instead of being written into `session.title` for every client.
42
+
43
+ Session-scoped aggregation can include descendant subagent sessions when `sidebar.includeChildren=true`. Day/week/month summaries do not merge descendants.
44
+
45
+ ## Supported Providers
46
+
47
+ Built-in quota adapters:
48
+
49
+ | Provider | Endpoint family | Auth | Quota shape | Notes |
50
+ | ------------------- | ---------------------------------------------------------- | ------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
51
+ | OpenAI Codex | `chatgpt.com/backend-api/wham/usage` | OAuth | Multi-window subscription | Reads ChatGPT usage windows such as short-term + weekly; Pro plans may also expose Codex Spark limits (`additional_rate_limits`) |
52
+ | GitHub Copilot | `api.github.com/copilot_internal/user` | OAuth | Monthly subscription | Uses the Copilot internal user endpoint |
53
+ | Anthropic | `api.anthropic.com/api/oauth/usage` | OAuth | Multi-window subscription | Supports plan-based usage windows |
54
+ | Kimi For Coding | `api.kimi.com/coding/v1/usages` | API key | Multi-window subscription | Typically `5h` + weekly windows |
55
+ | Zhipu Coding Plan | `bigmodel.cn/api/monitor/usage/quota/limit` | API key | Token quota | Coding-plan style quota window |
56
+ | MiniMax Coding Plan | `www.minimaxi.com/v1/api/openplatform/coding_plan/remains` | API key | Multi-window subscription | Typically `5h` + weekly windows |
57
+ | RightCode | `www.right.codes/account/summary` | API key | Daily quota and/or balance | Prefix-based subscription matching, with balance fallback |
58
+
59
+ Generic providers without a built-in quota endpoint can still contribute usage totals, but they will not show quota/balance unless an adapter exists.
60
+
61
+ Provider notes:
62
+
63
+ - OpenAI, Copilot, and Anthropic quota support is based on OAuth/session auth, not generic API-key billing endpoints
64
+ - **OpenAI Codex Spark**: OpenAI Pro subscriptions may expose additional per-feature windows (e.g. `GPT-5.3-Codex-Spark`) in the `additional_rate_limits` field of the `wham/usage` response. When present, the plugin automatically parses and renders these as extra windows under the OpenAI quota line. No extra config is required. Code review quota (`code_review_rate_limit`) is not displayed yet.
65
+ - RightCode can show both a daily allowance line and a balance line
66
+ - Copilot quota is supported, but API-equivalent cost is intentionally not shown because runtime pricing is not reliable enough
67
+
68
+ ## Display Rules
69
+
70
+ - Sidebar quota lines show providers actually used in the current session and recognized by an adapter
71
+ - `quota_summary` fetches default quota providers even if they were not used in the current session
72
+ - The TUI sidebar reads persisted `sidebarPanel` / usage state first, so historical sessions can render quickly on open or resume
73
+ - Compact-title quota parsing is only a fallback path; the TUI panel prefers persisted structured data
74
+ - `quota_show` toggles title decoration on or off, but the TUI panel remains the main rich display path
75
+
76
+ ## Install
77
+
78
+ OpenCode loads the server plugin from `opencode.json` and the TUI plugin from `tui.json`.
79
+
80
+ `opencode.json`
81
+
82
+ ```json
83
+ {
84
+ "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
85
+ }
86
+ ```
87
+
88
+ `tui.json`
89
+
90
+ ```json
91
+ {
92
+ "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
93
+ }
94
+ ```
95
+
96
+ For OpenCode `>=1.2.15`, keep server plugins in `opencode.json` and TUI plugins in `tui.json`.
97
+
98
+ ## Sidebar Demo
99
+
100
+ Typical TUI sidebar layout (with Codex Spark windows):
101
+
102
+ ```text
103
+ TITLE
104
+ Fix quota adapter matching
105
+ USAGE
106
+ R184 I189k O53.2k
107
+ CR31.4k CW3.2k Cd66%
108
+ API $12.8
109
+ QUOTA
110
+ OAI 5h80 R3h20m
111
+ W70 R2D04h
112
+ Sk5h100 R1h00m
113
+ SkW100 R3D04h
114
+ Cop M78 R12D00h
115
+ RC D$88.9/$60 E6D00h
116
+ B260
117
+ ```
118
+
119
+ Compact shared title example:
120
+
121
+ ```text
122
+ Fix quota adapter matching | OAI 5h80 R3h20m W70 R2D04h | RC D$88.9/$60 B260 | Cd66% | API$12.8
123
+ ```
124
+
125
+ ## Pricing And API Cost
126
+
127
+ `API cost` is an API-equivalent estimate. It is not your subscription fee, not a provider invoice, and not necessarily what the upstream service actually billed.
128
+
129
+ Pricing source priority:
130
+
131
+ 1. explicit model prices from your OpenCode main config (`opencode.jsonc` / `opencode.json`)
132
+ 2. fast/tier prices derived from those explicit base-model prices
133
+ 3. non-zero runtime prices from `provider.list()`
134
+ 4. fast/tier prices derived from runtime metadata
135
+ 5. `models.dev` remote model catalog pricing
136
+ 6. bundled fallback pricing shipped by this plugin
137
+
138
+ Important notes:
139
+
140
+ - OpenAI / Anthropic OAuth runtime metadata often reports `cost: 0`, so runtime pricing is only a supplemental source
141
+ - `provider.list()` is a runtime metadata view of the models OpenCode currently exposes; it is useful when non-zero, but it is not a stable authoritative pricing API
142
+ - OpenCode main-config pricing layers merge field-by-field, so a later override can replace only `input` / `output` without discarding earlier `cache_*` or `context_over_200k` fields
143
+ - `models.dev` is used as a structured remote supplement only for runtime-discovered models that are still missing prices after config, runtime, and bundled sources are considered; this mainly helps newer models that are not in the plugin's bundled table yet
144
+ - if `models.dev` is unreachable, the plugin falls back to the higher-priority sources above; API cost may remain `0` for uncovered models until pricing becomes available again
145
+ - bundled pricing covers common models, so token usage and quota features do not require manual pricing config
146
+ - if you use a new or not-yet-bundled model and runtime pricing is still zero, `API cost` may stay `$0.00` until you add an explicit model price override in your OpenCode main config
147
+
148
+ To get complete `API cost` coverage for new models, at least one of these must be true:
149
+
150
+ 1. the model already exists in the plugin's bundled pricing table
151
+ 2. you configured that model's price in OpenCode main config
152
+ 3. runtime `provider.list()` returns a non-zero price for it
153
+ 4. runtime `provider.list()` exposes the model and `models.dev` has pricing for it
154
+
155
+ ## Tool Report Demo
156
+
157
+ Example historical `quota_summary` markdown output shape:
158
+
159
+ ```md
160
+ ## Quota History - Daily since 2026-02-18
161
+
162
+ ### Quota Status
163
+
164
+ - OpenAI: 5h | 80.0% | reset 3h20m; Weekly | 70.0% | reset 2D04h
165
+ - Copilot: Monthly | 78.0% | reset 12D00h
166
+ - RightCode: Daily $88.9/$60 | reset 6D00h
167
+
168
+ ### Totals
169
+
170
+ | Metric | Total | Avg/Period |
171
+ | ------------ | ----: | ---------: |
172
+ | Requests | 184 | 26.3 |
173
+ | Total Tokens | 277k | 39.6k |
174
+ | Cache Hit | 63.1% | 58.4% |
175
+ | API Cost | $12.8 | $1.83 |
176
+
177
+ ### Provider Breakdown
178
+
179
+ | Provider | Req | Input | Output | Total | Share | Cache Hit | API Cost |
180
+ | --------- | --: | ----: | -----: | ----: | ----: | --------: | -------: |
181
+ | OpenAI | 140 | 160k | 61k | 221k | 79.8% | 66.2% | $10.4 |
182
+ | Anthropic | 44 | 29k | 27.1k | 56.1k | 20.2% | 51.3% | $2.34 |
183
+
184
+ ### Period Detail
185
+
186
+ | Period | Requests | Input | Output | Cache | Cache Hit | Total | API Cost |
187
+ | ------------ | -------: | ----: | -----: | ----: | --------: | ----: | -------: |
188
+ | 2026-02-18 | 12 | 18.3k | 4.2k | 8.9k | 32.7% | 31.4k | $1.12 |
189
+ | 2026-02-24\* | 17 | 8.1k | 2.0k | 3.4k | 66.0% | 13.5k | $0.88 |
190
+ ```
191
+
192
+ The tool already returns full markdown. Clients should display that report directly instead of replacing it with a short summary.
193
+
194
+ Sidebar output uses compact tokens. Toasts and markdown reports keep fuller human-readable wording.
195
+
196
+ ## Why The TUI Panel Exists
197
+
198
+ OpenCode renders the sidebar title as plain text inside a single styled title field. That means the shared title is great for compact telemetry, but not ideal for a rich multi-section layout.
199
+
200
+ This plugin therefore uses:
201
+
202
+ - shared compact titles for cross-client compatibility
203
+ - a dedicated TUI sidebar panel for the detailed block layout
204
+
205
+ That split avoids polluting Desktop/Web titles while still giving TUI users a readable quota dashboard.
206
+
207
+ ## Abbreviations
208
+
209
+ Usage tokens:
210
+
211
+ - `R`: requests
212
+ - `I`: input tokens
213
+ - `O`: output tokens, including reasoning tokens
214
+ - `CR`: cache read tokens
215
+ - `CW`: cache write tokens
216
+ - `Cd`: cached ratio / cache coverage
217
+ - `API`: API-equivalent cost estimate
218
+
219
+ Quota tokens:
220
+
221
+ - `OAI`: OpenAI
222
+ - `Cop`: GitHub Copilot
223
+ - `Ant`: Anthropic
224
+ - `RC`: RightCode
225
+ - `B`: balance
226
+ - `D`: daily window
227
+ - `W`: weekly window
228
+ - `M`: monthly window
229
+ - `Sk5h`: OpenAI Codex Spark short window (e.g. 5h)
230
+ - `SkW`: OpenAI Codex Spark weekly window
231
+ - `R3h20m`: resets in `3h20m`
232
+ - `R2D04h`: resets in `2D04h`
233
+ - `E6D00h`: expires in `6D00h`
234
+
235
+ Example compact quota fragments:
236
+
237
+ - `OAI 5h80 R3h20m`: OpenAI short window, 80% remaining, resets in `3h20m`
238
+ - `OAI Sk5h100 R1h00m`: OpenAI Codex Spark 5h window, 100% remaining, resets in `1h00m`
239
+ - `OAI SkW100 R3D04h`: OpenAI Codex Spark weekly window, 100% remaining, resets in `3D04h`
240
+ - `Cop M78 R12D00h`: Copilot monthly quota, 78% remaining, resets in `12D00h`
241
+ - `RC D$88.9/$60 E6D00h B260`: RightCode daily quota plus balance
242
+
243
+ ## Config
244
+
245
+ Recommended config file locations:
246
+
247
+ - `~/.config/opencode/quota-sidebar.config.json`
248
+ - `<worktree>/quota-sidebar.config.json`
249
+ - `<directory>/quota-sidebar.config.json`
250
+ - `<worktree>/.opencode/quota-sidebar.config.json`
251
+ - `<directory>/.opencode/quota-sidebar.config.json`
252
+ - `OPENCODE_QUOTA_CONFIG=/absolute/path/to/config.json`
253
+
254
+ Minimal example:
255
+
256
+ ```json
257
+ {
258
+ "sidebar": {
259
+ "enabled": true,
260
+ "titleMode": "auto",
261
+ "showCost": true,
262
+ "showQuota": true,
263
+ "includeChildren": true
264
+ }
265
+ }
266
+ ```
267
+
268
+ See [`quota-sidebar.config.example.json`](./quota-sidebar.config.example.json) for a fuller config example.
269
+
270
+ Important config notes:
271
+
272
+ - `sidebar.titleMode`: `auto`, `compact`, or `multiline`
273
+ - `sidebar.showCost`: controls API-equivalent cost in sidebar, title, markdown report, toast, and CLI output
274
+ - `sidebar.wrapQuotaLines`: wraps long quota lines with indentation instead of dropping fields
275
+ - `sidebar.includeChildren`: includes descendant subagent sessions for `period=session`
276
+ Config is layered. The later source overrides the earlier one:
277
+
278
+ 1. global config
279
+ 2. worktree config
280
+ 3. directory config
281
+ 4. worktree `.opencode` config
282
+ 5. directory `.opencode` config
283
+ 6. `OPENCODE_QUOTA_CONFIG`
284
+
285
+ OpenCode main pricing config candidates are read from these locations:
286
+
287
+ 1. `~/.config/opencode/opencode.jsonc`
288
+ 2. `~/.config/opencode/opencode.json`
289
+ 3. `<worktree>/opencode.jsonc`
290
+ 4. `<worktree>/opencode.json`
291
+ 5. `<directory>/opencode.jsonc`
292
+ 6. `<directory>/opencode.json`
293
+ 7. `<worktree>/.opencode/opencode.jsonc`
294
+ 8. `<worktree>/.opencode/opencode.json`
295
+ 9. `<directory>/.opencode/opencode.jsonc`
296
+ 10. `<directory>/.opencode/opencode.json`
297
+
298
+ ## Linux CLI Notes
299
+
300
+ - `opencode-quota` will auto-start a temporary `opencode serve` when no local server is already reachable
301
+ - recent releases clean up that temporary server more aggressively on Linux/Unix so the CLI can exit normally after printing the report
302
+ - if the CLI still hangs on Linux, upgrade to a version that includes the server-process cleanup fix
303
+
304
+ ## Persistence And Aggregation
305
+
306
+ The plugin stores:
307
+
308
+ - global state in `<opencode-data>/quota-sidebar.state.json`
309
+ - session chunks in `<opencode-data>/quota-sidebar-sessions/YYYY/MM/DD.json`
310
+
311
+ Those persisted chunks keep title state, cached usage, sidebar-panel payloads, and quota cache data. This lets the TUI sidebar render from stored structured state on session open/resume instead of depending entirely on live message scans.
312
+
313
+ Usage aggregation is incremental. The plugin tracks a cursor per session and processes only new messages when possible. If message history changes in a way that invalidates the incremental view, it can rescan and refresh persisted usage.
314
+
315
+ Pricing cache notes:
316
+
317
+ - newer releases persist a `pricingFingerprint` alongside cached session usage
318
+ - changing a model price from one non-zero value to another non-zero value now invalidates old cached API-cost totals automatically
319
+ - older plugin versions only keyed cache freshness on billing version, so historical session API-cost values could remain stale until a forced recompute
320
+ - legacy decorated titles that still contain `Est$...` are recognized during cleanup/restore, but newly rendered titles always use `API$...`
321
+
322
+ ## Tools
323
+
324
+ - `quota_summary`: shows usage and quota for `session`, `day`, `week`, or `month`
325
+ - `quota_show`: toggles title decoration on or off
326
+
327
+ Behavior notes:
328
+
329
+ - `quota_summary` returns the full markdown report body
330
+ - `quota_summary` can show a toast in addition to returning markdown
331
+ - `quota_summary` accepts `period`, `since`, `last`, `toast`, and `includeChildren`
332
+ - `quota_summary(includeChildren=true)` only changes `period=session`
333
+ - `day/week/month` scans all sessions in the selected time range, so child sessions are included when they have activity in that range
334
+ - `day/week/month` does not do parent-tree rollup; child sessions are counted as independent sessions, not merged through `includeChildren`
335
+ - `since` and `last` are mutually exclusive
336
+ - `period=session` does not accept `since` or `last`
337
+ - `quota_show(enabled=true|false)` can explicitly force a state instead of toggling
338
+ - Historical reports support both absolute `since` and relative `last`
339
+ - `since` accepts `YYYY-MM` or `YYYY-MM-DD`
340
+ - `last` accepts a positive integer and is relative to the current period: `day=7`, `week=8`, `month=6`
341
+ - Empty `period=day|week|month` means the current natural day/week/month
342
+
343
+ Example command aliases:
344
+
345
+ For direct in-chat history output, define command aliases that call `quota_summary`.
346
+ The old TUI history popup path was removed; history now goes through the tool report directly.
347
+ These aliases are still OpenCode command templates, so they expand into prompt text before the model/tool chain runs. For the cleanest direct output path, prefer the standalone CLI.
348
+
349
+ Examples:
350
+
351
+ - `quota_summary(period=day)` -> today
352
+ - `quota_summary(period=week)` -> this week
353
+ - `quota_summary(period=month)` -> this month
354
+ - `quota_summary(period=day,last=7)` -> last 7 days
355
+ - `quota_summary(period=week,last=8)` -> last 8 weeks
356
+ - `quota_summary(period=month,last=6)` -> last 6 months
357
+ - `quota_summary(period=month,since=2026-01)` -> since Jan 2026
358
+
359
+ ```json
360
+ {
361
+ "command": {
362
+ "qday": {
363
+ "description": "Today / last N days / since date",
364
+ "template": "Run /qday for opencode-quota-sidebar. Call tool quota_summary exactly once and return its full report directly. If `$ARGUMENTS` is empty: period=day, toast=true. If `$ARGUMENTS` is a positive integer: period=day, last=<that integer>, toast=true. If `$ARGUMENTS` matches YYYY-MM-DD: period=day, since=<that date>, toast=true. Otherwise briefly explain: empty, positive integer, or YYYY-MM-DD."
365
+ },
366
+ "qweek": {
367
+ "description": "This week / last N weeks / since date",
368
+ "template": "Run /qweek for opencode-quota-sidebar. Call tool quota_summary exactly once and return its full report directly. If `$ARGUMENTS` is empty: period=week, toast=true. If `$ARGUMENTS` is a positive integer: period=week, last=<that integer>, toast=true. If `$ARGUMENTS` matches YYYY-MM-DD: period=week, since=<that date>, toast=true. Otherwise briefly explain: empty, positive integer, or YYYY-MM-DD."
369
+ },
370
+ "qmonth": {
371
+ "description": "This month / last N months / since month",
372
+ "template": "Run /qmonth for opencode-quota-sidebar. Call tool quota_summary exactly once and return its full report directly. If `$ARGUMENTS` is empty: period=month, toast=true. If `$ARGUMENTS` is a positive integer: period=month, last=<that integer>, toast=true. If `$ARGUMENTS` matches YYYY-MM: period=month, since=<that month>, toast=true. Otherwise briefly explain: empty, positive integer, or YYYY-MM."
373
+ },
374
+ "qtoggle": {
375
+ "description": "Toggle sidebar usage display on/off",
376
+ "template": "Call tool quota_show (no arguments, it toggles)."
377
+ }
378
+ }
379
+ }
380
+ ```
381
+
382
+ ## CLI
383
+
384
+ The package exposes a standalone CLI dashboard. After installing globally or making the `bin` available:
385
+
386
+ ```bash
387
+ npm install -g @leo000001/opencode-quota-sidebar
388
+ ```
389
+
390
+ If you prefer not to install globally, you can also run it with `npx @leo000001/opencode-quota-sidebar <args>`.
391
+
392
+ ```bash
393
+ # Current period (single snapshot)
394
+ opencode-quota day # today
395
+ opencode-quota week # this week (Monday-based)
396
+ opencode-quota month # this month
397
+
398
+ # Multi-period history
399
+ opencode-quota day 7 # last 7 days
400
+ opencode-quota week 8 # last 8 weeks
401
+ opencode-quota month 6 # last 6 months
402
+
403
+ # Absolute start date
404
+ opencode-quota day --since 2026-04-01
405
+ opencode-quota week --since 2026-04-01
406
+ opencode-quota month --since 2026-01
407
+
408
+ # Also accepted as positional (equivalent to --since)
409
+ opencode-quota day 2026-04-01
410
+ opencode-quota month 2026-01
411
+ ```
412
+
413
+ When called without `last` or `--since`, the CLI renders a single-period snapshot (`QUOTA + TOTALS + PROVIDERS`). When called with `last` or `--since`, it renders multi-period history with a larger multi-line `TREND` block.
414
+
415
+ ### CLI semantics
416
+
417
+ - `day` = current natural day; `week` = current natural week (Monday-based); `month` = current natural month
418
+ - A positional integer maps to `last=<N>` (number of periods back from now)
419
+ - A positional date string maps to `--since` (`YYYY-MM-DD` for day/week, `YYYY-MM` for month)
420
+ - `--since` and `--last` are mutually exclusive
421
+ - `last` is limited to 90 for day, reasonable ranges for week/month
422
+
423
+ ### Trend section
424
+
425
+ The `TREND` block appears only in multi-period mode. Each metric (`Requests`, `Tokens`, `Cache`, `Cost`) is rendered as a small multi-line bar chart:
426
+
427
+ - one summary line: the current value only
428
+ - one bar row per visible period (latest 8 periods max), ordered oldest-to-newest
429
+ - the current period is marked with `*`
430
+
431
+ Interpretation example:
432
+
433
+ ```text
434
+ Requests 12.3k
435
+ 04-08 | ███░░░░░░░░░░░░░░░ | 4.1k
436
+ 04-09 | ██████░░░░░░░░░░░░ | 8.2k
437
+ 04-10* | █████████████░░░░░ | 12.3k
438
+ ```
439
+
440
+ This means the current bucket has `12.3k` requests, and the bar rows below show the relative size of each visible bucket from oldest to newest.
441
+
442
+ ### Connection behavior
443
+
444
+ - The CLI talks to the local OpenCode API at `http://localhost:4096` by default
445
+ - Set `OPENCODE_BASE_URL` to override (e.g. `http://192.168.1.10:4096`)
446
+ - If no server is running and `OPENCODE_BASE_URL` is not set, the CLI attempts to start one:
447
+ - **Linux/macOS**: runs `opencode serve --hostname=127.0.0.1 --port=4096`
448
+ - **Windows**: tries `opencode.cmd`, then `opencode` via `shell: true`, then `bash -lc opencode`
449
+ - The auto-start waits up to 10 seconds for the server to print `opencode server listening on <url>`
450
+ - If auto-start fails, check that `opencode` is in your `PATH`
451
+ - On Windows, the `shell: true` path is usually the most reliable when `opencode.cmd` is not directly spawnable from Node
452
+
453
+ ### Platform notes
454
+
455
+ - **Terminal encoding**: the dashboard uses Unicode box-drawing and block elements (`█░`). Requires a UTF-8 capable terminal. Windows users should use Windows Terminal, PowerShell 7+, or a terminal that supports UTF-8. Classic cmd.exe with legacy codepages (CP437/CP850) may render garbled characters.
456
+ - **Alignment**: weekly/monthly trend labels can still be truncated when the visible label is very long (for example long absolute week ranges). This is a known presentation tradeoff in the current terminal renderer.
457
+ - **Windows PATH**: the CLI tries multiple command forms to find `opencode`. If none work, ensure `opencode` or `opencode.cmd` is on your PATH, or start the server manually and set `OPENCODE_BASE_URL`.
458
+ - **Node.js**: requires `>=18`
459
+
460
+ ### Environment variables
461
+
462
+ | Variable | Default | Purpose |
463
+ | ---------------------------- | ------------------------- | -------------------------------------------------------------------------------- |
464
+ | `OPENCODE_BASE_URL` | `http://localhost:4096` | OpenCode API endpoint; set this if the server is remote or on a non-default port |
465
+ | `OPENCODE_QUOTA_CONFIG_HOME` | `~/.config/opencode` | Global config directory override |
466
+ | `OPENCODE_QUOTA_DATA_HOME` | `~/.local/share/opencode` | Global data directory override |
467
+
468
+ ## Development
469
+
470
+ ```bash
471
+ npm install
472
+ npm run build
473
+ npm test
474
+ ```
475
+
476
+ For local development, load:
477
+
478
+ - `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/index.js` in `opencode.json`
479
+ - `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/tui.tsx` in `tui.json`
480
+
481
+ On Windows, use forward slashes in `file:///` URLs.
482
+
483
+ ## Compatibility And Caveats
484
+
485
+ - Node.js `>=18`
486
+ - OpenCode plugin SDK `@opencode-ai/plugin` / `@opencode-ai/sdk` `^1.3.5`
487
+ - For OpenCode `>=1.2.15`, TUI config belongs in `tui.json`
488
+ - The shared title is still one `session.title` value for all clients
489
+ - The plugin avoids ANSI styling in sidebar titles to keep resize behavior stable
490
+ - Some providers expose true quota windows, others only expose balance data
491
+
492
+ ## Contributing
493
+
494
+ - Changelog: [CHANGELOG.md](./CHANGELOG.md)
495
+ - Adapter and architecture notes: [CONTRIBUTING.md](./CONTRIBUTING.md)
496
+ - Security policy: [SECURITY.md](./SECURITY.md)
497
+
498
+ ## Documentation Navigation
499
+
500
+ - English README: [`README.md`](./README.md)
501
+ - Simplified Chinese README: [`README.zh-CN.md`](./README.zh-CN.md)
502
+ - Changelog: [`CHANGELOG.md`](./CHANGELOG.md)
503
+ - Contributing guide: [`CONTRIBUTING.md`](./CONTRIBUTING.md)
504
+ - Security policy: [`SECURITY.md`](./SECURITY.md)