@slkiser/opencode-quota 4.0.1 → 4.1.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 (251) hide show
  1. package/README.md +36 -26
  2. package/dist/bin/opencode-quota.d.ts.map +1 -1
  3. package/dist/bin/opencode-quota.js +8 -0
  4. package/dist/bin/opencode-quota.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/lib/anthropic.d.ts.map +1 -1
  8. package/dist/lib/anthropic.js +78 -72
  9. package/dist/lib/anthropic.js.map +1 -1
  10. package/dist/lib/api-key-resolver.d.ts.map +1 -1
  11. package/dist/lib/api-key-resolver.js +14 -24
  12. package/dist/lib/api-key-resolver.js.map +1 -1
  13. package/dist/lib/atomic-json.d.ts +3 -1
  14. package/dist/lib/atomic-json.d.ts.map +1 -1
  15. package/dist/lib/atomic-json.js +23 -9
  16. package/dist/lib/atomic-json.js.map +1 -1
  17. package/dist/lib/chutes.d.ts.map +1 -1
  18. package/dist/lib/chutes.js +28 -23
  19. package/dist/lib/chutes.js.map +1 -1
  20. package/dist/lib/cli-show.d.ts +9 -0
  21. package/dist/lib/cli-show.d.ts.map +1 -1
  22. package/dist/lib/cli-show.js +34 -5
  23. package/dist/lib/cli-show.js.map +1 -1
  24. package/dist/lib/cli-status.d.ts +8 -0
  25. package/dist/lib/cli-status.d.ts.map +1 -0
  26. package/dist/lib/cli-status.js +125 -0
  27. package/dist/lib/cli-status.js.map +1 -0
  28. package/dist/lib/config.d.ts +1 -1
  29. package/dist/lib/config.d.ts.map +1 -1
  30. package/dist/lib/config.js +47 -12
  31. package/dist/lib/config.js.map +1 -1
  32. package/dist/lib/copilot.d.ts +11 -5
  33. package/dist/lib/copilot.d.ts.map +1 -1
  34. package/dist/lib/copilot.js +300 -52
  35. package/dist/lib/copilot.js.map +1 -1
  36. package/dist/lib/deepseek.d.ts.map +1 -1
  37. package/dist/lib/deepseek.js +22 -17
  38. package/dist/lib/deepseek.js.map +1 -1
  39. package/dist/lib/display-sanitize.d.ts.map +1 -1
  40. package/dist/lib/display-sanitize.js +8 -0
  41. package/dist/lib/display-sanitize.js.map +1 -1
  42. package/dist/lib/entries.d.ts +11 -0
  43. package/dist/lib/entries.d.ts.map +1 -1
  44. package/dist/lib/entries.js.map +1 -1
  45. package/dist/lib/format-utils.d.ts +7 -0
  46. package/dist/lib/format-utils.d.ts.map +1 -1
  47. package/dist/lib/format-utils.js +18 -0
  48. package/dist/lib/format-utils.js.map +1 -1
  49. package/dist/lib/format.d.ts +1 -0
  50. package/dist/lib/format.d.ts.map +1 -1
  51. package/dist/lib/format.js +22 -7
  52. package/dist/lib/format.js.map +1 -1
  53. package/dist/lib/google-agy.d.ts.map +1 -1
  54. package/dist/lib/google-agy.js +203 -92
  55. package/dist/lib/google-agy.js.map +1 -1
  56. package/dist/lib/google-gemini-cli.d.ts.map +1 -1
  57. package/dist/lib/google-gemini-cli.js +53 -41
  58. package/dist/lib/google-gemini-cli.js.map +1 -1
  59. package/dist/lib/google-token-cache.d.ts.map +1 -1
  60. package/dist/lib/google-token-cache.js +87 -34
  61. package/dist/lib/google-token-cache.js.map +1 -1
  62. package/dist/lib/google.d.ts.map +1 -1
  63. package/dist/lib/google.js +57 -39
  64. package/dist/lib/google.js.map +1 -1
  65. package/dist/lib/grouped-entry-normalization.d.ts.map +1 -1
  66. package/dist/lib/grouped-entry-normalization.js +7 -1
  67. package/dist/lib/grouped-entry-normalization.js.map +1 -1
  68. package/dist/lib/http.d.ts +12 -7
  69. package/dist/lib/http.d.ts.map +1 -1
  70. package/dist/lib/http.js +28 -15
  71. package/dist/lib/http.js.map +1 -1
  72. package/dist/lib/init-installer.d.ts +2 -1
  73. package/dist/lib/init-installer.d.ts.map +1 -1
  74. package/dist/lib/init-installer.js +29 -2
  75. package/dist/lib/init-installer.js.map +1 -1
  76. package/dist/lib/kimi.d.ts.map +1 -1
  77. package/dist/lib/kimi.js +20 -15
  78. package/dist/lib/kimi.js.map +1 -1
  79. package/dist/lib/maintainer-announcements.d.ts.map +1 -1
  80. package/dist/lib/maintainer-announcements.js +5 -0
  81. package/dist/lib/maintainer-announcements.js.map +1 -1
  82. package/dist/lib/mimo-config.d.ts +29 -0
  83. package/dist/lib/mimo-config.d.ts.map +1 -0
  84. package/dist/lib/mimo-config.js +147 -0
  85. package/dist/lib/mimo-config.js.map +1 -0
  86. package/dist/lib/mimo.d.ts +35 -0
  87. package/dist/lib/mimo.d.ts.map +1 -0
  88. package/dist/lib/mimo.js +236 -0
  89. package/dist/lib/mimo.js.map +1 -0
  90. package/dist/lib/modelsdev-pricing.d.ts.map +1 -1
  91. package/dist/lib/modelsdev-pricing.js +39 -27
  92. package/dist/lib/modelsdev-pricing.js.map +1 -1
  93. package/dist/lib/nanogpt.d.ts.map +1 -1
  94. package/dist/lib/nanogpt.js +40 -30
  95. package/dist/lib/nanogpt.js.map +1 -1
  96. package/dist/lib/ollama-cloud.d.ts +1 -1
  97. package/dist/lib/ollama-cloud.d.ts.map +1 -1
  98. package/dist/lib/ollama-cloud.js +73 -68
  99. package/dist/lib/ollama-cloud.js.map +1 -1
  100. package/dist/lib/openai.d.ts.map +1 -1
  101. package/dist/lib/openai.js +53 -48
  102. package/dist/lib/openai.js.map +1 -1
  103. package/dist/lib/opencode-config-providers.d.ts.map +1 -1
  104. package/dist/lib/opencode-config-providers.js +13 -16
  105. package/dist/lib/opencode-config-providers.js.map +1 -1
  106. package/dist/lib/opencode-config-read.d.ts +23 -0
  107. package/dist/lib/opencode-config-read.d.ts.map +1 -0
  108. package/dist/lib/opencode-config-read.js +30 -0
  109. package/dist/lib/opencode-config-read.js.map +1 -0
  110. package/dist/lib/opencode-go.d.ts.map +1 -1
  111. package/dist/lib/opencode-go.js +44 -39
  112. package/dist/lib/opencode-go.js.map +1 -1
  113. package/dist/lib/opencode-zen-config.d.ts +34 -0
  114. package/dist/lib/opencode-zen-config.d.ts.map +1 -0
  115. package/dist/lib/opencode-zen-config.js +103 -0
  116. package/dist/lib/opencode-zen-config.js.map +1 -0
  117. package/dist/lib/opencode-zen.d.ts +27 -0
  118. package/dist/lib/opencode-zen.d.ts.map +1 -0
  119. package/dist/lib/opencode-zen.js +143 -0
  120. package/dist/lib/opencode-zen.js.map +1 -0
  121. package/dist/lib/provider-add-command.d.ts.map +1 -1
  122. package/dist/lib/provider-add-command.js +4 -12
  123. package/dist/lib/provider-add-command.js.map +1 -1
  124. package/dist/lib/provider-add-json-v1-questionnaire.d.ts +32 -0
  125. package/dist/lib/provider-add-json-v1-questionnaire.d.ts.map +1 -0
  126. package/dist/lib/provider-add-json-v1-questionnaire.js +336 -0
  127. package/dist/lib/provider-add-json-v1-questionnaire.js.map +1 -0
  128. package/dist/lib/provider-availability.d.ts +2 -2
  129. package/dist/lib/provider-availability.d.ts.map +1 -1
  130. package/dist/lib/provider-availability.js +2 -3
  131. package/dist/lib/provider-availability.js.map +1 -1
  132. package/dist/lib/provider-metadata.d.ts +289 -1
  133. package/dist/lib/provider-metadata.d.ts.map +1 -1
  134. package/dist/lib/provider-metadata.js +321 -227
  135. package/dist/lib/provider-metadata.js.map +1 -1
  136. package/dist/lib/quota-command-format.d.ts.map +1 -1
  137. package/dist/lib/quota-command-format.js +12 -16
  138. package/dist/lib/quota-command-format.js.map +1 -1
  139. package/dist/lib/quota-dialog-commands.d.ts +59 -2
  140. package/dist/lib/quota-dialog-commands.d.ts.map +1 -1
  141. package/dist/lib/quota-dialog-commands.js +71 -30
  142. package/dist/lib/quota-dialog-commands.js.map +1 -1
  143. package/dist/lib/quota-entry-display.d.ts +2 -0
  144. package/dist/lib/quota-entry-display.d.ts.map +1 -1
  145. package/dist/lib/quota-entry-display.js +27 -13
  146. package/dist/lib/quota-entry-display.js.map +1 -1
  147. package/dist/lib/quota-render-data.d.ts +4 -0
  148. package/dist/lib/quota-render-data.d.ts.map +1 -1
  149. package/dist/lib/quota-render-data.js +101 -121
  150. package/dist/lib/quota-render-data.js.map +1 -1
  151. package/dist/lib/quota-runtime-context.d.ts +9 -1
  152. package/dist/lib/quota-runtime-context.d.ts.map +1 -1
  153. package/dist/lib/quota-runtime-context.js +5 -0
  154. package/dist/lib/quota-runtime-context.js.map +1 -1
  155. package/dist/lib/quota-state.d.ts.map +1 -1
  156. package/dist/lib/quota-state.js +25 -1
  157. package/dist/lib/quota-state.js.map +1 -1
  158. package/dist/lib/quota-stats.d.ts +1 -0
  159. package/dist/lib/quota-stats.d.ts.map +1 -1
  160. package/dist/lib/quota-stats.js +14 -4
  161. package/dist/lib/quota-stats.js.map +1 -1
  162. package/dist/lib/quota-status.d.ts.map +1 -1
  163. package/dist/lib/quota-status.js +96 -1
  164. package/dist/lib/quota-status.js.map +1 -1
  165. package/dist/lib/runtime-provider-ids.d.ts +4 -0
  166. package/dist/lib/runtime-provider-ids.d.ts.map +1 -0
  167. package/dist/lib/runtime-provider-ids.js +10 -0
  168. package/dist/lib/runtime-provider-ids.js.map +1 -0
  169. package/dist/lib/session-tokens.d.ts +2 -0
  170. package/dist/lib/session-tokens.d.ts.map +1 -1
  171. package/dist/lib/session-tokens.js +4 -2
  172. package/dist/lib/session-tokens.js.map +1 -1
  173. package/dist/lib/synthetic.d.ts.map +1 -1
  174. package/dist/lib/synthetic.js +37 -32
  175. package/dist/lib/synthetic.js.map +1 -1
  176. package/dist/lib/toast-format-grouped.d.ts +1 -0
  177. package/dist/lib/toast-format-grouped.d.ts.map +1 -1
  178. package/dist/lib/toast-format-grouped.js +33 -29
  179. package/dist/lib/toast-format-grouped.js.map +1 -1
  180. package/dist/lib/tui-refresh-lifecycle.d.ts +17 -0
  181. package/dist/lib/tui-refresh-lifecycle.d.ts.map +1 -0
  182. package/dist/lib/tui-refresh-lifecycle.js +83 -0
  183. package/dist/lib/tui-refresh-lifecycle.js.map +1 -0
  184. package/dist/lib/tui-runtime.d.ts +0 -5
  185. package/dist/lib/tui-runtime.d.ts.map +1 -1
  186. package/dist/lib/tui-runtime.js +1 -6
  187. package/dist/lib/tui-runtime.js.map +1 -1
  188. package/dist/lib/tui-sidebar-format.d.ts +1 -1
  189. package/dist/lib/tui-sidebar-format.d.ts.map +1 -1
  190. package/dist/lib/tui-sidebar-format.js +1 -0
  191. package/dist/lib/tui-sidebar-format.js.map +1 -1
  192. package/dist/lib/types.d.ts +61 -7
  193. package/dist/lib/types.d.ts.map +1 -1
  194. package/dist/lib/types.js +2 -0
  195. package/dist/lib/types.js.map +1 -1
  196. package/dist/lib/xai.d.ts +39 -0
  197. package/dist/lib/xai.d.ts.map +1 -0
  198. package/dist/lib/xai.js +153 -0
  199. package/dist/lib/xai.js.map +1 -0
  200. package/dist/lib/zai.d.ts.map +1 -1
  201. package/dist/lib/zai.js +62 -56
  202. package/dist/lib/zai.js.map +1 -1
  203. package/dist/lib/zhipu.d.ts.map +1 -1
  204. package/dist/lib/zhipu.js +55 -50
  205. package/dist/lib/zhipu.js.map +1 -1
  206. package/dist/plugin.d.ts.map +1 -1
  207. package/dist/plugin.js +20 -145
  208. package/dist/plugin.js.map +1 -1
  209. package/dist/providers/copilot.d.ts.map +1 -1
  210. package/dist/providers/copilot.js +24 -3
  211. package/dist/providers/copilot.js.map +1 -1
  212. package/dist/providers/glm-coding-plan-provider.d.ts +35 -0
  213. package/dist/providers/glm-coding-plan-provider.d.ts.map +1 -0
  214. package/dist/providers/glm-coding-plan-provider.js +40 -0
  215. package/dist/providers/glm-coding-plan-provider.js.map +1 -0
  216. package/dist/providers/google-agy.d.ts.map +1 -1
  217. package/dist/providers/google-agy.js +60 -36
  218. package/dist/providers/google-agy.js.map +1 -1
  219. package/dist/providers/mimo.d.ts +3 -0
  220. package/dist/providers/mimo.d.ts.map +1 -0
  221. package/dist/providers/mimo.js +120 -0
  222. package/dist/providers/mimo.js.map +1 -0
  223. package/dist/providers/minimax-coding-plan.d.ts.map +1 -1
  224. package/dist/providers/minimax-coding-plan.js +31 -26
  225. package/dist/providers/minimax-coding-plan.js.map +1 -1
  226. package/dist/providers/opencode-zen.d.ts +3 -0
  227. package/dist/providers/opencode-zen.d.ts.map +1 -0
  228. package/dist/providers/opencode-zen.js +94 -0
  229. package/dist/providers/opencode-zen.js.map +1 -0
  230. package/dist/providers/quota-providers.d.ts.map +1 -1
  231. package/dist/providers/quota-providers.js +1 -2
  232. package/dist/providers/quota-providers.js.map +1 -1
  233. package/dist/providers/registry.d.ts.map +1 -1
  234. package/dist/providers/registry.js +6 -0
  235. package/dist/providers/registry.js.map +1 -1
  236. package/dist/providers/xai.d.ts +6 -0
  237. package/dist/providers/xai.d.ts.map +1 -0
  238. package/dist/providers/xai.js +51 -0
  239. package/dist/providers/xai.js.map +1 -0
  240. package/dist/providers/zai.d.ts +1 -7
  241. package/dist/providers/zai.d.ts.map +1 -1
  242. package/dist/providers/zai.js +9 -45
  243. package/dist/providers/zai.js.map +1 -1
  244. package/dist/providers/zhipu.d.ts +1 -7
  245. package/dist/providers/zhipu.d.ts.map +1 -1
  246. package/dist/providers/zhipu.js +9 -45
  247. package/dist/providers/zhipu.js.map +1 -1
  248. package/dist/tui.d.ts.map +1 -1
  249. package/dist/tui.js +47 -136
  250. package/dist/tui.tsx +62 -179
  251. package/package.json +1 -1
