@cortexkit/opencode-antigravity-auth 1.1.0 → 2.0.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 (379) hide show
  1. package/README.md +225 -569
  2. package/dist/cli.js +4017 -0
  3. package/dist/cli.js.map +7 -0
  4. package/dist/index.d.ts +1 -3
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +20742 -14586
  7. package/dist/index.js.map +4 -4
  8. package/dist/src/antigravity/oauth.d.ts +1 -1
  9. package/dist/src/antigravity/oauth.js +1 -1
  10. package/dist/src/cli.d.ts +21 -0
  11. package/dist/src/cli.d.ts.map +1 -0
  12. package/dist/src/cli.js +237 -0
  13. package/dist/src/cli.js.map +1 -0
  14. package/dist/src/constants.d.ts +1 -1
  15. package/dist/src/constants.js +1 -1
  16. package/dist/src/hooks/auto-update-checker/cache.d.ts.map +1 -1
  17. package/dist/src/hooks/auto-update-checker/cache.js +13 -13
  18. package/dist/src/hooks/auto-update-checker/cache.js.map +1 -1
  19. package/dist/src/hooks/auto-update-checker/checker.d.ts +1 -1
  20. package/dist/src/hooks/auto-update-checker/checker.d.ts.map +1 -1
  21. package/dist/src/hooks/auto-update-checker/checker.js +75 -51
  22. package/dist/src/hooks/auto-update-checker/checker.js.map +1 -1
  23. package/dist/src/hooks/auto-update-checker/constants.d.ts.map +1 -1
  24. package/dist/src/hooks/auto-update-checker/constants.js +12 -12
  25. package/dist/src/hooks/auto-update-checker/constants.js.map +1 -1
  26. package/dist/src/hooks/auto-update-checker/index.d.ts +6 -6
  27. package/dist/src/hooks/auto-update-checker/index.d.ts.map +1 -1
  28. package/dist/src/hooks/auto-update-checker/index.js +17 -17
  29. package/dist/src/hooks/auto-update-checker/index.js.map +1 -1
  30. package/dist/src/hooks/auto-update-checker/logging.js +2 -2
  31. package/dist/src/hooks/auto-update-checker/logging.js.map +1 -1
  32. package/dist/src/hooks/auto-update-checker/types.d.ts.map +1 -1
  33. package/dist/src/plugin/account-access.d.ts +99 -0
  34. package/dist/src/plugin/account-access.d.ts.map +1 -0
  35. package/dist/src/plugin/account-access.js +480 -0
  36. package/dist/src/plugin/account-access.js.map +1 -0
  37. package/dist/src/plugin/account-command-oauth.d.ts +34 -0
  38. package/dist/src/plugin/account-command-oauth.d.ts.map +1 -0
  39. package/dist/src/plugin/account-command-oauth.js +156 -0
  40. package/dist/src/plugin/account-command-oauth.js.map +1 -0
  41. package/dist/src/plugin/accounts.d.ts +7 -256
  42. package/dist/src/plugin/accounts.d.ts.map +1 -1
  43. package/dist/src/plugin/accounts.js +31 -1367
  44. package/dist/src/plugin/accounts.js.map +1 -1
  45. package/dist/src/plugin/agy-request-metadata.d.ts +1 -1
  46. package/dist/src/plugin/agy-request-metadata.js +1 -1
  47. package/dist/src/plugin/agy-transport.d.ts +1 -1
  48. package/dist/src/plugin/agy-transport.js +1 -1
  49. package/dist/src/plugin/auth-doctor.d.ts +6 -6
  50. package/dist/src/plugin/auth-doctor.d.ts.map +1 -1
  51. package/dist/src/plugin/auth-doctor.js +74 -66
  52. package/dist/src/plugin/auth-doctor.js.map +1 -1
  53. package/dist/src/plugin/auth-drift.d.ts +4 -4
  54. package/dist/src/plugin/auth-drift.d.ts.map +1 -1
  55. package/dist/src/plugin/auth-drift.js +15 -15
  56. package/dist/src/plugin/auth-drift.js.map +1 -1
  57. package/dist/src/plugin/auth-loader.d.ts +50 -0
  58. package/dist/src/plugin/auth-loader.d.ts.map +1 -0
  59. package/dist/src/plugin/auth-loader.js +225 -0
  60. package/dist/src/plugin/auth-loader.js.map +1 -0
  61. package/dist/src/plugin/auth.d.ts +1 -1
  62. package/dist/src/plugin/auth.js +1 -1
  63. package/dist/src/plugin/background-quota-refresh.d.ts +146 -0
  64. package/dist/src/plugin/background-quota-refresh.d.ts.map +1 -0
  65. package/dist/src/plugin/background-quota-refresh.js +347 -0
  66. package/dist/src/plugin/background-quota-refresh.js.map +1 -0
  67. package/dist/src/plugin/cache/index.d.ts +1 -1
  68. package/dist/src/plugin/cache/index.d.ts.map +1 -1
  69. package/dist/src/plugin/cache/index.js +1 -1
  70. package/dist/src/plugin/cache/index.js.map +1 -1
  71. package/dist/src/plugin/cache/signature-cache.d.ts +1 -1
  72. package/dist/src/plugin/cache/signature-cache.d.ts.map +1 -1
  73. package/dist/src/plugin/cache/signature-cache.js +23 -24
  74. package/dist/src/plugin/cache/signature-cache.js.map +1 -1
  75. package/dist/src/plugin/cache.d.ts +6 -5
  76. package/dist/src/plugin/cache.d.ts.map +1 -1
  77. package/dist/src/plugin/cache.js +21 -7
  78. package/dist/src/plugin/cache.js.map +1 -1
  79. package/dist/src/plugin/catalog.d.ts +23 -0
  80. package/dist/src/plugin/catalog.d.ts.map +1 -0
  81. package/dist/src/plugin/catalog.js +60 -0
  82. package/dist/src/plugin/catalog.js.map +1 -0
  83. package/dist/src/plugin/cli.d.ts +7 -7
  84. package/dist/src/plugin/cli.d.ts.map +1 -1
  85. package/dist/src/plugin/cli.js +62 -55
  86. package/dist/src/plugin/cli.js.map +1 -1
  87. package/dist/src/plugin/command-data.d.ts +329 -0
  88. package/dist/src/plugin/command-data.d.ts.map +1 -0
  89. package/dist/src/plugin/command-data.js +517 -0
  90. package/dist/src/plugin/command-data.js.map +1 -0
  91. package/dist/src/plugin/commands.d.ts +154 -0
  92. package/dist/src/plugin/commands.d.ts.map +1 -0
  93. package/dist/src/plugin/commands.js +746 -0
  94. package/dist/src/plugin/commands.js.map +1 -0
  95. package/dist/src/plugin/config/index.d.ts +2 -2
  96. package/dist/src/plugin/config/index.d.ts.map +1 -1
  97. package/dist/src/plugin/config/index.js +2 -2
  98. package/dist/src/plugin/config/index.js.map +1 -1
  99. package/dist/src/plugin/config/loader.d.ts +1 -1
  100. package/dist/src/plugin/config/loader.d.ts.map +1 -1
  101. package/dist/src/plugin/config/loader.js +18 -16
  102. package/dist/src/plugin/config/loader.js.map +1 -1
  103. package/dist/src/plugin/config/models.d.ts +2 -2
  104. package/dist/src/plugin/config/models.d.ts.map +1 -1
  105. package/dist/src/plugin/config/models.js +1 -1
  106. package/dist/src/plugin/config/models.js.map +1 -1
  107. package/dist/src/plugin/config/operator-settings-schema.d.ts +29 -0
  108. package/dist/src/plugin/config/operator-settings-schema.d.ts.map +1 -0
  109. package/dist/src/plugin/config/operator-settings-schema.js +28 -0
  110. package/dist/src/plugin/config/operator-settings-schema.js.map +1 -0
  111. package/dist/src/plugin/config/schema.d.ts +23 -2
  112. package/dist/src/plugin/config/schema.d.ts.map +1 -1
  113. package/dist/src/plugin/config/schema.js +109 -25
  114. package/dist/src/plugin/config/schema.js.map +1 -1
  115. package/dist/src/plugin/config/updater.d.ts.map +1 -1
  116. package/dist/src/plugin/config/updater.js +15 -15
  117. package/dist/src/plugin/config/updater.js.map +1 -1
  118. package/dist/src/plugin/config/writer.d.ts +34 -0
  119. package/dist/src/plugin/config/writer.d.ts.map +1 -0
  120. package/dist/src/plugin/config/writer.js +81 -0
  121. package/dist/src/plugin/config/writer.js.map +1 -0
  122. package/dist/src/plugin/core/streaming/index.d.ts +1 -1
  123. package/dist/src/plugin/core/streaming/index.d.ts.map +1 -1
  124. package/dist/src/plugin/core/streaming/index.js +1 -1
  125. package/dist/src/plugin/core/streaming/index.js.map +1 -1
  126. package/dist/src/plugin/core/streaming/transformer.d.ts.map +1 -1
  127. package/dist/src/plugin/core/streaming/transformer.js +85 -44
  128. package/dist/src/plugin/core/streaming/transformer.js.map +1 -1
  129. package/dist/src/plugin/core/streaming/types.d.ts.map +1 -1
  130. package/dist/src/plugin/debug.d.ts +10 -3
  131. package/dist/src/plugin/debug.d.ts.map +1 -1
  132. package/dist/src/plugin/debug.js +110 -47
  133. package/dist/src/plugin/debug.js.map +1 -1
  134. package/dist/src/plugin/dependencies.d.ts +129 -0
  135. package/dist/src/plugin/dependencies.d.ts.map +1 -0
  136. package/dist/src/plugin/dependencies.js +107 -0
  137. package/dist/src/plugin/dependencies.js.map +1 -0
  138. package/dist/src/plugin/errors.d.ts +27 -0
  139. package/dist/src/plugin/errors.d.ts.map +1 -1
  140. package/dist/src/plugin/errors.js +29 -3
  141. package/dist/src/plugin/errors.js.map +1 -1
  142. package/dist/src/plugin/event-handler.d.ts +53 -0
  143. package/dist/src/plugin/event-handler.d.ts.map +1 -0
  144. package/dist/src/plugin/event-handler.js +101 -0
  145. package/dist/src/plugin/event-handler.js.map +1 -0
  146. package/dist/src/plugin/fetch/retry-state.d.ts +61 -0
  147. package/dist/src/plugin/fetch/retry-state.d.ts.map +1 -0
  148. package/dist/src/plugin/fetch/retry-state.js +248 -0
  149. package/dist/src/plugin/fetch/retry-state.js.map +1 -0
  150. package/dist/src/plugin/fetch/warmup.d.ts +30 -0
  151. package/dist/src/plugin/fetch/warmup.d.ts.map +1 -0
  152. package/dist/src/plugin/fetch/warmup.js +76 -0
  153. package/dist/src/plugin/fetch/warmup.js.map +1 -0
  154. package/dist/src/plugin/fetch-interceptor.d.ts +59 -0
  155. package/dist/src/plugin/fetch-interceptor.d.ts.map +1 -0
  156. package/dist/src/plugin/fetch-interceptor.js +1399 -0
  157. package/dist/src/plugin/fetch-interceptor.js.map +1 -0
  158. package/dist/src/plugin/fetch-routing.d.ts +26 -0
  159. package/dist/src/plugin/fetch-routing.d.ts.map +1 -0
  160. package/dist/src/plugin/fetch-routing.js +99 -0
  161. package/dist/src/plugin/fetch-routing.js.map +1 -0
  162. package/dist/src/plugin/fingerprint.d.ts +1 -1
  163. package/dist/src/plugin/fingerprint.js +1 -1
  164. package/dist/src/plugin/gemini-dump.d.ts +5 -5
  165. package/dist/src/plugin/gemini-dump.d.ts.map +1 -1
  166. package/dist/src/plugin/gemini-dump.js +109 -55
  167. package/dist/src/plugin/gemini-dump.js.map +1 -1
  168. package/dist/src/plugin/google-search-tool.d.ts +9 -0
  169. package/dist/src/plugin/google-search-tool.d.ts.map +1 -0
  170. package/dist/src/plugin/google-search-tool.js +56 -0
  171. package/dist/src/plugin/google-search-tool.js.map +1 -0
  172. package/dist/src/plugin/image-saver.d.ts.map +1 -1
  173. package/dist/src/plugin/image-saver.js +16 -16
  174. package/dist/src/plugin/image-saver.js.map +1 -1
  175. package/dist/src/plugin/index.d.ts +30 -0
  176. package/dist/src/plugin/index.d.ts.map +1 -0
  177. package/dist/src/plugin/index.js +411 -0
  178. package/dist/src/plugin/index.js.map +1 -0
  179. package/dist/src/plugin/killswitch.d.ts +94 -0
  180. package/dist/src/plugin/killswitch.d.ts.map +1 -0
  181. package/dist/src/plugin/killswitch.js +197 -0
  182. package/dist/src/plugin/killswitch.js.map +1 -0
  183. package/dist/src/plugin/lifecycle.d.ts +40 -0
  184. package/dist/src/plugin/lifecycle.d.ts.map +1 -0
  185. package/dist/src/plugin/lifecycle.js +73 -0
  186. package/dist/src/plugin/lifecycle.js.map +1 -0
  187. package/dist/src/plugin/logger.d.ts +10 -1
  188. package/dist/src/plugin/logger.d.ts.map +1 -1
  189. package/dist/src/plugin/logger.js +38 -12
  190. package/dist/src/plugin/logger.js.map +1 -1
  191. package/dist/src/plugin/logging-utils.d.ts +35 -1
  192. package/dist/src/plugin/logging-utils.d.ts.map +1 -1
  193. package/dist/src/plugin/logging-utils.js +98 -15
  194. package/dist/src/plugin/logging-utils.js.map +1 -1
  195. package/dist/src/plugin/model-registry.d.ts +1 -1
  196. package/dist/src/plugin/model-registry.js +1 -1
  197. package/dist/src/plugin/oauth-login.d.ts +22 -0
  198. package/dist/src/plugin/oauth-login.d.ts.map +1 -0
  199. package/dist/src/plugin/oauth-login.js +37 -0
  200. package/dist/src/plugin/oauth-login.js.map +1 -0
  201. package/dist/src/plugin/oauth-methods.d.ts +42 -0
  202. package/dist/src/plugin/oauth-methods.d.ts.map +1 -0
  203. package/dist/src/plugin/oauth-methods.js +1276 -0
  204. package/dist/src/plugin/oauth-methods.js.map +1 -0
  205. package/dist/src/plugin/operator-settings.d.ts +40 -0
  206. package/dist/src/plugin/operator-settings.d.ts.map +1 -0
  207. package/dist/src/plugin/operator-settings.js +148 -0
  208. package/dist/src/plugin/operator-settings.js.map +1 -0
  209. package/dist/src/plugin/persist-account-pool.d.ts +37 -0
  210. package/dist/src/plugin/persist-account-pool.d.ts.map +1 -0
  211. package/dist/src/plugin/persist-account-pool.js +142 -0
  212. package/dist/src/plugin/persist-account-pool.js.map +1 -0
  213. package/dist/src/plugin/project.d.ts +1 -1
  214. package/dist/src/plugin/project.js +1 -1
  215. package/dist/src/plugin/prompt-context.d.ts.map +1 -1
  216. package/dist/src/plugin/prompt-context.js +11 -11
  217. package/dist/src/plugin/prompt-context.js.map +1 -1
  218. package/dist/src/plugin/quota.d.ts +138 -41
  219. package/dist/src/plugin/quota.d.ts.map +1 -1
  220. package/dist/src/plugin/quota.js +417 -285
  221. package/dist/src/plugin/quota.js.map +1 -1
  222. package/dist/src/plugin/recovery/constants.d.ts.map +1 -1
  223. package/dist/src/plugin/recovery/constants.js +24 -15
  224. package/dist/src/plugin/recovery/constants.js.map +1 -1
  225. package/dist/src/plugin/recovery/index.d.ts +3 -3
  226. package/dist/src/plugin/recovery/index.d.ts.map +1 -1
  227. package/dist/src/plugin/recovery/index.js +3 -3
  228. package/dist/src/plugin/recovery/index.js.map +1 -1
  229. package/dist/src/plugin/recovery/storage.d.ts +1 -1
  230. package/dist/src/plugin/recovery/storage.d.ts.map +1 -1
  231. package/dist/src/plugin/recovery/storage.js +31 -39
  232. package/dist/src/plugin/recovery/storage.js.map +1 -1
  233. package/dist/src/plugin/recovery/types.d.ts +12 -12
  234. package/dist/src/plugin/recovery/types.d.ts.map +1 -1
  235. package/dist/src/plugin/recovery.d.ts +3 -3
  236. package/dist/src/plugin/recovery.d.ts.map +1 -1
  237. package/dist/src/plugin/recovery.js +62 -58
  238. package/dist/src/plugin/recovery.js.map +1 -1
  239. package/dist/src/plugin/refresh-queue.d.ts +5 -2
  240. package/dist/src/plugin/refresh-queue.d.ts.map +1 -1
  241. package/dist/src/plugin/refresh-queue.js +37 -15
  242. package/dist/src/plugin/refresh-queue.js.map +1 -1
  243. package/dist/src/plugin/request-helpers.d.ts +1 -1
  244. package/dist/src/plugin/request-helpers.d.ts.map +1 -1
  245. package/dist/src/plugin/request-helpers.js +451 -352
  246. package/dist/src/plugin/request-helpers.js.map +1 -1
  247. package/dist/src/plugin/request.d.ts +7 -7
  248. package/dist/src/plugin/request.d.ts.map +1 -1
  249. package/dist/src/plugin/request.js +622 -384
  250. package/dist/src/plugin/request.js.map +1 -1
  251. package/dist/src/plugin/rotation.d.ts +1 -168
  252. package/dist/src/plugin/rotation.d.ts.map +1 -1
  253. package/dist/src/plugin/rotation.js +1 -327
  254. package/dist/src/plugin/rotation.js.map +1 -1
  255. package/dist/src/plugin/search.d.ts.map +1 -1
  256. package/dist/src/plugin/search.js +39 -35
  257. package/dist/src/plugin/search.js.map +1 -1
  258. package/dist/src/plugin/server.d.ts +1 -1
  259. package/dist/src/plugin/server.d.ts.map +1 -1
  260. package/dist/src/plugin/server.js +42 -37
  261. package/dist/src/plugin/server.js.map +1 -1
  262. package/dist/src/plugin/session-context.d.ts +2 -1
  263. package/dist/src/plugin/session-context.d.ts.map +1 -1
  264. package/dist/src/plugin/session-context.js +11 -7
  265. package/dist/src/plugin/session-context.js.map +1 -1
  266. package/dist/src/plugin/storage.d.ts +59 -120
  267. package/dist/src/plugin/storage.d.ts.map +1 -1
  268. package/dist/src/plugin/storage.js +140 -485
  269. package/dist/src/plugin/storage.js.map +1 -1
  270. package/dist/src/plugin/stores/signature-store.d.ts.map +1 -1
  271. package/dist/src/plugin/stores/signature-store.js.map +1 -1
  272. package/dist/src/plugin/thinking-recovery.d.ts.map +1 -1
  273. package/dist/src/plugin/thinking-recovery.js +35 -33
  274. package/dist/src/plugin/thinking-recovery.js.map +1 -1
  275. package/dist/src/plugin/token.d.ts +2 -2
  276. package/dist/src/plugin/token.d.ts.map +1 -1
  277. package/dist/src/plugin/token.js +30 -21
  278. package/dist/src/plugin/token.js.map +1 -1
  279. package/dist/src/plugin/transform/cross-model-sanitizer.d.ts +1 -1
  280. package/dist/src/plugin/transform/cross-model-sanitizer.js +1 -1
  281. package/dist/src/plugin/transform/index.d.ts +1 -1
  282. package/dist/src/plugin/transform/index.js +1 -1
  283. package/dist/src/plugin/transform/model-resolver.d.ts +1 -1
  284. package/dist/src/plugin/transform/model-resolver.js +1 -1
  285. package/dist/src/plugin/transform/types.d.ts +1 -1
  286. package/dist/src/plugin/transform/types.js +1 -1
  287. package/dist/src/plugin/types.d.ts +25 -69
  288. package/dist/src/plugin/types.d.ts.map +1 -1
  289. package/dist/src/plugin/ui/ansi.d.ts.map +1 -1
  290. package/dist/src/plugin/ui/ansi.js.map +1 -1
  291. package/dist/src/plugin/ui/auth-menu.d.ts +1 -1
  292. package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
  293. package/dist/src/plugin/ui/auth-menu.js +84 -35
  294. package/dist/src/plugin/ui/auth-menu.js.map +1 -1
  295. package/dist/src/plugin/ui/confirm.d.ts.map +1 -1
  296. package/dist/src/plugin/ui/confirm.js.map +1 -1
  297. package/dist/src/plugin/ui/model-status.d.ts +3 -3
  298. package/dist/src/plugin/ui/model-status.d.ts.map +1 -1
  299. package/dist/src/plugin/ui/model-status.js +3 -3
  300. package/dist/src/plugin/ui/model-status.js.map +1 -1
  301. package/dist/src/plugin/ui/quota-status.d.ts +4 -4
  302. package/dist/src/plugin/ui/quota-status.d.ts.map +1 -1
  303. package/dist/src/plugin/ui/quota-status.js +49 -49
  304. package/dist/src/plugin/ui/quota-status.js.map +1 -1
  305. package/dist/src/plugin/ui/select.d.ts.map +1 -1
  306. package/dist/src/plugin/ui/select.js +22 -12
  307. package/dist/src/plugin/ui/select.js.map +1 -1
  308. package/dist/src/plugin/version.d.ts +1 -1
  309. package/dist/src/plugin/version.js +1 -1
  310. package/dist/src/rpc/notifications.d.ts +6 -0
  311. package/dist/src/rpc/notifications.d.ts.map +1 -0
  312. package/dist/src/rpc/notifications.js +46 -0
  313. package/dist/src/rpc/notifications.js.map +1 -0
  314. package/dist/src/rpc/port-file.d.ts +13 -0
  315. package/dist/src/rpc/port-file.d.ts.map +1 -0
  316. package/dist/src/rpc/port-file.js +109 -0
  317. package/dist/src/rpc/port-file.js.map +1 -0
  318. package/dist/src/rpc/protocol.d.ts +22 -0
  319. package/dist/src/rpc/protocol.d.ts.map +1 -0
  320. package/dist/src/rpc/protocol.js +1 -0
  321. package/dist/src/rpc/protocol.js.map +1 -0
  322. package/dist/src/rpc/rpc-client.d.ts +10 -0
  323. package/dist/src/rpc/rpc-client.d.ts.map +1 -0
  324. package/dist/src/rpc/rpc-client.js +58 -0
  325. package/dist/src/rpc/rpc-client.js.map +1 -0
  326. package/dist/src/rpc/rpc-dir.d.ts +4 -0
  327. package/dist/src/rpc/rpc-dir.d.ts.map +1 -0
  328. package/dist/src/rpc/rpc-dir.js +19 -0
  329. package/dist/src/rpc/rpc-dir.js.map +1 -0
  330. package/dist/src/rpc/rpc-server.d.ts +13 -0
  331. package/dist/src/rpc/rpc-server.d.ts.map +1 -0
  332. package/dist/src/rpc/rpc-server.js +231 -0
  333. package/dist/src/rpc/rpc-server.js.map +1 -0
  334. package/dist/src/sidebar-state.d.ts +357 -0
  335. package/dist/src/sidebar-state.d.ts.map +1 -0
  336. package/dist/src/sidebar-state.js +680 -0
  337. package/dist/src/sidebar-state.js.map +1 -0
  338. package/dist/src/tui/command-dialogs.d.ts +61 -0
  339. package/dist/src/tui/command-dialogs.d.ts.map +1 -0
  340. package/dist/src/tui/command-dialogs.js +595 -0
  341. package/dist/src/tui/command-dialogs.js.map +1 -0
  342. package/dist/src/tui/file-logger.d.ts +41 -0
  343. package/dist/src/tui/file-logger.d.ts.map +1 -0
  344. package/dist/src/tui/file-logger.js +114 -0
  345. package/dist/src/tui/file-logger.js.map +1 -0
  346. package/dist/src/tui-preferences.d.ts +40 -0
  347. package/dist/src/tui-preferences.d.ts.map +1 -0
  348. package/dist/src/tui-preferences.js +328 -0
  349. package/dist/src/tui-preferences.js.map +1 -0
  350. package/dist/src/tui.d.ts +68 -0
  351. package/dist/src/tui.d.ts.map +1 -0
  352. package/dist/src/tui.js +697 -0
  353. package/dist/src/tui.js.map +1 -0
  354. package/package.json +46 -22
  355. package/src/plugin/command-data.ts +931 -0
  356. package/src/rpc/port-file.ts +143 -0
  357. package/src/rpc/protocol.ts +31 -0
  358. package/src/rpc/rpc-client.ts +102 -0
  359. package/src/rpc/rpc-dir.ts +24 -0
  360. package/src/sidebar-state.ts +991 -0
  361. package/src/tui/command-dialogs.tsx +832 -0
  362. package/src/tui/entry.mjs +74 -0
  363. package/src/tui/file-logger.ts +145 -0
  364. package/src/tui-compiled/plugin/command-data.ts +931 -0
  365. package/src/tui-compiled/rpc/port-file.ts +143 -0
  366. package/src/tui-compiled/rpc/protocol.ts +31 -0
  367. package/src/tui-compiled/rpc/rpc-client.ts +102 -0
  368. package/src/tui-compiled/rpc/rpc-dir.ts +24 -0
  369. package/src/tui-compiled/sidebar-state.ts +991 -0
  370. package/src/tui-compiled/tui/command-dialogs.tsx +779 -0
  371. package/src/tui-compiled/tui/file-logger.ts +145 -0
  372. package/src/tui-compiled/tui-preferences.ts +416 -0
  373. package/src/tui-compiled/tui.tsx +1193 -0
  374. package/src/tui-preferences.ts +416 -0
  375. package/src/tui.tsx +1171 -0
  376. package/dist/src/plugin.d.ts +0 -48
  377. package/dist/src/plugin.d.ts.map +0 -1
  378. package/dist/src/plugin.js +0 -3311
  379. package/dist/src/plugin.js.map +0 -1
