@leo000001/opencode-quota-sidebar 3.0.1 → 3.0.2

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/CONTRIBUTING.md +4 -1
  3. package/README.md +210 -514
  4. package/README.zh-CN.md +337 -0
  5. package/SECURITY.md +2 -2
  6. package/assets/OpenCode-Quota-Sidebar.png +0 -0
  7. package/dist/cost.d.ts +3 -3
  8. package/dist/cost.js +258 -169
  9. package/dist/format.js +10 -3
  10. package/dist/providers/common.d.ts +6 -0
  11. package/dist/providers/common.js +32 -12
  12. package/dist/providers/core/anthropic.d.ts +1 -1
  13. package/dist/providers/core/anthropic.js +43 -39
  14. package/dist/providers/core/kimi_for_coding.d.ts +1 -1
  15. package/dist/providers/core/kimi_for_coding.js +44 -64
  16. package/dist/providers/core/minimax_cn_coding_plan.d.ts +2 -0
  17. package/dist/providers/core/minimax_cn_coding_plan.js +214 -0
  18. package/dist/providers/core/zhipu_coding_plan.d.ts +1 -1
  19. package/dist/providers/core/zhipu_coding_plan.js +41 -61
  20. package/dist/providers/index.d.ts +3 -3
  21. package/dist/providers/index.js +5 -5
  22. package/dist/providers/third_party/rightcode.d.ts +1 -1
  23. package/dist/providers/third_party/rightcode.js +41 -61
  24. package/dist/providers/third_party/xyai.d.ts +2 -0
  25. package/dist/providers/third_party/{xyai_vibe.js → xyai.js} +113 -79
  26. package/dist/quota.d.ts +2 -2
  27. package/dist/quota.js +24 -18
  28. package/dist/quota_render.d.ts +1 -1
  29. package/dist/quota_render.js +23 -17
  30. package/dist/storage_parse.js +1 -0
  31. package/dist/title.js +7 -7
  32. package/dist/title_apply.js +18 -1
  33. package/dist/tui.tsx +2 -1
  34. package/dist/tui_helpers.d.ts +2 -1
  35. package/dist/tui_helpers.js +6 -1
  36. package/dist/types.d.ts +2 -0
  37. package/package.json +9 -2
  38. package/quota-sidebar.config.example.json +45 -45
  39. package/dist/providers/third_party/buzz.d.ts +0 -2
  40. package/dist/providers/third_party/buzz.js +0 -156
  41. package/dist/providers/third_party/xyai_vibe.d.ts +0 -2