@@ -0,0 +1,6 @@
1
+ /**
2
+ * xAI SuperGrok provider wrapper.
3
+ */
4
+ import type { QuotaProvider } from "../lib/entries.js";
5
+ export declare const xaiProvider: QuotaProvider;
6
+ //# sourceMappingURL=xai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xai.d.ts","sourceRoot":"","sources":["../../src/providers/xai.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,aAAa,EAId,MAAM,mBAAmB,CAAC;AAW3B,eAAO,MAAM,WAAW,EAAE,aAkDzB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * xAI SuperGrok provider wrapper.
3
+ */
4
+ import { isCanonicalProviderAvailable } from "../lib/provider-availability.js";
5
+ import { modelProviderMatchesRuntimeId } from "../lib/provider-model-matching.js";
6
+ import { DEFAULT_XAI_AUTH_CACHE_MAX_AGE_MS, hasXaiOAuthCached, periodKindLabel, queryXaiQuota, } from "../lib/xai.js";
7
+ import { attemptedResult, mapNullableProviderResult } from "./result-helpers.js";
8
+ export const xaiProvider = {
9
+ id: "xai",
10
+ async isAvailable(ctx) {
11
+ const providerAvailable = await isCanonicalProviderAvailable({
12
+ ctx,
13
+ providerId: "xai",
14
+ fallbackOnError: false,
15
+ });
16
+ if (providerAvailable)
17
+ return hasXaiOAuthCached({ maxAgeMs: 0 });
18
+ return hasXaiOAuthCached({ maxAgeMs: DEFAULT_XAI_AUTH_CACHE_MAX_AGE_MS });
19
+ },
20
+ matchesCurrentModel(model, context) {
21
+ if (context?.currentProviderID) {
22
+ return context.currentProviderID.trim().toLowerCase() === "xai";
23
+ }
24
+ return modelProviderMatchesRuntimeId(model, "xai");
25
+ },
26
+ async fetch(ctx) {
27
+ const result = await queryXaiQuota({ requestTimeoutMs: ctx.config?.requestTimeoutMs });
28
+ return mapNullableProviderResult(result, {
29
+ errorLabel: "xAI",
30
+ onSuccess: (result) => {
31
+ const period = periodKindLabel(result.window.kind);
32
+ return attemptedResult([
33
+ {
34
+ accounting: {
35
+ resultType: "quota",
36
+ acquisitionMethod: "remote_api",
37
+ ownership: "maintained",
38
+ authority: "provider_reported",
39
+ },
40
+ name: `${result.label} ${period}`,
41
+ group: result.label,
42
+ label: `${period}:`,
43
+ percentRemaining: result.window.percentRemaining,
44
+ resetTimeIso: result.window.resetTimeIso,
45
+ },
46
+ ], [], { singleWindowDisplayName: result.label });
47
+ },
48
+ });
49
+ },
50
+ };
51
+ //# sourceMappingURL=xai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xai.js","sourceRoot":"","sources":["../../src/providers/xai.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EACL,iCAAiC,EACjC,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,MAAM,CAAC,MAAM,WAAW,GAAkB;IACxC,EAAE,EAAE,KAAK;IAET,KAAK,CAAC,WAAW,CAAC,GAAyB;QACzC,MAAM,iBAAiB,GAAG,MAAM,4BAA4B,CAAC;YAC3D,GAAG;YACH,UAAU,EAAE,KAAK;YACjB,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC;QACH,IAAI,iBAAiB;YAAE,OAAO,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QAEjE,OAAO,iBAAiB,CAAC,EAAE,QAAQ,EAAE,iCAAiC,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,mBAAmB,CAAC,KAAa,EAAE,OAAmC;QACpE,IAAI,OAAO,EAAE,iBAAiB,EAAE,CAAC;YAC/B,OAAO,OAAO,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;QAClE,CAAC;QACD,OAAO,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAyB;QACnC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAEvF,OAAO,yBAAyB,CAAC,MAAM,EAAE;YACvC,UAAU,EAAE,KAAK;YACjB,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;gBACpB,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACnD,OAAO,eAAe,CACpB;oBACE;wBACE,UAAU,EAAE;4BACV,UAAU,EAAE,OAAO;4BACnB,iBAAiB,EAAE,YAAY;4BAC/B,SAAS,EAAE,YAAY;4BACvB,SAAS,EAAE,mBAAmB;yBAC/B;wBACD,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,EAAE;wBACjC,KAAK,EAAE,MAAM,CAAC,KAAK;wBACnB,KAAK,EAAE,GAAG,MAAM,GAAG;wBACnB,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,gBAAgB;wBAChD,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY;qBACzC;iBACF,EACD,EAAE,EACF,EAAE,uBAAuB,EAAE,MAAM,CAAC,KAAK,EAAE,CAC1C,CAAC;YACJ,CAAC;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -1,8 +1,2 @@
1
- /**
2
- * Z.ai provider wrapper.
3
- *
4
- * Normalizes Z.ai quota into generic toast entries.
5
- */
6
- import type { QuotaProvider } from "../lib/entries.js";
7
- export declare const zaiProvider: QuotaProvider;
1
+ export declare const zaiProvider: import("../lib/entries.js").QuotaProvider;
8
2
  //# sourceMappingURL=zai.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"zai.d.ts","sourceRoot":"","sources":["../../src/providers/zai.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA6C,MAAM,mBAAmB,CAAC;AAUlG,eAAO,MAAM,WAAW,EAAE,aAwDzB,CAAC"}
