@cortexkit/opencode-antigravity-auth 3.0.5 → 3.0.6

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 (370) hide show
  1. package/README.md +38 -352
  2. package/dist/cli.js +9199 -8799
  3. package/dist/cli.js.map +4 -4
  4. package/dist/cli.meta.json +1286 -1064
  5. package/dist/index.js +13641 -14559
  6. package/dist/index.js.map +4 -4
  7. package/dist/index.meta.json +1333 -1342
  8. package/dist/server.js +10618 -11080
  9. package/dist/server.js.map +4 -4
  10. package/dist/server.meta.json +1 -1
  11. package/dist/src/auth-method-labels.d.ts +3 -0
  12. package/dist/src/auth-method-labels.d.ts.map +1 -0
  13. package/dist/src/auth-method-labels.js +5 -0
  14. package/dist/src/auth-method-labels.js.map +1 -0
  15. package/dist/src/cli.d.ts +3 -1
  16. package/dist/src/cli.d.ts.map +1 -1
  17. package/dist/src/cli.js +98 -12
  18. package/dist/src/cli.js.map +1 -1
  19. package/dist/src/common-auth-embedded/claustrum/consumer.d.ts +33 -3
  20. package/dist/src/common-auth-embedded/claustrum/consumer.js +96 -1
  21. package/dist/src/common-auth-embedded/claustrum/custody.d.ts +4 -1
  22. package/dist/src/common-auth-embedded/claustrum/enrollment.d.ts +6 -1
  23. package/dist/src/common-auth-embedded/claustrum/enrollment.js +6 -1
  24. package/dist/src/common-auth-embedded/claustrum/index.d.ts +2 -1
  25. package/dist/src/common-auth-embedded/claustrum/index.js +5 -3
  26. package/dist/src/common-auth-embedded/claustrum/insula.d.ts +69 -0
  27. package/dist/src/common-auth-embedded/claustrum/insula.js +152 -0
  28. package/dist/src/common-auth-embedded/claustrum/roster.d.ts +13 -0
  29. package/dist/src/common-auth-embedded/claustrum/roster.js +33 -3
  30. package/dist/src/common-auth-embedded/fs/refresh-file-lock.js +20 -11
  31. package/dist/src/common-auth-embedded/quota/codec.d.ts +2 -0
  32. package/dist/src/common-auth-embedded/quota/codec.js +2 -1
  33. package/dist/src/common-auth-embedded/quota/format.d.ts +26 -0
  34. package/dist/src/common-auth-embedded/quota/format.js +78 -42
  35. package/dist/src/common-auth-embedded/quota/index.d.ts +4 -4
  36. package/dist/src/common-auth-embedded/quota/index.js +2 -2
  37. package/dist/src/common-auth-embedded/quota/map.d.ts +11 -0
  38. package/dist/src/common-auth-embedded/quota/map.js +36 -1
  39. package/dist/src/common-auth-embedded/quota/merge.d.ts +3 -1
  40. package/dist/src/common-auth-embedded/quota/merge.js +21 -5
  41. package/dist/src/common-auth-embedded/quota/projection.d.ts +2 -1
  42. package/dist/src/common-auth-embedded/quota/projection.js +3 -1
  43. package/dist/src/common-auth-embedded/setup/command-runner.js +49 -0
  44. package/dist/src/common-auth-embedded/setup/command.d.ts +3 -0
  45. package/dist/src/common-auth-embedded/setup/command.js +215 -0
  46. package/dist/src/common-auth-embedded/setup/config-write.js +60 -0
  47. package/dist/src/common-auth-embedded/setup/detect.d.ts +6 -0
  48. package/dist/src/common-auth-embedded/setup/detect.js +224 -0
  49. package/dist/src/common-auth-embedded/setup/enroll.js +70 -0
  50. package/dist/src/common-auth-embedded/setup/index.d.ts +5 -0
  51. package/dist/src/common-auth-embedded/setup/index.js +4 -0
  52. package/dist/src/common-auth-embedded/setup/paths.js +33 -0
  53. package/dist/src/common-auth-embedded/setup/process-fence.d.ts +43 -0
  54. package/dist/src/common-auth-embedded/setup/process-fence.js +270 -0
  55. package/dist/src/common-auth-embedded/setup/prompts.js +26 -0
  56. package/dist/src/common-auth-embedded/setup/register.d.ts +11 -0
  57. package/dist/src/common-auth-embedded/setup/register.js +130 -0
  58. package/dist/src/common-auth-embedded/setup/types.d.ts +136 -0
  59. package/dist/src/common-auth-embedded/source-output.json +228 -80
  60. package/dist/src/common-auth-embedded/store/schema.d.ts +2 -0
  61. package/dist/src/common-auth-embedded/store/schema.js +3 -2
  62. package/dist/src/ga/rpc/protocol.d.ts +4 -7
  63. package/dist/src/ga/rpc/protocol.d.ts.map +1 -1
  64. package/dist/src/ga/rpc/protocol.js +5 -13
  65. package/dist/src/ga/rpc/protocol.js.map +1 -1
  66. package/dist/src/ga/server/auth.d.ts +8 -3
  67. package/dist/src/ga/server/auth.d.ts.map +1 -1
  68. package/dist/src/ga/server/auth.js +25 -9
  69. package/dist/src/ga/server/auth.js.map +1 -1
  70. package/dist/src/ga/server/index.d.ts +39 -12
  71. package/dist/src/ga/server/index.d.ts.map +1 -1
  72. package/dist/src/ga/server/index.js +214 -53
  73. package/dist/src/ga/server/index.js.map +1 -1
  74. package/dist/src/ga/server/pool-login.d.ts +22 -0
  75. package/dist/src/ga/server/pool-login.d.ts.map +1 -0
  76. package/dist/src/ga/server/pool-login.js +150 -0
  77. package/dist/src/ga/server/pool-login.js.map +1 -0
  78. package/dist/src/ga/tui/host-ga.d.ts +2 -5
  79. package/dist/src/ga/tui/host-ga.d.ts.map +1 -1
  80. package/dist/src/ga/tui/host-ga.js +17 -49
  81. package/dist/src/ga/tui/host-ga.js.map +1 -1
  82. package/dist/src/plugin/accounts.d.ts +1 -1
  83. package/dist/src/plugin/accounts.d.ts.map +1 -1
  84. package/dist/src/plugin/accounts.js +1 -0
  85. package/dist/src/plugin/accounts.js.map +1 -1
  86. package/dist/src/plugin/background-vault-quota-refresh.d.ts +2 -0
  87. package/dist/src/plugin/background-vault-quota-refresh.d.ts.map +1 -1
  88. package/dist/src/plugin/background-vault-quota-refresh.js +125 -10
  89. package/dist/src/plugin/background-vault-quota-refresh.js.map +1 -1
  90. package/dist/src/plugin/command-apply.d.ts +2 -2
  91. package/dist/src/plugin/command-apply.d.ts.map +1 -1
  92. package/dist/src/plugin/command-apply.js +3 -10
  93. package/dist/src/plugin/command-apply.js.map +1 -1
  94. package/dist/src/plugin/commands.d.ts +1 -0
  95. package/dist/src/plugin/commands.d.ts.map +1 -1
  96. package/dist/src/plugin/commands.js +2 -2
  97. package/dist/src/plugin/commands.js.map +1 -1
  98. package/dist/src/plugin/config/operator-settings-schema.d.ts +0 -4
  99. package/dist/src/plugin/config/operator-settings-schema.d.ts.map +1 -1
  100. package/dist/src/plugin/config/operator-settings-schema.js +0 -5
  101. package/dist/src/plugin/config/operator-settings-schema.js.map +1 -1
  102. package/dist/src/plugin/config/schema.d.ts +0 -7
  103. package/dist/src/plugin/config/schema.d.ts.map +1 -1
  104. package/dist/src/plugin/config/schema.js +0 -43
  105. package/dist/src/plugin/config/schema.js.map +1 -1
  106. package/dist/src/plugin/config/writer.d.ts +1 -1
  107. package/dist/src/plugin/config/writer.js +1 -1
  108. package/dist/src/plugin/fetch/retry-state.d.ts +19 -3
  109. package/dist/src/plugin/fetch/retry-state.d.ts.map +1 -1
  110. package/dist/src/plugin/fetch/retry-state.js +0 -6
  111. package/dist/src/plugin/fetch/retry-state.js.map +1 -1
  112. package/dist/src/plugin/fetch-interceptor.d.ts.map +1 -1
  113. package/dist/src/plugin/fetch-interceptor.js +0 -3
  114. package/dist/src/plugin/fetch-interceptor.js.map +1 -1
  115. package/dist/src/plugin/fetch-routing.d.ts +0 -16
  116. package/dist/src/plugin/fetch-routing.d.ts.map +1 -1
  117. package/dist/src/plugin/fetch-routing.js +0 -49
  118. package/dist/src/plugin/fetch-routing.js.map +1 -1
  119. package/dist/src/plugin/ga-location-services.d.ts +14 -9
  120. package/dist/src/plugin/ga-location-services.d.ts.map +1 -1
  121. package/dist/src/plugin/ga-location-services.js +120 -40
  122. package/dist/src/plugin/ga-location-services.js.map +1 -1
  123. package/dist/src/plugin/google-origin.d.ts +7 -0
  124. package/dist/src/plugin/google-origin.d.ts.map +1 -0
  125. package/dist/src/plugin/google-origin.js +14 -0
  126. package/dist/src/plugin/google-origin.js.map +1 -0
  127. package/dist/src/plugin/index.d.ts.map +1 -1
  128. package/dist/src/plugin/index.js +15 -4
  129. package/dist/src/plugin/index.js.map +1 -1
  130. package/dist/src/plugin/oauth-methods.d.ts.map +1 -1
  131. package/dist/src/plugin/oauth-methods.js +0 -33
  132. package/dist/src/plugin/oauth-methods.js.map +1 -1
  133. package/dist/src/plugin/operator-settings.d.ts +1 -1
  134. package/dist/src/plugin/operator-settings.d.ts.map +1 -1
  135. package/dist/src/plugin/operator-settings.js +1 -8
  136. package/dist/src/plugin/operator-settings.js.map +1 -1
  137. package/dist/src/plugin/quota.d.ts +11 -7
  138. package/dist/src/plugin/quota.d.ts.map +1 -1
  139. package/dist/src/plugin/quota.js +128 -102
  140. package/dist/src/plugin/quota.js.map +1 -1
  141. package/dist/src/plugin/request.d.ts +5 -3
  142. package/dist/src/plugin/request.d.ts.map +1 -1
  143. package/dist/src/plugin/request.js +87 -168
  144. package/dist/src/plugin/request.js.map +1 -1
  145. package/dist/src/plugin/server.d.ts.map +1 -1
  146. package/dist/src/plugin/server.js +60 -53
  147. package/dist/src/plugin/server.js.map +1 -1
  148. package/dist/src/plugin/shared/advisory-repository.d.ts +20 -0
  149. package/dist/src/plugin/shared/advisory-repository.d.ts.map +1 -0
  150. package/dist/src/plugin/shared/advisory-repository.js +91 -0
  151. package/dist/src/plugin/shared/advisory-repository.js.map +1 -0
  152. package/dist/src/plugin/shared/request-services.d.ts +19 -25
  153. package/dist/src/plugin/shared/request-services.d.ts.map +1 -1
  154. package/dist/src/plugin/shared/request-services.js +105 -188
  155. package/dist/src/plugin/shared/request-services.js.map +1 -1
  156. package/dist/src/plugin/shared/runtime.js +4 -2
  157. package/dist/src/plugin/shared/runtime.js.map +1 -1
  158. package/dist/src/plugin/shared/vault-request-credentials.d.ts +4 -1
  159. package/dist/src/plugin/shared/vault-request-credentials.d.ts.map +1 -1
  160. package/dist/src/plugin/shared/vault-request-credentials.js +41 -7
  161. package/dist/src/plugin/shared/vault-request-credentials.js.map +1 -1
  162. package/dist/src/plugin/storage.d.ts +2 -2
  163. package/dist/src/plugin/storage.d.ts.map +1 -1
  164. package/dist/src/plugin/storage.js.map +1 -1
  165. package/dist/src/plugin/vault-custody.d.ts.map +1 -1
  166. package/dist/src/plugin/vault-custody.js +147 -35
  167. package/dist/src/plugin/vault-custody.js.map +1 -1
  168. package/dist/src/plugin/vault-quota-outcome.d.ts +19 -0
  169. package/dist/src/plugin/vault-quota-outcome.d.ts.map +1 -0
  170. package/dist/src/plugin/vault-quota-outcome.js +47 -0
  171. package/dist/src/plugin/vault-quota-outcome.js.map +1 -0
  172. package/dist/src/quota-presentation.d.ts +29 -1
  173. package/dist/src/quota-presentation.d.ts.map +1 -1
  174. package/dist/src/quota-presentation.js +52 -17
  175. package/dist/src/quota-presentation.js.map +1 -1
  176. package/dist/src/setup.d.ts +15 -0
  177. package/dist/src/setup.d.ts.map +1 -0
  178. package/dist/src/setup.js +320 -0
  179. package/dist/src/setup.js.map +1 -0
  180. package/dist/src/sidebar-projection.d.ts +1 -1
  181. package/dist/src/sidebar-projection.d.ts.map +1 -1
  182. package/dist/src/sidebar-state.d.ts +1 -1
  183. package/dist/src/sidebar-state.d.ts.map +1 -1
  184. package/dist/src/sidebar-state.js +19 -1
  185. package/dist/src/sidebar-state.js.map +1 -1
  186. package/dist/src/tui.d.ts.map +1 -1
  187. package/dist/src/tui.js +205 -110
  188. package/dist/src/tui.js.map +1 -1
  189. package/dist/tui-build-map.json +37 -37
  190. package/dist/tui-ga-build-map.json +33 -33
  191. package/dist/types/src/auth-method-labels.d.ts +3 -0
  192. package/dist/types/src/auth-method-labels.d.ts.map +1 -0
  193. package/dist/types/src/cli.d.ts +3 -1
  194. package/dist/types/src/cli.d.ts.map +1 -1
  195. package/dist/types/src/common-auth-embedded/auth-menu/terminal.d.ts +43 -0
  196. package/dist/types/src/common-auth-embedded/claustrum/consumer.d.ts +132 -0
  197. package/dist/types/src/common-auth-embedded/claustrum/custody.d.ts +178 -0
  198. package/dist/types/src/common-auth-embedded/claustrum/enrollment.d.ts +164 -0
  199. package/dist/types/src/common-auth-embedded/claustrum/errors.d.ts +16 -0
  200. package/dist/types/src/common-auth-embedded/claustrum/host-slot.d.ts +39 -0
  201. package/dist/types/src/common-auth-embedded/claustrum/index.d.ts +19 -0
  202. package/dist/types/src/common-auth-embedded/claustrum/insula.d.ts +69 -0
  203. package/dist/types/src/common-auth-embedded/claustrum/interlock.d.ts +27 -0
  204. package/dist/types/src/common-auth-embedded/claustrum/roster.d.ts +221 -0
  205. package/dist/types/src/common-auth-embedded/opencode2/errors.d.ts +25 -0
  206. package/dist/types/src/common-auth-embedded/opencode2/index.d.ts +8 -0
  207. package/dist/types/src/common-auth-embedded/opencode2/install.d.ts +55 -0
  208. package/dist/types/src/common-auth-embedded/opencode2/integration.d.ts +112 -0
  209. package/dist/types/src/common-auth-embedded/opencode2/sse.d.ts +12 -0
  210. package/dist/types/src/common-auth-embedded/opencode2/types.d.ts +413 -0
  211. package/dist/types/src/common-auth-embedded/quota/codec.d.ts +2 -0
  212. package/dist/types/src/common-auth-embedded/quota/format.d.ts +26 -0
  213. package/dist/types/src/common-auth-embedded/quota/index.d.ts +4 -4
  214. package/dist/types/src/common-auth-embedded/quota/map.d.ts +11 -0
  215. package/dist/types/src/common-auth-embedded/quota/merge.d.ts +3 -1
  216. package/dist/types/src/common-auth-embedded/quota/projection.d.ts +2 -1
  217. package/dist/types/src/common-auth-embedded/routing/admission.d.ts +93 -0
  218. package/dist/types/src/common-auth-embedded/routing/index.d.ts +8 -0
  219. package/dist/types/src/common-auth-embedded/routing/ordered.d.ts +47 -0
  220. package/dist/types/src/common-auth-embedded/routing/pins.d.ts +22 -0
  221. package/dist/types/src/common-auth-embedded/routing/sticky.d.ts +158 -0
  222. package/dist/types/src/common-auth-embedded/setup/command.d.ts +3 -0
  223. package/dist/types/src/common-auth-embedded/setup/detect.d.ts +6 -0
  224. package/dist/types/src/common-auth-embedded/setup/index.d.ts +5 -0
  225. package/dist/types/src/common-auth-embedded/setup/process-fence.d.ts +43 -0
  226. package/dist/types/src/common-auth-embedded/setup/register.d.ts +11 -0
  227. package/dist/types/src/common-auth-embedded/setup/types.d.ts +136 -0
  228. package/dist/types/src/common-auth-embedded/store/schema.d.ts +2 -0
  229. package/dist/types/src/ga/rpc/protocol.d.ts +4 -7
  230. package/dist/types/src/ga/rpc/protocol.d.ts.map +1 -1
  231. package/dist/types/src/ga/server/auth.d.ts +8 -3
  232. package/dist/types/src/ga/server/auth.d.ts.map +1 -1
  233. package/dist/types/src/ga/server/index.d.ts +39 -12
  234. package/dist/types/src/ga/server/index.d.ts.map +1 -1
  235. package/dist/types/src/ga/server/pool-login.d.ts +22 -0
  236. package/dist/types/src/ga/server/pool-login.d.ts.map +1 -0
  237. package/dist/types/src/plugin/accounts.d.ts +1 -1
  238. package/dist/types/src/plugin/accounts.d.ts.map +1 -1
  239. package/dist/types/src/plugin/background-vault-quota-refresh.d.ts +2 -0
  240. package/dist/types/src/plugin/background-vault-quota-refresh.d.ts.map +1 -1
  241. package/dist/types/src/plugin/command-apply.d.ts +2 -2
  242. package/dist/types/src/plugin/command-apply.d.ts.map +1 -1
  243. package/dist/types/src/plugin/commands.d.ts +1 -0
  244. package/dist/types/src/plugin/commands.d.ts.map +1 -1
  245. package/dist/types/src/plugin/config/operator-settings-schema.d.ts +0 -4
  246. package/dist/types/src/plugin/config/operator-settings-schema.d.ts.map +1 -1
  247. package/dist/types/src/plugin/config/schema.d.ts +0 -7
  248. package/dist/types/src/plugin/config/schema.d.ts.map +1 -1
  249. package/dist/types/src/plugin/config/writer.d.ts +1 -1
  250. package/dist/types/src/plugin/fetch/retry-state.d.ts +19 -3
  251. package/dist/types/src/plugin/fetch/retry-state.d.ts.map +1 -1
  252. package/dist/types/src/plugin/fetch-interceptor.d.ts.map +1 -1
  253. package/dist/types/src/plugin/fetch-routing.d.ts +0 -16
  254. package/dist/types/src/plugin/fetch-routing.d.ts.map +1 -1
  255. package/dist/types/src/plugin/ga-location-services.d.ts +14 -9
  256. package/dist/types/src/plugin/ga-location-services.d.ts.map +1 -1
  257. package/dist/types/src/plugin/google-origin.d.ts +7 -0
  258. package/dist/types/src/plugin/google-origin.d.ts.map +1 -0
  259. package/dist/types/src/plugin/index.d.ts.map +1 -1
  260. package/dist/types/src/plugin/oauth-methods.d.ts.map +1 -1
  261. package/dist/types/src/plugin/operator-settings.d.ts +1 -1
  262. package/dist/types/src/plugin/operator-settings.d.ts.map +1 -1
  263. package/dist/types/src/plugin/quota.d.ts +11 -7
  264. package/dist/types/src/plugin/quota.d.ts.map +1 -1
  265. package/dist/types/src/plugin/request.d.ts +5 -3
  266. package/dist/types/src/plugin/request.d.ts.map +1 -1
  267. package/dist/types/src/plugin/server.d.ts.map +1 -1
  268. package/dist/types/src/plugin/shared/advisory-repository.d.ts +20 -0
  269. package/dist/types/src/plugin/shared/advisory-repository.d.ts.map +1 -0
  270. package/dist/types/src/plugin/shared/request-services.d.ts +19 -25
  271. package/dist/types/src/plugin/shared/request-services.d.ts.map +1 -1
  272. package/dist/types/src/plugin/shared/vault-request-credentials.d.ts +4 -1
  273. package/dist/types/src/plugin/shared/vault-request-credentials.d.ts.map +1 -1
  274. package/dist/types/src/plugin/storage.d.ts +2 -2
  275. package/dist/types/src/plugin/storage.d.ts.map +1 -1
  276. package/dist/types/src/plugin/vault-custody.d.ts.map +1 -1
  277. package/dist/types/src/plugin/vault-quota-outcome.d.ts +19 -0
  278. package/dist/types/src/plugin/vault-quota-outcome.d.ts.map +1 -0
  279. package/dist/types/src/setup.d.ts +15 -0
  280. package/dist/types/src/setup.d.ts.map +1 -0
  281. package/dist/types/src/sidebar-state.d.ts +1 -1
  282. package/dist/types/src/sidebar-state.d.ts.map +1 -1
  283. package/package.json +5 -4
  284. package/src/common-auth-embedded/claustrum/consumer.d.ts +33 -3
  285. package/src/common-auth-embedded/claustrum/consumer.js +96 -1
  286. package/src/common-auth-embedded/claustrum/custody.d.ts +4 -1
  287. package/src/common-auth-embedded/claustrum/enrollment.d.ts +6 -1
  288. package/src/common-auth-embedded/claustrum/enrollment.js +6 -1
  289. package/src/common-auth-embedded/claustrum/index.d.ts +2 -1
  290. package/src/common-auth-embedded/claustrum/index.js +5 -3
  291. package/src/common-auth-embedded/claustrum/insula.d.ts +69 -0
  292. package/src/common-auth-embedded/claustrum/insula.js +152 -0
  293. package/src/common-auth-embedded/claustrum/roster.d.ts +13 -0
  294. package/src/common-auth-embedded/claustrum/roster.js +33 -3
  295. package/src/common-auth-embedded/fs/refresh-file-lock.js +20 -11
  296. package/src/common-auth-embedded/quota/codec.d.ts +2 -0
  297. package/src/common-auth-embedded/quota/codec.js +2 -1
  298. package/src/common-auth-embedded/quota/format.d.ts +26 -0
  299. package/src/common-auth-embedded/quota/format.js +78 -42
  300. package/src/common-auth-embedded/quota/index.d.ts +4 -4
  301. package/src/common-auth-embedded/quota/index.js +2 -2
  302. package/src/common-auth-embedded/quota/map.d.ts +11 -0
  303. package/src/common-auth-embedded/quota/map.js +36 -1
  304. package/src/common-auth-embedded/quota/merge.d.ts +3 -1
  305. package/src/common-auth-embedded/quota/merge.js +21 -5
  306. package/src/common-auth-embedded/quota/projection.d.ts +2 -1
  307. package/src/common-auth-embedded/quota/projection.js +3 -1
  308. package/src/common-auth-embedded/setup/command-runner.js +49 -0
  309. package/src/common-auth-embedded/setup/command.d.ts +3 -0
  310. package/src/common-auth-embedded/setup/command.js +215 -0
  311. package/src/common-auth-embedded/setup/config-write.js +60 -0
  312. package/src/common-auth-embedded/setup/detect.d.ts +6 -0
  313. package/src/common-auth-embedded/setup/detect.js +224 -0
  314. package/src/common-auth-embedded/setup/enroll.js +70 -0
  315. package/src/common-auth-embedded/setup/index.d.ts +5 -0
  316. package/src/common-auth-embedded/setup/index.js +4 -0
  317. package/src/common-auth-embedded/setup/paths.js +33 -0
  318. package/src/common-auth-embedded/setup/process-fence.d.ts +43 -0
  319. package/src/common-auth-embedded/setup/process-fence.js +270 -0
  320. package/src/common-auth-embedded/setup/prompts.js +26 -0
  321. package/src/common-auth-embedded/setup/register.d.ts +11 -0
  322. package/src/common-auth-embedded/setup/register.js +130 -0
  323. package/src/common-auth-embedded/setup/types.d.ts +136 -0
  324. package/src/common-auth-embedded/source-output.json +228 -80
  325. package/src/common-auth-embedded/store/schema.d.ts +2 -0
  326. package/src/common-auth-embedded/store/schema.js +3 -2
  327. package/src/sidebar-state.ts +19 -2
  328. package/src/tui-compiled/common-auth-embedded/quota/codec.js +2 -1
  329. package/src/tui-compiled/common-auth-embedded/quota/format.js +78 -42
  330. package/src/tui-compiled/common-auth-embedded/quota/index.js +2 -2
  331. package/src/tui-compiled/common-auth-embedded/quota/map.js +36 -1
  332. package/src/tui-compiled/common-auth-embedded/quota/merge.js +21 -5
  333. package/src/tui-compiled/common-auth-embedded/quota/projection.js +3 -1
  334. package/src/tui-compiled/quota-presentation.js +42 -9
  335. package/src/tui-compiled/sidebar-state.js +15 -1
  336. package/src/tui-compiled/tui.js +516 -334
  337. package/src/tui-ga-compiled/host-ga.js +11 -38
  338. package/src/tui-ga-compiled/shared/1ff34afa7c42d196-projection.js +3 -1
  339. package/src/tui-ga-compiled/shared/77138df80c7ae006-map.js +36 -1
  340. package/src/tui-ga-compiled/shared/98cc0c186c46c42d-quota-presentation.js +42 -9
  341. package/src/tui-ga-compiled/shared/aef2402f8fc342f0-format.js +78 -42
  342. package/src/tui-ga-compiled/shared/bf17cbee28e58197-index.js +2 -2
  343. package/src/tui-ga-compiled/shared/ca21dc417b36dc5d-codec.js +2 -1
  344. package/src/tui-ga-compiled/shared/ecc214300932dfb6-merge.js +21 -5
  345. package/src/tui-ga-raw/host-ga.tsx +31 -62
  346. package/src/tui-ga-raw/shared/1ff34afa7c42d196-projection.js +3 -1
  347. package/src/tui-ga-raw/shared/77138df80c7ae006-map.js +36 -1
  348. package/src/tui-ga-raw/shared/98cc0c186c46c42d-quota-presentation.js +42 -9
  349. package/src/tui-ga-raw/shared/aef2402f8fc342f0-format.js +78 -42
  350. package/src/tui-ga-raw/shared/bf17cbee28e58197-index.js +2 -2
  351. package/src/tui-ga-raw/shared/ca21dc417b36dc5d-codec.js +2 -1
  352. package/src/tui-ga-raw/shared/ecc214300932dfb6-merge.js +21 -5
  353. package/src/tui-raw/common-auth-embedded/quota/codec.js +2 -1
  354. package/src/tui-raw/common-auth-embedded/quota/format.js +78 -42
  355. package/src/tui-raw/common-auth-embedded/quota/index.js +2 -2
  356. package/src/tui-raw/common-auth-embedded/quota/map.js +36 -1
  357. package/src/tui-raw/common-auth-embedded/quota/merge.js +21 -5
  358. package/src/tui-raw/common-auth-embedded/quota/projection.js +3 -1
  359. package/src/tui-raw/quota-presentation.js +42 -9
  360. package/src/tui-raw/sidebar-state.js +15 -1
  361. package/src/tui-raw/tui.tsx +423 -218
  362. package/src/tui.tsx +423 -218
  363. package/dist/src/tui/vault-setup.d.ts +0 -98
  364. package/dist/src/tui/vault-setup.d.ts.map +0 -1
  365. package/dist/src/tui/vault-setup.js +0 -198
  366. package/dist/src/tui/vault-setup.js.map +0 -1
  367. package/dist/types/src/plugin/transform/model-resolver.d.ts +0 -2
  368. package/dist/types/src/plugin/transform/model-resolver.d.ts.map +0 -1
  369. package/dist/types/src/quota-presentation.d.ts +0 -18
  370. package/dist/types/src/quota-presentation.d.ts.map +0 -1
