@slkiser/opencode-quota 4.10.5 → 5.0.0-beta.1

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 (390) hide show
  1. package/README.md +43 -31
  2. package/dist/bin/opencode-quota.d.ts +6 -0
  3. package/dist/bin/opencode-quota.d.ts.map +1 -1
  4. package/dist/bin/opencode-quota.js +17 -0
  5. package/dist/bin/opencode-quota.js.map +1 -1
  6. package/dist/data/modelsdev-pricing.min.json +83 -2
  7. package/dist/index.d.ts +1 -6
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -13
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/accounting-format.js +1 -1
  12. package/dist/lib/accounting-format.js.map +1 -1
  13. package/dist/lib/alibaba-auth.d.ts +5 -5
  14. package/dist/lib/alibaba-auth.d.ts.map +1 -1
  15. package/dist/lib/alibaba-auth.js +15 -8
  16. package/dist/lib/alibaba-auth.js.map +1 -1
  17. package/dist/lib/alibaba-token-plan.d.ts +7 -4
  18. package/dist/lib/alibaba-token-plan.d.ts.map +1 -1
  19. package/dist/lib/alibaba-token-plan.js +32 -10
  20. package/dist/lib/alibaba-token-plan.js.map +1 -1
  21. package/dist/lib/anthropic-auth.d.ts +4 -1
  22. package/dist/lib/anthropic-auth.d.ts.map +1 -1
  23. package/dist/lib/anthropic-auth.js +5 -1
  24. package/dist/lib/anthropic-auth.js.map +1 -1
  25. package/dist/lib/anthropic.d.ts +7 -1
  26. package/dist/lib/anthropic.d.ts.map +1 -1
  27. package/dist/lib/anthropic.js +61 -7
  28. package/dist/lib/anthropic.js.map +1 -1
  29. package/dist/lib/api-key-resolver.d.ts +27 -16
  30. package/dist/lib/api-key-resolver.d.ts.map +1 -1
  31. package/dist/lib/api-key-resolver.js +46 -19
  32. package/dist/lib/api-key-resolver.js.map +1 -1
  33. package/dist/lib/chutes-config.d.ts +2 -2
  34. package/dist/lib/chutes-config.d.ts.map +1 -1
  35. package/dist/lib/chutes-config.js +4 -4
  36. package/dist/lib/chutes-config.js.map +1 -1
  37. package/dist/lib/cli-reports.d.ts +30 -0
  38. package/dist/lib/cli-reports.d.ts.map +1 -0
  39. package/dist/lib/cli-reports.js +172 -0
  40. package/dist/lib/cli-reports.js.map +1 -0
  41. package/dist/lib/cli-show.d.ts +14 -1
  42. package/dist/lib/cli-show.d.ts.map +1 -1
  43. package/dist/lib/cli-show.js +40 -138
  44. package/dist/lib/cli-show.js.map +1 -1
  45. package/dist/lib/cli-status.d.ts.map +1 -1
  46. package/dist/lib/cli-status.js +9 -36
  47. package/dist/lib/cli-status.js.map +1 -1
  48. package/dist/lib/config-file-utils.d.ts +10 -13
  49. package/dist/lib/config-file-utils.d.ts.map +1 -1
  50. package/dist/lib/config-file-utils.js +28 -30
  51. package/dist/lib/config-file-utils.js.map +1 -1
  52. package/dist/lib/config.d.ts +2 -2
  53. package/dist/lib/config.d.ts.map +1 -1
  54. package/dist/lib/config.js +6 -17
  55. package/dist/lib/config.js.map +1 -1
  56. package/dist/lib/copilot.d.ts +4 -1
  57. package/dist/lib/copilot.d.ts.map +1 -1
  58. package/dist/lib/copilot.js +17 -19
  59. package/dist/lib/copilot.js.map +1 -1
  60. package/dist/lib/cursor-detection.d.ts +1 -1
  61. package/dist/lib/cursor-detection.d.ts.map +1 -1
  62. package/dist/lib/cursor-detection.js +54 -51
  63. package/dist/lib/cursor-detection.js.map +1 -1
  64. package/dist/lib/cursor-pricing.d.ts +1 -1
  65. package/dist/lib/cursor-pricing.d.ts.map +1 -1
  66. package/dist/lib/cursor-pricing.js +85 -0
  67. package/dist/lib/cursor-pricing.js.map +1 -1
  68. package/dist/lib/deepseek-auth.d.ts +2 -2
  69. package/dist/lib/deepseek-auth.d.ts.map +1 -1
  70. package/dist/lib/deepseek-auth.js +4 -4
  71. package/dist/lib/deepseek-auth.js.map +1 -1
  72. package/dist/lib/display-sanitize.js +1 -1
  73. package/dist/lib/display-sanitize.js.map +1 -1
  74. package/dist/lib/entries.d.ts +7 -2
  75. package/dist/lib/entries.d.ts.map +1 -1
  76. package/dist/lib/format-utils.d.ts +1 -1
  77. package/dist/lib/format-utils.js +1 -1
  78. package/dist/lib/glm-coding-plan.d.ts +1 -0
  79. package/dist/lib/glm-coding-plan.d.ts.map +1 -1
  80. package/dist/lib/glm-coding-plan.js +3 -1
  81. package/dist/lib/glm-coding-plan.js.map +1 -1
  82. package/dist/lib/google-agy.d.ts +6 -12
  83. package/dist/lib/google-agy.d.ts.map +1 -1
  84. package/dist/lib/google-agy.js +26 -24
  85. package/dist/lib/google-agy.js.map +1 -1
  86. package/dist/lib/google-companion-credentials.d.ts.map +1 -1
  87. package/dist/lib/google-companion-credentials.js +76 -26
  88. package/dist/lib/google-companion-credentials.js.map +1 -1
  89. package/dist/lib/google-gemini-cli.d.ts +7 -12
  90. package/dist/lib/google-gemini-cli.d.ts.map +1 -1
  91. package/dist/lib/google-gemini-cli.js +42 -31
  92. package/dist/lib/google-gemini-cli.js.map +1 -1
  93. package/dist/lib/init-installer.d.ts +3 -1
  94. package/dist/lib/init-installer.d.ts.map +1 -1
  95. package/dist/lib/init-installer.js +69 -172
  96. package/dist/lib/init-installer.js.map +1 -1
  97. package/dist/lib/kilo-config.d.ts +2 -2
  98. package/dist/lib/kilo-config.d.ts.map +1 -1
  99. package/dist/lib/kilo-config.js +6 -5
  100. package/dist/lib/kilo-config.js.map +1 -1
  101. package/dist/lib/kimi-auth.d.ts +2 -2
  102. package/dist/lib/kimi-auth.d.ts.map +1 -1
  103. package/dist/lib/kimi-auth.js +4 -4
  104. package/dist/lib/kimi-auth.js.map +1 -1
  105. package/dist/lib/maintainer-announcements.d.ts.map +1 -1
  106. package/dist/lib/maintainer-announcements.js +5 -5
  107. package/dist/lib/maintainer-announcements.js.map +1 -1
  108. package/dist/lib/mimo-config.d.ts.map +1 -1
  109. package/dist/lib/mimo-config.js +2 -3
  110. package/dist/lib/mimo-config.js.map +1 -1
  111. package/dist/lib/minimax-auth.d.ts +6 -6
  112. package/dist/lib/minimax-auth.d.ts.map +1 -1
  113. package/dist/lib/minimax-auth.js +18 -17
  114. package/dist/lib/minimax-auth.js.map +1 -1
  115. package/dist/lib/nanogpt-config.d.ts +2 -2
  116. package/dist/lib/nanogpt-config.d.ts.map +1 -1
  117. package/dist/lib/nanogpt-config.js +4 -4
  118. package/dist/lib/nanogpt-config.js.map +1 -1
  119. package/dist/lib/ollama-cloud-config.d.ts +2 -2
  120. package/dist/lib/ollama-cloud-config.d.ts.map +1 -1
  121. package/dist/lib/ollama-cloud-config.js +6 -5
  122. package/dist/lib/ollama-cloud-config.js.map +1 -1
  123. package/dist/lib/openai.d.ts +7 -2
  124. package/dist/lib/openai.d.ts.map +1 -1
  125. package/dist/lib/openai.js +22 -8
  126. package/dist/lib/openai.js.map +1 -1
  127. package/dist/lib/opencode-auth-sqlite.d.ts +17 -0
  128. package/dist/lib/opencode-auth-sqlite.d.ts.map +1 -0
  129. package/dist/lib/opencode-auth-sqlite.js +105 -0
  130. package/dist/lib/opencode-auth-sqlite.js.map +1 -0
  131. package/dist/lib/opencode-auth.d.ts +127 -14
  132. package/dist/lib/opencode-auth.d.ts.map +1 -1
  133. package/dist/lib/opencode-auth.js +373 -46
  134. package/dist/lib/opencode-auth.js.map +1 -1
  135. package/dist/lib/opencode-config-providers.d.ts.map +1 -1
  136. package/dist/lib/opencode-config-providers.js +17 -18
  137. package/dist/lib/opencode-config-providers.js.map +1 -1
  138. package/dist/lib/opencode-console-auth.d.ts +47 -0
  139. package/dist/lib/opencode-console-auth.d.ts.map +1 -0
  140. package/dist/lib/opencode-console-auth.js +63 -0
  141. package/dist/lib/opencode-console-auth.js.map +1 -0
  142. package/dist/lib/opencode-db-path.d.ts +9 -0
  143. package/dist/lib/opencode-db-path.d.ts.map +1 -0
  144. package/dist/lib/opencode-db-path.js +16 -0
  145. package/dist/lib/opencode-db-path.js.map +1 -0
  146. package/dist/lib/opencode-go-auth.d.ts +4 -2
  147. package/dist/lib/opencode-go-auth.d.ts.map +1 -1
  148. package/dist/lib/opencode-go-auth.js +9 -5
  149. package/dist/lib/opencode-go-auth.js.map +1 -1
  150. package/dist/lib/opencode-go.d.ts +12 -0
  151. package/dist/lib/opencode-go.d.ts.map +1 -1
  152. package/dist/lib/opencode-go.js +113 -0
  153. package/dist/lib/opencode-go.js.map +1 -1
  154. package/dist/lib/opencode-runtime-paths.d.ts +6 -12
  155. package/dist/lib/opencode-runtime-paths.d.ts.map +1 -1
  156. package/dist/lib/opencode-runtime-paths.js +12 -82
  157. package/dist/lib/opencode-runtime-paths.js.map +1 -1
  158. package/dist/lib/opencode-sqlite.d.ts +4 -0
  159. package/dist/lib/opencode-sqlite.d.ts.map +1 -1
  160. package/dist/lib/opencode-sqlite.js +17 -52
  161. package/dist/lib/opencode-sqlite.js.map +1 -1
  162. package/dist/lib/opencode-storage.d.ts +4 -8
  163. package/dist/lib/opencode-storage.d.ts.map +1 -1
  164. package/dist/lib/opencode-storage.js +66 -90
  165. package/dist/lib/opencode-storage.js.map +1 -1
  166. package/dist/lib/opencode-version.d.ts +6 -0
  167. package/dist/lib/opencode-version.d.ts.map +1 -0
  168. package/dist/lib/opencode-version.js +26 -0
  169. package/dist/lib/opencode-version.js.map +1 -0
  170. package/dist/lib/opencode-zen.d.ts +10 -4
  171. package/dist/lib/opencode-zen.d.ts.map +1 -1
  172. package/dist/lib/opencode-zen.js +23 -13
  173. package/dist/lib/opencode-zen.js.map +1 -1
  174. package/dist/lib/provider-registration.d.ts +3 -4
  175. package/dist/lib/provider-registration.d.ts.map +1 -1
  176. package/dist/lib/provider-registration.js +3 -4
  177. package/dist/lib/provider-registration.js.map +1 -1
  178. package/dist/lib/quota-dialog-command-specs.d.ts +59 -0
  179. package/dist/lib/quota-dialog-command-specs.d.ts.map +1 -0
  180. package/dist/lib/quota-dialog-command-specs.js +145 -0
  181. package/dist/lib/quota-dialog-command-specs.js.map +1 -0
  182. package/dist/lib/quota-dialog-commands.d.ts +1 -25
  183. package/dist/lib/quota-dialog-commands.d.ts.map +1 -1
  184. package/dist/lib/quota-dialog-commands.js +13 -132
  185. package/dist/lib/quota-dialog-commands.js.map +1 -1
  186. package/dist/lib/quota-export.d.ts.map +1 -1
  187. package/dist/lib/quota-export.js +1 -0
  188. package/dist/lib/quota-export.js.map +1 -1
  189. package/dist/lib/quota-providers-remote.d.ts +2 -2
  190. package/dist/lib/quota-providers-remote.d.ts.map +1 -1
  191. package/dist/lib/quota-providers-remote.js +6 -4
  192. package/dist/lib/quota-providers-remote.js.map +1 -1
  193. package/dist/lib/quota-render-data.d.ts +3 -0
  194. package/dist/lib/quota-render-data.d.ts.map +1 -1
  195. package/dist/lib/quota-render-data.js +2 -0
  196. package/dist/lib/quota-render-data.js.map +1 -1
  197. package/dist/lib/quota-report-message.d.ts +21 -0
  198. package/dist/lib/quota-report-message.d.ts.map +1 -0
  199. package/dist/lib/quota-report-message.js +40 -0
  200. package/dist/lib/quota-report-message.js.map +1 -0
  201. package/dist/lib/quota-runtime-context.d.ts +1 -0
  202. package/dist/lib/quota-runtime-context.d.ts.map +1 -1
  203. package/dist/lib/quota-runtime-context.js +1 -0
  204. package/dist/lib/quota-runtime-context.js.map +1 -1
  205. package/dist/lib/quota-state-codec.js +5 -5
  206. package/dist/lib/quota-state-codec.js.map +1 -1
  207. package/dist/lib/quota-stats.js +2 -1
  208. package/dist/lib/quota-stats.js.map +1 -1
  209. package/dist/lib/quota-status.d.ts +2 -10
  210. package/dist/lib/quota-status.d.ts.map +1 -1
  211. package/dist/lib/quota-status.js +33 -42
  212. package/dist/lib/quota-status.js.map +1 -1
  213. package/dist/lib/quota-surface-data.d.ts +26 -0
  214. package/dist/lib/quota-surface-data.d.ts.map +1 -0
  215. package/dist/lib/quota-surface-data.js +299 -0
  216. package/dist/lib/quota-surface-data.js.map +1 -0
  217. package/dist/lib/scoped-update-migration.d.ts +4 -4
  218. package/dist/lib/scoped-update-migration.d.ts.map +1 -1
  219. package/dist/lib/scoped-update-migration.js +11 -18
  220. package/dist/lib/scoped-update-migration.js.map +1 -1
  221. package/dist/lib/scoped-update.d.ts +5 -1
  222. package/dist/lib/scoped-update.d.ts.map +1 -1
  223. package/dist/lib/scoped-update.js +73 -51
  224. package/dist/lib/scoped-update.js.map +1 -1
  225. package/dist/lib/synthetic-config.d.ts +2 -2
  226. package/dist/lib/synthetic-config.d.ts.map +1 -1
  227. package/dist/lib/synthetic-config.js +4 -4
  228. package/dist/lib/synthetic-config.js.map +1 -1
  229. package/dist/lib/toast-format-grouped.js +1 -1
  230. package/dist/lib/toast-format-grouped.js.map +1 -1
  231. package/dist/lib/tui-panel-state.d.ts +0 -49
  232. package/dist/lib/tui-panel-state.d.ts.map +1 -1
  233. package/dist/lib/tui-panel-state.js +1 -41
  234. package/dist/lib/tui-panel-state.js.map +1 -1
  235. package/dist/lib/tui-sidebar-format.d.ts.map +1 -1
  236. package/dist/lib/tui-sidebar-format.js +8 -1
  237. package/dist/lib/tui-sidebar-format.js.map +1 -1
  238. package/dist/lib/types.d.ts +26 -7
  239. package/dist/lib/types.d.ts.map +1 -1
  240. package/dist/lib/types.js +1 -2
  241. package/dist/lib/types.js.map +1 -1
  242. package/dist/lib/xai.d.ts +5 -1
  243. package/dist/lib/xai.d.ts.map +1 -1
  244. package/dist/lib/xai.js +13 -4
  245. package/dist/lib/xai.js.map +1 -1
  246. package/dist/lib/zai-auth.d.ts +2 -2
  247. package/dist/lib/zai-auth.d.ts.map +1 -1
  248. package/dist/lib/zai-auth.js +4 -4
  249. package/dist/lib/zai-auth.js.map +1 -1
  250. package/dist/lib/zai.d.ts +1 -0
  251. package/dist/lib/zai.d.ts.map +1 -1
  252. package/dist/lib/zai.js.map +1 -1
  253. package/dist/lib/zhipu-auth.d.ts +2 -2
  254. package/dist/lib/zhipu-auth.d.ts.map +1 -1
  255. package/dist/lib/zhipu-auth.js +4 -4
  256. package/dist/lib/zhipu-auth.js.map +1 -1
  257. package/dist/lib/zhipu.d.ts +1 -0
  258. package/dist/lib/zhipu.d.ts.map +1 -1
  259. package/dist/lib/zhipu.js.map +1 -1
  260. package/dist/plugin.d.ts +6 -9
  261. package/dist/plugin.d.ts.map +1 -1
  262. package/dist/plugin.js +213 -259
  263. package/dist/plugin.js.map +1 -1
  264. package/dist/providers/alibaba-coding-plan.d.ts.map +1 -1
  265. package/dist/providers/alibaba-coding-plan.js +1 -1
  266. package/dist/providers/alibaba-coding-plan.js.map +1 -1
  267. package/dist/providers/alibaba-token-plan.d.ts.map +1 -1
  268. package/dist/providers/alibaba-token-plan.js +1 -0
  269. package/dist/providers/alibaba-token-plan.js.map +1 -1
  270. package/dist/providers/anthropic.d.ts.map +1 -1
  271. package/dist/providers/anthropic.js +83 -1
  272. package/dist/providers/anthropic.js.map +1 -1
  273. package/dist/providers/cache-policies.d.ts +2 -2
  274. package/dist/providers/cache-policies.d.ts.map +1 -1
  275. package/dist/providers/cache-policies.js +10 -9
  276. package/dist/providers/cache-policies.js.map +1 -1
  277. package/dist/providers/copilot.d.ts.map +1 -1
  278. package/dist/providers/copilot.js +60 -9
  279. package/dist/providers/copilot.js.map +1 -1
  280. package/dist/providers/deepseek.js +1 -1
  281. package/dist/providers/deepseek.js.map +1 -1
  282. package/dist/providers/glm-coding-plan-provider.d.ts +13 -1
  283. package/dist/providers/glm-coding-plan-provider.d.ts.map +1 -1
  284. package/dist/providers/glm-coding-plan-provider.js +53 -0
  285. package/dist/providers/glm-coding-plan-provider.js.map +1 -1
  286. package/dist/providers/google-agy.d.ts.map +1 -1
  287. package/dist/providers/google-agy.js +56 -7
  288. package/dist/providers/google-agy.js.map +1 -1
  289. package/dist/providers/google-gemini-cli.d.ts.map +1 -1
  290. package/dist/providers/google-gemini-cli.js +72 -7
  291. package/dist/providers/google-gemini-cli.js.map +1 -1
  292. package/dist/providers/kilo.js +1 -1
  293. package/dist/providers/kilo.js.map +1 -1
  294. package/dist/providers/kimi-code.d.ts.map +1 -1
  295. package/dist/providers/kimi-code.js +60 -1
  296. package/dist/providers/kimi-code.js.map +1 -1
  297. package/dist/providers/minimax-coding-plan.d.ts.map +1 -1
  298. package/dist/providers/minimax-coding-plan.js +52 -1
  299. package/dist/providers/minimax-coding-plan.js.map +1 -1
  300. package/dist/providers/nanogpt.js +1 -1
  301. package/dist/providers/nanogpt.js.map +1 -1
  302. package/dist/providers/ollama-cloud.js +1 -1
  303. package/dist/providers/ollama-cloud.js.map +1 -1
  304. package/dist/providers/openai.d.ts.map +1 -1
  305. package/dist/providers/openai.js +59 -29
  306. package/dist/providers/openai.js.map +1 -1
  307. package/dist/providers/opencode-go.d.ts.map +1 -1
  308. package/dist/providers/opencode-go.js +207 -55
  309. package/dist/providers/opencode-go.js.map +1 -1
  310. package/dist/providers/opencode-zen.d.ts.map +1 -1
  311. package/dist/providers/opencode-zen.js +39 -54
  312. package/dist/providers/opencode-zen.js.map +1 -1
  313. package/dist/providers/openrouter.js +1 -1
  314. package/dist/providers/openrouter.js.map +1 -1
  315. package/dist/providers/quota-providers.js +9 -9
  316. package/dist/providers/quota-providers.js.map +1 -1
  317. package/dist/providers/result-helpers.d.ts +2 -2
  318. package/dist/providers/result-helpers.d.ts.map +1 -1
  319. package/dist/providers/result-helpers.js +2 -2
  320. package/dist/providers/result-helpers.js.map +1 -1
  321. package/dist/providers/xai.d.ts.map +1 -1
  322. package/dist/providers/xai.js +49 -1
  323. package/dist/providers/xai.js.map +1 -1
  324. package/dist/providers/zai.d.ts.map +1 -1
  325. package/dist/providers/zai.js +3 -1
  326. package/dist/providers/zai.js.map +1 -1
  327. package/dist/providers/zhipu.d.ts.map +1 -1
  328. package/dist/providers/zhipu.js +3 -1
  329. package/dist/providers/zhipu.js.map +1 -1
  330. package/dist/rpc.d.ts +72 -0
  331. package/dist/rpc.d.ts.map +1 -0
  332. package/dist/rpc.js +77 -0
  333. package/dist/rpc.js.map +1 -0
  334. package/dist/tui-v2.d.ts +5 -0
  335. package/dist/tui-v2.d.ts.map +1 -0
  336. package/dist/tui-v2.jsx +498 -0
  337. package/dist/tui-v2.jsx.map +1 -0
  338. package/dist/tui.d.ts +4 -6
  339. package/dist/tui.d.ts.map +1 -1
  340. package/dist/tui.js +542 -955
  341. package/package.json +7 -19
  342. package/dist/lib/cache.d.ts +0 -39
  343. package/dist/lib/cache.d.ts.map +0 -1
  344. package/dist/lib/cache.js +0 -124
  345. package/dist/lib/cache.js.map +0 -1
  346. package/dist/lib/command-handled.d.ts +0 -17
  347. package/dist/lib/command-handled.d.ts.map +0 -1
  348. package/dist/lib/command-handled.js +0 -36
  349. package/dist/lib/command-handled.js.map +0 -1
  350. package/dist/lib/command-surfaces.d.ts +0 -10
  351. package/dist/lib/command-surfaces.d.ts.map +0 -1
  352. package/dist/lib/command-surfaces.js +0 -9
  353. package/dist/lib/command-surfaces.js.map +0 -1
  354. package/dist/lib/opencode-zen-config.d.ts +0 -33
  355. package/dist/lib/opencode-zen-config.d.ts.map +0 -1
  356. package/dist/lib/opencode-zen-config.js +0 -158
  357. package/dist/lib/opencode-zen-config.js.map +0 -1
  358. package/dist/lib/path-pick.d.ts +0 -2
  359. package/dist/lib/path-pick.d.ts.map +0 -1
  360. package/dist/lib/path-pick.js +0 -15
  361. package/dist/lib/path-pick.js.map +0 -1
  362. package/dist/lib/quota-toast-runtime.d.ts +0 -60
  363. package/dist/lib/quota-toast-runtime.d.ts.map +0 -1
  364. package/dist/lib/quota-toast-runtime.js +0 -766
  365. package/dist/lib/quota-toast-runtime.js.map +0 -1
  366. package/dist/lib/tui-config-diagnostics.d.ts +0 -17
  367. package/dist/lib/tui-config-diagnostics.d.ts.map +0 -1
  368. package/dist/lib/tui-config-diagnostics.js +0 -72
  369. package/dist/lib/tui-config-diagnostics.js.map +0 -1
  370. package/dist/lib/tui-line-style.d.ts +0 -3
  371. package/dist/lib/tui-line-style.d.ts.map +0 -1
  372. package/dist/lib/tui-line-style.js +0 -7
  373. package/dist/lib/tui-line-style.js.map +0 -1
  374. package/dist/lib/tui-native-provider-quota.d.ts +0 -7
  375. package/dist/lib/tui-native-provider-quota.d.ts.map +0 -1
  376. package/dist/lib/tui-native-provider-quota.js +0 -25
  377. package/dist/lib/tui-native-provider-quota.js.map +0 -1
  378. package/dist/lib/tui-refresh-lifecycle.d.ts +0 -17
  379. package/dist/lib/tui-refresh-lifecycle.d.ts.map +0 -1
  380. package/dist/lib/tui-refresh-lifecycle.js +0 -82
  381. package/dist/lib/tui-refresh-lifecycle.js.map +0 -1
  382. package/dist/lib/tui-runtime.d.ts +0 -84
  383. package/dist/lib/tui-runtime.d.ts.map +0 -1
  384. package/dist/lib/tui-runtime.js +0 -527
  385. package/dist/lib/tui-runtime.js.map +0 -1
  386. package/dist/lib/tui-sidebar-content.d.ts +0 -32
  387. package/dist/lib/tui-sidebar-content.d.ts.map +0 -1
  388. package/dist/lib/tui-sidebar-content.js +0 -52
  389. package/dist/lib/tui-sidebar-content.js.map +0 -1
  390. package/dist/tui.tsx +0 -1030