package/src/tui.tsx ADDED
@@ -0,0 +1,1171 @@
1
+ /** @jsxImportSource @opentui/solid */
2
+
3
+ /**
4
+ * Read-only OpenTUI sidebar for the Antigravity plugin.
5
+ *
6
+ * Mirrors the fleet sibling layout (see `anthropic-auth-live` /
7
+ * `openai-auth-live` `packages/opencode/src/tui.tsx`): a single bordered
8
+ * column with a labelled header badge, a Quota section that lists one
9
+ * `AccountBlock` per visible account, a Routing section that surfaces the
10
+ * most recent session route, and a Health section that only appears when
11
+ * something is wrong. The Antigravity data model is richer than the
12
+ * Claude/Codex one (per-account Gemini + Non-Gemini quota pools, a health
13
+ * score, and per-session routing decisions), so the fleet components are
14
+ * adapted rather than copied verbatim:
15
+ *
16
+ * - `QuotaRow` reads `remainingPercent + resetAt` from the Antigravity
17
+ * `SidebarQuotaEntry` shape and colors via `quotaTone` (remaining
18
+ * threshold) instead of `usageTone` (used threshold).
19
+ * - `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.
26
+ *
27
+ * Hard rules for this file:
28
+ *
29
+ * - NO direct writes to the host terminal anywhere in the render path.
30
+ * The host terminal is the frame buffer; one stray byte corrupts every
31
+ * subsequent cell. Errors flow through `createTuiFileLogger()`.
32
+ * - NO imports from `./plugin/storage`, `./plugin/accounts`, OAuth code,
33
+ * or anything that touches tokens.
34
+ * - The component uses Solid's `onCleanup` to release the polling timer
35
+ * on unmount so a route change can never leak an interval.
36
+ */
37
+
38
+ import { readFileSync } from 'node:fs'
39
+ import { dirname, join } from 'node:path'
40
+ import { fileURLToPath } from 'node:url'
41
+ import type {
42
+ TuiPlugin,
43
+ TuiPluginApi,
44
+ TuiPluginModule,
45
+ TuiThemeCurrent,
46
+ } from '@opencode-ai/plugin/tui'
47
+ import { createSlot } from '@opentui/solid'
48
+ import {
49
+ createEffect,
50
+ createSignal,
51
+ For,
52
+ type JSX,
53
+ onCleanup,
54
+ onMount,
55
+ Show,
56
+ } from 'solid-js'
57
+ import type { RpcNotification } from './rpc/protocol'
58
+ import { createRpcClient } from './rpc/rpc-client'
59
+ import { getRpcDir } from './rpc/rpc-dir'
60
+ import {
61
+ readSidebarState,
62
+ type SidebarAccountState,
63
+ type SidebarQuotaEntry,
64
+ type SidebarQuotaKey,
65
+ type SidebarStateV1,
66
+ } from './sidebar-state'
67
+ import { openCommandDialog } from './tui/command-dialogs'
68
+ import { createTuiFileLogger, type TuiLogger } from './tui/file-logger'
69
+ import {
70
+ type AntigravityAuthTuiPrefs,
71
+ type AppearancePrefs,
72
+ computeEffectiveOrder,
73
+ DEFAULT_PREFS,
74
+ DEFAULT_SLOT_ORDER,
75
+ PLUGIN_KEY,
76
+ queueTuiPreferenceUpdate,
77
+ readTuiPreferencesFile,
78
+ resolveAntigravityAuthPrefs,
79
+ watchTuiPreferences,
80
+ } from './tui-preferences'
81
+
82
+ const ID = 'cortexkit.antigravity-auth'
83
+
84
+ const POLL_INTERVAL_MS = 2000
85
+ const RPC_POLL_MS = 500
86
+
87
+ const SINGLE_BORDER = { type: 'single' } as any
88
+
89
+ // Read package metadata from either the raw src/ entry or its generated
90
+ // src/tui-compiled/ counterpart. Avoid a JSON import because package.json sits
91
+ // outside the declaration build's rootDir.
92
+ const PLUGIN_VERSION: string = (() => {
93
+ const here = dirname(fileURLToPath(import.meta.url))
94
+ for (const packageFile of [
95
+ join(here, '..', 'package.json'),
96
+ join(here, '..', '..', 'package.json'),
97
+ ]) {
98
+ try {
99
+ const raw = readFileSync(packageFile, 'utf8')
100
+ const version = (JSON.parse(raw) as { version?: string }).version
101
+ if (version) return version
102
+ } catch {
103
+ // Try the path for the other TUI entry layout.
104
+ }
105
+ }
106
+ return ''
107
+ })()
108
+ // Module-scoped state — TEST ISOLATION CONTRACT:
109
+ //
110
+ // `rpcPollStarted`, notification cursor/deduplication state, `rpcInFlight`, and the lazy
111
+ // `sidebarController` below persist across tests via the cached ES
112
+ // module Bun loads once per `--isolate` file. Tests must NOT assert
113
+ // fresh-process behaviour from these variables — by the time the first
114
+ // `startRpcNotificationPolling` runs, all four carry stale values from
115
+ // prior tests in the same file. Test isolation comes from:
116
+ // 1. fresh-importing `tui.tsx` via a cache-busted dynamic `import`
117
+ // so the module re-evaluates with fresh module-scoped bindings, and
118
+ // 2. constructing a fresh `SidebarController` via
119
+ // `createSidebarController(prefs)` and passing it through
120
+ // `SidebarPanel.props.controller` — never through
121
+ // `getSidebarController()`, which lazily seeds the module-scoped
122
+ // singleton.
123
+ let rpcPollStarted = false
124
+ const notificationCursorBySession = new Map<string, number>()
125
+ const dispatchedNotificationIds = new Set<number>()
126
+ let rpcInFlight = false
127
+ const GLOBAL_NOTIFICATION_CURSOR = '__global__'
128
+ const MAX_NOTIFICATION_CURSORS = 256
129
+
130
+ const QUOTA_LABELS: Record<SidebarQuotaKey, string> = {
131
+ gemini: 'Gm',
132
+ 'non-gemini': 'NG',
133
+ }
134
+
135
+ const QUOTA_ORDER: readonly SidebarQuotaKey[] = ['gemini', 'non-gemini']
136
+
137
+ const WINDOW_GUTTER: Record<string, string> = {
138
+ weekly: '7d',
139
+ '5h': '5h',
140
+ }
141
+
142
+ // --- Theme tokens ----------------------------------------------------------
143
+ //
144
+ // The sidebar pulls its colors from the live host theme (`api.theme.current`
145
+ // — a Solid-friendly getter that re-emits when the user switches theme).
146
+ // Every render path resolves through `toneColor(theme, tone)` with a
147
+ // sibling-style `??` fallback chain, so the sidebar always tracks the
148
+ // active theme and never hardcodes an ANSI literal.
149
+ //
150
+ // `FALLBACK_THEME` is the safety net for tests and any future host that
151
+ // does not implement the theme surface. The fields the sidebar actually
152
+ // uses are populated; the rest of the TuiThemeCurrent fields are left
153
+ // undefined and the `??` chain in `toneColor` falls through cleanly.
154
+
155
+ type Tone = 'ok' | 'warn' | 'err' | 'muted' | 'accent' | 'text'
156
+ type Theme = TuiThemeCurrent
157
+ type ThemeColor = Theme['text']
158
+
159
+ const FALLBACK_THEME = {
160
+ primary: '#3b82f6',
161
+ secondary: '#a855f7',
162
+ accent: '#7c3aed',
163
+ error: '#ef4444',
164
+ warning: '#eab308',
165
+ success: '#22c55e',
166
+ info: '#0ea5e9',
167
+ text: '#e5e7eb',
168
+ textMuted: '#6b7280',
169
+ selectedListItemText: '#ffffff',
170
+ background: '#0b0d12',
171
+ backgroundPanel: '#11131a',
172
+ backgroundElement: '#1a1d27',
173
+ backgroundMenu: '#1a1d27',
174
+ border: '#2a2d3a',
175
+ borderActive: '#7c3aed',
176
+ borderSubtle: '#2a2d3a',
177
+ diffAdded: '#22c55e',
178
+ diffRemoved: '#ef4444',
179
+ diffContext: '#6b7280',
180
+ diffHunkHeader: '#7c3aed',
181
+ diffHighlightAdded: '#166534',
182
+ diffHighlightRemoved: '#7f1d1d',
183
+ diffAddedBg: '#0a1f12',
184
+ diffRemovedBg: '#1f0a0a',
185
+ diffContextBg: '#0b0d12',
186
+ diffLineNumber: '#4b5563',
187
+ diffAddedLineNumberBg: '#0a1f12',
188
+ diffRemovedLineNumberBg: '#1f0a0a',
189
+ markdownText: '#e5e7eb',
190
+ markdownHeading: '#7c3aed',
191
+ markdownLink: '#3b82f6',
192
+ markdownLinkText: '#60a5fa',
193
+ markdownCode: '#22c55e',
194
+ markdownBlockQuote: '#6b7280',
195
+ markdownEmph: '#eab308',
196
+ markdownStrong: '#7c3aed',
197
+ markdownHorizontalRule: '#2a2d3a',
198
+ markdownListItem: '#7c3aed',
199
+ markdownListEnumeration: '#a855f7',
200
+ markdownImage: '#3b82f6',
201
+ markdownImageText: '#60a5fa',
202
+ markdownCodeBlock: '#11131a',
203
+ syntaxComment: '#6b7280',
204
+ syntaxKeyword: '#a855f7',
205
+ syntaxFunction: '#3b82f6',
206
+ syntaxVariable: '#e5e7eb',
207
+ syntaxString: '#22c55e',
208
+ syntaxNumber: '#eab308',
209
+ syntaxType: '#7c3aed',
210
+ syntaxOperator: '#e5e7eb',
211
+ syntaxPunctuation: '#6b7280',
212
+ thinkingOpacity: 0.6,
213
+ } as unknown as Theme
214
+
215
+ // Mirror the fleet siblings' tone chain: every tone falls through to a
216
+ // sibling token, so a sparse custom theme still renders readably.
217
+ function toneColor(theme: Theme, tone: Tone): ThemeColor {
218
+ switch (tone) {
219
+ case 'ok':
220
+ return theme.success ?? theme.accent
221
+ case 'warn':
222
+ return theme.warning ?? theme.accent
223
+ case 'err':
224
+ return theme.error ?? theme.accent
225
+ case 'muted':
226
+ return theme.textMuted ?? theme.text
227
+ case 'accent':
228
+ return theme.accent ?? theme.text
229
+ default:
230
+ return theme.text
231
+ }
232
+ }
233
+
234
+ function quotaTone(usedPct: number, appearance: AppearancePrefs): Tone {
235
+ if (usedPct < appearance.warnThreshold) return 'ok'
236
+ if (usedPct < appearance.errorThreshold) return 'warn'
237
+ return 'err'
238
+ }
239
+
240
+ interface BarSegment {
241
+ text: string
242
+ tone: Tone
243
+ }
244
+
245
+ function quotaBarSegments(
246
+ usedPct: number,
247
+ appearance: AppearancePrefs,
248
+ ): BarSegment[] {
249
+ const width = appearance.barWidth
250
+ const usedCells = Math.max(
251
+ 0,
252
+ Math.min(Math.round((usedPct / 100) * width), width),
253
+ )
254
+ const tone = quotaTone(usedPct, appearance)
255
+ return [
256
+ { text: appearance.barFilledChar.repeat(usedCells), tone },
257
+ { text: appearance.barEmptyChar.repeat(width - usedCells), tone },
258
+ ].filter((segment) => segment.text.length > 0)
259
+ }
260
+
261
+ export interface SidebarPanelProps {
262
+ /** Override the file the TUI polls. Defaults to `getSidebarStateFile()`. */
263
+ stateFile?: string
264
+ /** Override the polling interval. Defaults to 2000ms. */
265
+ pollIntervalMs?: number
266
+ /** Override the logger; tests inject a logger that captures into memory. */
267
+ logger?: TuiLogger
268
+ /** Optional override for the current epoch in milliseconds (tests). */
269
+ now?: () => number
270
+ /** Optional prefs controller — when present, drives collapse/expand and
271
+ * section toggles. Module-scoped controller is created at plugin init. */
272
+ controller?: SidebarController
273
+ /** Optional live-theme accessor. The host's `api.theme.current` is wired
274
+ * through here in production; tests pass a custom accessor to flip theme
275
+ * and assert re-render. Falls back to FALLBACK_THEME when unset. */
276
+ theme?: () => Theme
277
+ /** The slot session determines which route is relevant to this sidebar. */
278
+ sessionId?: string
279
+ }
280
+
281
+ export interface SidebarController {
282
+ prefs: () => AntigravityAuthTuiPrefs
283
+ collapsed: () => boolean
284
+ toggleCollapsed: () => void
285
+ }
286
+
287
+ interface InternalState {
288
+ loaded: SidebarStateV1
289
+ lastReadAt: number
290
+ lastError: string | null
291
+ }
292
+
293
+ const EMPTY_STATE: SidebarStateV1 = {
294
+ version: 1,
295
+ checkedAt: 0,
296
+ accounts: [],
297
+ activeRouting: {},
298
+ routingAuthoritative: false,
299
+ }
300
+
301
+ function resolveLogger(logger: TuiLogger | undefined): TuiLogger {
302
+ if (logger) return logger
303
+ try {
304
+ return createTuiFileLogger()
305
+ } catch {
306
+ // Fall back to a stub that drops everything — the sidebar must still
307
+ // render even if file logging cannot be initialized.
308
+ return noopLogger()
309
+ }
310
+ }
311
+
312
+ function noopLogger(): TuiLogger {
313
+ const drop = () => undefined
314
+ return {
315
+ debug: drop,
316
+ info: drop,
317
+ warn: drop,
318
+ error: drop,
319
+ getLogPath: () => undefined,
320
+ }
321
+ }
322
+
323
+ // Module-scoped controller singleton, initialized at plugin startup. The
324
+ // watcher is intentionally never disposed — collapse/prefs state survives
325
+ // sidebar remounts (route changes) for the lifetime of the TUI process,
326
+ // mirroring the fleet siblings.
327
+ //
328
+ // TEST ISOLATION: this singleton, like `rpcPollStarted` above, persists
329
+ // across tests via the cached ES module. Tests construct a controller
330
+ // via `createSidebarController(prefs)` and pass it through
331
+ // `SidebarPanel.props.controller` — they MUST NOT rely on this lazy
332
+ // singleton for isolation. See the block above for the full contract.
333
+ let sidebarController: SidebarController | null = null
334
+
335
+ // The TUI may unmount and remount sidebar_content when the user switches
336
+ // views. A remount re-runs the component body, so any signal created inside
337
+ // the component would reset to its seed. The controller lives in the plugin
338
+ // closure (process lifetime) and owns the durable prefs/collapse signals
339
+ // plus the single shared watcher subscription, so collapse and live pref
340
+ // reloads survive the remount.
341
+ //
342
+ // Exported so tests can construct a controller with a controlled initial
343
+ // prefs snapshot without touching the module-scoped singleton.
344
+ export function createSidebarController(
345
+ initialPrefs: AntigravityAuthTuiPrefs,
346
+ ): SidebarController {
347
+ const [prefs, setPrefs] = createSignal<AntigravityAuthTuiPrefs>(initialPrefs)
348
+ const seedCollapsed =
349
+ initialPrefs.rememberCollapsed && initialPrefs.collapsed != null
350
+ ? initialPrefs.collapsed
351
+ : initialPrefs.startCollapsed
352
+ const [collapsed, setCollapsed] = createSignal(seedCollapsed)
353
+ let lastPersistedCollapsed: boolean | null = initialPrefs.collapsed
354
+ let lastApplied = JSON.stringify(initialPrefs)
355
+
356
+ // The watcher lives for the plugin/process lifetime — it is intentionally
357
+ // never disposed. Collapse guard mirrors the race-fix in toggleCollapsed:
358
+ // lastPersistedCollapsed is advanced only once our own write lands, so
359
+ // watcher echoes of the previous persisted value are rejected by the
360
+ // `!==` check and cannot revert a user click.
361
+ watchTuiPreferences(() => {
362
+ void (async () => {
363
+ const next = resolveAntigravityAuthPrefs(await readTuiPreferencesFile())
364
+ const serialized = JSON.stringify(next)
365
+ if (serialized === lastApplied) return
366
+ lastApplied = serialized
367
+ setPrefs(next)
368
+ if (
369
+ next.rememberCollapsed &&
370
+ next.collapsed != null &&
371
+ next.collapsed !== lastPersistedCollapsed
372
+ ) {
373
+ lastPersistedCollapsed = next.collapsed
374
+ setCollapsed(next.collapsed)
375
+ }
376
+ })()
377
+ })
378
+
379
+ function toggleCollapsed() {
380
+ const next = !collapsed()
381
+ setCollapsed(next)
382
+ if (prefs().rememberCollapsed) {
383
+ void queueTuiPreferenceUpdate(PLUGIN_KEY, ['collapsed'], next).then(
384
+ () => {
385
+ lastPersistedCollapsed = next
386
+ },
387
+ )
388
+ }
389
+ }
390
+
391
+ return { prefs, collapsed, toggleCollapsed }
392
+ }
393
+
394
+ // Lazy module-scoped accessor used by the plugin entry. Tests should NOT go
395
+ // through this — they construct their own controller via createSidebarController
396
+ // and pass it via SidebarPanelProps.controller.
397
+ function getSidebarController(): SidebarController {
398
+ if (!sidebarController) {
399
+ sidebarController = createSidebarController(DEFAULT_PREFS)
400
+ }
401
+ return sidebarController
402
+ }
403
+
404
+ // --- Shared helpers (fleet shape, antigravity data) ------------------------
405
+
406
+ function clamp(value: number, min: number, max: number): number {
407
+ if (value < min) return min
408
+ if (value > max) return max
409
+ return value
410
+ }
411
+
412
+ // Render a "reset in Nm/Nh/NdNh" string from an epoch-ms reset time.
413
+ // Empty string when no resetAt is cached. The fleet's `formatResetIn`
414
+ // reads ISO strings; Antigravity persists resetAt as a numeric epoch, so
415
+ // the wrapper adapts to that without changing the shape of the output.
416
+ function formatResetIn(resetAt: number | undefined, now: () => number): string {
417
+ if (!resetAt) return ''
418
+ const ms = resetAt - now()
419
+ if (ms <= 0) return 'now'
420
+ const mins = Math.floor(ms / 60_000)
421
+ if (mins < 60) return `${mins}m`
422
+ const hrs = Math.floor(mins / 60)
423
+ if (hrs < 24) {
424
+ const rm = mins % 60
425
+ return rm > 0 ? `${hrs}h${rm}m` : `${hrs}h`
426
+ }
427
+ const days = Math.floor(hrs / 24)
428
+ const rh = hrs % 24
429
+ return rh > 0 ? `${days}d${rh}h` : `${days}d`
430
+ }
431
+
432
+ function formatError(value: unknown): string {
433
+ if (value instanceof Error) return value.message
434
+ try {
435
+ return JSON.stringify(value)
436
+ } catch {
437
+ return String(value)
438
+ }
439
+ }
440
+
441
+ // --- Fleet components (adapted to antigravity data) ------------------------
442
+
443
+ // Section header — fleet pattern: bold title, themed text color, top margin
444
+ // for vertical breathing room between sections.
445
+ function SectionHeader(props: {
446
+ theme: () => Theme
447
+ title: string
448
+ }): JSX.Element {
449
+ return (
450
+ <box width='100%' marginTop={1}>
451
+ <text fg={toneColor(props.theme(), 'text')}>
452
+ <b>{props.title}</b>
453
+ </text>
454
+ </box>
455
+ )
456
+ }
457
+
458
+ // Fleet StatRow: muted label left, value (optionally tone-tinted, bold)
459
+ // right. Mirrors the layout the Claude/Codex sidebars use for routing
460
+ // rows, plan rows, and "resets N" rows.
461
+ function StatRow(props: {
462
+ theme: () => Theme
463
+ label: string
464
+ value: string
465
+ tone?: Tone
466
+ }): JSX.Element {
467
+ return (
468
+ <box width='100%' flexDirection='row' justifyContent='space-between'>
469
+ <text fg={props.theme().textMuted}>{props.label}</text>
470
+ <text fg={toneColor(props.theme(), props.tone ?? 'text')}>
471
+ <b>{props.value}</b>
472
+ </text>
473
+ </box>
474
+ )
475
+ }
476
+
477
+ // Fleet CollapsedRow: muted label left, caller-supplied value right. Used
478
+ // for the collapsed sidebar view; the Antigravity adapter renders one
479
+ // CollapsedRow per visible account (Claude's sibling collapses to a
480
+ // single primary-quota line, but Antigravity's multi-quota model fits a
481
+ // per-account row better when the sidebar is collapsed).
482
+ function CollapsedRow(props: {
483
+ theme: () => Theme
484
+ label: string
485
+ children: JSX.Element
486
+ }): JSX.Element {
487
+ return (
488
+ <box width='100%' flexDirection='row' justifyContent='space-between'>
489
+ <text fg={props.theme().textMuted}>{props.label}</text>
490
+ {props.children}
491
+ </box>
492
+ )
493
+ }
494
+
495
+ // Fleet QuotaRow, adapted to Antigravity's `remainingPercent + resetAt`
496
+ // shape. The left group stacks label + bar + pct in fixed columns so
497
+ // bars line up across rows; the right group carries the reset countdown
498
+ // when the plugin has cached a reset time. Tone is read off the
499
+ // remaining percentage via the antigravity threshold rules — the fleet's
500
+ // `usageTone` would invert the polarity (healthy = high remaining vs
501
+ // healthy = low used), so we keep the antigravity-local `quotaTone`.
502
+ function QuotaRow(props: {
503
+ theme: () => Theme
504
+ appearance: AppearancePrefs
505
+ label: string
506
+ entry: SidebarQuotaEntry | undefined
507
+ now: () => number
508
+ }): JSX.Element {
509
+ const used = () =>
510
+ props.entry ? 100 - clamp(props.entry.remainingPercent, 0, 100) : null
511
+ const reset = () =>
512
+ props.entry ? formatResetIn(props.entry.resetAt, props.now) : ''
513
+ return (
514
+ <Show
515
+ when={used() != null}
516
+ fallback={
517
+ <box width='100%' flexDirection='row' justifyContent='space-between'>
518
+ <text fg={props.theme().textMuted}>{props.label.padEnd(5)}</text>
519
+ <text fg={props.theme().textMuted}>{'\u2014'}</text>
520
+ </box>
521
+ }
522
+ >
523
+ <box width='100%' flexDirection='row' justifyContent='space-between'>
524
+ <box flexDirection='row'>
525
+ <text width={5} flexShrink={0} fg={props.theme().textMuted}>
526
+ {props.label}
527
+ </text>
528
+ <box
529
+ width={props.appearance.barWidth}
530
+ flexShrink={0}
531
+ flexDirection='row'
532
+ >
533
+ <For each={quotaBarSegments(used() ?? 0, props.appearance)}>
534
+ {(segment) => (
535
+ <text fg={toneColor(props.theme(), segment.tone)}>
536
+ {segment.text}
537
+ </text>
538
+ )}
539
+ </For>
540
+ </box>
541
+ <text
542
+ fg={toneColor(
543
+ props.theme(),
544
+ quotaTone(used() ?? 0, props.appearance),
545
+ )}
546
+ >
547
+ {` ${String(Math.round(used() ?? 0)).padStart(3)}%`}
548
+ </text>
549
+ </box>
550
+ <Show when={reset()}>
551
+ <text fg={props.theme().textMuted}>{reset()}</text>
552
+ </Show>
553
+ </box>
554
+ </Show>
555
+ )
556
+ }
557
+
558
+ // Fleet AccountBlock, adapted to Antigravity's per-account data. Header
559
+ // row is account label (left) + status word (right). The body renders one
560
+ // QuotaRow per present quota group in the fleet's stable order, then a
561
+ // muted secondary line with the account's health score and, when
562
+ // relevant, a cooldown countdown. Status word priorities (first match
563
+ // wins):
564
+ // - `off` when the account is disabled
565
+ // - `cooling` when cooldownUntil is still in the future
566
+ // - `active` when current === true
567
+ // - `idle` for the fallback (current === false) case
568
+ function AccountBlock(props: {
569
+ theme: () => Theme
570
+ appearance: AppearancePrefs
571
+ account: SidebarAccountState
572
+ now: () => number
573
+ active?: boolean
574
+ marginTop?: number
575
+ }): JSX.Element {
576
+ const active = () => props.active ?? props.account.current
577
+ const statusWord = (): string => {
578
+ if (!props.account.enabled) return 'off'
579
+ const cd = props.account.cooldownUntil
580
+ if (typeof cd === 'number' && cd > props.now()) return 'cooling'
581
+ return active() ? 'active' : 'idle'
582
+ }
583
+ const statusTone = (): Tone => {
584
+ if (!props.account.enabled) return 'muted'
585
+ const cd = props.account.cooldownUntil
586
+ if (typeof cd === 'number' && cd > props.now()) return 'warn'
587
+ return active() ? 'ok' : 'muted'
588
+ }
589
+ const cooldownMs = (): number => {
590
+ const cd = props.account.cooldownUntil
591
+ if (typeof cd !== 'number') return 0
592
+ return Math.max(0, cd - props.now())
593
+ }
594
+ const healthText = () => {
595
+ const base = `health ${Math.round(clamp(props.account.health, 0, 100))}`
596
+ return cooldownMs() > 0
597
+ ? `${base} · cooling ${formatWait(cooldownMs())}`
598
+ : base
599
+ }
600
+ return (
601
+ <box width='100%' flexDirection='column' marginTop={props.marginTop ?? 0}>
602
+ <box width='100%' flexDirection='row' justifyContent='space-between'>
603
+ <text fg={props.theme().text}>
604
+ <b>{props.account.label}</b>
605
+ </text>
606
+ <text fg={toneColor(props.theme(), statusTone())}>
607
+ <b>{statusWord()}</b>
608
+ </text>
609
+ </box>
610
+ <For each={QUOTA_ORDER}>
611
+ {(key) => {
612
+ const poolEntry = props.account.quota[key]
613
+ const windows = poolEntry?.windows
614
+ // Legacy: no windows array — render a single row with the pool label.
615
+ const hasWindows = windows && windows.length > 0
616
+ if (!hasWindows) {
617
+ return (
618
+ <QuotaRow
619
+ theme={props.theme}
620
+ appearance={props.appearance}
621
+ label={QUOTA_LABELS[key]}
622
+ entry={poolEntry}
623
+ now={props.now}
624
+ />
625
+ )
626
+ }
627
+ return (
628
+ <>
629
+ {windows.map((w) => (
630
+ <QuotaRow
631
+ theme={props.theme}
632
+ appearance={props.appearance}
633
+ label={`${QUOTA_LABELS[key]} ${WINDOW_GUTTER[w.window] ?? w.window}`}
634
+ entry={{
635
+ remainingPercent: w.remainingPercent,
636
+ resetAt: w.resetAt,
637
+ }}
638
+ now={props.now}
639
+ />
640
+ ))}
641
+ </>
642
+ )
643
+ }}
644
+ </For>
645
+ <box width='100%' flexDirection='row'>
646
+ <text fg={props.theme().textMuted}>{` ${healthText()}`}</text>
647
+ </box>
648
+ </box>
649
+ )
650
+ }
651
+
652
+ function formatWait(ms: number): string {
653
+ const seconds = Math.ceil(ms / 1000)
654
+ if (seconds < 60) return `${seconds}s`
655
+ const minutes = Math.floor(seconds / 60)
656
+ const remaining = seconds % 60
657
+ return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`
658
+ }
659
+
660
+ export function resolveQuotaDialogActiveId(
661
+ state: SidebarStateV1,
662
+ sessionId: string | undefined,
663
+ ): string | undefined {
664
+ return (
665
+ (sessionId ? state.activeRouting[sessionId]?.accountId : undefined) ??
666
+ state.accounts.find((account) => account.current)?.id
667
+ )
668
+ }
669
+
670
+ export function QuotaDialogContent(props: {
671
+ api: TuiPluginApi
672
+ controller: SidebarController
673
+ sessionId: string | undefined
674
+ }): JSX.Element {
675
+ const prefs = props.controller.prefs
676
+ const [state, setState] = createSignal<SidebarStateV1>(EMPTY_STATE)
677
+ const refresh = (): void => {
678
+ setState(readSidebarState())
679
+ }
680
+ createEffect(() => {
681
+ const timer = setInterval(refresh, prefs().pollMs)
682
+ onCleanup(() => clearInterval(timer))
683
+ })
684
+ setTimeout(refresh, 0)
685
+
686
+ const theme = (): Theme => props.api.theme.current
687
+ const visibleAccounts = () => {
688
+ if (prefs().sections.fallbackAccounts) return state().accounts
689
+ return state().accounts.filter((account) => account.current)
690
+ }
691
+ const activeId = () => resolveQuotaDialogActiveId(state(), props.sessionId)
692
+
693
+ return (
694
+ <box flexDirection='column' padding={2} width='100%' alignItems='center'>
695
+ <box flexDirection='column' width={58}>
696
+ <box width='100%' justifyContent='center' marginBottom={1}>
697
+ <text fg={theme().text}>
698
+ <b>Antigravity Quota</b>
699
+ </text>
700
+ </box>
701
+ <For each={visibleAccounts()}>
702
+ {(account, index) => (
703
+ <AccountBlock
704
+ theme={theme}
705
+ appearance={prefs().appearance}
706
+ account={account}
707
+ active={activeId() === account.id}
708
+ now={() => Date.now()}
709
+ marginTop={index() === 0 ? 0 : 1}
710
+ />
711
+ )}
712
+ </For>
713
+ </box>
714
+ </box>
715
+ )
716
+ }
717
+
718
+ // --- SidebarPanel ----------------------------------------------------------
719
+
720
+ export function SidebarPanel(props: SidebarPanelProps): JSX.Element {
721
+ const logger = resolveLogger(props.logger)
722
+ const now = props.now ?? (() => Date.now())
723
+ const pollMs = props.pollIntervalMs ?? POLL_INTERVAL_MS
724
+ const controller = props.controller ?? getSidebarController()
725
+ const collapsed = controller.collapsed
726
+ const prefs = controller.prefs
727
+ // Live host theme — falls back to a sensible dark palette when the host
728
+ // does not expose one. Solid tracks `theme()` so a theme switch re-renders
729
+ // every styled span/border without a manual subscription.
730
+ const theme = (): Theme => props.theme?.() ?? FALLBACK_THEME
731
+
732
+ const [state, setState] = createSignal<InternalState>({
733
+ loaded: EMPTY_STATE,
734
+ lastReadAt: 0,
735
+ lastError: null,
736
+ })
737
+
738
+ const refresh = (): void => {
739
+ try {
740
+ const loaded = readSidebarState(props.stateFile)
741
+ setState({
742
+ loaded,
743
+ lastReadAt: now(),
744
+ lastError: null,
745
+ })
746
+ } catch (error) {
747
+ logger.warn('sidebar-poll-failed', { error: formatError(error) })
748
+ setState((prev) => ({
749
+ ...prev,
750
+ lastReadAt: now(),
751
+ lastError: formatError(error),
752
+ }))
753
+ }
754
+ }
755
+
756
+ onMount(() => {
757
+ refresh()
758
+ const interval = setInterval(refresh, pollMs)
759
+ onCleanup(() => clearInterval(interval))
760
+ })
761
+
762
+ const hasData = () => state().loaded.accounts.length > 0
763
+ const backoffActive = () => {
764
+ const until = state().loaded.quotaBackoffUntil
765
+ return typeof until === 'number' && until > now()
766
+ }
767
+ const lastError = () => state().lastError ?? state().loaded.lastError
768
+ const degraded = () => backoffActive() || !!lastError()
769
+ // Honors sections.fallbackAccounts. Both the expanded (AccountBlock list)
770
+ // and the collapsed (CollapsedRow list) paths use the same filter so the
771
+ // user sees the same account set in either mode.
772
+ const visibleAccounts = () => {
773
+ const showFallback = prefs().sections.fallbackAccounts
774
+ if (showFallback) return state().loaded.accounts
775
+ return state().loaded.accounts.filter((account) => account.current)
776
+ }
777
+
778
+ const currentRoute = (): {
779
+ strategy?: 'sticky' | 'round-robin' | 'hybrid'
780
+ family: string
781
+ style: string
782
+ } | null => {
783
+ const routes = state().loaded.activeRouting
784
+ const entry = props.sessionId
785
+ ? routes[props.sessionId]
786
+ : Object.values(routes).sort((a, b) => b.updatedAt - a.updatedAt)[0]
787
+ if (!entry) return null
788
+ return {
789
+ strategy: entry.strategy,
790
+ family: entry.modelFamily,
791
+ style: entry.headerStyle,
792
+ }
793
+ }
794
+
795
+ // Header badge: ▼/▶ {label}. The fleet shows the version string on the
796
+ // right when no degraded state is present; the antigravity adapter keeps
797
+ // that right-alignment and surfaces `degraded` as a "LIMITED" badge
798
+ // instead of the "1/1 ready" count the previous revision rendered.
799
+ const headerLabel = () => {
800
+ const name = prefs().header.label
801
+ return !hasData() ? name : collapsed() ? `\u25b6 ${name}` : `\u25bc ${name}`
802
+ }
803
+
804
+ return (
805
+ <box
806
+ width='100%'
807
+ flexDirection='column'
808
+ border={SINGLE_BORDER}
809
+ borderColor={theme().borderActive}
810
+ paddingTop={1}
811
+ paddingBottom={1}
812
+ paddingLeft={1}
813
+ paddingRight={1}
814
+ >
815
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: opentui renders to a terminal, not the DOM — ARIA roles do not apply */}
816
+ <box
817
+ width='100%'
818
+ flexDirection='row'
819
+ justifyContent='space-between'
820
+ alignItems='center'
821
+ onMouseDown={() => {
822
+ // Mirror the fleet guard: only toggle when there is data to expand
823
+ // into. Without this a click on the empty-awaiting header would just
824
+ // toggle the affordance for nothing.
825
+ if (hasData()) controller.toggleCollapsed()
826
+ }}
827
+ >
828
+ <box paddingLeft={1} paddingRight={1} backgroundColor={theme().accent}>
829
+ <text fg={theme().background}>
830
+ <b>{headerLabel()}</b>
831
+ </text>
832
+ </box>
833
+ <Show
834
+ when={degraded()}
835
+ fallback={
836
+ <Show when={prefs().header.showVersion && PLUGIN_VERSION !== ''}>
837
+ <text fg={theme().textMuted}>{`v${PLUGIN_VERSION}`}</text>
838
+ </Show>
839
+ }
840
+ >
841
+ <box
842
+ paddingLeft={1}
843
+ paddingRight={1}
844
+ backgroundColor={theme().warning}
845
+ >
846
+ <text fg={theme().background}>
847
+ <b>{'LIMITED'}</b>
848
+ </text>
849
+ </box>
850
+ </Show>
851
+ </box>
852
+
853
+ {/* Collapsed: the active account's primary quota and fleet status dot. */}
854
+ <Show when={collapsed() && hasData()}>
855
+ {(() => {
856
+ const account = () =>
857
+ visibleAccounts().find((entry) => entry.current) ??
858
+ visibleAccounts()[0]
859
+ const used = () => {
860
+ const q = account()?.quota
861
+ // Derive tone from the WORST (most-constrained) pool so a
862
+ // critical non-gemini pool doesn't hide behind a healthy
863
+ // gemini tone. Worst = highest used-percent across available pools.
864
+ const geminiUsed =
865
+ q?.gemini != null
866
+ ? 100 - clamp(q.gemini.remainingPercent, 0, 100)
867
+ : null
868
+ const nonGeminiUsed =
869
+ q?.['non-gemini'] != null
870
+ ? 100 - clamp(q['non-gemini'].remainingPercent, 0, 100)
871
+ : null
872
+ if (geminiUsed === null && nonGeminiUsed === null) return null
873
+ return Math.max(geminiUsed ?? 0, nonGeminiUsed ?? 0)
874
+ }
875
+ const poolText = (key: 'gemini' | 'non-gemini') => {
876
+ const entry = account()?.quota[key]
877
+ if (!entry) return `${QUOTA_LABELS[key]}: —`
878
+ const pct = Math.round(100 - clamp(entry.remainingPercent, 0, 100))
879
+ // Show binding window when available, else just pool label.
880
+ const bindingWindow = entry.windows?.reduce<
881
+ (typeof entry.windows)[0] | null
882
+ >(
883
+ (best, w) =>
884
+ best === null || w.remainingPercent < best.remainingPercent
885
+ ? w
886
+ : best,
887
+ null,
888
+ )
889
+ const gutter = bindingWindow
890
+ ? ` ${WINDOW_GUTTER[bindingWindow.window] ?? bindingWindow.window}`
891
+ : ''
892
+ return `${QUOTA_LABELS[key]}${gutter}: ${pct}%`
893
+ }
894
+ const unavailable = () => {
895
+ const selected = account()
896
+ return (
897
+ selected != null &&
898
+ (!selected.enabled ||
899
+ (selected.cooldownUntil != null &&
900
+ selected.cooldownUntil > now()))
901
+ )
902
+ }
903
+ return (
904
+ <Show when={account()}>
905
+ {(selected) => (
906
+ <CollapsedRow theme={theme} label={selected().label}>
907
+ <box flexDirection='row'>
908
+ <text
909
+ fg={toneColor(
910
+ theme(),
911
+ used() == null
912
+ ? 'muted'
913
+ : quotaTone(used() ?? 0, prefs().appearance),
914
+ )}
915
+ >
916
+ <b>
917
+ {account()?.quota.gemini == null &&
918
+ account()?.quota['non-gemini'] == null
919
+ ? '—'
920
+ : `${poolText('gemini')} · ${poolText('non-gemini')}`}
921
+ </b>
922
+ </text>
923
+ <text
924
+ fg={toneColor(
925
+ theme(),
926
+ unavailable()
927
+ ? 'err'
928
+ : quotaTone(used() ?? 0, prefs().appearance),
929
+ )}
930
+ >
931
+ {unavailable() ? ' ⊘' : ' ●'}
932
+ </text>
933
+ </box>
934
+ </CollapsedRow>
935
+ )}
936
+ </Show>
937
+ )
938
+ })()}
939
+ </Show>
940
+
941
+ {/* Expanded: full sections. Also render when there's no data so the
942
+ sidebar can never go blank if data clears while collapsed. */}
943
+ <Show when={!collapsed() || !hasData()}>
944
+ <Show
945
+ when={hasData()}
946
+ fallback={
947
+ <box marginTop={1} width='100%'>
948
+ <text fg={theme().textMuted}>{'Waiting for quota\u2026'}</text>
949
+ </box>
950
+ }
951
+ >
952
+ {/* Quota */}
953
+ <Show when={prefs().sections.quota}>
954
+ <SectionHeader theme={theme} title='Quota' />
955
+ <For each={visibleAccounts()}>
956
+ {(account, index) => (
957
+ <AccountBlock
958
+ theme={theme}
959
+ appearance={prefs().appearance}
960
+ account={account}
961
+ now={now}
962
+ marginTop={index() === 0 ? 0 : 1}
963
+ />
964
+ )}
965
+ </For>
966
+ </Show>
967
+
968
+ {/* Routing */}
969
+ <Show when={prefs().sections.routing}>
970
+ <SectionHeader theme={theme} title='Routing' />
971
+ <Show
972
+ when={currentRoute()}
973
+ fallback={
974
+ <StatRow theme={theme} label='Route' value='—' tone='muted' />
975
+ }
976
+ >
977
+ {(route) => (
978
+ <StatRow
979
+ theme={theme}
980
+ label='Route'
981
+ value={`${route().strategy ? `${route().strategy} · ` : ''}${route().family}: ${route().style}`}
982
+ tone='accent'
983
+ />
984
+ )}
985
+ </Show>
986
+ </Show>
987
+
988
+ {/* Health — only when something is wrong AND sections.health is true */}
989
+ <Show when={degraded() && prefs().sections.health}>
990
+ <SectionHeader theme={theme} title='Health' />
991
+ <Show when={backoffActive()}>
992
+ <StatRow
993
+ theme={theme}
994
+ label='Quota API'
995
+ value={`backoff ${formatResetIn(state().loaded.quotaBackoffUntil, now)}`}
996
+ tone='warn'
997
+ />
998
+ </Show>
999
+ <Show when={lastError()}>
1000
+ <StatRow
1001
+ theme={theme}
1002
+ label='Last error'
1003
+ value={lastError() ?? ''}
1004
+ tone='err'
1005
+ />
1006
+ </Show>
1007
+ </Show>
1008
+ </Show>
1009
+ </Show>
1010
+ </box>
1011
+ )
1012
+ }
1013
+
1014
+ /**
1015
+ * Solid slot registry binding exposed for hosts that want to mount the
1016
+ * sidebar inside their renderer. Hosts that already own a slot registry can
1017
+ * use `createSlot` directly; we re-export for convenience and to keep the
1018
+ * contract visible at the module entry point.
1019
+ */
1020
+ export const sidebar_content = createSlot
1021
+
1022
+ interface RpcNotificationPollOptions {
1023
+ pending: (
1024
+ lastReceivedId: number,
1025
+ sessionId?: string,
1026
+ ) => Promise<RpcNotification[]>
1027
+ currentSessionId: () => string | undefined
1028
+ dispatch: (notification: RpcNotification) => void | Promise<void>
1029
+ schedule: (poll: () => Promise<void>, intervalMs: number) => void
1030
+ /**
1031
+ * File logger used to surface poll errors. Must be a file logger — the
1032
+ * host terminal is the frame buffer, so writes to stdout/stderr would
1033
+ * corrupt the sidebar render. The plugin production path uses
1034
+ * `resolveLogger(undefined)` to fall through to the on-disk file
1035
+ * logger.
1036
+ */
1037
+ logger: TuiLogger
1038
+ }
1039
+
1040
+ export function startRpcNotificationPolling(
1041
+ options: RpcNotificationPollOptions,
1042
+ ): void {
1043
+ if (rpcPollStarted) return
1044
+ rpcPollStarted = true
1045
+ options.schedule(async () => {
1046
+ if (rpcInFlight) return
1047
+ rpcInFlight = true
1048
+ try {
1049
+ const sessionId = options.currentSessionId()
1050
+ const cursorKey = sessionId ?? GLOBAL_NOTIFICATION_CURSOR
1051
+ let cursor = notificationCursorBySession.get(cursorKey) ?? 0
1052
+ const notifications = await options.pending(cursor, sessionId)
1053
+ for (const notification of [...notifications].sort(
1054
+ (a, b) => a.id - b.id,
1055
+ )) {
1056
+ if (notification.id <= cursor) continue
1057
+ cursor = Math.max(cursor, notification.id)
1058
+ if (dispatchedNotificationIds.has(notification.id)) continue
1059
+ dispatchedNotificationIds.add(notification.id)
1060
+ if (dispatchedNotificationIds.size > 100) {
1061
+ const oldestId = dispatchedNotificationIds.values().next().value
1062
+ if (oldestId !== undefined) dispatchedNotificationIds.delete(oldestId)
1063
+ }
1064
+ await options.dispatch(notification)
1065
+ }
1066
+ if (!notificationCursorBySession.has(cursorKey)) {
1067
+ while (notificationCursorBySession.size >= MAX_NOTIFICATION_CURSORS) {
1068
+ const oldest = notificationCursorBySession.keys().next().value
1069
+ if (oldest === undefined) break
1070
+ notificationCursorBySession.delete(oldest)
1071
+ }
1072
+ }
1073
+ notificationCursorBySession.set(cursorKey, cursor)
1074
+ } catch (error) {
1075
+ // Surface the swallowed error through the file logger rather than
1076
+ // stdout/stderr (the host terminal is the frame buffer — any byte
1077
+ // written here corrupts every subsequent cell). Without this, a
1078
+ // transient RPC outage is invisible to operators. The catch stays
1079
+ // because one failed poll must never break the next — the scheduler
1080
+ // is a setInterval and a thrown error there would crash the
1081
+ // process.
1082
+ options.logger.warn('rpc-poll-failed', {
1083
+ error: error instanceof Error ? error.message : String(error),
1084
+ })
1085
+ } finally {
1086
+ rpcInFlight = false
1087
+ }
1088
+ }, RPC_POLL_MS)
1089
+ }
1090
+
1091
+ const tui: TuiPlugin = async (api) => {
1092
+ const logger = resolveLogger(undefined)
1093
+ const prefsRoot = await readTuiPreferencesFile()
1094
+ if (!sidebarController) {
1095
+ sidebarController = createSidebarController(
1096
+ resolveAntigravityAuthPrefs(prefsRoot),
1097
+ )
1098
+ }
1099
+ const rpcClient = createRpcClient(
1100
+ getRpcDir(api.state.path.directory ?? ''),
1101
+ process.pid,
1102
+ )
1103
+
1104
+ startRpcNotificationPolling({
1105
+ pending: (lastReceivedId, sessionId) =>
1106
+ rpcClient.pendingNotifications(lastReceivedId, sessionId),
1107
+ currentSessionId: () => {
1108
+ const current = (api.route as { current?: unknown }).current
1109
+ const resolved =
1110
+ typeof current === 'function' ? (current as () => unknown)() : current
1111
+ return (resolved as { params?: { sessionID?: string } } | undefined)
1112
+ ?.params?.sessionID
1113
+ },
1114
+ dispatch: async (notification) => {
1115
+ logger.debug('rpc-notification-received', {
1116
+ command: notification.payload.command,
1117
+ id: notification.id,
1118
+ sessionId: notification.sessionId,
1119
+ })
1120
+ // Call the imperative dispatcher directly — the prior
1121
+ // two-phase `collectDialogFlow` + `renderDialogFlow` is gone.
1122
+ // The dispatcher awaits `apply`, toasts the result, then clears
1123
+ // (or replaces for multi-step flows). The RPC `apply` accepts the
1124
+ // optional `timeoutMs` knob so account add / refresh can opt into
1125
+ // the 120s RPC timeout without the dialog layer having to know
1126
+ // about it.
1127
+ if (notification.payload.command === 'antigravity-quota') {
1128
+ api.ui.dialog.setSize('xlarge')
1129
+ api.ui.dialog.replace(() => (
1130
+ <QuotaDialogContent
1131
+ api={api}
1132
+ controller={getSidebarController()}
1133
+ sessionId={notification.sessionId}
1134
+ />
1135
+ ))
1136
+ return
1137
+ }
1138
+ openCommandDialog(api, notification.payload, (command, args, options) =>
1139
+ rpcClient.apply(
1140
+ { command, arguments: args, sessionId: notification.sessionId },
1141
+ options,
1142
+ ),
1143
+ )
1144
+ },
1145
+ schedule: (poll, intervalMs) => {
1146
+ setInterval(() => void poll(), intervalMs)
1147
+ },
1148
+ logger,
1149
+ })
1150
+
1151
+ // The host supplies the live theme via `api.theme.current` — the slot
1152
+ // callback's closure captures `api` so the accessor always reads the
1153
+ // current theme. A Solid re-render follows whenever the user switches.
1154
+ const liveTheme = (): TuiThemeCurrent => api.theme.current
1155
+
1156
+ api.slots.register({
1157
+ order: computeEffectiveOrder(prefsRoot, PLUGIN_KEY, DEFAULT_SLOT_ORDER),
1158
+ slots: {
1159
+ sidebar_content: (_context, { session_id: sessionId }) => (
1160
+ <SidebarPanel logger={logger} theme={liveTheme} sessionId={sessionId} />
1161
+ ),
1162
+ },
1163
+ })
1164
+ }
1165
+
1166
+ const plugin: TuiPluginModule & { id: string } = {
1167
+ id: ID,
1168
+ tui,
1169
+ }
1170
+
1171
+ export default plugin