@slkiser/opencode-quota 2.7.1 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +192 -213
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/config.d.ts.map +1 -1
  6. package/dist/lib/config.js +14 -0
  7. package/dist/lib/config.js.map +1 -1
  8. package/dist/lib/cursor-detection.d.ts.map +1 -1
  9. package/dist/lib/cursor-detection.js +49 -5
  10. package/dist/lib/cursor-detection.js.map +1 -1
  11. package/dist/lib/cursor-pricing.d.ts +1 -0
  12. package/dist/lib/cursor-pricing.d.ts.map +1 -1
  13. package/dist/lib/cursor-pricing.js +5 -3
  14. package/dist/lib/cursor-pricing.js.map +1 -1
  15. package/dist/lib/entries.d.ts +1 -0
  16. package/dist/lib/entries.d.ts.map +1 -1
  17. package/dist/lib/jsonc.d.ts +8 -1
  18. package/dist/lib/jsonc.d.ts.map +1 -1
  19. package/dist/lib/jsonc.js +12 -2
  20. package/dist/lib/jsonc.js.map +1 -1
  21. package/dist/lib/modelsdev-pricing.d.ts +8 -2
  22. package/dist/lib/modelsdev-pricing.d.ts.map +1 -1
  23. package/dist/lib/modelsdev-pricing.js +84 -29
  24. package/dist/lib/modelsdev-pricing.js.map +1 -1
  25. package/dist/lib/quota-stats-format.d.ts.map +1 -1
  26. package/dist/lib/quota-stats-format.js +30 -38
  27. package/dist/lib/quota-stats-format.js.map +1 -1
  28. package/dist/lib/quota-status.d.ts +2 -1
  29. package/dist/lib/quota-status.d.ts.map +1 -1
  30. package/dist/lib/quota-status.js +10 -1
  31. package/dist/lib/quota-status.js.map +1 -1
  32. package/dist/lib/types.d.ts +14 -0
  33. package/dist/lib/types.d.ts.map +1 -1
  34. package/dist/lib/types.js +4 -0
  35. package/dist/lib/types.js.map +1 -1
  36. package/dist/plugin.d.ts.map +1 -1
  37. package/dist/plugin.js +300 -115
  38. package/dist/plugin.js.map +1 -1
  39. package/dist/providers/cursor.d.ts.map +1 -1
  40. package/dist/providers/cursor.js +3 -1
  41. package/dist/providers/cursor.js.map +1 -1
  42. package/package.json +4 -1
package/README.md CHANGED
@@ -1,24 +1,34 @@
1
- # Opencode Quota
1
+ # OpenCode Quota
2
2
 
3
3
  `opencode-quota` gives you two things:
4
4
 
5
5
  - Automatic quota toasts after assistant responses