package/dist/tui.tsx DELETED
@@ -1,1030 +0,0 @@
1
- /** @jsxImportSource @opentui/solid */
2
-
3
- import type {
4
- TuiPlugin,
5
- TuiPluginApi,
6
- TuiPluginModule,
7
- TuiPromptRef,
8
- } from "@opencode-ai/plugin/tui";
9
- import type { JSX } from "@opentui/solid";
10
- import { createEffect, createSignal, Index, onCleanup, Show } from "solid-js";
11
- import {
12
- formatQuotaModeHeading,
13
- formatResetCountdown,
14
- isResetTimeDecimals,
15
- resolveDisplayedPercent,
16
- } from "./lib/format-utils.js";
17
- import {
18
- buildQuotaDialogCommandOutput,
19
- QUOTA_DIALOG_COMMANDS,
20
- type QuotaDialogCommandId,
21
- type QuotaDialogCommandSpec,
22
- } from "./lib/quota-dialog-commands.js";
23
- import type { SessionTokenError } from "./lib/quota-status.js";
24
- import { disposeQuotaTelemetryOwner } from "./lib/quota-telemetry.js";
25
- import { getSidebarBodyLineColor } from "./lib/tui-line-style.js";
26
- import type {
27
- CompactStatusState,
28
- HomeBottomState,
29
- PromptBarState,
30
- SidebarPanelState,
31
- } from "./lib/tui-panel-state.js";
32
- import {
33
- getCompactStatusText,
34
- getHomeBottomAnnouncementText,
35
- getSidebarPanelLines,
36
- getSidebarPanelLinesExpanded,
37
- shouldRenderCompactStatus,
38
- shouldRenderHomeBottom,
39
- shouldRenderSidebarPanel,
40
- } from "./lib/tui-panel-state.js";
41
- import {
42
- formatPromptBarPercentMeta,
43
- PROMPT_BAR_WIDTH,
44
- resolvePromptBarLabel,
45
- } from "./lib/tui-prompt-bar-format.js";
46
- import { createTuiRefreshLifecycle } from "./lib/tui-refresh-lifecycle.js";
47
- import {
48
- createTuiQuotaClient,
49
- getTuiRuntimeRootHints,
50
- getTuiSessionModelMeta,
51
- loadTuiHomeBottomStatus,
52
- loadTuiSessionQuotaSurfaces,
53
- normalizeTuiSessionID,
54
- resolveTuiSurfaceRegistration,
55
- type TuiInitialRuntimeSeed,
56
- type TuiSurfaceRegistration,
57
- writeTuiQuotaExportIfEnabled,
58
- } from "./lib/tui-runtime.js";
59
- import { buildSidebarContentRows, type SidebarContentRow } from "./lib/tui-sidebar-content.js";
60
- import type { TuiCommandDisplay } from "./lib/types.js";
61
-
62
- const id = "@slkiser/opencode-quota";
63
- // Place Quota near the top so variable-height built-in sections
64
- // (MCP/LSP/Todo/Files) do not push it below the visible fold.
65
- const SIDEBAR_ORDER = 150;
66
- const COMPACT_ORDER = 90;
67
- const REFRESH_INTERVAL_MS = 60_000;
68
- const EVENT_REFRESH_DELAYS_MS = [150, 600] as const;
69
- const MOUNT_RECOVERY_DELAYS_MS = [500, 1_500, 4_000] as const;
70
-
71
- type TuiPromptRefCallback = (ref: TuiPromptRef | undefined) => void;
72
- type DialogSize = "medium" | "large" | "xlarge";
73
-
74
- type QuotaDialogCommandState = {
75
- lastSessionTokenError?: SessionTokenError;
76
- };
77
- type SessionQuotaResource = {
78
- sessionID: string;
79
- sidebar: () => SidebarPanelState;
80
- compact: () => CompactStatusState;
81
- promptBar: () => PromptBarState;
82
- retain: () => SessionQuotaResource;
83
- release: () => void;
84
- };
85
-
86
- type HomeBottomResource = {
87
- bottom: () => HomeBottomState;
88
- retain: () => HomeBottomResource;
89
- release: () => void;
90
- };
91
-
92
- type TuiInitialLoadCoordinator = {
93
- takeInitialSession: () => TuiInitialRuntimeSeed | undefined;
94
- takeInitialHome: () => TuiInitialRuntimeSeed | undefined;
95
- };
96
-
97
- type TuiRegistrationState =
98
- | { status: "pending" }
99
- | {
100
- status: "active";
101
- registration: TuiSurfaceRegistration;
102
- initialLoads?: TuiInitialLoadCoordinator;
103
- }
104
- | { status: "disposed" };
105
-
106
- type TuiRegistrationGate = {
107
- current: () => TuiRegistrationState;
108
- activate: (
109
- registration: TuiSurfaceRegistration,
110
- initialLoads?: TuiInitialLoadCoordinator,
111
- ) => void;
112
- dispose: () => void;
113
- };
114
-
115
- const FALLBACK_SURFACE_REGISTRATION: TuiSurfaceRegistration = {
116
- commandDisplay: "inline",
117
- sidebar: { enabled: true },
118
- compact: {
119
- enabled: false,
120
- homeBottom: false,
121
- sessionPrompt: false,
122
- hasNativeProviderQuota: false,
123
- suppressedByNativeProviderQuota: false,
124
- },
125
- promptBar: { enabled: false },
126
- announcements: { homeBottom: false },
127
- homeBottom: false,
128
- };
129
-
130
- function createTuiInitialLoadCoordinator(seed: TuiInitialRuntimeSeed): TuiInitialLoadCoordinator {
131
- let sessionAvailable = true;
132
- let homeAvailable = true;
133
-
134
- return {
135
- takeInitialSession() {
136
- if (!sessionAvailable) return undefined;
137
- sessionAvailable = false;
138
- return seed;
139
- },
140
- takeInitialHome() {
141
- if (!homeAvailable) return undefined;
142
- homeAvailable = false;
143
- return seed;
144
- },
145
- };
146
- }
147
-
148
- function createTuiRegistrationGate(): TuiRegistrationGate {
149
- const [current, setCurrent] = createSignal<TuiRegistrationState>({ status: "pending" });
150
-
151
- return {
152
- current,
153
- activate(registration, initialLoads) {
154
- if (current().status !== "pending") return;
155
- setCurrent({ status: "active", registration, initialLoads });
156
- },
157
- dispose() {
158
- if (current().status === "disposed") return;
159
- setCurrent({ status: "disposed" });
160
- },
161
- };
162
- }
163
-
164
- const sessionResources = new WeakMap<TuiPluginApi, Map<string, SessionQuotaResource>>();
165
- const homeResources = new WeakMap<TuiPluginApi, HomeBottomResource>();
166
-
167
- function getSessionResourceMap(api: TuiPluginApi): Map<string, SessionQuotaResource> {
168
- const existing = sessionResources.get(api);
169
- if (existing) return existing;
170
-
171
- const next = new Map<string, SessionQuotaResource>();
172
- sessionResources.set(api, next);
173
- return next;
174
- }
175
-
176
- function createSessionQuotaResource(
177
- api: TuiPluginApi,
178
- sessionID: string,
179
- initialLoads?: TuiInitialLoadCoordinator,
180
- ): SessionQuotaResource {
181
- const [sidebar, setSidebar] = createSignal<SidebarPanelState>({
182
- status: "loading",
183
- lines: [],
184
- });
185
- const [compact, setCompact] = createSignal<CompactStatusState>({ status: "loading" });
186
- const [promptBar, setPromptBar] = createSignal<PromptBarState>({ status: "loading" });
187
-
188
- let loadOrdinal = 0;
189
- const lifecycle = createTuiRefreshLifecycle({
190
- load: () => {
191
- const initialRuntimeSeed = loadOrdinal === 0 ? initialLoads?.takeInitialSession() : undefined;
192
- loadOrdinal += 1;
193
- return loadTuiSessionQuotaSurfaces({
194
- api,
195
- sessionID,
196
- ...(initialRuntimeSeed ? { initialRuntimeSeed } : {}),
197
- });
198
- },
199
- apply: (next) => {
200
- setSidebar(next.sidebar);
201
- setCompact(next.compact);
202
- setPromptBar(next.promptBar ?? { status: "loading" });
203
- },
204
- intervalMs: REFRESH_INTERVAL_MS,
205
- eventRefreshDelaysMs: EVENT_REFRESH_DELAYS_MS,
206
- // TUI/session state can hydrate asynchronously after mount or session switch,
207
- // so retry a few times to recover from empty first-load reads.
208
- recoveryDelaysMs: MOUNT_RECOVERY_DELAYS_MS,
209
- subscribe: (scheduleRefresh) => [
210
- api.event.on("session.updated", (event) => {
211
- if (event.properties?.info?.id === sessionID) {
212
- scheduleRefresh();
213
- }
214
- }),
215
- api.event.on("message.updated", (event) => {
216
- if (event.properties?.info?.sessionID === sessionID) {
217
- scheduleRefresh();
218
- }
219
- }),
220
- api.event.on("message.removed", (event) => {
221
- if (event.properties?.sessionID === sessionID) {
222
- scheduleRefresh();
223
- }
224
- }),
225
- api.event.on("tui.session.select", (event) => {
226
- if (event.properties?.sessionID === sessionID) {
227
- scheduleRefresh();
228
- }
229
- }),
230
- ],
231
- onDispose: () => {
232
- getSessionResourceMap(api).delete(sessionID);
233
- },
234
- });
235
-
236
- const resource: SessionQuotaResource = {
237
- sessionID,
238
- sidebar,
239
- compact,
240
- promptBar,
241
- retain: () => {
242
- lifecycle.retain();
243
- return resource;
244
- },
245
- release: lifecycle.release,
246
- };
247
-
248
- return resource;
249
- }
250
-
251
- function acquireSessionQuotaResource(
252
- api: TuiPluginApi,
253
- sessionID: string,
254
- initialLoads?: TuiInitialLoadCoordinator,
255
- ): SessionQuotaResource {
256
- const resources = getSessionResourceMap(api);
257
- const existing = resources.get(sessionID);
258
- if (existing) return existing.retain();
259
-
260
- const next = createSessionQuotaResource(api, sessionID, initialLoads).retain();
261
- resources.set(sessionID, next);
262
- return next;
263
- }
264
-
265
- function createHomeBottomResource(
266
- api: TuiPluginApi,
267
- compactHomeBottomEnabled: boolean,
268
- initialLoads?: TuiInitialLoadCoordinator,
269
- ): HomeBottomResource {
270
- const [bottom, setBottom] = createSignal<HomeBottomState>({
271
- status: "loading",
272
- compact: compactHomeBottomEnabled ? { status: "loading" } : { status: "disabled" },
273
- });
274
-
275
- let loadOrdinal = 0;
276
- const lifecycle = createTuiRefreshLifecycle({
277
- load: () => {
278
- const initialRuntimeSeed = loadOrdinal === 0 ? initialLoads?.takeInitialHome() : undefined;
279
- loadOrdinal += 1;
280
- return loadTuiHomeBottomStatus({
281
- api,
282
- ...(initialRuntimeSeed ? { initialRuntimeSeed } : {}),
283
- });
284
- },
285
- apply: setBottom,
286
- afterApply: () => {
287
- // Fire-and-forget: write export file if enabled. A failed write must
288
- // never affect TUI rendering, so log a warning and continue.
289
- void writeTuiQuotaExportIfEnabled({ api }).catch((err) => {
290
- console.warn(`[opencode-quota] quota export write failed: ${String(err)}`);
291
- });
292
- },
293
- intervalMs: REFRESH_INTERVAL_MS,
294
- eventRefreshDelaysMs: EVENT_REFRESH_DELAYS_MS,
295
- subscribe: (scheduleRefresh) => [
296
- api.event.on("session.updated", scheduleRefresh),
297
- api.event.on("message.updated", scheduleRefresh),
298
- api.event.on("message.removed", scheduleRefresh),
299
- api.event.on("tui.session.select", scheduleRefresh),
300
- ],
301
- onDispose: () => {
302
- homeResources.delete(api);
303
- },
304
- });
305
-
306
- const resource: HomeBottomResource = {
307
- bottom,
308
- retain: () => {
309
- lifecycle.retain();
310
- return resource;
311
- },
312
- release: lifecycle.release,
313
- };
314
-
315
- return resource;
316
- }
317
-
318
- function acquireHomeBottomResource(
319
- api: TuiPluginApi,
320
- compactHomeBottomEnabled: boolean,
321
- initialLoads?: TuiInitialLoadCoordinator,
322
- ): HomeBottomResource {
323
- const existing = homeResources.get(api);
324
- if (existing) return existing.retain();
325
-
326
- const next = createHomeBottomResource(api, compactHomeBottomEnabled, initialLoads).retain();
327
- homeResources.set(api, next);
328
- return next;
329
- }
330
-
331
- function useSessionQuotaResource(
332
- api: TuiPluginApi,
333
- sessionID: () => string,
334
- initialLoads?: TuiInitialLoadCoordinator,
335
- ): () => SessionQuotaResource {
336
- let current = acquireSessionQuotaResource(api, sessionID(), initialLoads);
337
- const [resource, setResource] = createSignal(current);
338
-
339
- createEffect(() => {
340
- const nextSessionID = sessionID();
341
- if (current.sessionID === nextSessionID) return;
342
-
343
- const previous = current;
344
- current = acquireSessionQuotaResource(api, nextSessionID, initialLoads);
345
- setResource(current);
346
- previous.release();
347
- });
348
-
349
- onCleanup(() => {
350
- current.release();
351
- });
352
-
353
- return resource;
354
- }
355
-
356
- function SidebarContentView(props: {
357
- api: TuiPluginApi;
358
- sessionID: string;
359
- initialLoads?: TuiInitialLoadCoordinator;
360
- }) {
361
- const resource = useSessionQuotaResource(props.api, () => props.sessionID, props.initialLoads);
362
- const panel = () => resource().sidebar();
363
-
364
- const lines = () => getSidebarPanelLines(panel());
365
- const hasDetailLines = () => Boolean(panel().linesExpanded?.length);
366
-
367
- const [collapsed, setCollapsed] = createSignal(
368
- props.api.kv?.get("quota-sidebar-collapsed", true) ?? true,
369
- );
370
-
371
- const toggleCollapsed = () => {
372
- if (!hasDetailLines()) return;
373
-
374
- const next = !collapsed();
375
- setCollapsed(next);
376
- props.api.kv?.set("quota-sidebar-collapsed", next);
377
- };
378
-
379
- const displayLines = () => {
380
- if (!hasDetailLines()) return lines();
381
- return collapsed() ? lines() : getSidebarPanelLinesExpanded(panel());
382
- };
383
-
384
- const contentRows = (): SidebarContentRow[] => {
385
- const heading = panel().headerPercentMode
386
- ? formatQuotaModeHeading(panel().headerPercentMode)
387
- : "Quota";
388
- return buildSidebarContentRows({
389
- collapsed: collapsed(),
390
- heading,
391
- hasDetailLines: hasDetailLines(),
392
- providerCount: panel().providerCount ?? 0,
393
- lines: displayLines(),
394
- });
395
- };
396
-
397
- return (
398
- <Show when={shouldRenderSidebarPanel(panel())}>
399
- <box gap={0} width="100%">
400
- <Index each={contentRows()}>
401
- {(row: () => SidebarContentRow) => (
402
- <text
403
- fg={
404
- row().kind === "header"
405
- ? props.api.theme.current.text
406
- : getSidebarBodyLineColor(row().text, props.api.theme.current)
407
- }
408
- wrapMode="none"
409
- width="100%"
410
- onMouseDown={row().kind === "header" ? toggleCollapsed : undefined}
411
- >
412
- {row().kind === "header"
413
- ? row().segments.map((segment) =>
414
- segment.style === "muted" ? (
415
- <span fg={props.api.theme.current.textMuted}>{segment.text}</span>
416
- ) : (
417
- <b>{segment.text}</b>
418
- ),
419
- )
420
- : row().text || " "}
421
- </text>
422
- )}
423
- </Index>
424
- </box>
425
- </Show>
426
- );
427
- }
428
-
429
- function CompactStatusLine(props: {
430
- api: TuiPluginApi;
431
- panel: () => CompactStatusState;
432
- justifyContent: "flex-start" | "center" | "flex-end";
433
- blankLineBefore?: boolean;
434
- }) {
435
- const text = () => {
436
- const panel = props.panel();
437
- if (!shouldRenderCompactStatus(panel)) return "";
438
- return getCompactStatusText(panel);
439
- };
440
-
441
- const line = () => (
442
- <box flexDirection="row" justifyContent={props.justifyContent}>
443
- <text fg={props.api.theme.current.textMuted} wrapMode="none">
444
- {text()}
445
- </text>
446
- </box>
447
- );
448
-
449
- return (
450
- <Show when={text()}>
451
- <Show when={props.blankLineBefore} fallback={line()}>
452
- <box gap={0}>
453
- <text> </text>
454
- {line()}
455
- </box>
456
- </Show>
457
- </Show>
458
- );
459
- }
460
-
461
- function SessionPromptWithCompactStatus(props: {
462
- api: TuiPluginApi;
463
- sessionID: string;
464
- initialLoads?: TuiInitialLoadCoordinator;
465
- visible?: boolean;
466
- disabled?: boolean;
467
- onSubmit?: () => void;
468
- promptRef?: TuiPromptRefCallback;
469
- }) {
470
- const resource = useSessionQuotaResource(props.api, () => props.sessionID, props.initialLoads);
471
- const panel = () => resource().compact();
472
-
473
- return (
474
- <box gap={0}>
475
- <props.api.ui.Prompt
476
- sessionID={props.sessionID}
477
- visible={props.visible}
478
- disabled={props.disabled}
479
- onSubmit={props.onSubmit}
480
- ref={props.promptRef}
481
- />
482
- <CompactStatusLine api={props.api} panel={panel} justifyContent="flex-end" />
483
- </box>
484
- );
485
- }
486
-
487
- function shouldRenderPromptBar(
488
- bar: PromptBarState,
489
- ): bar is Extract<PromptBarState, { status: "ready" }> {
490
- return bar.status === "ready" && Boolean(bar.entry);
491
- }
492
-
493
- function useSessionRunning(api: TuiPluginApi, sessionID: () => string): () => boolean {
494
- const [running, setRunning] = createSignal(false);
495
- createEffect(() => {
496
- const id = sessionID();
497
- if (!id) {
498
- setRunning(false);
499
- return;
500
- }
501
- const update = () => {
502
- try {
503
- const sessionState = api.state.session as {
504
- status?: (sessionID: string) => { type?: string } | undefined;
505
- };
506
- const status = sessionState.status?.(id);
507
- setRunning(status?.type === "busy" || status?.type === "retry");
508
- } catch {
509
- setRunning(false);
510
- }
511
- };
512
- update();
513
- const disposers = [
514
- api.event.on("session.status", (event) => {
515
- if (event.properties?.sessionID === id) {
516
- update();
517
- }
518
- }),
519
- api.event.on("session.updated", (event) => {
520
- if (event.properties?.info?.id === id) {
521
- update();
522
- }
523
- }),
524
- ];
525
- onCleanup(() => {
526
- for (const dispose of disposers) {
527
- if (typeof dispose === "function") {
528
- dispose();
529
- }
530
- }
531
- });
532
- });
533
- return running;
534
- }
535
-
536
- function buildPromptBarParts(params: {
537
- bar: () => PromptBarState;
538
- running: () => boolean;
539
- phase: () => number;
540
- }): { label: string; barText: string; meta: string } | undefined {
541
- const bar = params.bar();
542
- if (!shouldRenderPromptBar(bar)) return undefined;
543
- const entry = bar.entry;
544
- if (!entry) return undefined;
545
-
546
- const hasPercent = Number.isFinite(entry.percentRemaining);
547
- if (entry.semanticSegment && !hasPercent) {
548
- const reset = entry.resetTimeIso
549
- ? formatResetCountdown(
550
- entry.resetTimeIso,
551
- isResetTimeDecimals(bar.resetTimeDecimals)
552
- ? { compactRounded: true, decimals: bar.resetTimeDecimals }
553
- : { spaced: bar.resetTimeSpaced },
554
- )
555
- : "";
556
- return { label: resolvePromptBarLabel(entry), barText: "", meta: reset };
557
- }
558
-
559
- const p = Math.min(
560
- 100,
561
- resolveDisplayedPercent(entry.percentRemaining ?? 0, bar.percentDisplayMode ?? "remaining"),
562
- );
563
- const filled = Math.round((p / 100) * PROMPT_BAR_WIDTH);
564
- const empty = PROMPT_BAR_WIDTH - filled;
565
- let barText = "█".repeat(filled) + "░".repeat(empty);
566
- if (params.running() && filled > 0) {
567
- const cells = Array(filled).fill("▓");
568
- const center = params.phase() % filled;
569
- const gradient = ["▒", "▓", "█", "▓", "▒"];
570
- for (let offset = -2; offset <= 2; offset++) {
571
- const position = (center + offset + filled) % filled;
572
- cells[position] = gradient[offset + 2];
573
- }
574
- barText = cells.join("") + "░".repeat(empty);
575
- }
576
- return {
577
- label: resolvePromptBarLabel(entry),
578
- barText,
579
- meta: formatPromptBarPercentMeta({
580
- percentRemaining: entry.percentRemaining ?? 0,
581
- percentDisplayMode: bar.percentDisplayMode,
582
- resetTimeIso: entry.resetTimeIso,
583
- resetTimeDecimals: bar.resetTimeDecimals,
584
- resetTimeSpaced: bar.resetTimeSpaced,
585
- runway: entry.runway,
586
- }),
587
- };
588
- }
589
-
590
- function PromptQuotaHint(props: {
591
- api: TuiPluginApi;
592
- bar: () => PromptBarState;
593
- running: () => boolean;
594
- phase: () => number;
595
- }) {
596
- const parts = () => buildPromptBarParts(props);
597
- const barColor = () => props.api.theme.current.textMuted;
598
- const label = () => parts()?.label ?? "";
599
- const bar = () => parts()?.barText ?? "";
600
- const meta = () => parts()?.meta ?? "";
601
-
602
- return (
603
- <Show when={parts()}>
604
- <box flexDirection="row" justifyContent="flex-end" gap={1}>
605
- <text fg={props.api.theme.current.textMuted} wrapMode="none">
606
- {label()}
607
- </text>
608
- <text fg={barColor()} wrapMode="none">
609
- {bar()}
610
- </text>
611
- <text fg={props.api.theme.current.textMuted} wrapMode="none">
612
- {meta()}
613
- </text>
614
- </box>
615
- </Show>
616
- );
617
- }
618
-
619
- function SessionQuotaPromptBar(props: {
620
- api: TuiPluginApi;
621
- sessionID: string;
622
- initialLoads?: TuiInitialLoadCoordinator;
623
- visible?: boolean;
624
- disabled?: boolean;
625
- onSubmit?: () => void;
626
- promptRef?: TuiPromptRefCallback;
627
- }) {
628
- const resource = useSessionQuotaResource(props.api, () => props.sessionID, props.initialLoads);
629
- const promptBar = () => resource().promptBar();
630
- const running = useSessionRunning(props.api, () => props.sessionID);
631
- const [phase, setPhase] = createSignal(0);
632
- createEffect(() => {
633
- if (!running() || !shouldRenderPromptBar(promptBar())) {
634
- setPhase(0);
635
- return;
636
- }
637
- const interval = setInterval(() => setPhase((p) => p + 1), 160);
638
- onCleanup(() => clearInterval(interval));
639
- });
640
-
641
- return (
642
- <box gap={0}>
643
- <props.api.ui.Prompt
644
- sessionID={props.sessionID}
645
- visible={props.visible}
646
- disabled={props.disabled}
647
- onSubmit={props.onSubmit}
648
- ref={props.promptRef}
649
- />
650
- <PromptQuotaHint api={props.api} bar={promptBar} running={running} phase={phase} />
651
- </box>
652
- );
653
- }
654
-
655
- function HomeBottomView(props: {
656
- api: TuiPluginApi;
657
- compactHomeBottomEnabled: boolean;
658
- initialLoads?: TuiInitialLoadCoordinator;
659
- }) {
660
- const resource = acquireHomeBottomResource(
661
- props.api,
662
- props.compactHomeBottomEnabled,
663
- props.initialLoads,
664
- );
665
- onCleanup(() => resource.release());
666
-
667
- const announcement = () => getHomeBottomAnnouncementText(resource.bottom());
668
- const compact = () => resource.bottom().compact;
669
- const visible = () => shouldRenderHomeBottom(resource.bottom());
670
-
671
- return (
672
- <box gap={0}>
673
- <Show when={visible()}>
674
- <text> </text>
675
- </Show>
676
- <Show when={visible() && announcement()}>
677
- <box flexDirection="row" justifyContent="center">
678
- <text fg={props.api.theme.current.textMuted} wrapMode="none">
679
- {announcement()}
680
- </text>
681
- </box>
682
- </Show>
683
- <Show when={visible()}>
684
- <CompactStatusLine api={props.api} panel={compact} justifyContent="center" />
685
- </Show>
686
- </box>
687
- );
688
- }
689
-
690
- function getActiveTuiSessionID(api: TuiPluginApi): string | undefined {
691
- if (api.route.current.name !== "session") return undefined;
692
- return normalizeTuiSessionID(api.route.current.params?.sessionID);
693
- }
694
-
695
- function getTuiCommandArguments(input: unknown): string | undefined {
696
- if (!input || typeof input !== "object") return undefined;
697
- const record = input as Record<string, unknown>;
698
- for (const key of ["arguments", "args", "query"] as const) {
699
- const value = record[key];
700
- if (typeof value === "string" && value.trim()) return value.trim();
701
- }
702
- return undefined;
703
- }
704
-
705
- function CommandLoadingDialog(props: { api: TuiPluginApi; title: string }) {
706
- return (
707
- <box gap={1} paddingLeft={2} paddingRight={2} paddingBottom={1}>
708
- <text fg={props.api.theme.current.text}>
709
- <b>{props.title}</b>
710
- </text>
711
- <text fg={props.api.theme.current.textMuted}>Loading deterministic local output…</text>
712
- </box>
713
- );
714
- }
715
-
716
- function CommandOutputDialog(props: { api: TuiPluginApi; title: string; output: string }) {
717
- const lines = () => props.output.split("\n");
718
- const bodyHeight = () => Math.min(28, Math.max(6, lines().length));
719
- return (
720
- <box gap={1} width="100%" flexGrow={1} paddingLeft={2} paddingRight={2} paddingBottom={1}>
721
- <text fg={props.api.theme.current.text}>
722
- <b>{props.title}</b>
723
- </text>
724
- <scrollbox width="100%" flexGrow={1} minHeight={bodyHeight()} maxHeight={28}>
725
- <box gap={0} width="100%" minWidth={0}>
726
- {lines().map((line) => (
727
- <text fg={props.api.theme.current.text} wrapMode="word" width="100%">
728
- {line || " "}
729
- </text>
730
- ))}
731
- </box>
732
- </scrollbox>
733
- <text fg={props.api.theme.current.textMuted}>esc closes</text>
734
- </box>
735
- );
736
- }
737
-
738
- function CommandErrorDialog(props: { api: TuiPluginApi; title: string; error: unknown }) {
739
- const message = props.error instanceof Error ? props.error.message : String(props.error);
740
- return (
741
- <box gap={1} paddingLeft={2} paddingRight={2} paddingBottom={1}>
742
- <text fg={props.api.theme.current.text}>
743
- <b>{props.title}</b>
744
- </text>
745
- <text fg={props.api.theme.current.text}>OpenCode Quota command failed.</text>
746
- <text fg={props.api.theme.current.textMuted} wrapMode="none">
747
- {message || "Unknown error"}
748
- </text>
749
- <text fg={props.api.theme.current.textMuted}>esc closes</text>
750
- </box>
751
- );
752
- }
753
-
754
- function getCommandPromptCopy(spec: QuotaDialogCommandSpec): {
755
- title: string;
756
- placeholder: string;
757
- description: string;
758
- } {
759
- switch (spec.id) {
760
- case "tokens_between":
761
- return {
762
- title: "OpenCode Quota Token Range",
763
- placeholder: "YYYY-MM-DD YYYY-MM-DD",
764
- description: "Enter start and end dates, for example: 2026-01-01 2026-01-15",
765
- };
766
- default:
767
- return {
768
- title: spec.title,
769
- placeholder: "Optional arguments",
770
- description: "Leave blank to run with no arguments.",
771
- };
772
- }
773
- }
774
-
775
- function replaceDialog(api: TuiPluginApi, size: DialogSize, render: () => JSX.Element): void {
776
- api.ui.dialog.replace(render);
777
- // OpenCode dialog.replace() resets size to medium.
778
- api.ui.dialog.setSize(size);
779
- }
780
-
781
- async function runQuotaDialogCommandAsync(
782
- api: TuiPluginApi,
783
- command: QuotaDialogCommandId,
784
- commandDisplay: TuiCommandDisplay,
785
- rawInput?: unknown,
786
- state?: QuotaDialogCommandState,
787
- ): Promise<void> {
788
- const spec = QUOTA_DIALOG_COMMANDS.find((item) => item.id === command)!;
789
- const argumentsText = getTuiCommandArguments(rawInput);
790
- const sessionID = getActiveTuiSessionID(api);
791
-
792
- if (spec.acceptsArguments && rawInput === undefined) {
793
- const prompt = getCommandPromptCopy(spec);
794
- replaceDialog(api, "medium", () => (
795
- <api.ui.DialogPrompt
796
- title={prompt.title}
797
- placeholder={prompt.placeholder}
798
- description={() => (
799
- <text fg={api.theme.current.textMuted} wrapMode="word">
800
- {prompt.description}
801
- </text>
802
- )}
803
- onCancel={() => api.ui.dialog.clear()}
804
- onConfirm={(value) => {
805
- void runQuotaDialogCommandAsync(
806
- api,
807
- command,
808
- commandDisplay,
809
- { arguments: value.trim() },
810
- state,
811
- );
812
- }}
813
- />
814
- ));
815
- return;
816
- }
817
-
818
- const destination =
819
- commandDisplay === "inline" && sessionID
820
- ? { type: "inline" as const, sessionID }
821
- : { type: "dialog" as const };
822
- if (destination.type === "dialog") {
823
- replaceDialog(api, spec.dialogSize, () => (
824
- <CommandLoadingDialog api={api} title={spec.title} />
825
- ));
826
- }
827
-
828
- try {
829
- const result = await buildQuotaDialogCommandOutput({
830
- command,
831
- arguments: argumentsText,
832
- client: createTuiQuotaClient(api),
833
- roots: getTuiRuntimeRootHints(api),
834
- sessionID,
835
- resolveSessionMeta: (id) => getTuiSessionModelMeta(api, id),
836
- lastSessionTokenError: state?.lastSessionTokenError,
837
- setLastSessionTokenError: state
838
- ? (error) => {
839
- state.lastSessionTokenError = error;
840
- }
841
- : undefined,
842
- log: async (message, extra) => {
843
- await api.client.app.log({
844
- body: {
845
- service: "quota-toast",
846
- level: "debug",
847
- message,
848
- extra,
849
- },
850
- });
851
- },
852
- });
853
-
854
- if (result.state === "noop") {
855
- if (destination.type === "dialog") api.ui.dialog.clear();
856
- return;
857
- }
858
-
859
- if (destination.type === "inline") {
860
- await api.client.session.prompt({
861
- sessionID: destination.sessionID,
862
- noReply: true,
863
- parts: [{ type: "text", text: result.output, ignored: true }],
864
- });
865
- return;
866
- }
867
-
868
- replaceDialog(api, result.dialogSize, () => (
869
- <CommandOutputDialog api={api} title={result.title} output={result.output} />
870
- ));
871
- } catch (error) {
872
- replaceDialog(api, "large", () => (
873
- <CommandErrorDialog api={api} title={spec.title} error={error} />
874
- ));
875
- api.ui.toast({
876
- variant: "error",
877
- message: "OpenCode Quota command failed",
878
- });
879
- }
880
- }
881
-
882
- function registerQuotaDialogCommands(api: TuiPluginApi, gate: TuiRegistrationGate): void {
883
- const commandState: QuotaDialogCommandState = {};
884
- const dispose = api.keymap.registerLayer({
885
- commands: QUOTA_DIALOG_COMMANDS.map((spec) => ({
886
- namespace: "palette",
887
- name: `opencode-quota.${spec.id}`,
888
- title: spec.title,
889
- desc: spec.description,
890
- category: "OpenCode Quota",
891
- slashName: spec.slashName,
892
- run(input?: unknown) {
893
- const state = gate.current();
894
- if (state.status !== "active") return;
895
- void runQuotaDialogCommandAsync(
896
- api,
897
- spec.id,
898
- state.registration.commandDisplay,
899
- input,
900
- commandState,
901
- );
902
- },
903
- })),
904
- bindings: [],
905
- });
906
-
907
- api.lifecycle.onDispose(dispose);
908
- }
909
-
910
- function registerStableTuiSlots(api: TuiPluginApi, current: () => TuiRegistrationState): void {
911
- api.slots.register({
912
- order: SIDEBAR_ORDER,
913
- slots: {
914
- sidebar_content(_ctx, props: { session_id: string }) {
915
- const state = current();
916
- if (state.status !== "active" || !state.registration.sidebar.enabled) return null;
917
- return (
918
- <SidebarContentView
919
- api={api}
920
- sessionID={props.session_id}
921
- initialLoads={state.initialLoads}
922
- />
923
- );
924
- },
925
- },
926
- });
927
-
928
- api.slots.register({
929
- order: COMPACT_ORDER,
930
- slots: {
931
- session_prompt(
932
- _ctx,
933
- props: {
934
- session_id: string;
935
- visible?: boolean;
936
- disabled?: boolean;
937
- on_submit?: () => void;
938
- ref?: TuiPromptRefCallback;
939
- },
940
- ) {
941
- const state = current();
942
- if (state.status !== "active") return null;
943
- if (state.registration.promptBar.enabled) {
944
- return (
945
- <SessionQuotaPromptBar
946
- api={api}
947
- sessionID={props.session_id}
948
- initialLoads={state.initialLoads}
949
- visible={props.visible}
950
- disabled={props.disabled}
951
- onSubmit={props.on_submit}
952
- promptRef={props.ref}
953
- />
954
- );
955
- }
956
- if (!state.registration.compact.sessionPrompt) return null;
957
- return (
958
- <SessionPromptWithCompactStatus
959
- api={api}
960
- sessionID={props.session_id}
961
- initialLoads={state.initialLoads}
962
- visible={props.visible}
963
- disabled={props.disabled}
964
- onSubmit={props.on_submit}
965
- promptRef={props.ref}
966
- />
967
- );
968
- },
969
- home_bottom() {
970
- const state = current();
971
- if (state.status !== "active" || !state.registration.homeBottom) return null;
972
- return (
973
- <HomeBottomView
974
- api={api}
975
- compactHomeBottomEnabled={state.registration.compact.homeBottom}
976
- initialLoads={state.initialLoads}
977
- />
978
- );
979
- },
980
- },
981
- });
982
- }
983
-
984
- async function initializeTuiRegistration(
985
- api: TuiPluginApi,
986
- gate: TuiRegistrationGate,
987
- ): Promise<void> {
988
- let initialRuntimeSeed: TuiInitialRuntimeSeed | undefined;
989
- let surfaceRegistration: Promise<{
990
- registration: TuiSurfaceRegistration;
991
- initialRuntimeSeed?: TuiInitialRuntimeSeed;
992
- }>;
993
- try {
994
- surfaceRegistration = resolveTuiSurfaceRegistration(api, {
995
- captureInitialRuntime(seed) {
996
- initialRuntimeSeed = seed;
997
- },
998
- })
999
- .then((registration) => ({ registration, initialRuntimeSeed }))
1000
- .catch(() => ({ registration: FALLBACK_SURFACE_REGISTRATION }));
1001
- } catch {
1002
- surfaceRegistration = Promise.resolve({ registration: FALLBACK_SURFACE_REGISTRATION });
1003
- }
1004
-
1005
- registerQuotaDialogCommands(api, gate);
1006
- void surfaceRegistration.then(({ registration, initialRuntimeSeed }) =>
1007
- gate.activate(
1008
- registration,
1009
- initialRuntimeSeed ? createTuiInitialLoadCoordinator(initialRuntimeSeed) : undefined,
1010
- ),
1011
- );
1012
- registerStableTuiSlots(api, gate.current);
1013
- }
1014
-
1015
- const tui: TuiPlugin = async (api) => {
1016
- const registrationGate = createTuiRegistrationGate();
1017
- api.lifecycle.onDispose(() => {
1018
- registrationGate.dispose();
1019
- disposeQuotaTelemetryOwner(createTuiQuotaClient(api));
1020
- });
1021
-
1022
- void initializeTuiRegistration(api, registrationGate).catch(() => {});
1023
- };
1024
-
1025
- const pluginModule: TuiPluginModule & { id: string } = {
1026
- id,
1027
- tui,
1028
- };
1029
-
1030
- export default pluginModule;