@@ -0,0 +1,337 @@
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、XYAI 的 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 窗口,例如短窗口 + 周窗口 |
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
+ | XYAI | `new.xychatai.com/frontend-api/*` | 登录态 session auth | 日额度 / 日余额 | 默认关闭,需要在 `quota.providers.xyai` 中显式开启 |
56
+
57
+ 补充说明:
58
+
59
+ - 没有内置 quota adapter 的 generic provider 仍然可以参与 usage 聚合,但不会显示 quota/balance
60
+ - OpenAI、Copilot、Anthropic 的 quota 支持依赖 OAuth / session auth,而不是通用 API key 账单接口
61
+ - RightCode 可能同时显示 daily allowance 和 balance 两行
62
+ - XYAI 需在配置中提供登录信息,插件会自行获取并缓存 session auth
63
+ - Copilot 支持 quota 展示,但当前不会显示 API-equivalent cost,因为 pricing metadata 不够稳定
64
+
65
+ ## 展示规则
66
+
67
+ - Sidebar quota 区块只展示当前 session 中实际使用过、且能被 adapter 识别的 provider
68
+ - `quota_summary` 会主动抓取默认 quota providers,即使当前 session 没用到
69
+ - TUI sidebar 优先读取持久化的 `sidebarPanel` / usage 数据,所以历史 session 打开时也能快速渲染
70
+ - compact title 中的 quota 解析只是兜底路径,TUI panel 优先消费结构化持久化数据
71
+ - `quota_show` 控制的是共享 title 的装饰开关,TUI panel 仍然是主要的富文本展示入口
72
+
73
+ title 相关补充:
74
+
75
+ - `sidebar.titleMode="auto"`:共享 title 保持 compact
76
+ - `sidebar.titleMode="compact"`:强制所有客户端都使用 compact title
77
+ - `sidebar.titleMode="multiline"`:使用旧的 multiline title 装饰路径
78
+ - 共享 title 本质上仍然只有一个 `session.title`,所以复杂布局更适合放在 TUI panel 中
79
+
80
+ ## Sidebar 示例
81
+
82
+ 典型的 TUI sidebar 布局:
83
+
84
+ ```text
85
+ TITLE
86
+ Fix quota adapter matching
87
+ USAGE
88
+ R184 I189k O53.2k
89
+ CR31.4k CW3.2k Cd66%
90
+ Est $12.8
91
+ QUOTA
92
+ OAI 5h80 R16:20
93
+ W70 R04-03
94
+ Cop M78 R04-01
95
+ RC D$88.9/$60 E02-27
96
+ B260
97
+ ```
98
+
99
+ compact shared title 示例:
100
+
101
+ ```text
102
+ Fix quota adapter matching | OAI 5h80 R16:20 W70 R04-03 | RC D$88.9/$60 B260 | Cd66% | Est$12.8
103
+ ```
104
+
105
+ 另一个多 provider 示例:
106
+
107
+ ```text
108
+ Add XYAI quota adapter | Ant 5h100 W77 O7d60 | Cop M78 R04-01 | Cd52% | Est$2.34
109
+ ```
110
+
111
+ ## Tool Report 示例
112
+
113
+ `quota_summary` 返回的 markdown 大致会是这样的结构:
114
+
115
+ ```md
116
+ ## Session Usage
117
+
118
+ - Requests: 184
119
+ - Input: 189k
120
+ - Output: 53.2k
121
+ - Cache Read: 31.4k
122
+ - Cache Write: 3.2k
123
+ - Cost as API: $12.8
124
+
125
+ ## Quota
126
+
127
+ - OpenAI: 5h 80% (reset 16:20), Weekly 70% (reset 04-03)
128
+ - Copilot: Monthly 78% (reset 04-01)
129
+ - RightCode: Daily $88.9/$60 (exp 02-27), Balance $260
130
+ ```
131
+
132
+ 这个工具本身就返回完整 markdown,调用方应该直接展示,而不是再压缩成一句话摘要。
133
+
134
+ 补充说明:
135
+
136
+ - Sidebar 里用的是 compact token
137
+ - Toast 和 markdown report 使用更完整的人类可读文案
138
+ - `quota_summary` 可以展示默认 quota providers,即使当前 session 没有使用它们
139
+
140
+ ## 为什么需要 TUI Panel
141
+
142
+ OpenCode 对 sidebar title 的渲染本质上是一个单一的文本字段。它适合放紧凑的 telemetry,但不适合承载结构化的多段布局。
143
+
144
+ 所以这个插件采用了两层设计:
145
+
146
+ - shared compact title:保证跨客户端兼容
147
+ - dedicated TUI sidebar panel:提供更清晰的区块化展示
148
+
149
+ 这样既不会污染 Desktop/Web 的标题,又能让 TUI 用户看到清晰的 quota dashboard。
150
+
151
+ ## 缩写说明
152
+
153
+ Usage token:
154
+
155
+ - `R`:requests
156
+ - `I`:input tokens
157
+ - `O`:output tokens,已包含 reasoning tokens
158
+ - `CR`:cache read tokens
159
+ - `CW`:cache write tokens
160
+ - `Cd`:cached ratio / cache coverage
161
+ - `Est`:API-equivalent cost estimate
162
+
163
+ Quota token:
164
+
165
+ - `OAI`:OpenAI
166
+ - `Cop`:GitHub Copilot
167
+ - `Ant`:Anthropic
168
+ - `RC`:RightCode
169
+ - `B`:balance
170
+ - `D`:daily window
171
+ - `W`:weekly window
172
+ - `M`:monthly window
173
+ - `R16:20`:重置时间为 `16:20`
174
+ - `R04-03`:重置日期为 `04-03`
175
+ - `E02-27`:到期日期为 `02-27`
176
+
177
+ compact quota 片段示例:
178
+
179
+ - `OAI 5h80 R16:20`:OpenAI 短窗口剩余 80%,`16:20` 重置
180
+ - `Cop M78 R04-01`:Copilot 月额度剩余 78%,`04-01` 重置
181
+ - `RC D$88.9/$60 E02-27 B260`:RightCode 日额度 + 余额
182
+
183
+ ## 安装
184
+
185
+ OpenCode 会分别从 `opencode.json` 和 `tui.json` 加载 server / TUI 插件。
186
+
187
+ `opencode.json`
188
+
189
+ ```json
190
+ {
191
+ "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
192
+ }
193
+ ```
194
+
195
+ `tui.json`
196
+
197
+ ```json
198
+ {
199
+ "plugin": ["@leo000001/opencode-quota-sidebar@latest"]
200
+ }
201
+ ```
202
+
203
+ OpenCode `>=1.2.15` 时,server 插件放在 `opencode.json`,TUI 插件放在 `tui.json`。
204
+
205
+ ## 配置
206
+
207
+ 常见配置路径:
208
+
209
+ - `~/.config/opencode/quota-sidebar.config.json`
210
+ - `<worktree>/quota-sidebar.config.json`
211
+ - `<worktree>/.opencode/quota-sidebar.config.json`
212
+ - `OPENCODE_QUOTA_CONFIG=/absolute/path/to/config.json`
213
+
214
+ 最小示例:
215
+
216
+ ```json
217
+ {
218
+ "sidebar": {
219
+ "enabled": true,
220
+ "titleMode": "auto",
221
+ "showCost": true,
222
+ "showQuota": true,
223
+ "includeChildren": true
224
+ }
225
+ }
226
+ ```
227
+
228
+ 完整配置示例见 [`quota-sidebar.config.example.json`](./quota-sidebar.config.example.json)。
229
+
230
+ 重要配置项:
231
+
232
+ - `sidebar.titleMode`:`auto`、`compact`、`multiline`
233
+ - `sidebar.showCost`:控制 sidebar、title、markdown report、toast 中的 API-equivalent cost 可见性
234
+ - `sidebar.wrapQuotaLines`:长 quota 行是否自动续行并缩进
235
+ - `sidebar.includeChildren`:`period=session` 时是否聚合 descendant subagent sessions
236
+ - `quota.providers.xyai.enabled`:是否启用 XYAI quota
237
+ - `quota.providers.xyai.login.username/password`:XYAI 登录信息,用于换取和刷新 session auth
238
+
239
+ 配置采用分层覆盖,后层配置覆盖前层配置:
240
+
241
+ 1. 全局配置
242
+ 2. worktree 配置
243
+ 3. directory 配置
244
+ 4. worktree `.opencode` 配置
245
+ 5. directory `.opencode` 配置
246
+ 6. `OPENCODE_QUOTA_CONFIG`
247
+
248
+ ## 持久化与聚合
249
+
250
+ 插件会把数据写入:
251
+
252
+ - 全局状态:`<opencode-data>/quota-sidebar.state.json`
253
+ - session 分片:`<opencode-data>/quota-sidebar-sessions/YYYY/MM/DD.json`
254
+
255
+ 这些持久化 chunk 会保存 title 状态、cached usage、sidebar-panel payload 和 quota cache,从而让 TUI sidebar 在 session open/resume 时可以直接从结构化状态恢复,而不必完全依赖实时 message 扫描。
256
+
257
+ usage 聚合采用增量模式。插件会为每个 session 维护 cursor,优先只处理新消息;如果消息历史变化导致增量视图失效,再回退到重扫并刷新持久化 usage。
258
+
259
+ ## 工具
260
+
261
+ - `quota_summary`:查看 `session`、`day`、`week`、`month` 的用量和 quota
262
+ - `quota_show`:切换 title 装饰开关
263
+
264
+ 行为说明:
265
+
266
+ - `quota_summary` 返回完整 markdown report
267
+ - `quota_summary` 可以同时触发 toast 展示
268
+ - `quota_summary(includeChildren=true)` 只影响 `period=session`
269
+ - `quota_show(enabled=true|false)` 可以显式指定目标状态,而不是只做 toggle
270
+
271
+ 命令别名示例:
272
+
273
+ ```json
274
+ {
275
+ "command": {
276
+ "qday": {
277
+ "description": "Show today's usage and quota",
278
+ "template": "Call tool quota_summary with period=day and toast=true."
279
+ },
280
+ "qweek": {
281
+ "description": "Show this week's usage and quota",
282
+ "template": "Call tool quota_summary with period=week and toast=true."
283
+ },
284
+ "qmonth": {
285
+ "description": "Show this month's usage and quota",
286
+ "template": "Call tool quota_summary with period=month and toast=true."
287
+ },
288
+ "qtoggle": {
289
+ "description": "Toggle sidebar usage display on/off",
290
+ "template": "Call tool quota_show (no arguments, it toggles)."
291
+ }
292
+ }
293
+ }
294
+ ```
295
+
296
+ ## 开发
297
+
298
+ ```bash
299
+ npm install
300
+ npm run build
301
+ npm test
302
+ ```
303
+
304
+ 如果改动涉及 TypeScript 类型、配置加载或公共行为,建议额外执行:
305
+
306
+ ```bash
307
+ npm run typecheck
308
+ ```
309
+
310
+ 本地调试时:
311
+
312
+ - 在 `opencode.json` 里加载 `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/index.js`
313
+ - 在 `tui.json` 里加载 `file:///ABSOLUTE/PATH/opencode-quota-sidebar/dist/tui.tsx`
314
+
315
+ Windows 下请使用正斜杠形式的 `file:///` 路径。
316
+
317
+ ## 兼容性与注意事项
318
+
319
+ - Node.js `>=18`
320
+ - OpenCode plugin SDK `@opencode-ai/plugin` / `@opencode-ai/sdk` `^1.3.5`
321
+ - OpenCode `>=1.2.15` 时,TUI 配置应放在 `tui.json`
322
+ - 共享 title 仍然只有一个 `session.title`,所有客户端共用
323
+ - 为了避免 resize 场景的渲染污染,sidebar title 默认避免使用 ANSI 样式码
324
+ - 有些 provider 提供真正的 quota window,有些 provider 只提供 balance 数据
325
+
326
+ ## 文档导航
327
+
328
+ - English README:[`README.md`](./README.md)
329
+ - 变更记录:[`CHANGELOG.md`](./CHANGELOG.md)
330
+ - 贡献指南:[`CONTRIBUTING.md`](./CONTRIBUTING.md)
331
+ - 安全策略:[`SECURITY.md`](./SECURITY.md)
332
+
333
+ ## 贡献
334
+
335
+ - 变更记录:[`CHANGELOG.md`](./CHANGELOG.md)
336
+ - 适配器与架构说明:[`CONTRIBUTING.md`](./CONTRIBUTING.md)
337
+ - 安全策略:[`SECURITY.md`](./SECURITY.md)
package/SECURITY.md CHANGED
@@ -25,7 +25,7 @@ We will acknowledge reports as quickly as possible and provide a remediation tim
25
25
  - Keep debug logs free of secrets.