6
- - Manual `/quota` and `/tokens_*` commands for deeper local reporting with zero context window pollution
6
+ - Manual `/quota`, `/pricing_refresh`, and `/tokens_*` commands for deeper local reporting with zero context window pollution
7
+
8
+ **Quota providers**: GitHub Copilot, OpenAI (Plus/Pro), Cursor, Qwen Code, Alibaba Coding Plan, Chutes AI, Firmware AI, Google Antigravity, and Z.ai coding plan.
9
+
10
+ **Token reports**: All models and providers in [models.dev](https://models.dev), plus deterministic local pricing for Cursor Auto/Composer and Cursor model aliases that are not on models.dev.
11
+
12
+ <table>
13
+ <tr>
14
+ <td width="50%" align="center">Example of toast</td>
15
+ <td width="50%" align="center">Example of <code>/tokens_weekly</code></td>
16
+ </tr>
17
+ <tr>
18
+ <td width="50%">
19
+ <img src="https://github.com/slkiser/opencode-quota/blob/main/toasts.png" alt="Image of opencode-quota toast" />
20
+ </td>
21
+ <td width="50%">
22
+ <img src="https://github.com/slkiser/opencode-quota/blob/main/tokens.png" alt="Image of opencode-quota /tokens_weekly output" />
23
+ </td>
24
+ </tr>
25
+ </table>
7
26
 
8
- **Quota provider supports**: GitHub Copilot, OpenAI (Plus/Pro), Cursor (ACP), Qwen Code, Alibaba Coding Plan, Chutes AI, Firmware AI, Google Antigravity, and Z.ai coding plan.
9
-
10
- **Token provider supports**: All models and providers in [models.dev](https://models.dev), plus deterministic local pricing for Cursor Auto/Composer and Cursor model aliases that are not on models.dev.
11
-
12
-
13
- ![Image of quota toasts](https://github.com/slkiser/opencode-quota/blob/main/toast.png)
14
-
15
- ![Image of /quota and /tokens_daily outputs](https://github.com/slkiser/opencode-quota/blob/main/quota.png)
27
+ Quota and `/tokens_*` output are computed from local OpenCode session history.
16
28
 
17
29
  ## Quick Start
18
30
 
19
- OpenCode `>= 1.2.0` is required.
20
-
21
- Add the plugin to your `opencode.json` or `opencode.jsonc`:
31
+ OpenCode `>= 1.2.0` is required. Add the plugin to your `opencode.json` or `opencode.jsonc`:
22
32
 
23
33
  ```jsonc
24
34
  {
@@ -30,42 +40,93 @@ Then:
30
40
 
31
41
  1. Restart or reload OpenCode.
32
42
  2. Run `/quota_status` to confirm provider detection.
33
- 3. Run `/quota` to see the manual grouped report.
34
-
35
- That is enough for most installs. Providers are auto-detected from your existing OpenCode setup.
43
+ 3. Run `/quota` or `/tokens_today`.
36
44
 
37
- ## What You Get
45
+ That is enough for most installs. Providers are auto-detected from your existing OpenCode setup, and most providers work from your existing OpenCode auth. If a provider needs anything extra, use the setup table below.
38
46
 
39
- - Toasts after assistant responses, idle transitions, and compaction events
40
- - `/quota` for a grouped manual quota report such as `[OpenAI] (Pro)` or `[Copilot] (business)`, with a local call timestamp in the heading
41
- - `/tokens_*` commands backed by local OpenCode history and a local pricing snapshot, each with a local call timestamp in the heading
42
- - No model calls to compute the toast or report output
47
+ <details>
48
+ <summary><strong>Example: Turn off auto-detection and choose providers</strong></summary>
43
49
 
44
- ## Common Install Patterns
50
+ ```jsonc
51
+ {
52
+ "experimental": {
53
+ "quotaToast": {
54
+ "enabledProviders": ["copilot", "openai", "google-antigravity"]
55
+ }
56
+ }
57
+ }
58
+ ```
45
59
 
46
- ### Basic install
60
+ </details>
47
61
 
48
- If you already use Copilot, OpenAI, Firmware, Chutes, or Z.ai in OpenCode, start here:
62
+ <details>
63
+ <summary><strong>Example: Grouped toast layout instead of the default classic toast</strong></summary>
49
64
 
50
65
  ```jsonc
51
66
  {
52
- "plugin": ["@slkiser/opencode-quota"]
67
+ "experimental": {
68
+ "quotaToast": {
69
+ "toastStyle": "grouped"
70
+ }
71
+ }
53
72
  }
54
73
  ```
55
74
 
56
- ### Cursor
75
+ </details>
76
+
77
+ ### Provider Setup At A Glance
78
+
79
+ | Provider | Auto setup | How it works |
80
+ | --- | --- | --- |
81
+ | **GitHub Copilot** | Usually | OpenCode auth; PAT only for managed billing. [**Notes**](#github-copilot-notes) |
82
+ | **OpenAI** | Yes | OpenCode auth. [**Notes**](#openai-notes) |
83
+ | **Cursor** | Needs [quick setup](#cursor-quick-setup) | Companion auth plugin + `provider.cursor`. [**Notes**](#cursor-notes) |
84
+ | **Qwen Code** | Needs [quick setup](#qwen-code-quick-setup) | Companion auth plugin. [**Notes**](#qwen-code-notes) |
85
+ | **Alibaba Coding Plan** | Yes | Native OpenCode auth with local request estimation. [**Notes**](#alibaba-coding-plan-notes) |
86
+ | **Firmware AI** | Usually | OpenCode config; API key optional. [**Notes**](#firmware-ai-notes) |
87
+ | **Chutes AI** | Usually | OpenCode config; API key optional. [**Notes**](#chutes-ai-notes) |
88
+ | **Google Antigravity** | Needs [quick setup](#google-antigravity-quick-setup) | Companion auth plugin. [**Notes**](#google-antigravity-notes) |
89
+ | **Z.ai** | Yes | OpenCode auth. [**Notes**](#zai-notes) |
90
+
91
+ <a id="cursor-quick-setup"></a>
92
+ <details>
93
+ <summary><strong>Quick setup: Cursor</strong></summary>
57
94
 
58
- Cursor model support requires the `opencode-cursor` [companion ACP plugin](https://github.com/Nomadcxx/opencode-cursor):
95
+ Cursor quota support requires the `opencode-cursor-oauth` [plugin](https://github.com/ephraimduncan/opencode-cursor):
59
96
 
60
97
  ```jsonc
61
98
  {
62
- "plugin": ["@rama_nigg/open-cursor", "@slkiser/opencode-quota"]
99
+ "$schema": "https://opencode.ai/config.json",
100
+ "plugin": ["opencode-cursor-oauth", "@slkiser/opencode-quota"],
101
+ "provider": {
102
+ "cursor": {
103
+ "name": "Cursor"
104
+ }
105
+ },
106
+ "experimental": {
107
+ "quotaToast": {
108
+ "cursorPlan": "pro",
109
+ "cursorBillingCycleStartDay": 7
110
+ }
111
+ }
63
112
  }
64
113
  ```
65
114
 
66
- ### Google Antigravity
115
+ Then authenticate once:
116
+
117
+ ```sh
118
+ opencode auth login --provider cursor
119
+ ```
120
+
121
+ For behavior details and troubleshooting, see [Cursor notes](#cursor-notes).
122
+
123
+ </details>
124
+
125
+ <a id="google-antigravity-quick-setup"></a>
126
+ <details>
127
+ <summary><strong>Quick setup: Google Antigravity</strong></summary>
67
128
 
68
- Google quota support requires the `opencode-antigravity-auth` [companion auth plugin](https://github.com/NoeFabris/opencode-antigravity-auth):
129
+ Google quota support requires the `opencode-antigravity-auth` [plugin](https://github.com/NoeFabris/opencode-antigravity-auth):
69
130
 
70
131
  ```jsonc
71
132
  {
@@ -73,9 +134,15 @@ Google quota support requires the `opencode-antigravity-auth` [companion auth pl
73
134
  }
74
135
  ```
75
136
 
76
- ### Qwen Code
137
+ For behavior details and troubleshooting, see [Google Antigravity notes](#google-antigravity-notes).
138
+
139
+ </details>
140
+
141
+ <a id="qwen-code-quick-setup"></a>
142
+ <details>
143
+ <summary><strong>Quick setup: Qwen Code</strong></summary>
77
144
 
78
- Qwen quota support requires the `opencode-qwencode-auth` [companion auth plugin](https://github.com/gustavodiasdev/opencode-qwencode-auth):
145
+ Qwen quota support requires the `opencode-qwencode-auth` [plugin](https://github.com/gustavodiasdev/opencode-qwencode-auth):
79
146
 
80
147
  ```jsonc
81
148
  {
@@ -83,7 +150,9 @@ Qwen quota support requires the `opencode-qwencode-auth` [companion auth plugin]
83
150
  }
84
151
  ```
85
152
 
86
- Quota and `/tokens_*` output are computed from local OpenCode session history.
153
+ For behavior details and troubleshooting, see [Qwen Code notes](#qwen-code-notes).
154
+
155
+ </details>
87
156
 
88
157
  ## Commands
89
158
 
@@ -91,6 +160,7 @@ Quota and `/tokens_*` output are computed from local OpenCode session history.
91
160
  | --- | --- |
92
161
  | `/quota` | Manual grouped quota report with a local call timestamp |
93
162
  | `/quota_status` | Concise diagnostics for config, provider availability, account detection, and pricing snapshot health |
163
+ | `/pricing_refresh` | Pull the local runtime pricing snapshot from `models.dev` on demand |
94
164
  | `/tokens_today` | Tokens used today (calendar day) |
95
165
  | `/tokens_daily` | Tokens used in the last 24 hours |
96
166
  | `/tokens_weekly` | Tokens used in the last 7 days |
@@ -101,70 +171,21 @@ Quota and `/tokens_*` output are computed from local OpenCode session history.
101
171
 
102
172
  There is no `/token` command. The reporting commands are the `/tokens_*` family.
103
173
 
104
- ## Minimal Config
105
-
106
- You do not need extra config to get started. If you want to narrow the plugin to specific providers, use:
107
-
108
- ```jsonc
109
- {
110
- "experimental": {
111
- "quotaToast": {
112
- "enabledProviders": ["copilot", "openai", "google-antigravity"]
113
- }
114
- }
115
- }
116
- ```
117
-
118
- If you want grouped toast layout instead of the default classic toast:
119
-
120
- ```jsonc
121
- {
122
- "experimental": {
123
- "quotaToast": {
124
- "toastStyle": "grouped"
125
- }
126
- }
127
- }
128
- ```
129
-
130
- If Alibaba Coding Plan auth does not include a `tier`, you can set the fallback tier here:
131
-
132
- ```jsonc
133
- {
134
- "experimental": {
135
- "quotaToast": {
136
- "alibabaCodingPlanTier": "lite"
137
- }
138
- }
139
- }
140
- ```
141
-
142
- `/quota` already uses grouped formatting by default, even if toast style stays `classic`.
143
-
144
- ## Provider Setup At A Glance
145
-
146
- | Provider | Works automatically | Extra setup when needed |
147
- | --- | --- | --- |
148
- | GitHub Copilot | Usually yes | Add `copilot-quota-token.json` only for managed org or enterprise billing |
149
- | OpenAI | Yes | None |
150
- | Cursor | Needs `opencode-cursor` | Optional `cursorPlan`, `cursorIncludedApiUsd`, and `cursorBillingCycleStartDay` for monthly API budget tracking |
151
- | Qwen Code | Needs `opencode-qwencode-auth` | Local free-tier request estimation |
152
- | Alibaba Coding Plan | Yes | Local request-count estimation |
153
- | Firmware AI | Usually yes | Optional API key |
154
- | Chutes AI | Usually yes | Optional API key |
155
- | Google Antigravity | Needs `opencode-antigravity-auth` | Multi-account account file lives in OpenCode runtime config |
156
- | Z.ai | Yes | None |
157
-
158
174
  ## Provider-Specific Notes
159
175
 
176
+ <a id="github-copilot-notes"></a>
160
177
  <details>
161
178
  <summary><strong>GitHub Copilot</strong></summary>
162
179
 
163
- Personal Copilot quota works automatically when OpenCode is already signed in. When no `copilot-quota-token.json` exists, the plugin reads the OpenCode Copilot OAuth token from `~/.local/share/opencode/auth.json` and queries `GET https://api.github.com/copilot_internal/user` with `Authorization: Bearer <access token>`.
180
+ Personal quota works automatically when OpenCode is already signed in. Without `copilot-quota-token.json`, the plugin reads the OpenCode Copilot OAuth token from `~/.local/share/opencode/auth.json` and calls `GET https://api.github.com/copilot_internal/user`.
164
181
 
165
- For managed billing, create `copilot-quota-token.json` under the OpenCode runtime config directory. You can find the directory with `opencode debug paths`.
182
+ - Managed billing uses `copilot-quota-token.json` in the OpenCode runtime config directory (`opencode debug paths`). `business` requires `organization`; `enterprise` requires `enterprise` and can also filter by `organization` or `username`.
183
+ - `copilot-quota-token.json` takes precedence over OAuth. If the PAT config is invalid, the plugin reports that error and does not silently fall back.
184
+ - Output is labeled `[Copilot] (personal)` or `[Copilot] (business)`, and managed output includes the org or enterprise slug.
185
+ - Enterprise premium usage does not support fine-grained PATs or GitHub App tokens.
186
+ - Check `/quota_status` for `copilot_quota_auth`, `billing_mode`, `billing_scope`, `quota_api`, `effective_source`, and `billing_api_access_likely`.
166
187
 
167
- Organization example:
188
+ Example `copilot-quota-token.json`:
168
189
 
169
190
  ```json
170
191
  {
@@ -174,8 +195,6 @@ Organization example:
174
195
  }
175
196
  ```
176
197
 
177
- Enterprise example:
178
-
179
198
  ```json
180
199
  {
181
200
  "token": "ghp_...",
@@ -186,24 +205,9 @@ Enterprise example:
186
205
  }
187
206
  ```
188
207
 
189
- Behavior notes:
190
-
191
- - Personal output is labeled `[Copilot] (personal)`.
192
- - Managed organization and enterprise output is labeled `[Copilot] (business)`.
193
- - Managed output includes the org or enterprise slug in the value line so the billing scope is still visible.
194
- - If both OpenCode OAuth and `copilot-quota-token.json` exist, the PAT config wins.
195
- - If no PAT config exists, OpenCode Copilot OAuth is treated as personal quota auth via `/copilot_internal/user`.
196
- - If the PAT config is invalid, the plugin reports that error and does not silently fall back to OAuth.
197
- - `business` requires `organization`.
198
- - Enterprise premium usage does not support fine-grained PATs or GitHub App tokens. Use a supported enterprise token such as a classic PAT.
199
-
200
- Useful checks:
201
-
202
- - Run `/quota_status` and inspect `copilot_quota_auth`.
203
- - Look for `billing_mode`, `billing_scope`, `quota_api`, `effective_source`, and `billing_api_access_likely`.
204
-
205
208
  </details>
206
209
 
210
+ <a id="openai-notes"></a>
207
211
  <details>
208
212
  <summary><strong>OpenAI</strong></summary>
209
213
 
@@ -211,43 +215,19 @@ No extra setup is required if OpenCode already has OpenAI or ChatGPT auth config
211
215
 
212
216
  </details>
213
217
 
218
+ <a id="cursor-notes"></a>
214
219
  <details>
215
220
  <summary><strong>Cursor</strong></summary>
216
221
 
217
- Cursor support requires the `opencode-cursor` plugin and stays local-only and deterministic once `@rama_nigg/open-cursor` is installed in OpenCode.
218
-
219
- Recommended install path:
220
-
221
- - Follow Option B from the upstream [`opencode-cursor` README](https://github.com/Nomadcxx/opencode-cursor).
222
- - Keep the Cursor model list in sync with `cursor-agent models`.
223
-
224
- Current behavior:
222
+ See [Cursor quick setup](#cursor-quick-setup) for auth. Quota and token reporting stays local to OpenCode history and local pricing data.
225
223
 
226
- - Detects Cursor usage from OpenCode history when the current model or stored message model is `cursor-acp/*`
227
- - `/tokens_*` maps Cursor API-pool models into official pricing and uses bundled static rates for `auto` and `composer*`
228
- - `/quota` and toasts estimate the current billing-cycle spend from local OpenCode history
229
- - Percentage remaining is shown only when you configure `cursorPlan` or `cursorIncludedApiUsd`
230
- - Billing cycle defaults to the local calendar month unless you set `cursorBillingCycleStartDay`
224
+ - Detects Cursor usage when the provider is `cursor` or the stored/current model id is `cursor/*`.
225
+ - `/tokens_*` maps Cursor API-pool models to official pricing and uses bundled static pricing for `auto` and `composer*`.
226
+ - `/quota` and toasts estimate the current billing-cycle spend from local history only. Session cookies and team APIs are not required.
227
+ - Remaining percentage appears only when `experimental.quotaToast.cursorPlan` or `experimental.quotaToast.cursorIncludedApiUsd` is set. Billing cycle defaults to the local calendar month unless `experimental.quotaToast.cursorBillingCycleStartDay` is set.
228
+ - Legacy `cursor-acp/*` history remains readable. Unknown future Cursor model ids appear in `/quota_status` under Cursor diagnostics and `unknown_pricing`.
231
229
 
232
- Notes:
233
-
234
- - Session cookies and Cursor team APIs are not required for this local reporting path
235
- - Unknown future Cursor model ids are surfaced in `/quota_status` under Cursor diagnostics and `unknown_pricing`
236
-
237
- Example config for a personal Pro account:
238
-
239
- ```jsonc
240
- {
241
- "experimental": {
242
- "quotaToast": {
243
- "cursorPlan": "pro",
244
- "cursorBillingCycleStartDay": 7
245
- }
246
- }
247
- }
248
- ```
249
-
250
- If you need a custom included API budget, override it directly:
230
+ Example override:
251
231
 
252
232
  ```jsonc
253
233
  {
@@ -262,49 +242,53 @@ If you need a custom included API budget, override it directly:
262
242
 
263
243
  </details>
264
244
 
245
+ <a id="qwen-code-notes"></a>
265
246
  <details>
266
247
  <summary><strong>Qwen Code</strong></summary>
267
248
 
268
- Qwen support is local-only estimation for the free plan. The plugin does not call an Alibaba quota API.
269
-
270
- Current behavior:
249
+ See [Qwen Code quick setup](#qwen-code-quick-setup) for auth. Usage is local-only estimation for the free plan; the plugin does not call an Alibaba quota API.
271
250
 
272
- - Free tier only: 1000 requests per UTC day
273
- - Free tier only: 60 requests per rolling minute
274
- - Counters increment on successful question-tool completions while the current model is `qwen-code/*`
275
-
276
- State file path:
277
-
278
- - `.../opencode/opencode-quota/qwen-local-quota.json`
279
-
280
- Run `/quota_status` to verify auth detection, `qwen_local_plan`, and local counter status.
251
+ - Free tier limits: `1000` requests per UTC day and `60` requests per rolling minute.
252
+ - Counters increment on successful question-tool completions while the current model is `qwen-code/*`.
253
+ - State file: `.../opencode/opencode-quota/qwen-local-quota.json`.
254
+ - Check `/quota_status` for auth detection, `qwen_local_plan`, and local counter state.
281
255
 
282
256
  </details>
283
257
 
258
+ <a id="alibaba-coding-plan-notes"></a>
284
259
  <details>
285
260
  <summary><strong>Alibaba Coding Plan</strong></summary>
286
261
 
287
- Alibaba Coding Plan uses native OpenCode auth from either `alibaba` or `alibaba-coding-plan` in `auth.json`, instead of the Qwen companion plugin. Quota estimation is request-count based with rolling windows.
288
-
289
- Supported tiers:
290
-
291
- - `lite`: 1200 requests / 5 hours, 9000 / week, 18000 / month
292
- - `pro`: 6000 requests / 5 hours, 45000 / week, 90000 / month
293
- - If `tier` is missing from auth, the plugin uses `experimental.quotaToast.alibabaCodingPlanTier` and defaults that setting to `lite`
294
- - Counters increment on successful question-tool completions while the current model is `alibaba/*` or `alibaba-cn/*`
262
+ Uses native OpenCode auth from `alibaba` or `alibaba-coding-plan`. Quota is local request-count estimation with rolling windows.
295
263
 
296
- State file path:
264
+ - `lite`: `1200 / 5h`, `9000 / week`, `18000 / month`
265
+ - `pro`: `6000 / 5h`, `45000 / week`, `90000 / month`
266
+ - If auth omits `tier`, the plugin uses `experimental.quotaToast.alibabaCodingPlanTier`, which defaults to `lite`.
267
+ - Counters increment on successful question-tool completions while the current model is `alibaba/*` or `alibaba-cn/*`.
268
+ - State file: `.../opencode/opencode-quota/alibaba-coding-plan-local-quota.json`.
269
+ - `/quota_status` shows auth detection, resolved tier, state-file path, and current 5h/weekly/monthly usage.
297
270
 
298
- - `.../opencode/opencode-quota/alibaba-coding-plan-local-quota.json`
271
+ Example fallback tier:
299
272
 
300
- `/quota_status` shows whether Alibaba auth is configured, the resolved Alibaba coding-plan tier, the Alibaba state-file path, and the current 5h/weekly/monthly usage when this plan is active.
273
+ ```jsonc
274
+ {
275
+ "experimental": {
276
+ "quotaToast": {
277
+ "alibabaCodingPlanTier": "lite"
278
+ }
279
+ }
280
+ }
281
+ ```
301
282
 
302
283
  </details>
303
284
 
285
+ <a id="firmware-ai-notes"></a>
304
286
  <details>
305
287
  <summary><strong>Firmware AI</strong></summary>
306
288
 
307
- If OpenCode already has Firmware configured, it usually works automatically. You can also provide an API key:
289
+ If OpenCode already has Firmware configured, it usually works automatically. Optional API key: `provider.firmware.options.apiKey`.
290
+
291
+ `{env:FIRMWARE_API_KEY}` and literal values are both supported.
308
292
 
309
293
  ```jsonc
310
294
  {
@@ -314,23 +298,19 @@ If OpenCode already has Firmware configured, it usually works automatically. You
314
298
  "apiKey": "{env:FIRMWARE_API_KEY}"
315
299
  }
316
300
  }
317
- },
318
- "experimental": {
319
- "quotaToast": {
320
- "enabledProviders": ["firmware"]
321
- }
322
301
  }
323
302
  }
324
303
  ```
325
304
 
326
- `{env:VAR_NAME}` and direct keys are both supported.
327
-
328
305
  </details>
329
306
 
307
+ <a id="chutes-ai-notes"></a>
330
308
  <details>
331
309
  <summary><strong>Chutes AI</strong></summary>
332
310
 
333
- If OpenCode already has Chutes configured, it usually works automatically. You can also provide an API key:
311
+ If OpenCode already has Chutes configured, it usually works automatically. Optional API key: `provider.chutes.options.apiKey`.
312
+
313
+ `{env:CHUTES_API_KEY}` and literal values are both supported.
334
314
 
335
315
  ```jsonc
336
316
  {
@@ -340,26 +320,23 @@ If OpenCode already has Chutes configured, it usually works automatically. You c
340
320
  "apiKey": "{env:CHUTES_API_KEY}"
341
321
  }
342
322
  }
343
- },
344
- "experimental": {
345
- "quotaToast": {
346
- "enabledProviders": ["chutes"]
347
- }
348
323
  }
349
324
  }
350
325
  ```
351
326
 
352
327
  </details>
353
328
 
329
+ <a id="google-antigravity-notes"></a>
354
330
  <details>
355
331
  <summary><strong>Google Antigravity</strong></summary>
356
332
 
357
- This provider requires the `opencode-antigravity-auth` plugin. Account credentials are stored under the OpenCode runtime config directory.
333
+ See [Google Antigravity quick setup](#google-antigravity-quick-setup). Credentials live under the OpenCode runtime config directory.
358
334
 
359
- If you are debugging detection, `/quota_status` prints the candidate paths checked for `antigravity-accounts.json`.
335
+ If detection looks wrong, `/quota_status` prints the candidate paths checked for `antigravity-accounts.json`.
360
336
 
361
337
  </details>
362
338
 
339
+ <a id="zai-notes"></a>
363
340
  <details>
364
341
  <summary><strong>Z.ai</strong></summary>
365
342
 
@@ -373,7 +350,7 @@ All plugin settings live under `experimental.quotaToast`.
373
350
 
374
351
  | Option | Default | Meaning |
375
352
  | --- | --- | --- |
376
- | `enabled` | `true` | Master switch for the plugin. When `false`, `/quota`, `/quota_status`, and `/tokens_*` are no-ops. |
353
+ | `enabled` | `true` | Master switch for the plugin. When `false`, `/quota`, `/quota_status`, `/pricing_refresh`, and `/tokens_*` are no-ops. |
377
354
  | `enableToast` | `true` | Show popup toasts |
378
355
  | `toastStyle` | `classic` | Toast layout: `classic` or `grouped` |
379
356
  | `enabledProviders` | `"auto"` | Auto-detect providers, or set an explicit provider list |
@@ -393,33 +370,26 @@ All plugin settings live under `experimental.quotaToast`.
393
370
  | `cursorPlan` | `"none"` | Cursor included API budget preset: `none`, `pro`, `pro-plus`, `ultra` |
394
371
  | `cursorIncludedApiUsd` | unset | Override Cursor monthly included API budget in USD |
395
372
  | `cursorBillingCycleStartDay` | unset | Local billing-cycle anchor day `1..28`; when unset, Cursor usage resets on the local calendar month |
373
+ | `pricingSnapshot.source` | `"auto"` | Token pricing snapshot selection: `auto`, `bundled`, or `runtime` |
374
+ | `pricingSnapshot.autoRefresh` | `5` | Refresh stale local pricing data after this many days |
396
375
  | `debug` | `false` | Include debug context in toast output |
397
376
 
398
377
  ## Token Pricing Snapshot
399
378
 
400
- `/tokens_*` uses a local `models.dev` pricing snapshot plus bundled static Cursor pricing for Cursor-only pool models.
379
+ `/tokens_*` uses a local `models.dev` pricing snapshot. A bundled snapshot ships for offline use, and Cursor `auto` and `composer*` pricing stays bundled because those ids are not on `models.dev`.
401
380
 
402
- Behavior:
403
-
404
- - A bundled snapshot ships with the plugin for offline use.
405
- - The plugin can refresh the local runtime snapshot when the data is stale.
406
- - Reports continue to work if refresh fails.
407
- - Cursor `auto` and `composer*` pricing is bundled in the plugin because those ids are not on `models.dev`.
408
-
409
- Useful environment variables:
410
-
411
- ```sh
412
- OPENCODE_QUOTA_PRICING_AUTO_REFRESH=0
413
- OPENCODE_QUOTA_PRICING_MAX_AGE_DAYS=5
414
- ```
415
-
416
- Maintainer refresh commands:
381
+ | `pricingSnapshot.source` | Active pricing behavior |
382
+ | --- | --- |
383
+ | `auto` | Newer runtime snapshot wins; otherwise bundled pricing stays active. |
384
+ | `bundled` | Packaged bundled snapshot stays active. |
385
+ | `runtime` | Runtime snapshot stays active when present; bundled pricing is fallback until one exists. |
417
386
 
418
- ```sh
419
- npm run pricing:refresh
420
- npm run pricing:refresh:if-stale
421
- npm run build
422
- ```
387
+ - See [Configuration Reference](#configuration-reference) for option defaults.
388
+ - `pricingSnapshot.autoRefresh` controls how many days a runtime snapshot can age before background refresh.
389
+ - `/pricing_refresh` refreshes only the local runtime snapshot under the OpenCode cache directory. It never rewrites the packaged bundled snapshot.
390
+ - If `pricingSnapshot.source` is `bundled`, `/pricing_refresh` still updates the runtime cache, but active pricing stays bundled.
391
+ - Reports keep working if refresh fails.
392
+ - Pricing selection stays local and deterministic. There are no custom URLs or arbitrary pricing sources.
423
393
 
424
394
  ## Troubleshooting
425
395
 
@@ -429,7 +399,7 @@ If something is missing or looks wrong:
429
399
  2. Confirm the expected provider appears in the detected provider list.
430
400
  3. If token reports are empty, make sure OpenCode has already created `opencode.db`.
431
401
  4. If Copilot managed billing is expected, confirm `copilot-quota-token.json` is present and valid.
432
- 5. If Google or Qwen support is expected, confirm the companion auth plugin is installed. If Alibaba Coding Plan support is expected, confirm OpenCode `alibaba` or `alibaba-coding-plan` auth is configured; `tier` may be `lite` or `pro`, and if it is missing the plugin falls back to `experimental.quotaToast.alibabaCodingPlanTier`.
402
+ 5. If provider setup looks wrong, check [Provider Setup At A Glance](#provider-setup-at-a-glance) and [Provider-Specific Notes](#provider-specific-notes). For Google Antigravity or Qwen Code, confirm the companion auth plugin is installed. For Alibaba Coding Plan, confirm OpenCode `alibaba` or `alibaba-coding-plan` auth is configured; `tier` may be `lite` or `pro`, and if it is missing the plugin falls back to `experimental.quotaToast.alibabaCodingPlanTier`.
433
403
 
434
404
  If `opencode.db` is missing, start OpenCode once and let its local migration complete.
435
405
 
@@ -442,21 +412,30 @@ npm test
442
412
  npm run build
443
413
  ```
444
414
 
445
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution workflow and repository policy.
415
+ Maintainer workflow for tracked upstream companion plugins:
446
416
 
447
- ## LLM Agent Installation Notes
417
+ ```sh
418
+ npm run upstream:check
419
+ npm run upstream:prepare-review
420
+ npm run upstream:sync
421
+ ```
448
422
 
449
- If you are using an agent to install the plugin for you, the safe default is:
423
+ - `npm run upstream:check` compares the committed references in `references/upstream-plugins/lock.json` with the latest npm releases for `opencode-qwencode-auth`, `opencode-antigravity-auth`, and `opencode-cursor-oauth`.
424
+ - `.github/workflows/upstream-plugin-update-check.yml` runs that check daily and keeps at most one open `[check] <plugin> had update` issue per plugin. If a newer npm release arrives before you act, that same issue is updated and gets a comment instead of piling up more issues.
425
+ - `npm run upstream:prepare-review` is the normal maintainer command. It syncs the latest published package copies, runs `npm test`, runs `npm run typecheck`, and prints a ready-to-paste prompt for another agent with changed relative paths and diff previews.
426
+ - `npm run upstream:sync` downloads the latest published package contents into `references/upstream-plugins/<plugin>/` and rewrites `references/upstream-plugins/lock.json`.
427
+ - Known embedded upstream credentials are redacted deterministically during sync before the reference copies are committed. Update issues still come from `references/upstream-plugins/lock.json` version comparisons.
428
+ - Syncing does not close the GitHub issue. The issue stays open until you finish review/fix/release work and close it manually.
429
+ - These reference copies are committed for maintainer review only. They are not published in this package because `package.json` ships only `dist`, `README.md`, and `LICENSE`.
450
430
 
451
- ```jsonc
452
- {
453
- "plugin": ["@slkiser/opencode-quota"]
454
- }
455
- ```
431
+ If you maintain this repo through an agentic `/command` workflow, keep your normal-language maintainer prompt rules in local `AGENTS.md`. The current local prompt patterns are:
456
432
 
457
- Then verify with `/quota_status`.
433
+ - `please help me add feature: <short description>`
434
+ - `please handle bug issue #<number>`
435
+ - `please handle feature request issue #<number>`
436
+ - `please sync our plugin updates`
458
437
 
459
- Only add explicit `enabledProviders` if you want to limit which providers are queried. Only add companion plugins when the user actually uses Google Antigravity or Qwen Code, and only add `@rama_nigg/open-cursor` when the user actually uses Cursor models in OpenCode.
438
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution workflow and repository policy.
460
439
 
461
440
  ## License
462
441
 
@@ -464,7 +443,7 @@ MIT
464
443
 
465
444
  ## Remarks
466
445
 
467
- Opencode Quota is not built by the OpenCode team and is not affiliated with OpenCode or any provider listed above.
446
+ OpenCode Quota is not built by the OpenCode team and is not affiliated with OpenCode or any provider listed above.
468
447
 
469
448
  ## Star History
470
449
 
package/dist/index.d.ts CHANGED
@@ -6,5 +6,5 @@
6
6
  * @packageDocumentation
7
7
  */
8
8
  export { QuotaToastPlugin } from "./plugin.js";
9
- export type { QuotaToastConfig, GoogleModelId, CopilotEnterpriseUsageResult, CopilotOrganizationUsageResult, CopilotQuotaResult, GoogleQuotaResult, GoogleModelQuota, } from "./lib/types.js";
9
+ export type { QuotaToastConfig, GoogleModelId, PricingSnapshotSource, CopilotEnterpriseUsageResult, CopilotOrganizationUsageResult, CopilotQuotaResult, GoogleQuotaResult, GoogleModelQuota, } from "./lib/types.js";
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,4BAA4B,EAC5B,8BAA8B,EAC9B,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,4BAA4B,EAC5B,8BAA8B,EAC9B,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8EAA8E;AAC9E,iFAAiF;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAa/C,yEAAyE;AACzE,wCAAwC;AAExC,6EAA6E;AAC7E,uFAAuF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8EAA8E;AAC9E,iFAAiF;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAc/C,yEAAyE;AACzE,wCAAwC;AAExC,6EAA6E;AAC7E,uFAAuF"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAmB,gBAAgB,EAAiB,MAAM,YAAY,CAAC;AAWnF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,UAAU,CAAC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AAuBD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE;IACN,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,YAAY,CAAC,EAAE;oBAAE,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KAC9F,CAAC;CACH,EACD,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAoM3B"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,UAAU,CAAC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AA+BD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE;IACN,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,YAAY,CAAC,EAAE;oBAAE,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KAC9F,CAAC;CACH,EACD,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,gBAAgB,CAAC,CA4M3B"}