@slkiser/opencode-quota 3.11.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +103 -57
  2. package/dist/bin/opencode-quota.d.ts.map +1 -1
  3. package/dist/bin/opencode-quota.js +24 -7
  4. package/dist/bin/opencode-quota.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/anthropic.d.ts.map +1 -1
  10. package/dist/lib/anthropic.js +106 -3
  11. package/dist/lib/anthropic.js.map +1 -1
  12. package/dist/lib/atomic-json.d.ts +1 -0
  13. package/dist/lib/atomic-json.d.ts.map +1 -1
  14. package/dist/lib/atomic-json.js +5 -2
  15. package/dist/lib/atomic-json.js.map +1 -1
  16. package/dist/lib/cli-show.d.ts.map +1 -1
  17. package/dist/lib/cli-show.js +11 -7
  18. package/dist/lib/cli-show.js.map +1 -1
  19. package/dist/lib/command-surfaces.d.ts +10 -0
  20. package/dist/lib/command-surfaces.d.ts.map +1 -0
  21. package/dist/lib/command-surfaces.js +9 -0
  22. package/dist/lib/command-surfaces.js.map +1 -0
  23. package/dist/lib/config-file-utils.d.ts +5 -0
  24. package/dist/lib/config-file-utils.d.ts.map +1 -1
  25. package/dist/lib/config-file-utils.js +20 -3
  26. package/dist/lib/config-file-utils.js.map +1 -1
  27. package/dist/lib/config.d.ts +5 -3
  28. package/dist/lib/config.d.ts.map +1 -1
  29. package/dist/lib/config.js +124 -32
  30. package/dist/lib/config.js.map +1 -1
  31. package/dist/lib/copilot.d.ts +19 -12
  32. package/dist/lib/copilot.d.ts.map +1 -1
  33. package/dist/lib/copilot.js +462 -566
  34. package/dist/lib/copilot.js.map +1 -1
  35. package/dist/lib/display-sanitize.d.ts.map +1 -1
  36. package/dist/lib/display-sanitize.js +13 -0
  37. package/dist/lib/display-sanitize.js.map +1 -1
  38. package/dist/lib/entries.d.ts +53 -4
  39. package/dist/lib/entries.d.ts.map +1 -1
  40. package/dist/lib/entries.js.map +1 -1
  41. package/dist/lib/init-installer.d.ts +11 -1
  42. package/dist/lib/init-installer.d.ts.map +1 -1
  43. package/dist/lib/init-installer.js +542 -154
  44. package/dist/lib/init-installer.js.map +1 -1
  45. package/dist/lib/openai.d.ts +9 -12
  46. package/dist/lib/openai.d.ts.map +1 -1
  47. package/dist/lib/openai.js +69 -40
  48. package/dist/lib/openai.js.map +1 -1
  49. package/dist/lib/opencode-config-editor.d.ts +53 -0
  50. package/dist/lib/opencode-config-editor.d.ts.map +1 -0
  51. package/dist/lib/opencode-config-editor.js +274 -0
  52. package/dist/lib/opencode-config-editor.js.map +1 -0
  53. package/dist/lib/opencode-config-providers.d.ts +18 -0
  54. package/dist/lib/opencode-config-providers.d.ts.map +1 -1
  55. package/dist/lib/opencode-config-providers.js +75 -4
  56. package/dist/lib/opencode-config-providers.js.map +1 -1
  57. package/dist/lib/opencode-runtime-paths.d.ts.map +1 -1
  58. package/dist/lib/opencode-runtime-paths.js +10 -3
  59. package/dist/lib/opencode-runtime-paths.js.map +1 -1
  60. package/dist/lib/opencode-storage.d.ts +10 -0
  61. package/dist/lib/opencode-storage.d.ts.map +1 -1
  62. package/dist/lib/opencode-storage.js +75 -0
  63. package/dist/lib/opencode-storage.js.map +1 -1
  64. package/dist/lib/provider-add-command.d.ts +35 -0
  65. package/dist/lib/provider-add-command.d.ts.map +1 -0
  66. package/dist/lib/provider-add-command.js +185 -0
  67. package/dist/lib/provider-add-command.js.map +1 -0
  68. package/dist/lib/provider-add.d.ts +21 -0
  69. package/dist/lib/provider-add.d.ts.map +1 -0
  70. package/dist/lib/provider-add.js +210 -0
  71. package/dist/lib/provider-add.js.map +1 -0
  72. package/dist/lib/provider-metadata.d.ts +1 -1
  73. package/dist/lib/provider-metadata.d.ts.map +1 -1
  74. package/dist/lib/provider-metadata.js +11 -5
  75. package/dist/lib/provider-metadata.js.map +1 -1
  76. package/dist/lib/quota-command-format.d.ts +2 -0
  77. package/dist/lib/quota-command-format.d.ts.map +1 -1
  78. package/dist/lib/quota-command-format.js +94 -26
  79. package/dist/lib/quota-command-format.js.map +1 -1
  80. package/dist/lib/quota-dialog-commands.d.ts.map +1 -1
  81. package/dist/lib/quota-dialog-commands.js +8 -4
  82. package/dist/lib/quota-dialog-commands.js.map +1 -1
  83. package/dist/lib/quota-export-types.d.ts +48 -27
  84. package/dist/lib/quota-export-types.d.ts.map +1 -1
  85. package/dist/lib/quota-export-types.js +0 -7
  86. package/dist/lib/quota-export-types.js.map +1 -1
  87. package/dist/lib/quota-export.d.ts.map +1 -1
  88. package/dist/lib/quota-export.js +63 -16
  89. package/dist/lib/quota-export.js.map +1 -1
  90. package/dist/lib/quota-providers-local.d.ts +55 -0
  91. package/dist/lib/quota-providers-local.d.ts.map +1 -0
  92. package/dist/lib/quota-providers-local.js +313 -0
  93. package/dist/lib/quota-providers-local.js.map +1 -0
  94. package/dist/lib/quota-providers-remote.d.ts +23 -0
  95. package/dist/lib/quota-providers-remote.d.ts.map +1 -0
  96. package/dist/lib/quota-providers-remote.js +318 -0
  97. package/dist/lib/quota-providers-remote.js.map +1 -0
  98. package/dist/lib/quota-providers.d.ts +77 -0
  99. package/dist/lib/quota-providers.d.ts.map +1 -0
  100. package/dist/lib/quota-providers.js +630 -0
  101. package/dist/lib/quota-providers.js.map +1 -0
  102. package/dist/lib/quota-render-data.d.ts +1 -0
  103. package/dist/lib/quota-render-data.d.ts.map +1 -1
  104. package/dist/lib/quota-render-data.js +35 -16
  105. package/dist/lib/quota-render-data.js.map +1 -1
  106. package/dist/lib/quota-runtime-context.d.ts.map +1 -1
  107. package/dist/lib/quota-runtime-context.js +3 -4
  108. package/dist/lib/quota-runtime-context.js.map +1 -1
  109. package/dist/lib/quota-state.d.ts +5 -2
  110. package/dist/lib/quota-state.d.ts.map +1 -1
  111. package/dist/lib/quota-state.js +268 -51
  112. package/dist/lib/quota-state.js.map +1 -1
  113. package/dist/lib/quota-status.d.ts +2 -1
  114. package/dist/lib/quota-status.d.ts.map +1 -1
  115. package/dist/lib/quota-status.js +247 -69
  116. package/dist/lib/quota-status.js.map +1 -1
  117. package/dist/lib/qwen-local-quota.d.ts +12 -11
  118. package/dist/lib/qwen-local-quota.d.ts.map +1 -1
  119. package/dist/lib/qwen-local-quota.js +82 -87
  120. package/dist/lib/qwen-local-quota.js.map +1 -1
  121. package/dist/lib/scoped-update.d.ts +63 -0
  122. package/dist/lib/scoped-update.d.ts.map +1 -0
  123. package/dist/lib/scoped-update.js +311 -0
  124. package/dist/lib/scoped-update.js.map +1 -0
  125. package/dist/lib/tui-runtime.d.ts +2 -0
  126. package/dist/lib/tui-runtime.d.ts.map +1 -1
  127. package/dist/lib/tui-runtime.js +33 -9
  128. package/dist/lib/tui-runtime.js.map +1 -1
  129. package/dist/lib/types.d.ts +51 -15
  130. package/dist/lib/types.d.ts.map +1 -1
  131. package/dist/lib/types.js +2 -1
  132. package/dist/lib/types.js.map +1 -1
  133. package/dist/plugin.d.ts.map +1 -1
  134. package/dist/plugin.js +75 -100
  135. package/dist/plugin.js.map +1 -1
  136. package/dist/providers/alibaba-coding-plan.d.ts.map +1 -1
  137. package/dist/providers/alibaba-coding-plan.js +35 -4
  138. package/dist/providers/alibaba-coding-plan.js.map +1 -1
  139. package/dist/providers/anthropic.d.ts.map +1 -1
  140. package/dist/providers/anthropic.js +13 -1
  141. package/dist/providers/anthropic.js.map +1 -1
  142. package/dist/providers/chutes.d.ts.map +1 -1
  143. package/dist/providers/chutes.js +6 -0
  144. package/dist/providers/chutes.js.map +1 -1
  145. package/dist/providers/copilot.d.ts +2 -1
  146. package/dist/providers/copilot.d.ts.map +1 -1
  147. package/dist/providers/copilot.js +143 -60
  148. package/dist/providers/copilot.js.map +1 -1
  149. package/dist/providers/cursor.d.ts.map +1 -1
  150. package/dist/providers/cursor.js +30 -1
  151. package/dist/providers/cursor.js.map +1 -1
  152. package/dist/providers/deepseek.d.ts.map +1 -1
  153. package/dist/providers/deepseek.js +13 -1
  154. package/dist/providers/deepseek.js.map +1 -1
  155. package/dist/providers/google-agy.d.ts.map +1 -1
  156. package/dist/providers/google-agy.js +7 -1
  157. package/dist/providers/google-agy.js.map +1 -1
  158. package/dist/providers/google-antigravity.d.ts.map +1 -1
  159. package/dist/providers/google-antigravity.js +10 -2
  160. package/dist/providers/google-antigravity.js.map +1 -1
  161. package/dist/providers/google-gemini-cli.d.ts.map +1 -1
  162. package/dist/providers/google-gemini-cli.js +8 -2
  163. package/dist/providers/google-gemini-cli.js.map +1 -1
  164. package/dist/providers/kimi-code.d.ts.map +1 -1
  165. package/dist/providers/kimi-code.js +6 -0
  166. package/dist/providers/kimi-code.js.map +1 -1
  167. package/dist/providers/minimax-coding-plan.d.ts.map +1 -1
  168. package/dist/providers/minimax-coding-plan.js +12 -4
  169. package/dist/providers/minimax-coding-plan.js.map +1 -1
  170. package/dist/providers/nanogpt.d.ts.map +1 -1
  171. package/dist/providers/nanogpt.js +19 -1
  172. package/dist/providers/nanogpt.js.map +1 -1
  173. package/dist/providers/ollama-cloud.d.ts.map +1 -1
  174. package/dist/providers/ollama-cloud.js +12 -0
  175. package/dist/providers/ollama-cloud.js.map +1 -1
  176. package/dist/providers/openai.d.ts.map +1 -1
  177. package/dist/providers/openai.js +7 -0
  178. package/dist/providers/openai.js.map +1 -1
  179. package/dist/providers/opencode-go.d.ts.map +1 -1
  180. package/dist/providers/opencode-go.js +9 -1
  181. package/dist/providers/opencode-go.js.map +1 -1
  182. package/dist/providers/quota-providers.d.ts +6 -0
  183. package/dist/providers/quota-providers.d.ts.map +1 -0
  184. package/dist/providers/quota-providers.js +276 -0
  185. package/dist/providers/quota-providers.js.map +1 -0
  186. package/dist/providers/qwen-code.d.ts.map +1 -1
  187. package/dist/providers/qwen-code.js +29 -3
  188. package/dist/providers/qwen-code.js.map +1 -1
  189. package/dist/providers/registry.d.ts.map +1 -1
  190. package/dist/providers/registry.js +2 -0
  191. package/dist/providers/registry.js.map +1 -1
  192. package/dist/providers/result-helpers.d.ts +2 -1
  193. package/dist/providers/result-helpers.d.ts.map +1 -1
  194. package/dist/providers/result-helpers.js +6 -1
  195. package/dist/providers/result-helpers.js.map +1 -1
  196. package/dist/providers/synthetic.d.ts.map +1 -1
  197. package/dist/providers/synthetic.js +7 -1
  198. package/dist/providers/synthetic.js.map +1 -1
  199. package/dist/providers/zai.d.ts.map +1 -1
  200. package/dist/providers/zai.js +7 -1
  201. package/dist/providers/zai.js.map +1 -1
  202. package/dist/providers/zhipu.d.ts.map +1 -1
  203. package/dist/providers/zhipu.js +7 -1
  204. package/dist/providers/zhipu.js.map +1 -1
  205. package/dist/tui.d.ts.map +1 -1
  206. package/dist/tui.js +148 -122
  207. package/dist/tui.tsx +122 -114
  208. package/package.json +29 -21
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  </picture>
8
8
  </a>