26
26
  - Prefer fail-closed behavior for writes (already enforced via symlink checks and atomic writes).
27
27
  - Quota fetching may contact provider-operated endpoints such as OpenAI, GitHub,
28
- Kimi, RightCode, Buzz, and Anthropic; review any new provider integration for
29
- outbound data exposure and header/token handling.
28
+ Anthropic, Kimi, Zhipu, MiniMax, RightCode, and XYAI; review any new
29
+ provider integration for outbound data exposure and header/token handling.
30
30
  - Some quota integrations rely on beta or internal-style endpoints; document
31
31
  instability risks clearly and avoid assuming long-term API compatibility.
package/dist/cost.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { AssistantMessage } from '@opencode-ai/sdk';
2
- import type { CacheCoverageMode } from './types.js';
1
+ import type { AssistantMessage } from "@opencode-ai/sdk";
2
+ import type { CacheCoverageMode } from "./types.js";
3
3
  export declare const API_COST_ENABLED_PROVIDERS: Set<string>;
4
- export type CanonicalPriceSource = 'official-doc' | 'runtime';
4
+ export type CanonicalPriceSource = "official-doc" | "runtime";
5
5
  export declare function canonicalPricingProviderID(providerID: string): string;
6
6
  export declare function canonicalApiCostProviderID(providerID: string): string;
7
7
  export type ModelCostRates = {