@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.zh-CN.md CHANGED
@@ -1,458 +1,516 @@
1
- # opencode-quota-sidebar
2
-
3
- [English](./README.md)
4
-
5
- OpenCode 插件:在 TUI sidebar 中显示 token 用量和 provider quota,同时让共享的 session title 在 Desktop、Web 和 TUI 中都保持紧凑可读。
6
-
7
- ![示例截图](./assets/OpenCode-Quota-Sidebar.png)
8
-
9
- 上面的截图来自 [`./assets/OpenCode-Quota-Sidebar.png`](./assets/OpenCode-Quota-Sidebar.png),展示的是这个插件实际渲染出来的 TUI 侧边栏布局。
10
-
11
- ## 核心能力
12
-
13
- - 在 TUI sidebar 中渲染结构化的 `TITLE`、`USAGE`、`QUOTA` 三个区块
14
- - 让共享 `session.title` 保持紧凑单行,而不是把多行遥测数据写进所有客户端都共用的标题
15
- - 统计 `session`、`day`、`week`、`month` 四种范围的 usage
16
- - 内置支持 OpenAI、Copilot、Anthropic、Kimi、Zhipu、MiniMax、RightCode 的 quota / balance 获取
17
- - session 级统计可选聚合 descendant subagent sessions
18
- - 提供 `quota_summary` 和 `quota_show` 两个工具
19
-
20
- ## 架构概览
21
-
22
- 这个仓库是纯插件实现,不修改 OpenCode 核心代码。
23
-
24
- - Server 层:负责 usage 聚合、quota 拉取、状态持久化、title 刷新和工具注册
25
- - TUI 层:负责侧边栏面板渲染,并读取持久化的 sidebar-panel 数据
26
- - Persistence 层:负责全局状态和按日期分片的 session chunk 存储
27
- - Provider Adapter 层:通过统一的 `QuotaSnapshot` 结构屏蔽不同 provider 的 quota/balance 接口差异
28
-
29
- 依赖 `@opencode-ai/plugin` 和 `@opencode-ai/sdk`。
30
-
31
- ## 工作方式
32
-
33
- 这个插件有两条展示路径:
34
-
35
- - TUI sidebar panel:渲染结构化的 `TITLE / USAGE / QUOTA`
36
- - Shared session title:保持紧凑单行,供 Desktop、Web、TUI 共同使用
37
-
38
- 在 `sidebar.titleMode="auto"` 下,共享 title 会保持 compact 单行。更丰富的多段布局由专门的 TUI 插件渲染,而不是直接写进 `session.title`。
39
-
40
- 当 `sidebar.includeChildren=true` 时,`period=session` 的统计可以聚合子代 subagent sessions。`day/week/month` 范围统计不会合并 descendants。
41
-
42
- ## 支持的 Provider
43
-
44
- 内置 quota adapter 如下:
45
-
46
- | Provider | Endpoint family | 鉴权方式 | 展示形态 | 说明 |
47
- | ------------------- | ---------------------------------------------------------- | -------- | --------------- | --------------------------------------------------------------------------------------------------------------- |
48
- | OpenAI Codex | `chatgpt.com/backend-api/wham/usage` | OAuth | 多窗口订阅额度 | 读取 ChatGPT usage 窗口,例如短窗口 + 周窗口;Pro 订阅可能额外提供 Codex Spark 限额(`additional_rate_limits`) |
49
- | GitHub Copilot | `api.github.com/copilot_internal/user` | OAuth | 月度额度 | 使用 Copilot internal user 接口 |
50
- | Anthropic | `api.anthropic.com/api/oauth/usage` | OAuth | 多窗口订阅额度 | 支持 plan-based usage windows |
51
- | Kimi For Coding | `api.kimi.com/coding/v1/usages` | API key | 多窗口订阅额度 | 通常为 `5h` + weekly |
52
- | Zhipu Coding Plan | `bigmodel.cn/api/monitor/usage/quota/limit` | API key | token quota | coding plan 风格额度 |
53
- | MiniMax Coding Plan | `www.minimaxi.com/v1/api/openplatform/coding_plan/remains` | API key | 多窗口订阅额度 | 通常为 `5h` + weekly |
54
- | RightCode | `www.right.codes/account/summary` | API key | 日额度和/或余额 | 按 prefix 匹配订阅,失败时回退为 balance |
55
-
56
- 补充说明:
57
-
58
- - 没有内置 quota adapter 的 generic provider 仍然可以参与 usage 聚合,但不会显示 quota/balance
59
- - OpenAI、Copilot、Anthropic 的 quota 支持依赖 OAuth / session auth,而不是通用 API key 账单接口
60
- - **OpenAI Codex Spark**:OpenAI Pro 订阅的 `wham/usage` 响应中可能包含 `additional_rate_limits` 字段,其中带有 `GPT-5.3-Codex-Spark` 等额外功能窗口。插件会自动解析并将其显示在 OpenAI quota 行下,无需额外配置。`code_review_rate_limit`(代码审查额度)目前暂不展示。
61
- - RightCode 可能同时显示 daily allowance 和 balance 两行
62
- - Copilot 支持 quota 展示,但当前不会显示 API-equivalent cost,因为 pricing metadata 不够稳定
63
-
64
- ## 展示规则
65
-
66
- - Sidebar quota 区块只展示当前 session 中实际使用过、且能被 adapter 识别的 provider
67
- - `quota_summary` 会主动抓取默认 quota providers,即使当前 session 没用到
68
- - TUI sidebar 优先读取持久化的 `sidebarPanel` / usage 数据,所以历史 session 打开时也能快速渲染
69
- - compact title 中的 quota 解析只是兜底路径,TUI panel 优先消费结构化持久化数据
70
- - `quota_show` 控制的是共享 title 的装饰开关,TUI panel 仍然是主要的富文本展示入口
71
-
72
- title 相关补充:
73
-
74
- - `sidebar.titleMode="auto"`:共享 title 保持 compact
75
- - `sidebar.titleMode="compact"`:强制所有客户端都使用 compact title
76
- - `sidebar.titleMode="multiline"`:使用旧的 multiline title 装饰路径
77
- - 共享 title 本质上仍然只有一个 `session.title`,所以复杂布局更适合放在 TUI panel 中
78
-
79
- ## Sidebar 示例
80
-
81
- 典型的 TUI sidebar 布局(含 Codex Spark 窗口):
82
-
83
- ```text
84
- TITLE
85
- Fix quota adapter matching
86
- USAGE
87
- R184 I189k O53.2k
88
- CR31.4k CW3.2k Cd66%
89
- Est $12.8
90
- QUOTA
91
- OAI 5h80 R3h20m
92
- W70 R2D04h
93
- Sk5h100 R1h00m
94
- SkW100 R3D04h
95
- Cop M78 R12D00h
96
- RC D$88.9/$60 E6D00h
97
- B260
98
- ```
99
-
100
- compact shared title 示例:
101
-
102
- ```text
103
- Fix quota adapter matching | OAI 5h80 R3h20m W70 R2D04h | RC D$88.9/$60 B260 | Cd66% | Est$12.8
104
- ```
105
-
106
- ## Tool Report 示例
107
-
108
- 历史 `quota_summary` markdown 大致会是这样的结构:
109
-
110
- ```md
111
- ## Quota History - Daily since 2026-02-18
112
-
113
- ### Quota Status
114
-
115
- - OpenAI: 5h | 80.0% | reset 3h20m; Weekly | 70.0% | reset 2D04h
116
- - Copilot: Monthly | 78.0% | reset 12D00h
117
- - RightCode: Daily $88.9/$60 | reset 6D00h
118
-
119
- ### Totals
120
-
121
- | Metric | Total | Avg/Period |
122
- | ------------ | ----: | ---------: |
123
- | Requests | 184 | 26.3 |
124
- | Total Tokens | 277k | 39.6k |
125
- | Cache Hit | 63.1% | 58.4% |
126
- | API Cost | $12.8 | $1.83 |
127
-
128
- ### Provider Breakdown
129
-
130
- | Provider | Req | Input | Output | Total | Share | Cache Hit | API Cost |
131
- | --------- | --: | ----: | -----: | ----: | ----: | --------: | -------: |
132
- | OpenAI | 140 | 160k | 61k | 221k | 79.8% | 66.2% | $10.4 |
133
- | Anthropic | 44 | 29k | 27.1k | 56.1k | 20.2% | 51.3% | $2.34 |
134
-
135
- ### Period Detail
136
-
137
- | Period | Requests | Input | Output | Cache | Cache Hit | Total | API Cost |
138
- | ------------ | -------: | ----: | -----: | ----: | --------: | ----: | -------: |
139
- | 2026-02-18 | 12 | 18.3k | 4.2k | 8.9k | 32.7% | 31.4k | $1.12 |
140
- | 2026-02-24\* | 17 | 8.1k | 2.0k | 3.4k | 66.0% | 13.5k | $0.88 |
141
- ```
142
-
143
- 这个工具本身就返回完整 markdown,调用方应该直接展示,而不是再压缩成一句话摘要。
144
-
145
- 补充说明:
146
-
147
- - Sidebar 里用的是 compact token
148
- - Toast 和 markdown report 使用更完整的人类可读文案
149
- - `quota_summary` 可以展示默认 quota providers,即使当前 session 没有使用它们
150
-
151
- ## 为什么需要 TUI Panel
152
-
153
- OpenCode sidebar title 的渲染本质上是一个单一的文本字段。它适合放紧凑的 telemetry,但不适合承载结构化的多段布局。
154
-
155
- 所以这个插件采用了两层设计:
156
-
157
- - shared compact title:保证跨客户端兼容
158
- - dedicated TUI sidebar panel:提供更清晰的区块化展示
159
-
160
- 这样既不会污染 Desktop/Web 的标题,又能让 TUI 用户看到清晰的 quota dashboard。
161
-
162
- ## 缩写说明
163
-
164
- Usage token:
165
-
166
- - `R`:requests
167
- - `I`:input tokens
168
- - `O`:output tokens,已包含 reasoning tokens
169
- - `CR`:cache read tokens
170
- - `CW`:cache write tokens
171
- - `Cd`:cached ratio / cache coverage
172
- - `Est`:API-equivalent cost estimate
173
-
174
- Quota token:
175
-
176
- - `OAI`:OpenAI
177
- - `Cop`:GitHub Copilot
178
- - `Ant`:Anthropic
179
- - `RC`:RightCode
180
- - `B`:balance
181
- - `D`:daily window
182
- - `W`:weekly window
183
- - `M`:monthly window
184
- - `Sk5h`:OpenAI Codex Spark 短窗口(如 5h)
185
- - `SkW`:OpenAI Codex Spark 周窗口
186
- - `R3h20m`:还剩 `3h20m` 重置
187
- - `R2D04h`:还剩 `2D04h` 重置
188
- - `E6D00h`:还剩 `6D00h` 到期
189
-
190
- compact quota 片段示例:
191
-
192
- - `OAI 5h80 R3h20m`:OpenAI 短窗口剩余 80%,还剩 `3h20m` 重置
193
- - `OAI Sk5h100 R1h00m`:OpenAI Codex Spark 5h 窗口剩余 100%,还剩 `1h00m` 重置
194
- - `OAI SkW100 R3D04h`:OpenAI Codex Spark 周窗口剩余 100%,还剩 `3D04h` 重置
195
- - `Cop M78 R12D00h`:Copilot 月额度剩余 78%,还剩 `12D00h` 重置
196
- - `RC D$88.9/$60 E6D00h B260`:RightCode 日额度 + 余额
197
-
198
- ## 安装
199
-
200
- OpenCode 会分别从 `opencode.json` `tui.json` 加载 server / TUI 插件。
201
-
202
- `opencode.json`
203
-
204
- ```json
205
- {
206
- "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
207
- }
208
- ```
209
-
210
- `tui.json`
211
-
212
- ```json
213
- {
214
- "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
215
- }
216
- ```
217
-
218
- OpenCode `>=1.2.15` 时,server 插件放在 `opencode.json`,TUI 插件放在 `tui.json`。
219
-
220
- ## 配置
221
-
222
- 常见配置路径:
223
-
224
- - `~/.config/opencode/quota-sidebar.config.json`
225
- - `<worktree>/quota-sidebar.config.json`
226
- - `<worktree>/.opencode/quota-sidebar.config.json`
227
- - `OPENCODE_QUOTA_CONFIG=/absolute/path/to/config.json`
228
-
229
- 最小示例:
230
-
231
- ```json
232
- {
233
- "sidebar": {
234
- "enabled": true,
235
- "titleMode": "auto",
236
- "showCost": true,
237
- "showQuota": true,
238
- "includeChildren": true
239
- }
240
- }
241
- ```
242
-
243
- 完整配置示例见 [`quota-sidebar.config.example.json`](./quota-sidebar.config.example.json)。
244
-
245
- 重要配置项:
246
-
247
- - `sidebar.titleMode`:`auto`、`compact`、`multiline`
248
- - `sidebar.showCost`:控制 sidebar、title、markdown report、toast CLI 中的 API-equivalent cost 可见性
249
- - `sidebar.wrapQuotaLines`:长 quota 行是否自动续行并缩进
250
- - `sidebar.includeChildren`:`period=session` 时是否聚合 descendant subagent sessions
251
- 配置采用分层覆盖,后层配置覆盖前层配置:
252
-
253
- 1. 全局配置
254
- 2. worktree 配置
255
- 3. directory 配置
256
- 4. worktree `.opencode` 配置
257
- 5. directory `.opencode` 配置
258
- 6. `OPENCODE_QUOTA_CONFIG`
259
-
260
- ## 持久化与聚合
261
-
262
- 插件会把数据写入:
263
-
264
- - 全局状态:`<opencode-data>/quota-sidebar.state.json`
265
- - session 分片:`<opencode-data>/quota-sidebar-sessions/YYYY/MM/DD.json`
266
-
267
- 这些持久化 chunk 会保存 title 状态、cached usage、sidebar-panel payload 和 quota cache,从而让 TUI sidebar 在 session open/resume 时可以直接从结构化状态恢复,而不必完全依赖实时 message 扫描。
268
-
269
- usage 聚合采用增量模式。插件会为每个 session 维护 cursor,优先只处理新消息;如果消息历史变化导致增量视图失效,再回退到重扫并刷新持久化 usage。
270
-
271
- ## 工具
272
-
273
- - `quota_summary`:查看 `session`、`day`、`week`、`month` 的用量和 quota
274
- - `quota_show`:切换 title 装饰开关
275
-
276
- 行为说明:
277
-
278
- - `quota_summary` 返回完整 markdown report
279
- - `quota_summary` 可以同时触发 toast 展示
280
- - `quota_summary` 参数为 `period`、`since`、`last`、`toast`、`includeChildren`
281
- - `quota_summary(includeChildren=true)` 只影响 `period=session`
282
- - `day/week/month` 会扫描所选时间范围内的全部 sessions,所以 child sessions 只要在该范围内有 activity 就会被统计进去
283
- - `day/week/month` 不做 parent 树 rollup;child sessions 会作为独立 sessions 计入,而不是通过 `includeChildren` 合并到 parent
284
- - `since` 和 `last` 互斥
285
- - `period=session` 不支持 `since` / `last`
286
- - `quota_show(enabled=true|false)` 可以显式指定目标状态,而不是只做 toggle
287
- - 历史报告同时支持绝对时间 `since` 和相对区间 `last`
288
- - `since` 支持 `YYYY-MM` 或 `YYYY-MM-DD`
289
- - `last` 需要正整数,并按当前 period 相对计算:`day=7`、`week=8`、`month=6`
290
- - 空参数的 `period=day|week|month` 表示当前自然日 / 自然周 / 自然月
291
-
292
- 命令别名示例:
293
-
294
- 如果你想直接走工具输出查看历史,可以在配置里声明调用 `quota_summary` 的命令别名。
295
- 旧的 TUI 历史弹窗路径已经移除,历史查看统一回到 tool report。
296
- 这些别名本质上仍然是 OpenCode 的 command template,因此会先展开为提示词,再进入模型 / tool 链路。若想要更直接、更干净的输出路径,优先使用独立 CLI。
297
-
298
- 示例:
299
-
300
- - `quota_summary(period=day)` -> 今天
301
- - `quota_summary(period=week)` -> 本周
302
- - `quota_summary(period=month)` -> 本月
303
- - `quota_summary(period=day,last=7)` -> 最近 7 天
304
- - `quota_summary(period=week,last=8)` -> 最近 8 周
305
- - `quota_summary(period=month,last=6)` -> 最近 6 个月
306
- - `quota_summary(period=month,since=2026-01)` -> 从 2026-01 开始
307
-
308
- ```json
309
- {
310
- "command": {
311
- "qday": {
312
- "description": "Today / last N days / since date",
313
- "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."
314
- },
315
- "qweek": {
316
- "description": "This week / last N weeks / since date",
317
- "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."
318
- },
319
- "qmonth": {
320
- "description": "This month / last N months / since month",
321
- "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."
322
- },
323
- "qtoggle": {
324
- "description": "Toggle sidebar usage display on/off",
325
- "template": "Call tool quota_show (no arguments, it toggles)."
326
- }
327
- }
328
- }
329
- ```
330
-
331
- ## CLI
332
-
333
- 这个包同时提供一个独立 CLI dashboard。全局安装或让 `bin` 可执行后:
334
-
335
- ```bash
336
- npm install -g @leo000001/opencode-quota-sidebar
337
- ```
338
-
339
- 如果不想全局安装,也可以直接用 `npx @leo000001/opencode-quota-sidebar <args>` 运行。
340
-
341
- ```bash
342
- # 当前周期(单次快照)
343
- opencode-quota day # 今天
344
- opencode-quota week # 本周(周一起算)
345
- opencode-quota month # 本月
346
-
347
- # 多周期历史
348
- opencode-quota day 7 # 最近 7
349
- opencode-quota week 8 # 最近 8 周
350
- opencode-quota month 6 # 最近 6 个月
351
-
352
- # 绝对起始日期
353
- opencode-quota day --since 2026-04-01
354
- opencode-quota week --since 2026-04-01
355
- opencode-quota month --since 2026-01
356
-
357
- # 位置参数也接受日期字符串(等同于 --since)
358
- opencode-quota day 2026-04-01
359
- opencode-quota month 2026-01
360
- ```
361
-
362
- 不带 `last` `--since` 时,CLI 渲染单周期快照(`QUOTA + TOTALS + PROVIDERS`)。带 `last` 或 `--since` 时渲染多周期历史,并额外展示更大的多行 `TREND` 区块。
363
-
364
- ### CLI 语义
365
-
366
- - `day` = 当前自然日;`week` = 当前自然周(周一起算);`month` = 当前自然月
367
- - 位置参数正整数映射为 `last=<N>`(从当前往回数 N 个周期)
368
- - 位置参数日期字符串映射为 `--since`(day/week 用 `YYYY-MM-DD`,month 用 `YYYY-MM`)
369
- - `--since` 和 `--last` 互斥
370
- - `last` day 限制 90,week/month 也有合理范围限制
371
-
372
- ### Trend 区块
373
-
374
- `TREND` 区块仅在多周期模式下出现。每个指标(`Requests`、`Tokens`、`Cache`、`Cost`)都会渲染为一个小型多行柱状图:
375
-
376
- - 一行摘要:当前值
377
- - 每个可见周期一行柱状条(最多展示最近 8 个周期)
378
- - 当前周期用 `*` 标记
379
-
380
- 解读示例:
381
-
382
- ```text
383
- Requests 12.3k
384
- 04-08 | ███░░░░░░░░░░░░░░░ | 4.1k
385
- 04-09 | ██████░░░░░░░░░░░░ | 8.2k
386
- 04-10* | █████████████░░░░░ | 12.3k
387
- ```
388
-
389
- 这表示当前 bucket 有 `12.3k` 请求,下面的柱状条则按时间顺序展示各个可见 bucket 的相对大小。
390
-
391
- ### 连接行为
392
-
393
- - CLI 默认连接本地 OpenCode API `http://localhost:4096`
394
- - 通过 `OPENCODE_BASE_URL` 覆盖(例如 `http://192.168.1.10:4096`)
395
- - 如果没有运行中的 server 且未设置 `OPENCODE_BASE_URL`,CLI 会尝试自动启动:
396
- - **Linux/macOS**:运行 `opencode serve --hostname=127.0.0.1 --port=4096`
397
- - **Windows**:依次尝试 `opencode.cmd`、`opencode`(通过 `shell: true`)、`bash -lc opencode`
398
- - 自动启动等待最多 10 秒,直到 server 输出 `opencode server listening on <url>`
399
- - 如果自动启动失败,请确认 `opencode` 在你的 `PATH` 中
400
- - 在 Windows 上,如果 `opencode.cmd` 不能被 Node 直接 spawn,`shell: true` 通常是更可靠的兜底路径
401
-
402
- ### 平台说明
403
-
404
- - **终端编码**:dashboard 使用 Unicode 绘框字符和方块元素(`█░`),需要支持 UTF-8 的终端。Windows 用户应使用 Windows Terminal、PowerShell 7+ 或其他支持 UTF-8 的终端。经典 cmd.exe 使用传统代码页(CP437/CP850)时可能显示乱码。
405
- - **对齐**:当 week/month 的可见标签非常长时(例如完整绝对日期范围),CLI 的 trend 标签仍可能被截断。这是当前终端 renderer 的已知展示取舍。
406
- - **Windows PATH**:CLI 会尝试多种命令形式来找到 `opencode`。如果全部失败,请确认 `opencode` 或 `opencode.cmd` 在 PATH 中,或手动启动 server 后设置 `OPENCODE_BASE_URL`。
407
- - **Node.js**:需要 `>=18`
408
-
409
- ### 环境变量
410
-
411
- | 变量 | 默认值 | 用途 |
412
- | ---------------------------- | ------------------------- | -------------------------------------------------- |
413
- | `OPENCODE_BASE_URL` | `http://localhost:4096` | OpenCode API 端点;server 在远程或非默认端口时设置 |
414
- | `OPENCODE_QUOTA_CONFIG_HOME` | `~/.config/opencode` | 全局配置目录覆盖 |
415
- | `OPENCODE_QUOTA_DATA_HOME` | `~/.local/share/opencode` | 全局数据目录覆盖 |
416
-
417
- ## 开发
418
-
419
- ```bash
420
- npm install
421
- npm run build
422
- npm test
423
- ```
424
-
425
- 如果改动涉及 TypeScript 类型、配置加载或公共行为,建议额外执行:
426
-
427
- ```bash
428
- npm run typecheck
429
- ```
430
-
431
- 本地调试时:
432
-
433
- - 在 `opencode.json` 里加载 `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/index.js`
434
- - 在 `tui.json` 里加载 `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/tui.tsx`
435
-
436
- Windows 下请使用正斜杠形式的 `file:///` 路径。
437
-
438
- ## 兼容性与注意事项
439
-
440
- - Node.js `>=18`
441
- - OpenCode plugin SDK `@opencode-ai/plugin` / `@opencode-ai/sdk` `^1.3.5`
442
- - OpenCode `>=1.2.15` 时,TUI 配置应放在 `tui.json`
443
- - 共享 title 仍然只有一个 `session.title`,所有客户端共用
444
- - 为了避免 resize 场景的渲染污染,sidebar title 默认避免使用 ANSI 样式码
445
- - 有些 provider 提供真正的 quota window,有些 provider 只提供 balance 数据
446
-
447
- ## 文档导航
448
-
449
- - English README:[`README.md`](./README.md)
450
- - 变更记录:[`CHANGELOG.md`](./CHANGELOG.md)
451
- - 贡献指南:[`CONTRIBUTING.md`](./CONTRIBUTING.md)
452
- - 安全策略:[`SECURITY.md`](./SECURITY.md)
453
-
454
- ## 贡献
455
-
456
- - 变更记录:[`CHANGELOG.md`](./CHANGELOG.md)
457
- - 适配器与架构说明:[`CONTRIBUTING.md`](./CONTRIBUTING.md)
458
- - 安全策略:[`SECURITY.md`](./SECURITY.md)
1
+ # opencode-quota-sidebar
2
+
3
+ [English](./README.md)
4
+
5
+ OpenCode 插件:在 TUI sidebar 中显示 token 用量和 provider quota,同时让共享的 session title 在 Desktop、Web 和 TUI 中都保持紧凑可读。
6
+
7
+ ![示例截图](./assets/OpenCode-Quota-Sidebar.png)
8
+
9
+ 上面的截图来自 [`./assets/OpenCode-Quota-Sidebar.png`](./assets/OpenCode-Quota-Sidebar.png),展示的是这个插件实际渲染出来的 TUI 侧边栏布局。
10
+
11
+ ## 核心能力
12
+
13
+ - 在 TUI sidebar 中渲染结构化的 `TITLE`、`USAGE`、`QUOTA` 三个区块
14
+ - 让共享 `session.title` 保持紧凑单行,而不是把多行遥测数据写进所有客户端都共用的标题
15
+ - 统计 `session`、`day`、`week`、`month` 四种范围的 usage
16
+ - 内置支持 OpenAI、Copilot、Anthropic、Kimi、Zhipu、MiniMax、RightCode 的 quota / balance 获取
17
+ - session 级统计可选聚合 descendant subagent sessions
18
+ - 提供 `quota_summary` 和 `quota_show` 两个工具
19
+
20
+ ## 架构概览
21
+
22
+ 这个仓库是纯插件实现,不修改 OpenCode 核心代码。
23
+
24
+ - Server 层:负责 usage 聚合、quota 拉取、状态持久化、title 刷新和工具注册
25
+ - TUI 层:负责侧边栏面板渲染,并读取持久化的 sidebar-panel 数据
26
+ - Persistence 层:负责全局状态和按日期分片的 session chunk 存储
27
+ - Provider Adapter 层:通过统一的 `QuotaSnapshot` 结构屏蔽不同 provider 的 quota/balance 接口差异
28
+
29
+ 依赖 `@opencode-ai/plugin` 和 `@opencode-ai/sdk`。
30
+
31
+ ## 工作方式
32
+
33
+ 这个插件有两条展示路径:
34
+
35
+ - TUI sidebar panel:渲染结构化的 `TITLE / USAGE / QUOTA`
36
+ - Shared session title:保持紧凑单行,供 Desktop、Web、TUI 共同使用
37
+
38
+ 在 `sidebar.titleMode="auto"` 下,共享 title 会保持 compact 单行。更丰富的多段布局由专门的 TUI 插件渲染,而不是直接写进 `session.title`。
39
+
40
+ 当 `sidebar.includeChildren=true` 时,`period=session` 的统计可以聚合子代 subagent sessions。`day/week/month` 范围统计不会合并 descendants。
41
+
42
+ ## 支持的 Provider
43
+
44
+ 内置 quota adapter 如下:
45
+
46
+ | Provider | Endpoint family | 鉴权方式 | 展示形态 | 说明 |
47
+ | ------------------- | ---------------------------------------------------------- | -------- | --------------- | --------------------------------------------------------------------------------------------------------------- |
48
+ | OpenAI Codex | `chatgpt.com/backend-api/wham/usage` | OAuth | 多窗口订阅额度 | 读取 ChatGPT usage 窗口,例如短窗口 + 周窗口;Pro 订阅可能额外提供 Codex Spark 限额(`additional_rate_limits`) |
49
+ | GitHub Copilot | `api.github.com/copilot_internal/user` | OAuth | 月度额度 | 使用 Copilot internal user 接口 |
50
+ | Anthropic | `api.anthropic.com/api/oauth/usage` | OAuth | 多窗口订阅额度 | 支持 plan-based usage windows |
51
+ | Kimi For Coding | `api.kimi.com/coding/v1/usages` | API key | 多窗口订阅额度 | 通常为 `5h` + weekly |
52
+ | Zhipu Coding Plan | `bigmodel.cn/api/monitor/usage/quota/limit` | API key | token quota | coding plan 风格额度 |
53
+ | MiniMax Coding Plan | `www.minimaxi.com/v1/api/openplatform/coding_plan/remains` | API key | 多窗口订阅额度 | 通常为 `5h` + weekly |
54
+ | RightCode | `www.right.codes/account/summary` | API key | 日额度和/或余额 | 按 prefix 匹配订阅,失败时回退为 balance |
55
+
56
+ 补充说明:
57
+
58
+ - 没有内置 quota adapter 的 generic provider 仍然可以参与 usage 聚合,但不会显示 quota/balance
59
+ - OpenAI、Copilot、Anthropic 的 quota 支持依赖 OAuth / session auth,而不是通用 API key 账单接口
60
+ - **OpenAI Codex Spark**:OpenAI Pro 订阅的 `wham/usage` 响应中可能包含 `additional_rate_limits` 字段,其中带有 `GPT-5.3-Codex-Spark` 等额外功能窗口。插件会自动解析并将其显示在 OpenAI quota 行下,无需额外配置。`code_review_rate_limit`(代码审查额度)目前暂不展示。
61
+ - RightCode 可能同时显示 daily allowance 和 balance 两行
62
+ - Copilot 支持 quota 展示,但当前不会显示 API-equivalent cost,因为 pricing metadata 不够稳定
63
+
64
+ ## 展示规则
65
+
66
+ - Sidebar quota 区块只展示当前 session 中实际使用过、且能被 adapter 识别的 provider
67
+ - `quota_summary` 会主动抓取默认 quota providers,即使当前 session 没用到
68
+ - TUI sidebar 优先读取持久化的 `sidebarPanel` / usage 数据,所以历史 session 打开时也能快速渲染
69
+ - compact title 中的 quota 解析只是兜底路径,TUI panel 优先消费结构化持久化数据
70
+ - `quota_show` 控制的是共享 title 的装饰开关,TUI panel 仍然是主要的富文本展示入口
71
+
72
+ title 相关补充:
73
+
74
+ - `sidebar.titleMode="auto"`:共享 title 保持 compact
75
+ - `sidebar.titleMode="compact"`:强制所有客户端都使用 compact title
76
+ - `sidebar.titleMode="multiline"`:使用旧的 multiline title 装饰路径
77
+ - 共享 title 本质上仍然只有一个 `session.title`,所以复杂布局更适合放在 TUI panel 中
78
+
79
+ ## Sidebar 示例
80
+
81
+ 典型的 TUI sidebar 布局(含 Codex Spark 窗口):
82
+
83
+ ```text
84
+ TITLE
85
+ Fix quota adapter matching
86
+ USAGE
87
+ R184 I189k O53.2k
88
+ CR31.4k CW3.2k Cd66%
89
+ API $12.8
90
+ QUOTA
91
+ OAI 5h80 R3h20m
92
+ W70 R2D04h
93
+ Sk5h100 R1h00m
94
+ SkW100 R3D04h
95
+ Cop M78 R12D00h
96
+ RC D$88.9/$60 E6D00h
97
+ B260
98
+ ```
99
+
100
+ compact shared title 示例:
101
+
102
+ ```text
103
+ Fix quota adapter matching | OAI 5h80 R3h20m W70 R2D04h | RC D$88.9/$60 B260 | Cd66% | API$12.8
104
+ ```
105
+
106
+ ## Pricing And API Cost
107
+
108
+ `API cost` 表示按 API 单价估算出来的 API 等价成本,不是订阅月费,也不是 provider 的真实账单。
109
+
110
+ 价格来源优先级:
111
+
112
+ 1. OpenCode 主配置(`opencode.jsonc` / `opencode.json`)里的显式模型价格
113
+ 2. 基于这些显式基础模型价格派生出的 fast/tier 价格
114
+ 3. `provider.list()` 返回的非零 runtime 价格
115
+ 4. 基于 runtime metadata 派生出的 fast/tier 价格
116
+ 5. `models.dev` 远端模型目录价格
117
+ 6. 插件内置 bundled fallback 价格表
118
+
119
+ 补充说明:
120
+
121
+ - OpenAI / Anthropic 这类 OAuth provider 的 runtime metadata 经常返回 `cost: 0`,所以 runtime 价格只能作为补充来源
122
+ - `provider.list()` 更像 OpenCode 当前运行时暴露出来的模型 metadata 视图,不是稳定权威的官方价格 API
123
+ - OpenCode 主配置里的价格层会按字段层叠合并,所以后面的 override 可以只改 `input` / `output`,不会把前层已有的 `cache_*` 或 `context_over_200k` 一起丢掉
124
+ - runtime 价格缺失时,插件会把 `models.dev` 作为结构化远端补充来源;这层只会补齐那些在 config / runtime / bundled 之后仍然缺价、且已经被 OpenCode runtime 暴露出来的模型,尤其是 bundled 还没覆盖的新模型
125
+ - 如果 `models.dev` 暂时不可达,插件会继续回退到前面的高优先级来源;对仍然没有价格的模型,`API cost` 可能继续显示为 `0`
126
+ - 主流模型通常已经被 bundled 表覆盖,因此 token usage 和 quota 功能本身不依赖用户手动配置价格
127
+ - 如果你使用的是插件还没内置的新模型,且 runtime 价格仍然是 `0`,那么 `API cost` 可能会显示 `$0.00`,直到你在 OpenCode 主配置里为该模型补充显式价格
128
+
129
+ 想让新模型也显示完整 `API cost`,至少需要满足以下一项:
130
+
131
+ 1. 该模型已经被插件 bundled pricing 覆盖
132
+ 2. 你在 OpenCode 主配置中为该模型配置了价格
133
+ 3. runtime `provider.list()` 返回了非零价格
134
+ 4. runtime `provider.list()` 已经暴露出这个模型,且 `models.dev` 有对应价格
135
+
136
+ ## Tool Report 示例
137
+
138
+ 历史 `quota_summary` markdown 大致会是这样的结构:
139
+
140
+ ```md
141
+ ## Quota History - Daily since 2026-02-18
142
+
143
+ ### Quota Status
144
+
145
+ - OpenAI: 5h | 80.0% | reset 3h20m; Weekly | 70.0% | reset 2D04h
146
+ - Copilot: Monthly | 78.0% | reset 12D00h
147
+ - RightCode: Daily $88.9/$60 | reset 6D00h
148
+
149
+ ### Totals
150
+
151
+ | Metric | Total | Avg/Period |
152
+ | ------------ | ----: | ---------: |
153
+ | Requests | 184 | 26.3 |
154
+ | Total Tokens | 277k | 39.6k |
155
+ | Cache Hit | 63.1% | 58.4% |
156
+ | API Cost | $12.8 | $1.83 |
157
+
158
+ ### Provider Breakdown
159
+
160
+ | Provider | Req | Input | Output | Total | Share | Cache Hit | API Cost |
161
+ | --------- | --: | ----: | -----: | ----: | ----: | --------: | -------: |
162
+ | OpenAI | 140 | 160k | 61k | 221k | 79.8% | 66.2% | $10.4 |
163
+ | Anthropic | 44 | 29k | 27.1k | 56.1k | 20.2% | 51.3% | $2.34 |
164
+
165
+ ### Period Detail
166
+
167
+ | Period | Requests | Input | Output | Cache | Cache Hit | Total | API Cost |
168
+ | ------------ | -------: | ----: | -----: | ----: | --------: | ----: | -------: |
169
+ | 2026-02-18 | 12 | 18.3k | 4.2k | 8.9k | 32.7% | 31.4k | $1.12 |
170
+ | 2026-02-24\* | 17 | 8.1k | 2.0k | 3.4k | 66.0% | 13.5k | $0.88 |
171
+ ```
172
+
173
+ 这个工具本身就返回完整 markdown,调用方应该直接展示,而不是再压缩成一句话摘要。
174
+
175
+ 补充说明:
176
+
177
+ - Sidebar 里用的是 compact token
178
+ - Toast 和 markdown report 使用更完整的人类可读文案
179
+ - `quota_summary` 可以展示默认 quota providers,即使当前 session 没有使用它们
180
+
181
+ ## 为什么需要 TUI Panel
182
+
183
+ OpenCode sidebar title 的渲染本质上是一个单一的文本字段。它适合放紧凑的 telemetry,但不适合承载结构化的多段布局。
184
+
185
+ 所以这个插件采用了两层设计:
186
+
187
+ - shared compact title:保证跨客户端兼容
188
+ - dedicated TUI sidebar panel:提供更清晰的区块化展示
189
+
190
+ 这样既不会污染 Desktop/Web 的标题,又能让 TUI 用户看到清晰的 quota dashboard。
191
+
192
+ ## 缩写说明
193
+
194
+ Usage token:
195
+
196
+ - `R`:requests
197
+ - `I`:input tokens
198
+ - `O`:output tokens,已包含 reasoning tokens
199
+ - `CR`:cache read tokens
200
+ - `CW`:cache write tokens
201
+ - `Cd`:cached ratio / cache coverage
202
+ - `API`:API-equivalent cost estimate
203
+
204
+ Quota token:
205
+
206
+ - `OAI`:OpenAI
207
+ - `Cop`:GitHub Copilot
208
+ - `Ant`:Anthropic
209
+ - `RC`:RightCode
210
+ - `B`:balance
211
+ - `D`:daily window
212
+ - `W`:weekly window
213
+ - `M`:monthly window
214
+ - `Sk5h`:OpenAI Codex Spark 短窗口(如 5h)
215
+ - `SkW`:OpenAI Codex Spark 周窗口
216
+ - `R3h20m`:还剩 `3h20m` 重置
217
+ - `R2D04h`:还剩 `2D04h` 重置
218
+ - `E6D00h`:还剩 `6D00h` 到期
219
+
220
+ compact quota 片段示例:
221
+
222
+ - `OAI 5h80 R3h20m`:OpenAI 短窗口剩余 80%,还剩 `3h20m` 重置
223
+ - `OAI Sk5h100 R1h00m`:OpenAI Codex Spark 5h 窗口剩余 100%,还剩 `1h00m` 重置
224
+ - `OAI SkW100 R3D04h`:OpenAI Codex Spark 周窗口剩余 100%,还剩 `3D04h` 重置
225
+ - `Cop M78 R12D00h`:Copilot 月额度剩余 78%,还剩 `12D00h` 重置
226
+ - `RC D$88.9/$60 E6D00h B260`:RightCode 日额度 + 余额
227
+
228
+ ## 安装
229
+
230
+ OpenCode 会分别从 `opencode.json` 和 `tui.json` 加载 server / TUI 插件。
231
+
232
+ `opencode.json`
233
+
234
+ ```json
235
+ {
236
+ "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
237
+ }
238
+ ```
239
+
240
+ `tui.json`
241
+
242
+ ```json
243
+ {
244
+ "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
245
+ }
246
+ ```
247
+
248
+ OpenCode `>=1.2.15` 时,server 插件放在 `opencode.json`,TUI 插件放在 `tui.json`。
249
+
250
+ ## 配置
251
+
252
+ 常见配置路径:
253
+
254
+ - `~/.config/opencode/quota-sidebar.config.json`
255
+ - `<worktree>/quota-sidebar.config.json`
256
+ - `<directory>/quota-sidebar.config.json`
257
+ - `<worktree>/.opencode/quota-sidebar.config.json`
258
+ - `<directory>/.opencode/quota-sidebar.config.json`
259
+ - `OPENCODE_QUOTA_CONFIG=/absolute/path/to/config.json`
260
+
261
+ 最小示例:
262
+
263
+ ```json
264
+ {
265
+ "sidebar": {
266
+ "enabled": true,
267
+ "titleMode": "auto",
268
+ "showCost": true,
269
+ "showQuota": true,
270
+ "includeChildren": true
271
+ }
272
+ }
273
+ ```
274
+
275
+ 完整配置示例见 [`quota-sidebar.config.example.json`](./quota-sidebar.config.example.json)。
276
+
277
+ 重要配置项:
278
+
279
+ - `sidebar.titleMode`:`auto`、`compact`、`multiline`
280
+ - `sidebar.showCost`:控制 sidebar、title、markdown report、toast 和 CLI 中的 API-equivalent cost 可见性
281
+ - `sidebar.wrapQuotaLines`:长 quota 行是否自动续行并缩进
282
+ - `sidebar.includeChildren`:`period=session` 时是否聚合 descendant subagent sessions
283
+ 配置采用分层覆盖,后层配置覆盖前层配置:
284
+
285
+ 1. 全局配置
286
+ 2. worktree 配置
287
+ 3. directory 配置
288
+ 4. worktree `.opencode` 配置
289
+ 5. directory `.opencode` 配置
290
+ 6. `OPENCODE_QUOTA_CONFIG`
291
+
292
+ 插件还会读取 OpenCode 主配置中的价格,候选路径如下:
293
+
294
+ 1. `~/.config/opencode/opencode.jsonc`
295
+ 2. `~/.config/opencode/opencode.json`
296
+ 3. `<worktree>/opencode.jsonc`
297
+ 4. `<worktree>/opencode.json`
298
+ 5. `<directory>/opencode.jsonc`
299
+ 6. `<directory>/opencode.json`
300
+ 7. `<worktree>/.opencode/opencode.jsonc`
301
+ 8. `<worktree>/.opencode/opencode.json`
302
+ 9. `<directory>/.opencode/opencode.jsonc`
303
+ 10. `<directory>/.opencode/opencode.json`
304
+
305
+ ## Linux CLI Notes
306
+
307
+ - 当本地没有可用 OpenCode server 时,`opencode-quota` 会自动临时拉起一个 `opencode serve`
308
+ - 新版本会在 Linux / Unix 下更主动地清理这个临时 server,让 CLI 在输出报告后自然退出
309
+ - 如果 Linux 下仍然出现挂住,建议升级到包含该修复的版本
310
+
311
+ ## 持久化与聚合
312
+
313
+ 插件会把数据写入:
314
+
315
+ - 全局状态:`<opencode-data>/quota-sidebar.state.json`
316
+ - session 分片:`<opencode-data>/quota-sidebar-sessions/YYYY/MM/DD.json`
317
+
318
+ 这些持久化 chunk 会保存 title 状态、cached usage、sidebar-panel payload 和 quota cache,从而让 TUI sidebar 在 session open/resume 时可以直接从结构化状态恢复,而不必完全依赖实时 message 扫描。
319
+
320
+ usage 聚合采用增量模式。插件会为每个 session 维护 cursor,优先只处理新消息;如果消息历史变化导致增量视图失效,再回退到重扫并刷新持久化 usage。
321
+
322
+ 价格缓存提醒:
323
+
324
+ - 新版本会在 session usage cache 中持久化 `pricingFingerprint`
325
+ - 当模型价格从一个非零值改成另一个非零值时,旧的 API cost cache 会自动失效并重算
326
+ - 更老版本只依赖 billing version 判断缓存新鲜度,因此历史 session 的 API cost 可能需要强制重算才能更新
327
+ - 历史上已经写入的 `Est$...` decorated title 仍会在清理/还原路径里被识别,但新版本生成的 title 统一使用 `API$...`
328
+
329
+ ## 工具
330
+
331
+ - `quota_summary`:查看 `session`、`day`、`week`、`month` 的用量和 quota
332
+ - `quota_show`:切换 title 装饰开关
333
+
334
+ 行为说明:
335
+
336
+ - `quota_summary` 返回完整 markdown report
337
+ - `quota_summary` 可以同时触发 toast 展示
338
+ - `quota_summary` 参数为 `period`、`since`、`last`、`toast`、`includeChildren`
339
+ - `quota_summary(includeChildren=true)` 只影响 `period=session`
340
+ - `day/week/month` 会扫描所选时间范围内的全部 sessions,所以 child sessions 只要在该范围内有 activity 就会被统计进去
341
+ - `day/week/month` 不做 parent 树 rollup;child sessions 会作为独立 sessions 计入,而不是通过 `includeChildren` 合并到 parent
342
+ - `since` 和 `last` 互斥
343
+ - `period=session` 不支持 `since` / `last`
344
+ - `quota_show(enabled=true|false)` 可以显式指定目标状态,而不是只做 toggle
345
+ - 历史报告同时支持绝对时间 `since` 和相对区间 `last`
346
+ - `since` 支持 `YYYY-MM` 或 `YYYY-MM-DD`
347
+ - `last` 需要正整数,并按当前 period 相对计算:`day=7`、`week=8`、`month=6`
348
+ - 空参数的 `period=day|week|month` 表示当前自然日 / 自然周 / 自然月
349
+
350
+ 命令别名示例:
351
+
352
+ 如果你想直接走工具输出查看历史,可以在配置里声明调用 `quota_summary` 的命令别名。
353
+ 旧的 TUI 历史弹窗路径已经移除,历史查看统一回到 tool report。
354
+ 这些别名本质上仍然是 OpenCode command template,因此会先展开为提示词,再进入模型 / tool 链路。若想要更直接、更干净的输出路径,优先使用独立 CLI。
355
+
356
+ 示例:
357
+
358
+ - `quota_summary(period=day)` -> 今天
359
+ - `quota_summary(period=week)` -> 本周
360
+ - `quota_summary(period=month)` -> 本月
361
+ - `quota_summary(period=day,last=7)` -> 最近 7 天
362
+ - `quota_summary(period=week,last=8)` -> 最近 8
363
+ - `quota_summary(period=month,last=6)` -> 最近 6 个月
364
+ - `quota_summary(period=month,since=2026-01)` -> 从 2026-01 开始
365
+
366
+ ```json
367
+ {
368
+ "command": {
369
+ "qday": {
370
+ "description": "Today / last N days / since date",
371
+ "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."
372
+ },
373
+ "qweek": {
374
+ "description": "This week / last N weeks / since date",
375
+ "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."
376
+ },
377
+ "qmonth": {
378
+ "description": "This month / last N months / since month",
379
+ "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."
380
+ },
381
+ "qtoggle": {
382
+ "description": "Toggle sidebar usage display on/off",
383
+ "template": "Call tool quota_show (no arguments, it toggles)."
384
+ }
385
+ }
386
+ }
387
+ ```
388
+
389
+ ## CLI
390
+
391
+ 这个包同时提供一个独立 CLI dashboard。全局安装或让 `bin` 可执行后:
392
+
393
+ ```bash
394
+ npm install -g @leo000001/opencode-quota-sidebar
395
+ ```
396
+
397
+ 如果不想全局安装,也可以直接用 `npx @leo000001/opencode-quota-sidebar <args>` 运行。
398
+
399
+ ```bash
400
+ # 当前周期(单次快照)
401
+ opencode-quota day # 今天
402
+ opencode-quota week # 本周(周一起算)
403
+ opencode-quota month # 本月
404
+
405
+ # 多周期历史
406
+ opencode-quota day 7 # 最近 7
407
+ opencode-quota week 8 # 最近 8 周
408
+ opencode-quota month 6 # 最近 6 个月
409
+
410
+ # 绝对起始日期
411
+ opencode-quota day --since 2026-04-01
412
+ opencode-quota week --since 2026-04-01
413
+ opencode-quota month --since 2026-01
414
+
415
+ # 位置参数也接受日期字符串(等同于 --since)
416
+ opencode-quota day 2026-04-01
417
+ opencode-quota month 2026-01
418
+ ```
419
+
420
+ 不带 `last` 或 `--since` 时,CLI 渲染单周期快照(`QUOTA + TOTALS + PROVIDERS`)。带 `last` 或 `--since` 时渲染多周期历史,并额外展示更大的多行 `TREND` 区块。
421
+
422
+ ### CLI 语义
423
+
424
+ - `day` = 当前自然日;`week` = 当前自然周(周一起算);`month` = 当前自然月
425
+ - 位置参数正整数映射为 `last=<N>`(从当前往回数 N 个周期)
426
+ - 位置参数日期字符串映射为 `--since`(day/week 用 `YYYY-MM-DD`,month 用 `YYYY-MM`)
427
+ - `--since` 和 `--last` 互斥
428
+ - `last` 对 day 限制 90,week/month 也有合理范围限制
429
+
430
+ ### Trend 区块
431
+
432
+ `TREND` 区块仅在多周期模式下出现。每个指标(`Requests`、`Tokens`、`Cache`、`Cost`)都会渲染为一个小型多行柱状图:
433
+
434
+ - 一行摘要:当前值
435
+ - 每个可见周期一行柱状条(最多展示最近 8 个周期)
436
+ - 当前周期用 `*` 标记
437
+
438
+ 解读示例:
439
+
440
+ ```text
441
+ Requests 12.3k
442
+ 04-08 | ███░░░░░░░░░░░░░░░ | 4.1k
443
+ 04-09 | ██████░░░░░░░░░░░░ | 8.2k
444
+ 04-10* | █████████████░░░░░ | 12.3k
445
+ ```
446
+
447
+ 这表示当前 bucket 有 `12.3k` 请求,下面的柱状条则按时间顺序展示各个可见 bucket 的相对大小。
448
+
449
+ ### 连接行为
450
+
451
+ - CLI 默认连接本地 OpenCode API `http://localhost:4096`
452
+ - 通过 `OPENCODE_BASE_URL` 覆盖(例如 `http://192.168.1.10:4096`)
453
+ - 如果没有运行中的 server 且未设置 `OPENCODE_BASE_URL`,CLI 会尝试自动启动:
454
+ - **Linux/macOS**:运行 `opencode serve --hostname=127.0.0.1 --port=4096`
455
+ - **Windows**:依次尝试 `opencode.cmd`、`opencode`(通过 `shell: true`)、`bash -lc opencode`
456
+ - 自动启动等待最多 10 秒,直到 server 输出 `opencode server listening on <url>`
457
+ - 如果自动启动失败,请确认 `opencode` 在你的 `PATH` 中
458
+ - 在 Windows 上,如果 `opencode.cmd` 不能被 Node 直接 spawn,`shell: true` 通常是更可靠的兜底路径
459
+
460
+ ### 平台说明
461
+
462
+ - **终端编码**:dashboard 使用 Unicode 绘框字符和方块元素(`█░`),需要支持 UTF-8 的终端。Windows 用户应使用 Windows Terminal、PowerShell 7+ 或其他支持 UTF-8 的终端。经典 cmd.exe 使用传统代码页(CP437/CP850)时可能显示乱码。
463
+ - **对齐**:当 week/month 的可见标签非常长时(例如完整绝对日期范围),CLI 的 trend 标签仍可能被截断。这是当前终端 renderer 的已知展示取舍。
464
+ - **Windows PATH**:CLI 会尝试多种命令形式来找到 `opencode`。如果全部失败,请确认 `opencode` 或 `opencode.cmd` 在 PATH 中,或手动启动 server 后设置 `OPENCODE_BASE_URL`。
465
+ - **Node.js**:需要 `>=18`
466
+
467
+ ### 环境变量
468
+
469
+ | 变量 | 默认值 | 用途 |
470
+ | ---------------------------- | ------------------------- | -------------------------------------------------- |
471
+ | `OPENCODE_BASE_URL` | `http://localhost:4096` | OpenCode API 端点;server 在远程或非默认端口时设置 |
472
+ | `OPENCODE_QUOTA_CONFIG_HOME` | `~/.config/opencode` | 全局配置目录覆盖 |
473
+ | `OPENCODE_QUOTA_DATA_HOME` | `~/.local/share/opencode` | 全局数据目录覆盖 |
474
+
475
+ ## 开发
476
+
477
+ ```bash
478
+ npm install
479
+ npm run build
480
+ npm test
481
+ ```
482
+
483
+ 如果改动涉及 TypeScript 类型、配置加载或公共行为,建议额外执行:
484
+
485
+ ```bash
486
+ npm run typecheck
487
+ ```
488
+
489
+ 本地调试时:
490
+
491
+ - 在 `opencode.json` 里加载 `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/index.js`
492
+ - 在 `tui.json` 里加载 `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/tui.tsx`
493
+
494
+ Windows 下请使用正斜杠形式的 `file:///` 路径。
495
+
496
+ ## 兼容性与注意事项
497
+
498
+ - Node.js `>=18`
499
+ - OpenCode plugin SDK `@opencode-ai/plugin` / `@opencode-ai/sdk` `^1.3.5`
500
+ - OpenCode `>=1.2.15` 时,TUI 配置应放在 `tui.json`
501
+ - 共享 title 仍然只有一个 `session.title`,所有客户端共用
502
+ - 为了避免 resize 场景的渲染污染,sidebar title 默认避免使用 ANSI 样式码
503
+ - 有些 provider 提供真正的 quota window,有些 provider 只提供 balance 数据
504
+
505
+ ## 文档导航
506
+
507
+ - English README:[`README.md`](./README.md)
508
+ - 变更记录:[`CHANGELOG.md`](./CHANGELOG.md)
509
+ - 贡献指南:[`CONTRIBUTING.md`](./CONTRIBUTING.md)
510
+ - 安全策略:[`SECURITY.md`](./SECURITY.md)
511
+
512
+ ## 贡献
513
+
514
+ - 变更记录:[`CHANGELOG.md`](./CHANGELOG.md)
515
+ - 适配器与架构说明:[`CONTRIBUTING.md`](./CONTRIBUTING.md)
516
+ - 安全策略:[`SECURITY.md`](./SECURITY.md)