9
9
  </p>
10
- <p align="center">Quota, usage, and token visibility for OpenCode and CLI.</p>
10
+ <p align="center">Quota, usage, and token visibility in OpenCode and your terminal.</p>
11
11
  <p align="center">
12
12
  <a href="https://www.npmjs.com/package/@slkiser/opencode-quota"><img alt="npm" src="https://img.shields.io/npm/v/%40slkiser%2Fopencode-quota?style=flat-square" /></a>
13
13
  <a href="https://www.npmjs.com/package/@slkiser/opencode-quota"><img alt="npm downloads" src="https://img.shields.io/npm/dm/%40slkiser%2Fopencode-quota?style=flat-square" /></a>
@@ -26,14 +26,32 @@ npx @slkiser/opencode-quota init
26
26
  ```
27
27
 
28
28
  > [!IMPORTANT]
29
- > OpenCode `>= 1.4.3` and Node.js `>= 20` are required.
29
+ > Node.js `>= 22` is required.
30
+
31
+ Upgrading from v3? Read the [v4 migration guide](docs/readme/v4-migration.md).
32
+
33
+ After installation:
30
34
 
31
35
  1. Restart OpenCode.
32
- 2. Run `/quota` in OpenCode, or use `opencode-quota show` from your terminal.
33
- 3. If you enabled the Sidebar panel, open the session sidebar and look for `Quota`.
34
- 4. If you enabled Compact status line, look for the home-bottom quota line and the chat/session prompt quota line.
36
+ 2. Run a slash command in OpenCode, or use `opencode-quota show` from your terminal.
37
+ 3. If you enabled the sidebar, open the session sidebar and look for `Quota`.
38
+ 4. If you enabled the compact status line, look at the bottom of Home or below the message input.
35
39
  5. If something looks wrong, run `/quota_status` in OpenCode or see [Troubleshooting](docs/readme/troubleshooting.md).
36
40
 
41
+ ## Updating
42
+
43
+ 1. Close OpenCode.
44
+ 2. Run:
45
+
46
+ ```bash
47
+ npx @slkiser/opencode-quota@latest update
48
+ ```
49
+
50
+ 3. Review the exact config edits and cache directories, then confirm.
51
+ 4. Restart OpenCode.
52
+
53
+ Use `--dry-run` to preview without changing anything. Otherwise, `update` shows the OpenCode Quota config and cache changes and asks before applying them. It leaves your other plugins and settings alone. Use `--yes` only when you intentionally need a noninteractive run.
54
+
37
55
  ## What you get
38
56
 
39
57
  <table>
@@ -47,7 +65,7 @@ npx @slkiser/opencode-quota init
47
65
  </tr>
48
66
  <tr>
49
67
  <td width="50%" align="center"><strong>Sidebar panel</strong><br />A full quota view in OpenCode's session sidebar.</td>
50
- <td width="50%" align="center"><strong>Toast</strong><br />Popup quota checks after idle, question, or compact events.</td>
68
+ <td width="50%" align="center"><strong>TUI toast</strong><br />Quota checks can appear automatically while you work.</td>
51
69
  </tr>
52
70
  <tr>
53
71
  <td width="50%">
@@ -58,17 +76,17 @@ npx @slkiser/opencode-quota init
58
76
  </td>
59
77
  </tr>
60
78
  <tr>
61
- <td width="50%" align="center"><strong>Compact status line</strong><br />Short quota text on home and chat/session prompt surfaces.</td>
79
+ <td width="50%" align="center"><strong>Compact status line</strong><br />Short quota text on Home and below the message input.</td>
62
80
  <td width="50%" align="center"><strong>Token reports</strong><br /><code>/tokens_today</code>, <code>/tokens_weekly</code>, session reports, and more.</td>
63
81
  </tr>
64
82
  </table>
65
83
 
66
84
  More ways to use it:
67
85
 
68
- - Terminal checks with `opencode-quota show` before or without opening OpenCode
69
- - JSON output for scripts, status bars, CI checks, and external tools
70
- - Local TUI dialogs for slash commands when the TUI plugin is installed
71
- - Provider diagnostics for auth, quota sources, pricing, and bundled maintainer announcements
86
+ - Check quota from a terminal with `opencode-quota show`
87
+ - Use JSON output in scripts, status bars, CI checks, and other tools
88
+ - Run the same slash commands in the TUI, Web, and Desktop
89
+ - Diagnose authentication, quota sources, pricing, and maintainer notices
72
90
 
73
91
  See [Configuration](docs/readme/configuration.md) for UI options and [Manual install](docs/readme/manual-install.md) for setup details.
74
92
 
@@ -76,62 +94,79 @@ See [Configuration](docs/readme/configuration.md) for UI options and [Manual ins
76
94
 
77
95
  ### Core slash commands
78
96
 
79
- | Command | Use when |
80
- | --- | --- |
81
- | `/quota` | Show your quota and usage details |
82
- | `/quota_status` | Diagnose setup, auth, provider detection, pricing, and announcements |
83
- | `/quota_announcements` | Read active bundled maintainer notices |
84
- | `/pricing_refresh` | Refresh local runtime pricing from `models.dev` |
85
- | `/tokens_today` | Show tokens used today |
86
- | `/tokens_daily` | Show tokens used in the last 24 hours |
87
- | `/tokens_weekly` | Show tokens used in the last 7 days |
88
- | `/tokens_monthly` | Show tokens used in the last 30 days, including pricing |
89
- | `/tokens_all` | Show tokens used across all local history |
90
- | `/tokens_session` | Show tokens used in the current session |
91
- | `/tokens_session_all` | Show current session plus descendant sessions |
92
- | `/tokens_between YYYY-MM-DD YYYY-MM-DD` | Show tokens used between two dates |
97
+ | Command | Use when |
98
+ | --------------------------------------- | --------------------------------------------------------------- |
99
+ | `/quota` | Show current quota |
100
+ | `/quota_status` | Diagnose setup, authentication, providers, pricing, and notices |
101
+ | `/quota_announcements` | Read active bundled maintainer notices |
102
+ | `/pricing_refresh` | Refresh local runtime pricing from `models.dev` |
103
+ | `/tokens_today` | Show tokens used today |
104
+ | `/tokens_daily` | Show tokens used in the last 24 hours |
105
+ | `/tokens_weekly` | Show tokens used in the last 7 days |
106
+ | `/tokens_monthly` | Show tokens used in the last 30 days, including pricing |
107
+ | `/tokens_all` | Show tokens used across all local history |
108
+ | `/tokens_session` | Show tokens used in the current session |
109
+ | `/tokens_session_all` | Show current session plus descendant sessions |
110
+ | `/tokens_between YYYY-MM-DD YYYY-MM-DD` | Show tokens used between two dates |
93
111
 
94
112
  ### CLI commands
95
113
 
96
- | Command | Use when |
97
- | --- | --- |
98
- | `opencode-quota show` | Check quota from your terminal |
99
- | `opencode-quota show --provider <id>` | Check one provider only, such as `copilot` or `openai` |
100
- | `opencode-quota show --json` | Print JSON for scripts, status bars, and other tools |
101
- | `opencode-quota show --json --threshold <pct>` | Fail the command when cached quota drops below your chosen percentage |
114
+ Use the CLI for scripts, CI, or a quick terminal check outside OpenCode.
115
+
116
+ | Command | Use when |
117
+ | ---------------------------------------------- | ------------------------------------------------------------ |
118
+ | `opencode-quota init --dry-run` | Validate and preview installer changes without writing files |
119
+ | `opencode-quota update` | Preview, confirm, and apply a scoped OpenCode Quota update |
120
+ | `opencode-quota update --dry-run` | Preview exact config and cache targets without changing them |
121
+ | `opencode-quota show` | Check quota from your terminal |
122
+ | `opencode-quota show --provider <id>` | Check one provider only, such as `copilot` or `openai` |
123
+ | `opencode-quota show --json` | Print JSON for scripts, status bars, and other tools |
124
+ | `opencode-quota show --json --threshold <pct>` | Fail when quota is low; return exit 2 for incomplete results |
102
125
 
103
126
  ## Providers
104
127
 
128
+ ### Pre-configured providers
129
+
105
130
  Most providers work automatically. If a provider has a “Needs setup” link, open that setup note only if you use that provider.
106
131
 
107
- | Provider | Auth/setup | Source | Reports |
108
- | --- | --- | --- | --- |
109
- | Anthropic (Claude) | [Needs setup](docs/readme/providers.md#anthropic-claude) | Local CLI/OAuth | Usage/quota |
110
- | GitHub Copilot | OpenCode OAuth or PAT | Remote API | Quota/usage |
111
- | OpenAI | Automatic | Remote API | Usage/quota |
112
- | Cursor | [Needs setup](docs/readme/providers.md#cursor) | Local estimate | Estimated quota |
113
- | Qwen Code | [Needs setup](docs/readme/providers.md#qwen-code) | Local estimate | Estimated quota |
114
- | Alibaba Coding Plan | OpenCode config | Local estimate | Estimated quota |
115
- | MiniMax Coding Plan | OpenCode config | Remote API | Usage/quota |
116
- | MiniMax Coding Plan (CN) | OpenCode config | Remote API | Usage/quota |
117
- | Kimi Code | OpenCode config | Remote API | Usage/quota |
118
- | Chutes AI | API key/config | Remote API | Usage/quota |
119
- | Synthetic | Automatic | Remote API | Quota |
120
- | Google Antigravity | [Needs setup](docs/readme/providers.md#google-antigravity) | Remote API | Usage/quota |
121
- | Google AGY | [Needs setup](docs/readme/providers.md#google-agy-quick-setup) | Remote API | Usage/quota |
122
- | Gemini CLI | [Needs setup](docs/readme/providers.md#gemini-cli) | Remote API | Usage/quota |
123
- | Z.ai Coding Plan | OpenCode config | Remote API | Usage/quota |
124
- | Zhipu Coding Plan | OpenCode config | Remote API | Usage/quota |
125
- | NanoGPT | API key/config | Remote API | Usage + balance |
126
- | DeepSeek | API key/config | Remote API | Balance/status |
127
- | Ollama Cloud | [Needs setup](docs/readme/providers.md#ollama-cloud) | Dashboard scraping | Dashboard usage |
128
- | OpenCode Go | [Needs setup](docs/readme/providers.md#opencode-go) | Dashboard scraping | Dashboard usage |
129
-
130
- Setup details live in the [Provider setup guide](docs/readme/providers.md).
132
+ | Provider | Auth/setup | Data from | Reports |
133
+ | ------------------------ | -------------------------------------------------------------- | ------------------ | ------------------ |
134
+ | Anthropic (Claude) | [Needs setup](docs/readme/providers.md#anthropic-claude) | Local CLI/OAuth | Quota |
135
+ | GitHub Copilot | [Needs setup](docs/readme/providers.md#github-copilot) | Remote API | Usage and budget |
136
+ | OpenAI | Automatic | Remote API | Quota |
137
+ | Cursor | [Needs setup](docs/readme/providers.md#cursor) | Local estimate | Budget and spend |
138
+ | Qwen Code | [Needs setup](docs/readme/providers.md#qwen-code) | Local estimate | Quota |
139
+ | Alibaba Coding Plan | OpenCode config | Local estimate | Quota |
140
+ | MiniMax Coding Plan | OpenCode config | Remote API | Quota |
141
+ | MiniMax Coding Plan (CN) | OpenCode config | Remote API | Quota |
142
+ | Kimi Code | OpenCode config | Remote API | Quota |
143
+ | Chutes AI | API key/config | Remote API | Quota |
144
+ | Synthetic | Automatic | Remote API | Quota |
145
+ | Google Antigravity | [Needs setup](docs/readme/providers.md#google-antigravity) | Remote API | Quota |
146
+ | Google AGY | [Needs setup](docs/readme/providers.md#google-agy-quick-setup) | Remote API | Quota |
147
+ | Gemini CLI | [Needs setup](docs/readme/providers.md#gemini-cli) | Remote API | Quota |
148
+ | Z.ai Coding Plan | OpenCode config | Remote API | Quota |
149
+ | Zhipu Coding Plan | OpenCode config | Remote API | Quota |
150
+ | NanoGPT | API key/config | Remote API | Quota and balance |
151
+ | DeepSeek | API key/config | Remote API | Balance and status |
152
+ | Ollama Cloud | [Needs setup](docs/readme/providers.md#ollama-cloud) | Dashboard scraping | Quota |
153
+ | OpenCode Go | [Needs setup](docs/readme/providers.md#opencode-go) | Dashboard scraping | Quota |
154
+
155
+ The quota view uses short labels such as `Day quota`, `5h quota`, `Day budget`, and `Balance`. Bar width varies by surface. JSON keeps the precise accounting type for scripts.
156
+
157
+ ### Custom providers
158
+
159
+ A quota provider definition tells OpenCode Quota how to obtain accounting data for an OpenCode provider. Run the guided command. It asks only structural questions, previews the exact global OpenCode config change, and asks before writing:
160
+
161
+ ```bash
162
+ npx @slkiser/opencode-quota@latest provider add
163
+ ```
164
+
165
+ Setup details live in the [Provider setup guide](docs/readme/providers.md#custom-providers).
131
166
 
132
167
  ## Troubleshooting
133
168
 
134
- Start here when quota or token data looks wrong:
169
+ If quota or token data looks wrong:
135
170
 
136
171
  1. Run `/quota_status`, or start with `opencode-quota show` for a terminal quota summary.
137
172
  2. Confirm the expected provider appears in the detected provider list.
@@ -141,12 +176,23 @@ Start here when quota or token data looks wrong:
141
176
 
142
177
  ## Reference
143
178
 
179
+ Project guides:
180
+
144
181
  - [Manual install](docs/readme/manual-install.md)
145
182
  - [Configuration](docs/readme/configuration.md)
146
183
  - [Providers](docs/readme/providers.md)
147
184
  - [Troubleshooting](docs/readme/troubleshooting.md)
148
185
  - [External integration](docs/readme/external-integration.md)
149
186
 
187
+ External references:
188
+
189
+ - [OpenCode docs](https://opencode.ai/docs/)
190
+ - [OpenCode config](https://opencode.ai/docs/config/)
191
+ - [OpenCode plugins](https://opencode.ai/docs/plugins/)
192
+ - [OpenCode TUI](https://opencode.ai/docs/tui/)
193
+ - [models.dev pricing data](https://models.dev/)
194
+ - [Node.js downloads](https://nodejs.org/en/download)
195
+
150
196
  ## Contributors
151
197
 
152
198
  Thanks to everyone who has contributed to OpenCode Quota.
@@ -165,4 +211,4 @@ OpenCode Quota is not built by the OpenCode team and is not affiliated with Open
165
211
 
166
212
  ## Star history
167
213
 
168
- [![Star History Chart](https://api.star-history.com/chart?repos=slkiser/opencode-quota&type=date&legend=bottom-right)](https://www.star-history.com/?repos=slkiser%2Fopencode-quota&type=date&legend=bottom-right)
214
+ ![Star History Chart](https://shawnkiser.com/opencode-quota/star-history-2026710.webp)
@@ -1 +1 @@
1
- {"version":3,"file":"opencode-quota.d.ts","sourceRoot":"","sources":["../../src/bin/opencode-quota.ts"],"names":[],"mappings":";AAmCA,wBAAgB,gBAAgB,CAC9B,KAAK,GAAE,MAAM,GAAG,SAA2B,EAC3C,UAAU,GAAE,MAAuC,EACnD,WAAW,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAuB,GACzD,OAAO,CAMT;AAED,wBAAsB,IAAI,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BxE"}
1
+ {"version":3,"file":"opencode-quota.d.ts","sourceRoot":"","sources":["../../src/bin/opencode-quota.ts"],"names":[],"mappings":";AA2CA,wBAAgB,gBAAgB,CAC9B,KAAK,GAAE,MAAM,GAAG,SAA2B,EAC3C,UAAU,GAAE,MAAuC,EACnD,WAAW,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAuB,GACzD,OAAO,CAMT;AAED,wBAAsB,IAAI,CAAC,IAAI,WAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwCxE"}
@@ -5,17 +5,25 @@ import { fileURLToPath } from "node:url";
5
5
  import { runInitInstaller } from "../lib/init-installer.js";
6
6
  const USAGE = [
7
7
  "Usage:",
8
- " npx @slkiser/opencode-quota init [--sync-legacy-config]",
8
+ " npx @slkiser/opencode-quota init [--dry-run] [--sync-legacy-config]",
9
9
  " npx @slkiser/opencode-quota show [--provider <provider-id>] [--json] [--threshold <pct>]",
10
+ " npx @slkiser/opencode-quota update [--dry-run] [--yes]",
11
+ " npx @slkiser/opencode-quota provider add [--dry-run]",
10
12
  " npx @slkiser/opencode-quota --help",
11
13
  "",
12
14
  "Commands:",
13
15
  " init Run the interactive quota installer",
16
+ " --dry-run Preview validated changes without writing files",
14
17
  " --sync-legacy-config also writes experimental.quotaToast",
15
18
  " show Print a quick quota glance",
16
19
  " --json Machine-readable JSON output (reads from cache)",
17
- " --threshold <pct> With --json, exit 1 if below <pct>%, 2 if no cached quota",
20
+ " --threshold <pct> With --json, exit 1 if below <pct>%, 2 if incomplete/not comparable",
18
21
  " --provider <id> Filter to one provider",
22
+ " update Safely refresh only OpenCode Quota config and verified cache entries",
23
+ " --dry-run Preview without changing config or cache",
24
+ " --yes Apply noninteractively after printing the preview",
25
+ " provider add Add or update one global quotaProviders definition",
26
+ " --dry-run Preview the exact global OpenCode config without writing",
19
27
  ].join("\n");
20
28
  function printUsage() {
21
29
  console.log(USAGE);
@@ -45,17 +53,26 @@ export async function main(argv = process.argv.slice(2)) {
45
53
  return 0;
46
54
  }
47
55
  if (command === "init") {
48
- if (rest.length === 0) {
49
- return await runInitInstaller();
50
- }
51
- if (rest.length === 1 && rest[0] === "--sync-legacy-config") {
52
- return await runInitInstaller({ syncLegacyConfig: true });
56
+ const allowed = new Set(["--dry-run", "--sync-legacy-config"]);
57
+ if (rest.every((arg) => allowed.has(arg)) && new Set(rest).size === rest.length) {
58
+ return await runInitInstaller({
59
+ ...(rest.includes("--dry-run") ? { dryRun: true } : {}),
60
+ ...(rest.includes("--sync-legacy-config") ? { syncLegacyConfig: true } : {}),
61
+ });
53
62
  }
54
63
  }
55
64
  if (command === "show") {
56
65
  const { runCliShowCommand } = await import("../lib/cli-show.js");
57
66
  return await runCliShowCommand({ argv: rest });
58
67
  }
68
+ if (command === "update") {
69
+ const { runScopedUpdateCommand } = await import("../lib/scoped-update.js");
70
+ return await runScopedUpdateCommand({ argv: rest });
71
+ }
72
+ if (command === "provider" && rest[0] === "add") {
73
+ const { runProviderAddCommand } = await import("../lib/provider-add-command.js");
74
+ return await runProviderAddCommand({ argv: rest.slice(1) });
75
+ }
59
76
  printUsage();
60
77
  return 1;
61
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"opencode-quota.js","sourceRoot":"","sources":["../../src/bin/opencode-quota.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,KAAK,GAAG;IACZ,QAAQ;IACR,2DAA2D;IAC3D,4FAA4F;IAC5F,sCAAsC;IACtC,EAAE;IACF,WAAW;IACX,+CAA+C;IAC/C,oEAAoE;IACpE,sCAAsC;IACtC,gFAAgF;IAChF,0FAA0F;IAC1F,uDAAuD;CACxD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB;IACtC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAA4B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAC3C,aAAqB,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EACnD,cAA4C,cAAc;IAE1D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,WAAW,CAAC,UAAU,CAAC,KAAK,WAAW,CAAC,KAAK,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAEhC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACnE,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,MAAM,gBAAgB,EAAE,CAAC;QAClC,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,sBAAsB,EAAE,CAAC;YAC5D,OAAO,MAAM,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACjE,OAAO,MAAM,iBAAiB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,UAAU,EAAE,CAAC;IACb,OAAO,CAAC,CAAC;AACX,CAAC;AAED,IAAI,gBAAgB,EAAE,EAAE,CAAC;IACvB,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"opencode-quota.js","sourceRoot":"","sources":["../../src/bin/opencode-quota.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -11,5 +11,7 @@ declare const pluginModule: {
11
11
  };
12
12
  export default pluginModule;
13
13
  export { QuotaToastPlugin } from "./plugin.js";
14
+ export { QUOTA_PROVIDER_MODES, QUOTA_PROVIDER_REMOTE_FORMATS, QUOTA_PROVIDER_WINDOW_TYPES, validateQuotaProviders, } from "./lib/quota-providers.js";
15
+ export type { QuotaProviderDefinition, RemoteApiQuotaProviderDefinition, LocalEstimateQuotaProviderDefinition, LocalEstimateWindow, } from "./lib/quota-providers.js";
14
16
  export type { QuotaToastConfig, MaintainerAnnouncementsConfig, GoogleModelId, PricingSnapshotSource, CopilotEnterpriseUsageResult, CopilotOrganizationUsageResult, CopilotQuotaResult, GoogleQuotaResult, GoogleModelQuota, MiniMaxResult, MiniMaxResultEntry, } from "./lib/types.js";
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,QAAA,MAAM,YAAY;;;CAGQ,CAAC;AAE3B,eAAe,YAAY,CAAC;AAI5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,YAAY,EACV,gBAAgB,EAChB,6BAA6B,EAC7B,aAAa,EACb,qBAAqB,EACrB,4BAA4B,EAC5B,8BAA8B,EAC9B,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,GACnB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,QAAA,MAAM,YAAY;;;CAGQ,CAAC;AAE3B,eAAe,YAAY,CAAC;AAI5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,uBAAuB,EACvB,gCAAgC,EAChC,oCAAoC,EACpC,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,gBAAgB,EAChB,6BAA6B,EAC7B,aAAa,EACb,qBAAqB,EACrB,4BAA4B,EAC5B,8BAA8B,EAC9B,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,GACnB,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -18,4 +18,6 @@ export default pluginModule;
18
18
  // Keep the named export for backward compatibility with consumers that import
19
19
  // { QuotaToastPlugin } directly.
20
20
  export { QuotaToastPlugin } from "./plugin.js";
21
+ // Re-export types for consumers (types are erased at runtime, so safe to export)
22
+ export { QUOTA_PROVIDER_MODES, QUOTA_PROVIDER_REMOTE_FORMATS, QUOTA_PROVIDER_WINDOW_TYPES, validateQuotaProviders, } from "./lib/quota-providers.js";
21
23
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAO/C,qDAAqD;AACrD,6EAA6E;AAC7E,wEAAwE;AACxE,yCAAyC;AACzC,MAAM,YAAY,GAAG;IACnB,EAAE,EAAE,yBAAyB;IAC7B,MAAM,EAAE,gBAAgB;CACA,CAAC;AAE3B,eAAe,YAAY,CAAC;AAE5B,8EAA8E;AAC9E,iCAAiC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAO/C,qDAAqD;AACrD,6EAA6E;AAC7E,wEAAwE;AACxE,yCAAyC;AACzC,MAAM,YAAY,GAAG;IACnB,EAAE,EAAE,yBAAyB;IAC7B,MAAM,EAAE,gBAAgB;CACA,CAAC;AAE3B,eAAe,YAAY,CAAC;AAE5B,8EAA8E;AAC9E,iCAAiC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,iFAAiF;AACjF,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,0BAA0B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/lib/anthropic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/lib/anthropic.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@
7
7
  * file) and Anthropic's OAuth usage endpoint.
8
8
  */
9
9
  import { execFile } from "child_process";
10
+ import { createHash } from "crypto";
10
11
  import { readFile } from "fs/promises";
11
12
  import { homedir } from "os";
12
13
  import { join } from "path";
@@ -15,6 +16,8 @@ import { fetchWithTimeout } from "./http.js";
15
16
  const DEFAULT_CLAUDE_BINARY = "claude";
16
17
  const CLAUDE_COMMAND_TIMEOUT_MS = 3_000;
17
18
  const ANTHROPIC_DIAGNOSTICS_TTL_MS = 5_000;
19
+ const ANTHROPIC_OAUTH_BACKOFF_BASE_MS = 30_000;
20
+ const ANTHROPIC_OAUTH_COOLDOWN_MAX_MS = 15 * 60_000;
18
21
  const ANTHROPIC_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
19
22
  const ANTHROPIC_BETA_HEADER = "oauth-2025-04-20";
20
23
  const CLAUDE_CODE_CREDENTIALS_SERVICE = "Claude Code-credentials";
@@ -22,6 +25,8 @@ const CLAUDE_NO_LOCAL_QUOTA_MESSAGE = "Claude CLI auth detected, but local quota
22
25
  const ANTHROPIC_NO_QUOTA_MESSAGE = "Claude CLI auth detected, but quota was unavailable from both the local CLI and Claude OAuth fallback.";
23
26
  const diagnosticsCache = new Map();
24
27
  const localDiagnosticsCache = new Map();
28
+ const anthropicOAuthCooldowns = new Map();
29
+ const anthropicOAuthInFlight = new Map();
25
30
  export function resolveAnthropicBinaryPath(binaryPath) {
26
31
  const trimmed = binaryPath?.trim();
27
32
  return trimmed ? trimmed : DEFAULT_CLAUDE_BINARY;
@@ -361,7 +366,53 @@ async function readClaudeCredentialsAccessToken() {
361
366
  detail: unavailableDetails[0] ?? getClaudeCredentialsNotFoundDetail(locationsChecked),
362
367
  };
363
368
  }
364
- async function queryAnthropicQuotaFromOAuthAccessToken(accessToken, requestTimeoutMs) {
369
+ function fingerprintAccessToken(accessToken) {
370
+ return createHash("sha256").update(accessToken).digest("hex");
371
+ }
372
+ function parseRetryAfterMs(value, nowMs) {
373
+ const trimmed = value?.trim();
374
+ if (!trimmed) {
375
+ return undefined;
376
+ }
377
+ if (/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(trimmed)) {
378
+ const seconds = Number(trimmed);
379
+ return Number.isFinite(seconds) && seconds > 0 ? seconds * 1_000 : undefined;
380
+ }
381
+ const retryAtMs = Date.parse(trimmed);
382
+ const durationMs = retryAtMs - nowMs;
383
+ return Number.isFinite(durationMs) && durationMs > 0 ? durationMs : undefined;
384
+ }
385
+ function getAnthropicOAuthCooldownDurationMs(failureCount, retryAfter, nowMs) {
386
+ const exponentialMs = Math.min(ANTHROPIC_OAUTH_COOLDOWN_MAX_MS, ANTHROPIC_OAUTH_BACKOFF_BASE_MS * 2 ** Math.min(failureCount, 30));
387
+ const retryAfterMs = Math.min(ANTHROPIC_OAUTH_COOLDOWN_MAX_MS, parseRetryAfterMs(retryAfter, nowMs) ?? 0);
388
+ return Math.max(exponentialMs, retryAfterMs);
389
+ }
390
+ function getAnthropicOAuthCooldownMessage(remainingMs) {
391
+ const boundedRemainingMs = Math.min(ANTHROPIC_OAUTH_COOLDOWN_MAX_MS, Math.max(0, remainingMs));
392
+ return `Anthropic OAuth usage probe paused after HTTP 429; retry in ${Math.ceil(boundedRemainingMs / 1_000)}s.`;
393
+ }
394
+ function retainAnthropicOAuthCooldownForToken(tokenFingerprint) {
395
+ for (const fingerprint of anthropicOAuthCooldowns.keys()) {
396
+ if (fingerprint !== tokenFingerprint) {
397
+ anthropicOAuthCooldowns.delete(fingerprint);
398
+ }
399
+ }
400
+ }
401
+ function redactAccessTokenFromSanitizedDetail(detail, accessToken) {
402
+ const sanitizedDetail = sanitizeDisplayText(detail);
403
+ const sanitizedAccessToken = sanitizeDisplayText(accessToken);
404
+ const redactedDetail = sanitizedDetail.split(accessToken).join("[redacted]");
405
+ return sanitizedAccessToken
406
+ ? redactedDetail.split(sanitizedAccessToken).join("[redacted]")
407
+ : redactedDetail;
408
+ }
409
+ function sanitizeAnthropicApiDetail(detail, accessToken) {
410
+ return redactAccessTokenFromSanitizedDetail(detail, accessToken).slice(0, 120);
411
+ }
412
+ function sanitizeAnthropicRequestError(detail, accessToken) {
413
+ return redactAccessTokenFromSanitizedDetail(detail, accessToken);
414
+ }
415
+ async function performAnthropicOAuthUsageRequest(accessToken, tokenFingerprint, requestTimeoutMs) {
365
416
  let response;
366
417
  try {
367
418
  response = await fetchWithTimeout(ANTHROPIC_USAGE_URL, {
@@ -374,13 +425,37 @@ async function queryAnthropicQuotaFromOAuthAccessToken(accessToken, requestTimeo
374
425
  catch (error) {
375
426
  return {
376
427
  state: "unavailable",
377
- detail: sanitizeDisplayText(error instanceof Error ? error.message : String(error)),
428
+ detail: sanitizeAnthropicRequestError(error instanceof Error ? error.message : String(error), accessToken),
378
429
  };
379
430
  }
431
+ if (response.status === 429) {
432
+ let detail = "";
433
+ try {
434
+ detail = sanitizeAnthropicApiDetail(await response.text(), accessToken);
435
+ }
436
+ catch {
437
+ detail = "";
438
+ }
439
+ const nowMs = Date.now();
440
+ const previousFailureCount = anthropicOAuthCooldowns.get(tokenFingerprint)?.failureCount ?? 0;
441
+ const durationMs = getAnthropicOAuthCooldownDurationMs(previousFailureCount, response.headers?.get?.("Retry-After") ?? null, nowMs);
442
+ anthropicOAuthCooldowns.set(tokenFingerprint, {
443
+ failureCount: previousFailureCount + 1,
444
+ blockedUntilMs: nowMs + durationMs,
445
+ });
446
+ const errorDetail = detail
447
+ ? `Anthropic API error ${response.status}: ${detail}`
448
+ : `Anthropic API returned ${response.status}`;
449
+ return {
450
+ state: "unavailable",
451
+ detail: `${errorDetail} ${getAnthropicOAuthCooldownMessage(durationMs)}`,
452
+ };
453
+ }
454
+ anthropicOAuthCooldowns.delete(tokenFingerprint);
380
455
  if (!response.ok) {
381
456
  let detail = "";
382
457
  try {
383
- detail = sanitizeDisplaySnippet(await response.text(), 120);
458
+ detail = sanitizeAnthropicApiDetail(await response.text(), accessToken);
384
459
  }
385
460
  catch {
386
461
  detail = "";
@@ -414,6 +489,32 @@ async function queryAnthropicQuotaFromOAuthAccessToken(accessToken, requestTimeo
414
489
  quota,
415
490
  };
416
491
  }
492
+ async function queryAnthropicQuotaFromOAuthAccessToken(accessToken, requestTimeoutMs) {
493
+ const tokenFingerprint = fingerprintAccessToken(accessToken);
494
+ retainAnthropicOAuthCooldownForToken(tokenFingerprint);
495
+ const nowMs = Date.now();
496
+ const cooldown = anthropicOAuthCooldowns.get(tokenFingerprint);
497
+ if (cooldown && cooldown.blockedUntilMs > nowMs) {
498
+ return {
499
+ state: "unavailable",
500
+ detail: getAnthropicOAuthCooldownMessage(cooldown.blockedUntilMs - nowMs),
501
+ };
502
+ }
503
+ const existingInFlight = anthropicOAuthInFlight.get(tokenFingerprint);
504
+ if (existingInFlight) {
505
+ return await existingInFlight;
506
+ }
507
+ const inFlight = performAnthropicOAuthUsageRequest(accessToken, tokenFingerprint, requestTimeoutMs);
508
+ anthropicOAuthInFlight.set(tokenFingerprint, inFlight);
509
+ try {
510
+ return await inFlight;
511
+ }
512
+ finally {
513
+ if (anthropicOAuthInFlight.get(tokenFingerprint) === inFlight) {
514
+ anthropicOAuthInFlight.delete(tokenFingerprint);
515
+ }
516
+ }
517
+ }
417
518
  function extractAuthBoolean(data) {
418
519
  const record = asRecord(data);
419
520
  if (!record) {
@@ -665,6 +766,8 @@ async function probeAnthropicLocalDiagnostics(options = {}) {
665
766
  export function clearAnthropicDiagnosticsCacheForTests() {
666
767
  diagnosticsCache.clear();
667
768
  localDiagnosticsCache.clear();
769
+ anthropicOAuthCooldowns.clear();
770
+ anthropicOAuthInFlight.clear();
668
771
  }
669
772
  async function getCachedAnthropicLocalDiagnostics(options = {}) {
670
773
  const binaryPath = resolveAnthropicBinaryPath(options.binaryPath);