package/src/tui.tsx CHANGED
@@ -13,16 +13,18 @@
13
13
  * score, and per-session routing decisions), so the fleet components are
14
14
  * adapted rather than copied verbatim:
15
15
  *
16
- * - `QuotaRow` reads `remainingPercent + resetAt` from the Antigravity
16
+ * - `WindowRow` reads `remainingPercent + resetAt` from the Antigravity
17
17
  * `SidebarQuotaEntry` shape and colors via `quotaTone` (remaining
18
- * threshold) instead of `usageTone` (used threshold).
18
+ * threshold) instead of `usageTone` (used threshold). Each window is one
19
+ * line sized from the measured sidebar width.
19
20
  * - `AccountBlock` derives its status word (`active` / `idle` /
20
- * `cooling` / `off` / `blocked`) from the Antigravity account state and
21
- * surfaces health as a muted secondary line rather than a top-level
22
- * bar — fleet parity for "Quota first, health second".
23
- * - `Routing` resolves to a single `Route` `StatRow` carrying the most
24
- * recent `activeRouting` entry, since Antigravity does not maintain a
25
- * per-session sidebar state alongside the slot-rendered tree.
21
+ * `cooling` / `off`) from the Antigravity account state, shows the
22
+ * reading age once per account, and groups windows under `Gemini` and
23
+ * `Other models` headers. The routing health score is not shown.
24
+ * - `Routing` shows the mounted session's most recent `activeRouting`
25
+ * entry, or `No request yet` before the session's first request, since
26
+ * Antigravity does not maintain a per-session sidebar state alongside the
27
+ * slot-rendered tree.
26
28
  *