1
+ {"version":3,"file":"zai.d.ts","sourceRoot":"","sources":["../../src/providers/zai.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,WAAW,2CAetB,CAAC"}
@@ -1,28 +1,14 @@
1
- /**
2
- * Z.ai provider wrapper.
3
- *
4
- * Normalizes Z.ai quota into generic toast entries.
5
- */
1
+ /** Z.ai provider wrapper. */
6
2
  import { queryZaiQuota } from "../lib/zai.js";
7
- import { isCanonicalProviderAvailable } from "../lib/provider-availability.js";
8
3
  import { DEFAULT_ZAI_AUTH_CACHE_MAX_AGE_MS, resolveZaiAuthCached } from "../lib/zai-auth.js";
9
- import { attemptedResult, groupedPercentWindowEntries, mapNullableProviderResult, } from "./result-helpers.js";
10
- export const zaiProvider = {
4
+ import { createGlmCodingPlanProvider } from "./glm-coding-plan-provider.js";
5
+ export const zaiProvider = createGlmCodingPlanProvider({
11
6
  id: "zai",
12
- async isAvailable(ctx) {
13
- const providerAvailable = await isCanonicalProviderAvailable({
14
- ctx,
15
- providerId: "zai",
16
- fallbackOnError: false,
17
- });
18
- if (!providerAvailable) {
19
- return false;
20
- }
21
- const auth = await resolveZaiAuthCached({
22
- maxAgeMs: DEFAULT_ZAI_AUTH_CACHE_MAX_AGE_MS,
23
- });
24
- return auth.state === "configured" || auth.state === "invalid";
25
- },
7
+ providerId: "zai",
8
+ errorLabel: "Z.ai",
9
+ authCacheMaxAgeMs: DEFAULT_ZAI_AUTH_CACHE_MAX_AGE_MS,
10
+ resolveAuth: resolveZaiAuthCached,
11
+ queryQuota: queryZaiQuota,
26
12
  matchesCurrentModel(model) {
27
13
  const lower = model.toLowerCase();
28
14
  const provider = lower.split("/")[0];
@@ -31,27 +17,5 @@ export const zaiProvider = {
31
17
  }
32
18
  return lower.includes("glm");
33
19
  },
34
- async fetch(ctx) {
35
- const result = await queryZaiQuota({ requestTimeoutMs: ctx.config?.requestTimeoutMs });
36
- return mapNullableProviderResult(result, {
37
- errorLabel: "Z.ai",
38
- onSuccess: (result) => attemptedResult(groupedPercentWindowEntries({
39
- group: result.label,
40
- accounting: {
41
- resultType: "quota",
42
- acquisitionMethod: "remote_api",
43
- ownership: "maintained",
44
- authority: "provider_reported",
45
- },
46
- windows: [
47
- { window: result.windows.fiveHour, suffix: "5h", label: "5h:" },
48
- { window: result.windows.weekly, suffix: "Weekly", label: "Weekly:" },
49
- { window: result.windows.mcp, suffix: "MCP", label: "MCP:" },
50
- ],
51
- }), [], {
52
- singleWindowDisplayName: result.label,
53
- }),
54
- });
55
- },
56
- };
20
+ });
57
21
  //# sourceMappingURL=zai.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zai.js","sourceRoot":"","sources":["../../src/providers/zai.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,iCAAiC,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,CAAC,MAAM,WAAW,GAAkB;IACxC,EAAE,EAAE,KAAK;IAET,KAAK,CAAC,WAAW,CAAC,GAAyB;QACzC,MAAM,iBAAiB,GAAG,MAAM,4BAA4B,CAAC;YAC3D,GAAG;YACH,UAAU,EAAE,KAAK;YACjB,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC;YACtC,QAAQ,EAAE,iCAAiC;SAC5C,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,KAAK,YAAY,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IACjE,CAAC;IAED,mBAAmB,CAAC,KAAa;QAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACvE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAyB;QACnC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAEvF,OAAO,yBAAyB,CAAC,MAAM,EAAE;YACvC,UAAU,EAAE,MAAM;YAClB,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CACpB,eAAe,CACb,2BAA2B,CAAC;gBAC1B,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,UAAU,EAAE;oBACV,UAAU,EAAE,OAAO;oBACnB,iBAAiB,EAAE,YAAY;oBAC/B,SAAS,EAAE,YAAY;oBACvB,SAAS,EAAE,mBAAmB;iBAC/B;gBACD,OAAO,EAAE;oBACP,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE;oBAC/D,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE;oBACrE,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE;iBAC7D;aACF,CAAC,EACF,EAAE,EACF;gBACE,uBAAuB,EAAE,MAAM,CAAC,KAAK;aACtC,CACF;SACJ,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"zai.js","sourceRoot":"","sources":["../../src/providers/zai.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,iCAAiC,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAE5E,MAAM,CAAC,MAAM,WAAW,GAAG,2BAA2B,CAAC;IACrD,EAAE,EAAE,KAAK;IACT,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,MAAM;IAClB,iBAAiB,EAAE,iCAAiC;IACpD,WAAW,EAAE,oBAAoB;IACjC,UAAU,EAAE,aAAa;IACzB,mBAAmB,CAAC,KAAK;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACvE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;CACF,CAAC,CAAC"}
@@ -1,8 +1,2 @@
1
- /**
2
- * Zhipu provider wrapper.
3
- *
4
- * Normalizes Zhipu quota into generic toast entries.
5
- */
6
- import type { QuotaProvider } from "../lib/entries.js";
7
- export declare const zhipuProvider: QuotaProvider;
1
+ export declare const zhipuProvider: import("../lib/entries.js").QuotaProvider;
8
2
  //# sourceMappingURL=zhipu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"zhipu.d.ts","sourceRoot":"","sources":["../../src/providers/zhipu.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA6C,MAAM,mBAAmB,CAAC;AAUlG,eAAO,MAAM,aAAa,EAAE,aAqD3B,CAAC"}
1
+ {"version":3,"file":"zhipu.d.ts","sourceRoot":"","sources":["../../src/providers/zhipu.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa,2CAYxB,CAAC"}
@@ -1,54 +1,18 @@
1
- /**
2
- * Zhipu provider wrapper.
3
- *
4
- * Normalizes Zhipu quota into generic toast entries.
5
- */
1
+ /** Zhipu provider wrapper. */
6
2
  import { queryZhipuQuota } from "../lib/zhipu.js";
7
- import { isCanonicalProviderAvailable } from "../lib/provider-availability.js";
8
3
  import { DEFAULT_ZHIPU_AUTH_CACHE_MAX_AGE_MS, resolveZhipuAuthCached } from "../lib/zhipu-auth.js";
9
- import { attemptedResult, groupedPercentWindowEntries, mapNullableProviderResult, } from "./result-helpers.js";
10
- export const zhipuProvider = {
4
+ import { createGlmCodingPlanProvider } from "./glm-coding-plan-provider.js";
5
+ export const zhipuProvider = createGlmCodingPlanProvider({
11
6
  id: "zhipu",
12
- async isAvailable(ctx) {
13
- const providerAvailable = await isCanonicalProviderAvailable({
14
- ctx,
15
- providerId: "zhipu",
16
- fallbackOnError: false,
17
- });
18
- if (!providerAvailable) {
19
- return false;
20
- }
21
- const auth = await resolveZhipuAuthCached({
22
- maxAgeMs: DEFAULT_ZHIPU_AUTH_CACHE_MAX_AGE_MS,
23
- });
24
- return auth.state === "configured" || auth.state === "invalid";
25
- },
7
+ providerId: "zhipu",
8
+ errorLabel: "Zhipu",
9
+ authCacheMaxAgeMs: DEFAULT_ZHIPU_AUTH_CACHE_MAX_AGE_MS,
10
+ resolveAuth: resolveZhipuAuthCached,
11
+ queryQuota: queryZhipuQuota,
26
12
  matchesCurrentModel(model) {
27
13
  const lower = model.toLowerCase();
28
14
  const provider = lower.split("/")[0];
29
15
  return !!provider && (provider.includes("zhipu") || provider === "glm-coding-plan");
30
16
  },
31
- async fetch(ctx) {
32
- const result = await queryZhipuQuota({ requestTimeoutMs: ctx.config?.requestTimeoutMs });
33
- return mapNullableProviderResult(result, {
34
- errorLabel: "Zhipu",
35
- onSuccess: (result) => attemptedResult(groupedPercentWindowEntries({
36
- group: result.label,
37
- accounting: {
38
- resultType: "quota",
39
- acquisitionMethod: "remote_api",
40
- ownership: "maintained",
41
- authority: "provider_reported",
42
- },
43
- windows: [
44
- { window: result.windows.fiveHour, suffix: "5h", label: "5h:" },
45
- { window: result.windows.weekly, suffix: "Weekly", label: "Weekly:" },
46
- { window: result.windows.mcp, suffix: "MCP", label: "MCP:" },
47
- ],
48
- }), [], {
49
- singleWindowDisplayName: result.label,
50
- }),
51
- });
52
- },
53
- };
17
+ });
54
18
  //# sourceMappingURL=zhipu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zhipu.js","sourceRoot":"","sources":["../../src/providers/zhipu.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,mCAAmC,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,EAAE,EAAE,OAAO;IAEX,KAAK,CAAC,WAAW,CAAC,GAAyB;QACzC,MAAM,iBAAiB,GAAG,MAAM,4BAA4B,CAAC;YAC3D,GAAG;YACH,UAAU,EAAE,OAAO;YACnB,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC;YACxC,QAAQ,EAAE,mCAAmC;SAC9C,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,KAAK,YAAY,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC;IACjE,CAAC;IAED,mBAAmB,CAAC,KAAa;QAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,OAAO,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,iBAAiB,CAAC,CAAC;IACtF,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAyB;QACnC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAEzF,OAAO,yBAAyB,CAAC,MAAM,EAAE;YACvC,UAAU,EAAE,OAAO;YACnB,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CACpB,eAAe,CACb,2BAA2B,CAAC;gBAC1B,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,UAAU,EAAE;oBACV,UAAU,EAAE,OAAO;oBACnB,iBAAiB,EAAE,YAAY;oBAC/B,SAAS,EAAE,YAAY;oBACvB,SAAS,EAAE,mBAAmB;iBAC/B;gBACD,OAAO,EAAE;oBACP,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE;oBAC/D,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE;oBACrE,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE;iBAC7D;aACF,CAAC,EACF,EAAE,EACF;gBACE,uBAAuB,EAAE,MAAM,CAAC,KAAK;aACtC,CACF;SACJ,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"zhipu.js","sourceRoot":"","sources":["../../src/providers/zhipu.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mCAAmC,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAE5E,MAAM,CAAC,MAAM,aAAa,GAAG,2BAA2B,CAAC;IACvD,EAAE,EAAE,OAAO;IACX,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,OAAO;IACnB,iBAAiB,EAAE,mCAAmC;IACtD,WAAW,EAAE,sBAAsB;IACnC,UAAU,EAAE,eAAe;IAC3B,mBAAmB,CAAC,KAAK;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,OAAO,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,iBAAiB,CAAC,CAAC;IACtF,CAAC;CACF,CAAC,CAAC"}
package/dist/tui.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,yBAAyB,CAAC;AA8vBjC,QAAA,MAAM,YAAY,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAGjD,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,yBAAyB,CAAC;AAyoBjC,QAAA,MAAM,YAAY,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAGjD,CAAC;AAEF,eAAe,YAAY,CAAC"}
package/dist/tui.js CHANGED
@@ -9,6 +9,7 @@ import { createElement as _$createElement } from "@opentui/solid";
9
9
  /** @jsxImportSource @opentui/solid */
10
10
 
11
11
  import { Show, createEffect, createSignal, onCleanup } from "solid-js";
12
+ import { createTuiRefreshLifecycle } from "./lib/tui-refresh-lifecycle.js";
12
13
  import { getCompactStatusText, getHomeBottomAnnouncementText, getSidebarPanelLines, getSidebarPanelLinesExpanded, shouldRenderCompactStatus, shouldRenderHomeBottom, shouldRenderSidebarPanel } from "./lib/tui-panel-state.js";
13
14
  import { getSidebarBodyLineColor } from "./lib/tui-line-style.js";
14
15
  import { createTuiQuotaClient, getTuiRuntimeRootHints, getTuiSessionModelMeta, loadTuiHomeBottomStatus, loadTuiSessionQuotaSurfaces, normalizeTuiSessionID, resolveTuiSurfaceRegistration, writeTuiQuotaExportIfEnabled } from "./lib/tui-runtime.js";
@@ -38,98 +39,51 @@ function createSessionQuotaResource(api, sessionID) {
38
39
  const [compact, setCompact] = createSignal({
39
40
  status: "loading"
40
41
  });
41
- let refCount = 0;
42
- let disposed = false;
43
- let loadVersion = 0;
44
- let inFlight = false;
45
- let queued = false;
46
- const timers = new Set();
47
- const reload = () => {
48
- if (disposed) return;
49
- if (inFlight) {
50
- queued = true;
51
- loadVersion += 1;
52
- return;
53
- }
54
- inFlight = true;
55
- const currentVersion = ++loadVersion;
56
- void loadTuiSessionQuotaSurfaces({
42
+ const lifecycle = createTuiRefreshLifecycle({
43
+ load: () => loadTuiSessionQuotaSurfaces({
57
44
  api,
58
45
  sessionID
59
- }).then(next => {
60
- if (disposed || currentVersion !== loadVersion) return;
46
+ }),
47
+ apply: next => {
61
48
  setSidebar(next.sidebar);
62
49
  setCompact(next.compact);
63
- }).catch(() => {
64
- if (disposed || currentVersion !== loadVersion) return;
65
- }).finally(() => {
66
- if (disposed) return;
67
- inFlight = false;
68
- if (queued) {
69
- queued = false;
70
- reload();
50
+ },
51
+ intervalMs: REFRESH_INTERVAL_MS,
52
+ eventRefreshDelaysMs: EVENT_REFRESH_DELAYS_MS,
53
+ // TUI/session state can hydrate asynchronously after mount or session switch,
54
+ // so retry a few times to recover from empty first-load reads.
55
+ recoveryDelaysMs: MOUNT_RECOVERY_DELAYS_MS,
56
+ subscribe: scheduleRefresh => [api.event.on("session.updated", event => {
57
+ if (event.properties?.info?.id === sessionID) {
58
+ scheduleRefresh();
71
59
  }
72
- });
73
- };
74
- const queueRefresh = delay => {
75
- if (disposed) return;
76
- const timer = setTimeout(() => {
77
- timers.delete(timer);
78
- reload();
79
- }, delay);
80
- timers.add(timer);
81
- };
82
- const scheduleRefresh = () => {
83
- for (const delay of EVENT_REFRESH_DELAYS_MS) queueRefresh(delay);
84
- };
85
-
86
- // TUI/session state can hydrate asynchronously after mount or session switch,
87
- // so retry a few times to recover from empty first-load reads.
88
- const scheduleMountRecovery = () => {
89
- for (const delay of MOUNT_RECOVERY_DELAYS_MS) queueRefresh(delay);
90
- };
91
- const interval = setInterval(reload, REFRESH_INTERVAL_MS);
92
- const unsubscribers = [api.event.on("session.updated", event => {
93
- if (event.properties?.info?.id === sessionID) {
94
- scheduleRefresh();
95
- }
96
- }), api.event.on("message.updated", event => {
97
- if (event.properties?.info?.sessionID === sessionID) {
98
- scheduleRefresh();
99
- }
100
- }), api.event.on("message.removed", event => {
101
- if (event.properties?.sessionID === sessionID) {
102
- scheduleRefresh();
103
- }
104
- }), api.event.on("tui.session.select", event => {
105
- if (event.properties?.sessionID === sessionID) {
106
- scheduleRefresh();
60
+ }), api.event.on("message.updated", event => {
61
+ if (event.properties?.info?.sessionID === sessionID) {
62
+ scheduleRefresh();
63
+ }
64
+ }), api.event.on("message.removed", event => {
65
+ if (event.properties?.sessionID === sessionID) {
66
+ scheduleRefresh();
67
+ }
68
+ }), api.event.on("tui.session.select", event => {
69
+ if (event.properties?.sessionID === sessionID) {
70
+ scheduleRefresh();
71
+ }
72
+ })],
73
+ onDispose: () => {
74
+ getSessionResourceMap(api).delete(sessionID);
107
75
  }
108
- })];
109
- const dispose = () => {
110
- if (disposed) return;
111
- disposed = true;
112
- clearInterval(interval);
113
- for (const timer of timers) clearTimeout(timer);
114
- timers.clear();
115
- for (const unsubscribe of unsubscribers) unsubscribe();
116
- getSessionResourceMap(api).delete(sessionID);
117
- };
76
+ });
118
77
  const resource = {
119
78
  sessionID,
120
79
  sidebar,
121
80
  compact,
122
81
  retain: () => {
123
- refCount += 1;
82
+ lifecycle.retain();
124
83
  return resource;
125
84
  },
126
- release: () => {
127
- refCount -= 1;
128
- if (refCount <= 0) dispose();
129
- }
85
+ release: lifecycle.release
130
86
  };
131
- reload();
132
- scheduleMountRecovery();
133
87
  return resource;
134
88
  }
135
89
  function acquireSessionQuotaResource(api, sessionID) {
@@ -149,26 +103,12 @@ function createHomeBottomResource(api, compactHomeBottomEnabled) {
149
103
  status: "disabled"
150
104
  }
151
105
  });
152
- let refCount = 0;
153
- let disposed = false;
154
- let loadVersion = 0;
155
- let inFlight = false;
156
- let queued = false;
157
- const timers = new Set();
158
- const reload = () => {
159
- if (disposed) return;
160
- if (inFlight) {
161
- queued = true;
162
- loadVersion += 1;
163
- return;
164
- }
165
- inFlight = true;
166
- const currentVersion = ++loadVersion;
167
- void loadTuiHomeBottomStatus({
106
+ const lifecycle = createTuiRefreshLifecycle({
107
+ load: () => loadTuiHomeBottomStatus({
168
108
  api
169
- }).then(next => {
170
- if (disposed || currentVersion !== loadVersion) return;
171
- setBottom(next);
109
+ }),
110
+ apply: setBottom,
111
+ afterApply: () => {
172
112
  // Fire-and-forget: write export file if enabled. A failed write must
173
113
  // never affect TUI rendering, so log a warning and continue.
174
114
  void writeTuiQuotaExportIfEnabled({
@@ -176,51 +116,22 @@ function createHomeBottomResource(api, compactHomeBottomEnabled) {
176
116
  }).catch(err => {
177
117
  console.warn(`[opencode-quota] quota export write failed: ${String(err)}`);
178
118
  });
179
- }).catch(() => {
180
- if (disposed || currentVersion !== loadVersion) return;
181
- }).finally(() => {
182
- if (disposed) return;
183
- inFlight = false;
184
- if (queued) {
185
- queued = false;
186
- reload();
187
- }
188
- });
189
- };
190
- const queueRefresh = delay => {
191
- if (disposed) return;
192
- const timer = setTimeout(() => {
193
- timers.delete(timer);
194
- reload();
195
- }, delay);
196
- timers.add(timer);
197
- };
198
- const scheduleRefresh = () => {
199
- for (const delay of EVENT_REFRESH_DELAYS_MS) queueRefresh(delay);
200
- };
201
- const interval = setInterval(reload, REFRESH_INTERVAL_MS);
202
- const unsubscribers = [api.event.on("session.updated", scheduleRefresh), api.event.on("message.updated", scheduleRefresh), api.event.on("message.removed", scheduleRefresh), api.event.on("tui.session.select", scheduleRefresh)];
203
- const dispose = () => {
204
- if (disposed) return;
205
- disposed = true;
206
- clearInterval(interval);
207
- for (const timer of timers) clearTimeout(timer);
208
- timers.clear();
209
- for (const unsubscribe of unsubscribers) unsubscribe();
210
- homeResources.delete(api);
211
- };
119
+ },
120
+ intervalMs: REFRESH_INTERVAL_MS,
121
+ eventRefreshDelaysMs: EVENT_REFRESH_DELAYS_MS,
122
+ subscribe: scheduleRefresh => [api.event.on("session.updated", scheduleRefresh), api.event.on("message.updated", scheduleRefresh), api.event.on("message.removed", scheduleRefresh), api.event.on("tui.session.select", scheduleRefresh)],
123
+ onDispose: () => {
124
+ homeResources.delete(api);
125
+ }
126
+ });
212
127
  const resource = {
213
128
  bottom,
214
129
  retain: () => {
215
- refCount += 1;
130
+ lifecycle.retain();
216
131
  return resource;
217
132
  },
218
- release: () => {
219
- refCount -= 1;
220
- if (refCount <= 0) dispose();
221
- }
133
+ release: lifecycle.release
222
134
  };
223
- reload();
224
135
  return resource;
225
136
  }
226
137
  function acquireHomeBottomResource(api, compactHomeBottomEnabled) {