27
29
  * Hard rules for this file:
28
30
  *
@@ -45,12 +47,17 @@ import type {
45
47
  } from '@opencode-ai/plugin/tui'
46
48
  import { createSlot } from '@opentui/solid'
47
49
  import { createSignal, For, type JSX, onCleanup, onMount, Show } from 'solid-js'
48
- import { formatQuotaReading, quotaReadingParts } from './quota-presentation'
50
+ import {
51
+ NO_QUOTA_READING,
52
+ type QuotaReading,
53
+ type QuotaWindowText,
54
+ quotaReadingAge,
55
+ quotaWindowText,
56
+ } from './quota-presentation'
49
57
  import { getRpcDir } from './rpc/rpc-dir'
50
58
  import {
51
59
  readSidebarState,
52
60
  type SidebarAccountState,
53
- type SidebarQuotaEntry,
54
61
  type SidebarQuotaKey,
55
62
  type SidebarStateV1,
56
63
  } from './sidebar-state'
@@ -121,17 +128,35 @@ let rpcInFlight = false
121
128
  const GLOBAL_NOTIFICATION_CURSOR = '__global__'
122
129
  const MAX_NOTIFICATION_CURSORS = 256
123
130
 
124
- const QUOTA_LABELS: Record<SidebarQuotaKey, string> = {
125
- gemini: 'Gm',
126
- 'non-gemini': 'NG',
131
+ // Group headers name each quota pool in plain words.
132
+ const GROUP_NAMES: Record<SidebarQuotaKey, string> = {
133
+ gemini: 'Gemini',
134
+ 'non-gemini': 'Other models',
135
+ }
136
+
137
+ // The collapsed row has room for one word before the window name.
138
+ const COLLAPSED_GROUP_NAMES: Record<SidebarQuotaKey, string> = {
139
+ gemini: 'Gemini',
140
+ 'non-gemini': 'Other',
127
141
  }
128
142
 
129
143
  const QUOTA_ORDER: readonly SidebarQuotaKey[] = ['gemini', 'non-gemini']
130
144
 
131
- const WINDOW_GUTTER: Record<string, string> = {
132
- weekly: '7d',
133
- '5h': '5h',
134
- }
145
+ // Shown instead of a stored figure once the window has reset since it was
146
+ // read: the stored figure describes the window before the reset.
147
+ const RESET_SINCE_READING = 'reset since last check'
148
+
149
+ // Content width (the panel's width inside its padding) used until the first
150
+ // layout pass reports the real one. It fits the narrowest OpenCode sidebar.
151
+ const ASSUMED_CONTENT_WIDTH = 28
152
+
153
+ // A window row is `<name> <bar> <figure> <reset>`. The bar shrinks to this
154
+ // before the reset column is dropped; below it the bar is dropped too.
155
+ const MIN_BAR_WIDTH = 4
156
+ // The widest figure the formatter writes: `100% left`.
157
+ const FIGURE_WIDTH = 9
158
+ // A window name (`5h`, `7d`) and the space after it.
159
+ const NAME_WIDTH = 3
135
160
 
136
161
  // --- Theme tokens ----------------------------------------------------------
137
162
  //
@@ -238,9 +263,9 @@ interface BarSegment {
238
263
 
239
264
  function quotaBarSegments(
240
265
  remainingPct: number,
266
+ width: number,
241
267
  appearance: AppearancePrefs,
242
268
  ): BarSegment[] {
243
- const width = appearance.barWidth
244
269
  const usedCells = Math.max(
245
270
  0,
246
271
  Math.min(Math.round((remainingPct / 100) * width), width),
@@ -397,12 +422,6 @@ function getSidebarController(): SidebarController {
397
422
 
398
423
  // --- Shared helpers (fleet shape, antigravity data) ------------------------
399
424
 
400
- function clamp(value: number, min: number, max: number): number {
401
- if (value < min) return min
402
- if (value > max) return max
403
- return value
404
- }
405
-
406
425
  // Render a "reset in Nm/Nh/NdNh" string from an epoch-ms reset time.
407
426
  // Empty string when no resetAt is cached. The fleet's `formatResetIn`
408
427
  // reads ISO strings; Antigravity persists resetAt as a numeric epoch, so
@@ -468,11 +487,9 @@ function StatRow(props: {
468
487
  )
469
488
  }
470
489
 
471
- // Fleet CollapsedRow: muted label left, caller-supplied value right. Used
472
- // for the collapsed sidebar view; the Antigravity adapter renders one
473
- // CollapsedRow per visible account (Claude's sibling collapses to a
474
- // single primary-quota line, but Antigravity's multi-quota model fits a
475
- // per-account row better when the sidebar is collapsed).
490
+ // Fleet CollapsedRow: muted label left, caller-supplied value right. The
491
+ // collapsed view renders one for the active account. The caller sizes both
492
+ // sides to the measured width, so neither side may wrap.
476
493
  function CollapsedRow(props: {
477
494
  theme: () => Theme
478
495
  label: string
@@ -480,77 +497,172 @@ function CollapsedRow(props: {
480
497
  }): JSX.Element {
481
498
  return (
482
499
  <box width='100%' flexDirection='row' justifyContent='space-between'>
483
- <text fg={props.theme().textMuted}>{props.label}</text>
500
+ <text flexShrink={0} wrapMode='none' fg={props.theme().textMuted}>
501
+ {props.label}
502
+ </text>
484
503
  {props.children}
485
504
  </box>
486
505
  )
487
506
  }
488
507
 
489
- // The bar fills with remaining quota. The shared formatter supplies the
490
- // figure, reset and reading age; the right column aligns reset and age text.
491
- function QuotaRow(props: {
508
+ // --- Width-fitted quota rows ------------------------------------------------
509
+ //
510
+ // The OpenCode 1 sidebar is about 32 to 48 columns wide. Every quota line is
511
+ // sized from the measured content width so that it fits on one line: text is
512
+ // never left to the renderer's wrapping, which breaks a row mid-word or down
513
+ // to one character per line once the right-hand column runs out of room.
514
+
515
+ interface AccountWindow {
516
+ key: SidebarQuotaKey
517
+ reading: QuotaReading
518
+ text: QuotaWindowText
519
+ }
520
+
521
+ // One entry per stored window, in group order. A pool stored before
522
+ // per-window readings existed contributes one window without a name.
523
+ function accountWindows(
524
+ account: SidebarAccountState,
525
+ now: number,
526
+ ): AccountWindow[] {
527
+ const result: AccountWindow[] = []
528
+ for (const key of QUOTA_ORDER) {
529
+ const entry = account.quota[key]
530
+ if (!entry) continue
531
+ const readings: QuotaReading[] =
532
+ entry.windows && entry.windows.length > 0
533
+ ? entry.windows.map((window) => ({
534
+ window: window.window,
535
+ remainingPercent: window.remainingPercent,
536
+ resetAt: window.resetAt,
537
+ checkedAt: window.checkedAt,
538
+ }))
539
+ : [
540
+ {
541
+ remainingPercent: entry.remainingPercent,
542
+ resetAt: entry.resetAt,
543
+ checkedAt: entry.checkedAt,
544
+ },
545
+ ]
546
+ for (const reading of readings)
547
+ result.push({ key, reading, text: quotaWindowText(reading, now) })
548
+ }
549
+ return result
550
+ }
551
+
552
+ // A window shows its figure only while no reset has happened since it was
553
+ // read and it has a read time at all.
554
+ function showsFigure(window: AccountWindow): boolean {
555
+ return window.text.remaining != null && !window.text.resetSinceReading
556
+ }
557
+
558
+ interface WindowRowLayout {
559
+ bar: number
560
+ reset: boolean
561
+ }
562
+
563
+ // Column widths for one `<name> <bar> <figure> <reset>` row within `width`
564
+ // cells. The bar shrinks first, down to MIN_BAR_WIDTH; then the reset column
565
+ // is dropped; then the bar.
566
+ function windowRowLayout(
567
+ width: number,
568
+ named: boolean,
569
+ resetWidth: number,
570
+ maxBar: number,
571
+ ): WindowRowLayout {
572
+ const room = width - (named ? NAME_WIDTH : 0) - FIGURE_WIDTH
573
+ if (resetWidth > 0) {
574
+ const bar = Math.min(maxBar, room - resetWidth - 2)
575
+ if (bar >= MIN_BAR_WIDTH) return { bar, reset: true }
576
+ }
577
+ const bar = Math.min(maxBar, room - 1)
578
+ return { bar: bar >= MIN_BAR_WIDTH ? bar : 0, reset: false }
579
+ }
580
+
581
+ // Shorten a label to `max` cells, marking the cut with an ellipsis.
582
+ function fitLabel(label: string, max: number): string {
583
+ if (label.length <= max) return label
584
+ if (max <= 0) return ''
585
+ if (max === 1) return '…'
586
+ return `${label.slice(0, max - 1)}…`
587
+ }
588
+
589
+ // One quota window on one line. The bar fills with remaining quota; the
590
+ // figure and reset come from the shared formatter. A window that reset since
591
+ // its reading shows that fact, dim, in place of the stale figure.
592
+ function WindowRow(props: {
492
593
  theme: () => Theme
493
594
  appearance: AppearancePrefs
494
- label: string
495
- entry: SidebarQuotaEntry | undefined
496
- now: () => number
595
+ window: AccountWindow
596
+ layout: WindowRowLayout
497
597
  }): JSX.Element {
498
- const parts = () => quotaReadingParts(props.label, props.entry, props.now())
499
- const remaining = () => parts().remaining
500
- const reset = () => parts().detail
598
+ const text = () => props.window.text
599
+ const tone = () => quotaTone(100 - (text().remaining ?? 0), props.appearance)
501
600
  return (
502
- <Show
503
- when={remaining() != null}
504
- fallback={
505
- <box width='100%' flexDirection='row' justifyContent='space-between'>
506
- <text fg={props.theme().textMuted}>{props.label.padEnd(5)}</text>
507
- <text fg={props.theme().textMuted}>{parts().figure}</text>
508
- </box>
509
- }
510
- >
511
- <box width='100%' flexDirection='row' justifyContent='space-between'>
512
- <box flexDirection='row'>
513
- <text width={5} flexShrink={0} fg={props.theme().textMuted}>
514
- {props.label}
601
+ <box width='100%' flexDirection='row'>
602
+ <Show when={text().name !== ''}>
603
+ <text
604
+ width={NAME_WIDTH}
605
+ flexShrink={0}
606
+ wrapMode='none'
607
+ fg={props.theme().textMuted}
608
+ >
609
+ {text().name}
610
+ </text>
611
+ </Show>
612
+ <Show
613
+ when={showsFigure(props.window)}
614
+ fallback={
615
+ <text flexShrink={1} wrapMode='none' fg={props.theme().textMuted}>
616
+ {text().resetSinceReading ? RESET_SINCE_READING : text().figure}
515
617
  </text>
516
- <box
517
- width={props.appearance.barWidth}
518
- flexShrink={0}
519
- flexDirection='row'
520
- >
521
- <For each={quotaBarSegments(remaining() ?? 0, props.appearance)}>
618
+ }
619
+ >
620
+ <Show when={props.layout.bar > 0}>
621
+ <box width={props.layout.bar + 1} flexShrink={0} flexDirection='row'>
622
+ <For
623
+ each={quotaBarSegments(
624
+ text().remaining ?? 0,
625
+ props.layout.bar,
626
+ props.appearance,
627
+ )}
628
+ >
522
629
  {(segment) => (
523
- <text fg={toneColor(props.theme(), segment.tone)}>
630
+ <text
631
+ wrapMode='none'
632
+ fg={toneColor(props.theme(), segment.tone)}
633
+ >
524
634
  {segment.text}
525
635
  </text>
526
636
  )}
527
637
  </For>
528
638
  </box>
529
- <text
530
- fg={toneColor(
531
- props.theme(),
532
- quotaTone(100 - (remaining() ?? 0), props.appearance),
533
- )}
534
- >
535
- {` ${parts().figure}`}
639
+ </Show>
640
+ <text
641
+ flexShrink={0}
642
+ wrapMode='none'
643
+ fg={toneColor(props.theme(), tone())}
644
+ >
645
+ {text().figure}
646
+ </text>
647
+ <box flexGrow={1} />
648
+ <Show when={props.layout.reset && text().reset !== ''}>
649
+ <text flexShrink={0} wrapMode='none' fg={props.theme().textMuted}>
650
+ {text().reset}
536
651
  </text>
537
- </box>
538
- <Show when={reset()}>
539
- <text fg={props.theme().textMuted}>{reset()}</text>
540
652
  </Show>
541
- </box>
542
- </Show>
653
+ </Show>
654
+ </box>
543
655
  )
544
656
  }
545
657
 
546
- // Fleet AccountBlock, adapted to Antigravity's per-account data. Header
547
- // row is account label (left) + status word (right). The body renders one
548
- // QuotaRow per present quota group in the fleet's stable order, then a
549
- // muted secondary line with the account's health score and, when
550
- // relevant, a cooldown countdown. Status word priorities (first match
551
- // wins):
658
+ // Fleet AccountBlock, adapted to Antigravity's per-account data. The header
659
+ // line is the account label, the reading age when the oldest reading is
660
+ // stale, and the status word right-aligned; the age moves to its own dim
661
+ // line when the three do not fit. Below it each quota group present gets a
662
+ // plain-word header and one WindowRow per window. Status word priorities
663
+ // (first match wins):
552
664
  // - `off` when the account is disabled
553
- // - `cooling` when cooldownUntil is still in the future
665
+ // - `cooling <wait>` when cooldownUntil is still in the future
554
666
  // - `active` when current === true
555
667
  // - `idle` for the fallback (current === false) case
556
668
  function AccountBlock(props: {
@@ -558,86 +670,157 @@ function AccountBlock(props: {
558
670
  appearance: AppearancePrefs
559
671
  account: SidebarAccountState
560
672
  now: () => number
673
+ width: () => number
561
674
  active?: boolean
562
675
  marginTop?: number
563
676
  }): JSX.Element {
564
677
  const active = () => props.active ?? props.account.current
678
+ const cooldownMs = (): number => {
679
+ const cd = props.account.cooldownUntil
680
+ if (typeof cd !== 'number') return 0
681
+ return Math.max(0, cd - props.now())
682
+ }
565
683
  const statusWord = (): string => {
566
684
  if (!props.account.enabled) return 'off'
567
- const cd = props.account.cooldownUntil
568
- if (typeof cd === 'number' && cd > props.now()) return 'cooling'
685
+ if (cooldownMs() > 0) return `cooling ${formatWait(cooldownMs())}`
569
686
  return active() ? 'active' : 'idle'
570
687
  }
571
688
  const statusTone = (): Tone => {
572
689
  if (!props.account.enabled) return 'muted'
573
- const cd = props.account.cooldownUntil
574
- if (typeof cd === 'number' && cd > props.now()) return 'warn'
690
+ if (cooldownMs() > 0) return 'warn'
575
691
  return active() ? 'ok' : 'muted'
576
692
  }
577
- const cooldownMs = (): number => {
578
- const cd = props.account.cooldownUntil
579
- if (typeof cd !== 'number') return 0
580
- return Math.max(0, cd - props.now())
581
- }
582
- const healthText = () => {
583
- const base = `health ${Math.round(clamp(props.account.health, 0, 100))}`
584
- return cooldownMs() > 0
585
- ? `${base} · cooling ${formatWait(cooldownMs())}`
586
- : base
587
- }
693
+ const windows = () => accountWindows(props.account, props.now())
694
+ const age = () =>
695
+ quotaReadingAge(
696
+ windows().map((window) => window.reading),
697
+ props.now(),
698
+ )
699
+ const ageInline = () =>
700
+ age() !== '' &&
701
+ props.account.label.length + 3 + age().length + 1 + statusWord().length <=
702
+ props.width()
703
+ const label = () =>
704
+ fitLabel(props.account.label, props.width() - 1 - statusWord().length)
705
+ // One reset column width per account, so bars line up across its rows.
706
+ const resetWidth = () =>
707
+ Math.max(
708
+ 0,
709
+ ...windows()
710
+ .filter(showsFigure)
711
+ .map((window) => window.text.reset.length),
712
+ )
713
+ const layoutFor = (window: AccountWindow) =>
714
+ windowRowLayout(
715
+ props.width(),
716
+ window.text.name !== '',
717
+ resetWidth(),
718
+ props.appearance.barWidth,
719
+ )
720
+ const groups = () =>
721
+ QUOTA_ORDER.map((key) => ({
722
+ key,
723
+ windows: windows().filter((window) => window.key === key),
724
+ })).filter((group) => group.windows.length > 0)
588
725
  return (
589
726
  <box width='100%' flexDirection='column' marginTop={props.marginTop ?? 0}>
590
- <box width='100%' flexDirection='row' justifyContent='space-between'>
591
- <text fg={props.theme().text}>
592
- <b>{props.account.label}</b>
727
+ <box width='100%' flexDirection='row'>
728
+ <text flexShrink={0} wrapMode='none' fg={props.theme().text}>
729
+ <b>{label()}</b>
593
730
  </text>
594
- <text fg={toneColor(props.theme(), statusTone())}>
731
+ <Show when={ageInline()}>
732
+ <text flexShrink={0} wrapMode='none' fg={props.theme().textMuted}>
733
+ {` · ${age()}`}
734
+ </text>
735
+ </Show>
736
+ <box flexGrow={1} />
737
+ <text
738
+ flexShrink={0}
739
+ wrapMode='none'
740
+ fg={toneColor(props.theme(), statusTone())}
741
+ >
595
742
  <b>{statusWord()}</b>
596
743
  </text>
597
744
  </box>
598
- <For each={QUOTA_ORDER}>
599
- {(key) => {
600
- const poolEntry = props.account.quota[key]
601
- const windows = poolEntry?.windows
602
- // Legacy: no windows array — render a single row with the pool label.
603
- const hasWindows = windows && windows.length > 0
604
- if (!hasWindows) {
605
- return (
606
- <QuotaRow
607
- theme={props.theme}
608
- appearance={props.appearance}
609
- label={QUOTA_LABELS[key]}
610
- entry={poolEntry}
611
- now={props.now}
612
- />
613
- )
614
- }
615
- return (
745
+ <Show when={age() !== '' && !ageInline()}>
746
+ <text wrapMode='none' fg={props.theme().textMuted}>
747
+ {age()}
748
+ </text>
749
+ </Show>
750
+ <Show
751
+ when={groups().length > 0}
752
+ fallback={
753
+ <text wrapMode='none' fg={props.theme().textMuted}>
754
+ {NO_QUOTA_READING}
755
+ </text>
756
+ }
757
+ >
758
+ <For each={groups()}>
759
+ {(group) => (
616
760
  <>
617
- {windows.map((w) => (
618
- <QuotaRow
619
- theme={props.theme}
620
- appearance={props.appearance}
621
- label={`${QUOTA_LABELS[key]} ${WINDOW_GUTTER[w.window] ?? w.window}`}
622
- entry={{
623
- remainingPercent: w.remainingPercent,
624
- resetAt: w.resetAt,
625
- checkedAt: w.checkedAt,
626
- }}
627
- now={props.now}
628
- />
629
- ))}
761
+ <text wrapMode='none' fg={props.theme().textMuted}>
762
+ {GROUP_NAMES[group.key]}
763
+ </text>
764
+ <For each={group.windows}>
765
+ {(window) => (
766
+ <WindowRow
767
+ theme={props.theme}
768
+ appearance={props.appearance}
769
+ window={window}
770
+ layout={layoutFor(window)}
771
+ />
772
+ )}
773
+ </For>
630
774
  </>
631
- )
632
- }}
633
- </For>
634
- <box width='100%' flexDirection='row'>
635
- <text fg={props.theme().textMuted}>{` ${healthText()}`}</text>
636
- </box>
775
+ )}
776
+ </For>
777
+ </Show>
637
778
  </box>
638
779
  )
639
780
  }
640
781
 
782
+ interface CollapsedQuota {
783
+ // The group and window of the figure (`Other 7d `), dropped when narrow.
784
+ prefix: string
785
+ text: string
786
+ remaining: number | null
787
+ }
788
+
789
+ // The collapsed row's figure: the lowest current window across both groups.
790
+ // A window that reset since its reading is not current and never binds.
791
+ function collapsedQuota(
792
+ account: SidebarAccountState,
793
+ now: number,
794
+ ): CollapsedQuota {
795
+ if (account.quota.gemini == null && account.quota['non-gemini'] == null)
796
+ return { prefix: '', text: '—', remaining: null }
797
+ const windows = accountWindows(account, now)
798
+ let lowest: AccountWindow | undefined
799
+ for (const window of windows) {
800
+ if (!showsFigure(window)) continue
801
+ if (
802
+ lowest === undefined ||
803
+ (window.text.remaining ?? 100) < (lowest.text.remaining ?? 100)
804
+ )
805
+ lowest = window
806
+ }
807
+ if (lowest) {
808
+ const name = lowest.text.name
809
+ return {
810
+ prefix: `${COLLAPSED_GROUP_NAMES[lowest.key]}${name ? ` ${name}` : ''} `,
811
+ text: lowest.text.figure,
812
+ remaining: lowest.text.remaining,
813
+ }
814
+ }
815
+ return {
816
+ prefix: '',
817
+ text: windows.some((window) => window.text.resetSinceReading)
818
+ ? RESET_SINCE_READING
819
+ : NO_QUOTA_READING,
820
+ remaining: null,
821
+ }
822
+ }
823
+
641
824
  function formatWait(ms: number): string {
642
825
  const seconds = Math.ceil(ms / 1000)
643
826
  if (seconds < 60) return `${seconds}s`
@@ -723,6 +906,36 @@ export function SidebarPanel(props: SidebarPanelProps): JSX.Element {
723
906
  }
724
907
  }
725
908
 
909
+ // The route on one `Route <value>` line when it fits, else one line per
910
+ // part with the strategy last.
911
+ const routeValue = (route: {
912
+ strategy?: string
913
+ family: string
914
+ style: string
915
+ }): string =>
916
+ `${route.strategy ? `${route.strategy} · ` : ''}${route.family}: ${route.style}`
917
+ const routeLines = (route: {
918
+ strategy?: string
919
+ family: string
920
+ style: string
921
+ }): string[] =>
922
+ [`${route.family}: ${route.style}`, route.strategy ?? ''].filter(
923
+ (line) => line !== '',
924
+ )
925
+
926
+ // The width available to content: the header row spans the panel inside
927
+ // its border and padding. Layout reports it during a render pass, so the
928
+ // signal is updated afterwards rather than rebuilding rows mid-pass.
929
+ const [contentWidth, setContentWidth] = createSignal(ASSUMED_CONTENT_WIDTH)
930
+ let headerRow: { width: number } | undefined
931
+ const measureContentWidth = (): void => {
932
+ const width = headerRow?.width
933
+ if (typeof width !== 'number' || width <= 0) return
934
+ queueMicrotask(() => {
935
+ if (width !== contentWidth()) setContentWidth(width)
936
+ })
937
+ }
938
+
726
939
  // Header badge: ▼/▶ {label}. The fleet shows the version string on the
727
940
  // right when no degraded state is present; the antigravity adapter keeps
728
941
  // that right-alignment and surfaces `degraded` as a "LIMITED" badge
@@ -745,6 +958,10 @@ export function SidebarPanel(props: SidebarPanelProps): JSX.Element {
745
958
  >
746
959
  {/* biome-ignore lint/a11y/noStaticElementInteractions: opentui renders to a terminal, not the DOM — ARIA roles do not apply */}
747
960
  <box
961
+ ref={(element) => {
962
+ headerRow = element
963
+ }}
964
+ onSizeChange={measureContentWidth}
748
965
  width='100%'
749
966
  flexDirection='row'
750
967
  justifyContent='space-between'
@@ -781,48 +998,12 @@ export function SidebarPanel(props: SidebarPanelProps): JSX.Element {
781
998
  </Show>
782
999
  </box>
783
1000
 
784
- {/* Collapsed: the active account's primary quota and fleet status dot. */}
1001
+ {/* Collapsed: the active account's lowest current figure and fleet status dot. */}
785
1002
  <Show when={collapsed() && hasData()}>
786
1003
  {(() => {
787
1004
  const account = () =>
788
1005
  visibleAccounts().find((entry) => entry.current) ??
789
1006
  visibleAccounts()[0]
790
- const remaining = () => {
791
- const q = account()?.quota
792
- // The lowest remaining percentage among dated readings determines the warning tone.
793
- const gemini = quotaReadingParts('Gm', q?.gemini, now()).remaining
794
- const nonGemini = quotaReadingParts(
795
- 'NG',
796
- q?.['non-gemini'],
797
- now(),
798
- ).remaining
799
- if (gemini === null && nonGemini === null) return null
800
- return Math.min(gemini ?? 100, nonGemini ?? 100)
801
- }
802
- const poolText = (key: 'gemini' | 'non-gemini') => {
803
- const entry = account()?.quota[key]
804
- if (!entry)
805
- return `${QUOTA_LABELS[key]}: ${formatQuotaReading(QUOTA_LABELS[key], undefined, now())}`
806
- // Show binding window when available, else just pool label.
807
- const bindingWindow = entry.windows?.reduce<
808
- (typeof entry.windows)[0] | null
809
- >(
810
- (best, w) =>
811
- best === null || w.remainingPercent < best.remainingPercent
812
- ? w
813
- : best,
814
- null,
815
- )
816
- const gutter = bindingWindow
817
- ? ` ${WINDOW_GUTTER[bindingWindow.window] ?? bindingWindow.window}`
818
- : ''
819
- const text = formatQuotaReading(
820
- `${QUOTA_LABELS[key]}${gutter}`,
821
- bindingWindow ?? entry,
822
- now(),
823
- )
824
- return bindingWindow ? `${QUOTA_LABELS[key]} ${text}` : text
825
- }
826
1007
  const unavailable = () => {
827
1008
  const selected = account()
828
1009
  return (
@@ -834,45 +1015,50 @@ export function SidebarPanel(props: SidebarPanelProps): JSX.Element {
834
1015
  }
835
1016
  return (
836
1017
  <Show when={account()}>
837
- {(selected) => (
838
- <CollapsedRow theme={theme} label={selected().label}>
839
- <box flexDirection='row'>
840
- <text
841
- fg={toneColor(
842
- theme(),
843
- remaining() == null
844
- ? 'muted'
845
- : quotaTone(
846
- 100 - (remaining() ?? 100),
847
- prefs().appearance,
848
- ),
849
- )}
850
- >
851
- <b>
852
- {account()?.quota.gemini == null &&
853
- account()?.quota['non-gemini'] == null
854
- ? '—'
855
- : `${poolText('gemini')} · ${poolText('non-gemini')}`}
856
- </b>
857
- </text>
858
- <text
859
- fg={toneColor(
860
- theme(),
861
- unavailable()
862
- ? 'err'
863
- : remaining() == null
864
- ? 'muted'
865
- : quotaTone(
866
- 100 - (remaining() ?? 100),
867
- prefs().appearance,
868
- ),
869
- )}
870
- >
871
- {unavailable() ? ' ⊘' : ' ●'}
872
- </text>
873
- </box>
874
- </CollapsedRow>
875
- )}
1018
+ {(selected) => {
1019
+ const quota = () => collapsedQuota(selected(), now())
1020
+ const tone = (): Tone => {
1021
+ const remaining = quota().remaining
1022
+ return remaining == null
1023
+ ? 'muted'
1024
+ : quotaTone(100 - remaining, prefs().appearance)
1025
+ }
1026
+ // Label, one space, the figure and the two-cell dot. The
1027
+ // group prefix goes first when narrow; then the label is cut.
1028
+ const figure = () => {
1029
+ const { prefix, text } = quota()
1030
+ const full = `${prefix}${text}`
1031
+ return selected().label.length + 1 + full.length + 2 <=
1032
+ contentWidth()
1033
+ ? full
1034
+ : text
1035
+ }
1036
+ const label = () =>
1037
+ fitLabel(
1038
+ selected().label,
1039
+ contentWidth() - 1 - figure().length - 2,
1040
+ )
1041
+ return (
1042
+ <CollapsedRow theme={theme} label={label()}>
1043
+ <box flexDirection='row' flexShrink={0}>
1044
+ <text
1045
+ flexShrink={0}
1046
+ wrapMode='none'
1047
+ fg={toneColor(theme(), tone())}
1048
+ >
1049
+ <b>{figure()}</b>
1050
+ </text>
1051
+ <text
1052
+ flexShrink={0}
1053
+ wrapMode='none'
1054
+ fg={toneColor(theme(), unavailable() ? 'err' : tone())}
1055
+ >
1056
+ {unavailable() ? ' ⊘' : ' ●'}
1057
+ </text>
1058
+ </box>
1059
+ </CollapsedRow>
1060
+ )
1061
+ }}
876
1062
  </Show>
877
1063
  )
878
1064
  })()}
@@ -899,28 +1085,47 @@ export function SidebarPanel(props: SidebarPanelProps): JSX.Element {
899
1085
  appearance={prefs().appearance}
900
1086
  account={account}
901
1087
  now={now}
1088
+ width={contentWidth}
902
1089
  marginTop={index() === 0 ? 0 : 1}
903
1090
  />
904
1091
  )}
905
1092
  </For>
906
1093
  </Show>
907
1094
 
908
- {/* Routing */}
1095
+ {/* Routing: the session's route, or one dim line before its first request. */}
909
1096
  <Show when={prefs().sections.routing}>
910
1097
  <SectionHeader theme={theme} title='Routing' />
911
1098
  <Show
912
1099
  when={currentRoute()}
913
1100
  fallback={
914
- <StatRow theme={theme} label='Route' value='—' tone='muted' />
1101
+ <text wrapMode='none' fg={theme().textMuted}>
1102
+ {'No request yet'}
1103
+ </text>
915
1104
  }
916
1105
  >
917
1106
  {(route) => (
918
- <StatRow
919
- theme={theme}
920
- label='Route'
921
- value={`${route().strategy ? `${route().strategy} · ` : ''}${route().family}: ${route().style}`}
922
- tone='accent'
923
- />
1107
+ <Show
1108
+ when={
1109
+ 'Route'.length + 1 + routeValue(route()).length <=
1110
+ contentWidth()
1111
+ }
1112
+ fallback={
1113
+ <For each={routeLines(route())}>
1114
+ {(line) => (
1115
+ <text wrapMode='none' fg={toneColor(theme(), 'accent')}>
1116
+ <b>{line}</b>
1117
+ </text>
1118
+ )}
1119
+ </For>
1120
+ }
1121
+ >
1122
+ <StatRow
1123
+ theme={theme}
1124
+ label='Route'
1125
+ value={routeValue(route())}
1126
+ tone='accent'
1127
+ />
1128
+ </Show>
924
1129
  )}
925
1130
  </Show>
926
1131
  </Show>