@cortexkit/opencode-antigravity-auth 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/README.md +225 -569
  2. package/dist/cli.js +3790 -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 +18767 -13829
  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/accounts.d.ts +7 -256
  38. package/dist/src/plugin/accounts.d.ts.map +1 -1
  39. package/dist/src/plugin/accounts.js +31 -1367
  40. package/dist/src/plugin/accounts.js.map +1 -1
  41. package/dist/src/plugin/agy-request-metadata.d.ts +1 -1
  42. package/dist/src/plugin/agy-request-metadata.js +1 -1
  43. package/dist/src/plugin/agy-transport.d.ts +1 -1
  44. package/dist/src/plugin/agy-transport.js +1 -1
  45. package/dist/src/plugin/auth-doctor.d.ts +6 -6
  46. package/dist/src/plugin/auth-doctor.d.ts.map +1 -1
  47. package/dist/src/plugin/auth-doctor.js +74 -66
  48. package/dist/src/plugin/auth-doctor.js.map +1 -1
  49. package/dist/src/plugin/auth-drift.d.ts +4 -4
  50. package/dist/src/plugin/auth-drift.d.ts.map +1 -1
  51. package/dist/src/plugin/auth-drift.js +15 -15
  52. package/dist/src/plugin/auth-drift.js.map +1 -1
  53. package/dist/src/plugin/auth-loader.d.ts +35 -0
  54. package/dist/src/plugin/auth-loader.d.ts.map +1 -0
  55. package/dist/src/plugin/auth-loader.js +144 -0
  56. package/dist/src/plugin/auth-loader.js.map +1 -0
  57. package/dist/src/plugin/auth.d.ts +1 -1
  58. package/dist/src/plugin/auth.js +1 -1
  59. package/dist/src/plugin/cache/index.d.ts +1 -1
  60. package/dist/src/plugin/cache/index.d.ts.map +1 -1
  61. package/dist/src/plugin/cache/index.js +1 -1
  62. package/dist/src/plugin/cache/index.js.map +1 -1
  63. package/dist/src/plugin/cache/signature-cache.d.ts +1 -1
  64. package/dist/src/plugin/cache/signature-cache.d.ts.map +1 -1
  65. package/dist/src/plugin/cache/signature-cache.js +23 -24
  66. package/dist/src/plugin/cache/signature-cache.js.map +1 -1
  67. package/dist/src/plugin/cache.d.ts +6 -5
  68. package/dist/src/plugin/cache.d.ts.map +1 -1
  69. package/dist/src/plugin/cache.js +21 -7
  70. package/dist/src/plugin/cache.js.map +1 -1
  71. package/dist/src/plugin/catalog.d.ts +23 -0
  72. package/dist/src/plugin/catalog.d.ts.map +1 -0
  73. package/dist/src/plugin/catalog.js +60 -0
  74. package/dist/src/plugin/catalog.js.map +1 -0
  75. package/dist/src/plugin/cli.d.ts +7 -7
  76. package/dist/src/plugin/cli.d.ts.map +1 -1
  77. package/dist/src/plugin/cli.js +62 -55
  78. package/dist/src/plugin/cli.js.map +1 -1
  79. package/dist/src/plugin/command-data.d.ts +276 -0
  80. package/dist/src/plugin/command-data.d.ts.map +1 -0
  81. package/dist/src/plugin/command-data.js +325 -0
  82. package/dist/src/plugin/command-data.js.map +1 -0
  83. package/dist/src/plugin/commands.d.ts +151 -0
  84. package/dist/src/plugin/commands.d.ts.map +1 -0
  85. package/dist/src/plugin/commands.js +639 -0
  86. package/dist/src/plugin/commands.js.map +1 -0
  87. package/dist/src/plugin/config/index.d.ts +2 -2
  88. package/dist/src/plugin/config/index.d.ts.map +1 -1
  89. package/dist/src/plugin/config/index.js +2 -2
  90. package/dist/src/plugin/config/index.js.map +1 -1
  91. package/dist/src/plugin/config/loader.d.ts +1 -1
  92. package/dist/src/plugin/config/loader.d.ts.map +1 -1
  93. package/dist/src/plugin/config/loader.js +18 -16
  94. package/dist/src/plugin/config/loader.js.map +1 -1
  95. package/dist/src/plugin/config/models.d.ts +2 -2
  96. package/dist/src/plugin/config/models.d.ts.map +1 -1
  97. package/dist/src/plugin/config/models.js +1 -1
  98. package/dist/src/plugin/config/models.js.map +1 -1
  99. package/dist/src/plugin/config/operator-settings-schema.d.ts +29 -0
  100. package/dist/src/plugin/config/operator-settings-schema.d.ts.map +1 -0
  101. package/dist/src/plugin/config/operator-settings-schema.js +28 -0
  102. package/dist/src/plugin/config/operator-settings-schema.js.map +1 -0
  103. package/dist/src/plugin/config/schema.d.ts +21 -2
  104. package/dist/src/plugin/config/schema.d.ts.map +1 -1
  105. package/dist/src/plugin/config/schema.js +85 -25
  106. package/dist/src/plugin/config/schema.js.map +1 -1
  107. package/dist/src/plugin/config/updater.d.ts.map +1 -1
  108. package/dist/src/plugin/config/updater.js +15 -15
  109. package/dist/src/plugin/config/updater.js.map +1 -1
  110. package/dist/src/plugin/config/writer.d.ts +34 -0
  111. package/dist/src/plugin/config/writer.d.ts.map +1 -0
  112. package/dist/src/plugin/config/writer.js +81 -0
  113. package/dist/src/plugin/config/writer.js.map +1 -0
  114. package/dist/src/plugin/core/streaming/index.d.ts +1 -1
  115. package/dist/src/plugin/core/streaming/index.d.ts.map +1 -1
  116. package/dist/src/plugin/core/streaming/index.js +1 -1
  117. package/dist/src/plugin/core/streaming/index.js.map +1 -1
  118. package/dist/src/plugin/core/streaming/transformer.d.ts.map +1 -1
  119. package/dist/src/plugin/core/streaming/transformer.js +85 -44
  120. package/dist/src/plugin/core/streaming/transformer.js.map +1 -1
  121. package/dist/src/plugin/core/streaming/types.d.ts.map +1 -1
  122. package/dist/src/plugin/debug.d.ts +3 -3
  123. package/dist/src/plugin/debug.d.ts.map +1 -1
  124. package/dist/src/plugin/debug.js +85 -47
  125. package/dist/src/plugin/debug.js.map +1 -1
  126. package/dist/src/plugin/dependencies.d.ts +129 -0
  127. package/dist/src/plugin/dependencies.d.ts.map +1 -0
  128. package/dist/src/plugin/dependencies.js +107 -0
  129. package/dist/src/plugin/dependencies.js.map +1 -0
  130. package/dist/src/plugin/errors.d.ts +27 -0
  131. package/dist/src/plugin/errors.d.ts.map +1 -1
  132. package/dist/src/plugin/errors.js +29 -3
  133. package/dist/src/plugin/errors.js.map +1 -1
  134. package/dist/src/plugin/event-handler.d.ts +53 -0
  135. package/dist/src/plugin/event-handler.d.ts.map +1 -0
  136. package/dist/src/plugin/event-handler.js +101 -0
  137. package/dist/src/plugin/event-handler.js.map +1 -0
  138. package/dist/src/plugin/fetch/retry-state.d.ts +61 -0
  139. package/dist/src/plugin/fetch/retry-state.d.ts.map +1 -0
  140. package/dist/src/plugin/fetch/retry-state.js +248 -0
  141. package/dist/src/plugin/fetch/retry-state.js.map +1 -0
  142. package/dist/src/plugin/fetch/warmup.d.ts +30 -0
  143. package/dist/src/plugin/fetch/warmup.d.ts.map +1 -0
  144. package/dist/src/plugin/fetch/warmup.js +76 -0
  145. package/dist/src/plugin/fetch/warmup.js.map +1 -0
  146. package/dist/src/plugin/fetch-interceptor.d.ts +59 -0
  147. package/dist/src/plugin/fetch-interceptor.d.ts.map +1 -0
  148. package/dist/src/plugin/fetch-interceptor.js +1387 -0
  149. package/dist/src/plugin/fetch-interceptor.js.map +1 -0
  150. package/dist/src/plugin/fetch-routing.d.ts +26 -0
  151. package/dist/src/plugin/fetch-routing.d.ts.map +1 -0
  152. package/dist/src/plugin/fetch-routing.js +99 -0
  153. package/dist/src/plugin/fetch-routing.js.map +1 -0
  154. package/dist/src/plugin/fingerprint.d.ts +1 -1
  155. package/dist/src/plugin/fingerprint.js +1 -1
  156. package/dist/src/plugin/gemini-dump.d.ts +5 -5
  157. package/dist/src/plugin/gemini-dump.d.ts.map +1 -1
  158. package/dist/src/plugin/gemini-dump.js +109 -55
  159. package/dist/src/plugin/gemini-dump.js.map +1 -1
  160. package/dist/src/plugin/google-search-tool.d.ts +9 -0
  161. package/dist/src/plugin/google-search-tool.d.ts.map +1 -0
  162. package/dist/src/plugin/google-search-tool.js +56 -0
  163. package/dist/src/plugin/google-search-tool.js.map +1 -0
  164. package/dist/src/plugin/image-saver.d.ts.map +1 -1
  165. package/dist/src/plugin/image-saver.js +16 -16
  166. package/dist/src/plugin/image-saver.js.map +1 -1
  167. package/dist/src/plugin/index.d.ts +23 -0
  168. package/dist/src/plugin/index.d.ts.map +1 -0
  169. package/dist/src/plugin/index.js +308 -0
  170. package/dist/src/plugin/index.js.map +1 -0
  171. package/dist/src/plugin/killswitch.d.ts +94 -0
  172. package/dist/src/plugin/killswitch.d.ts.map +1 -0
  173. package/dist/src/plugin/killswitch.js +194 -0
  174. package/dist/src/plugin/killswitch.js.map +1 -0
  175. package/dist/src/plugin/lifecycle.d.ts +40 -0
  176. package/dist/src/plugin/lifecycle.d.ts.map +1 -0
  177. package/dist/src/plugin/lifecycle.js +73 -0
  178. package/dist/src/plugin/lifecycle.js.map +1 -0
  179. package/dist/src/plugin/logger.d.ts +10 -1
  180. package/dist/src/plugin/logger.d.ts.map +1 -1
  181. package/dist/src/plugin/logger.js +38 -12
  182. package/dist/src/plugin/logger.js.map +1 -1
  183. package/dist/src/plugin/logging-utils.d.ts +35 -1
  184. package/dist/src/plugin/logging-utils.d.ts.map +1 -1
  185. package/dist/src/plugin/logging-utils.js +98 -15
  186. package/dist/src/plugin/logging-utils.js.map +1 -1
  187. package/dist/src/plugin/model-registry.d.ts +1 -1
  188. package/dist/src/plugin/model-registry.js +1 -1
  189. package/dist/src/plugin/oauth-login.d.ts +22 -0
  190. package/dist/src/plugin/oauth-login.d.ts.map +1 -0
  191. package/dist/src/plugin/oauth-login.js +37 -0
  192. package/dist/src/plugin/oauth-login.js.map +1 -0
  193. package/dist/src/plugin/oauth-methods.d.ts +42 -0
  194. package/dist/src/plugin/oauth-methods.d.ts.map +1 -0
  195. package/dist/src/plugin/oauth-methods.js +1284 -0
  196. package/dist/src/plugin/oauth-methods.js.map +1 -0
  197. package/dist/src/plugin/operator-settings.d.ts +40 -0
  198. package/dist/src/plugin/operator-settings.d.ts.map +1 -0
  199. package/dist/src/plugin/operator-settings.js +148 -0
  200. package/dist/src/plugin/operator-settings.js.map +1 -0
  201. package/dist/src/plugin/persist-account-pool.d.ts +37 -0
  202. package/dist/src/plugin/persist-account-pool.d.ts.map +1 -0
  203. package/dist/src/plugin/persist-account-pool.js +142 -0
  204. package/dist/src/plugin/persist-account-pool.js.map +1 -0
  205. package/dist/src/plugin/project.d.ts +1 -1
  206. package/dist/src/plugin/project.js +1 -1
  207. package/dist/src/plugin/prompt-context.d.ts.map +1 -1
  208. package/dist/src/plugin/prompt-context.js +11 -11
  209. package/dist/src/plugin/prompt-context.js.map +1 -1
  210. package/dist/src/plugin/quota.d.ts +97 -41
  211. package/dist/src/plugin/quota.d.ts.map +1 -1
  212. package/dist/src/plugin/quota.js +273 -285
  213. package/dist/src/plugin/quota.js.map +1 -1
  214. package/dist/src/plugin/recovery/constants.d.ts.map +1 -1
  215. package/dist/src/plugin/recovery/constants.js +24 -15
  216. package/dist/src/plugin/recovery/constants.js.map +1 -1
  217. package/dist/src/plugin/recovery/index.d.ts +3 -3
  218. package/dist/src/plugin/recovery/index.d.ts.map +1 -1
  219. package/dist/src/plugin/recovery/index.js +3 -3
  220. package/dist/src/plugin/recovery/index.js.map +1 -1
  221. package/dist/src/plugin/recovery/storage.d.ts +1 -1
  222. package/dist/src/plugin/recovery/storage.d.ts.map +1 -1
  223. package/dist/src/plugin/recovery/storage.js +31 -39
  224. package/dist/src/plugin/recovery/storage.js.map +1 -1
  225. package/dist/src/plugin/recovery/types.d.ts +12 -12
  226. package/dist/src/plugin/recovery/types.d.ts.map +1 -1
  227. package/dist/src/plugin/recovery.d.ts +3 -3
  228. package/dist/src/plugin/recovery.d.ts.map +1 -1
  229. package/dist/src/plugin/recovery.js +62 -58
  230. package/dist/src/plugin/recovery.js.map +1 -1
  231. package/dist/src/plugin/refresh-queue.d.ts +5 -2
  232. package/dist/src/plugin/refresh-queue.d.ts.map +1 -1
  233. package/dist/src/plugin/refresh-queue.js +37 -15
  234. package/dist/src/plugin/refresh-queue.js.map +1 -1
  235. package/dist/src/plugin/request-helpers.d.ts +1 -1
  236. package/dist/src/plugin/request-helpers.d.ts.map +1 -1
  237. package/dist/src/plugin/request-helpers.js +451 -352
  238. package/dist/src/plugin/request-helpers.js.map +1 -1
  239. package/dist/src/plugin/request.d.ts +7 -7
  240. package/dist/src/plugin/request.d.ts.map +1 -1
  241. package/dist/src/plugin/request.js +622 -384
  242. package/dist/src/plugin/request.js.map +1 -1
  243. package/dist/src/plugin/rotation.d.ts +1 -168
  244. package/dist/src/plugin/rotation.d.ts.map +1 -1
  245. package/dist/src/plugin/rotation.js +1 -327
  246. package/dist/src/plugin/rotation.js.map +1 -1
  247. package/dist/src/plugin/search.d.ts.map +1 -1
  248. package/dist/src/plugin/search.js +39 -35
  249. package/dist/src/plugin/search.js.map +1 -1
  250. package/dist/src/plugin/server.d.ts +1 -1
  251. package/dist/src/plugin/server.d.ts.map +1 -1
  252. package/dist/src/plugin/server.js +42 -37
  253. package/dist/src/plugin/server.js.map +1 -1
  254. package/dist/src/plugin/session-context.d.ts +2 -1
  255. package/dist/src/plugin/session-context.d.ts.map +1 -1
  256. package/dist/src/plugin/session-context.js +11 -7
  257. package/dist/src/plugin/session-context.js.map +1 -1
  258. package/dist/src/plugin/storage.d.ts +59 -120
  259. package/dist/src/plugin/storage.d.ts.map +1 -1
  260. package/dist/src/plugin/storage.js +140 -485
  261. package/dist/src/plugin/storage.js.map +1 -1
  262. package/dist/src/plugin/stores/signature-store.d.ts.map +1 -1
  263. package/dist/src/plugin/stores/signature-store.js.map +1 -1
  264. package/dist/src/plugin/thinking-recovery.d.ts.map +1 -1
  265. package/dist/src/plugin/thinking-recovery.js +35 -33
  266. package/dist/src/plugin/thinking-recovery.js.map +1 -1
  267. package/dist/src/plugin/token.d.ts +2 -2
  268. package/dist/src/plugin/token.d.ts.map +1 -1
  269. package/dist/src/plugin/token.js +30 -21
  270. package/dist/src/plugin/token.js.map +1 -1
  271. package/dist/src/plugin/transform/cross-model-sanitizer.d.ts +1 -1
  272. package/dist/src/plugin/transform/cross-model-sanitizer.js +1 -1
  273. package/dist/src/plugin/transform/index.d.ts +1 -1
  274. package/dist/src/plugin/transform/index.js +1 -1
  275. package/dist/src/plugin/transform/model-resolver.d.ts +1 -1
  276. package/dist/src/plugin/transform/model-resolver.js +1 -1
  277. package/dist/src/plugin/transform/types.d.ts +1 -1
  278. package/dist/src/plugin/transform/types.js +1 -1
  279. package/dist/src/plugin/types.d.ts +25 -69
  280. package/dist/src/plugin/types.d.ts.map +1 -1
  281. package/dist/src/plugin/ui/ansi.d.ts.map +1 -1
  282. package/dist/src/plugin/ui/ansi.js.map +1 -1
  283. package/dist/src/plugin/ui/auth-menu.d.ts +1 -1
  284. package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
  285. package/dist/src/plugin/ui/auth-menu.js +82 -31
  286. package/dist/src/plugin/ui/auth-menu.js.map +1 -1
  287. package/dist/src/plugin/ui/confirm.d.ts.map +1 -1
  288. package/dist/src/plugin/ui/confirm.js.map +1 -1
  289. package/dist/src/plugin/ui/model-status.d.ts +3 -3
  290. package/dist/src/plugin/ui/model-status.d.ts.map +1 -1
  291. package/dist/src/plugin/ui/model-status.js +3 -3
  292. package/dist/src/plugin/ui/model-status.js.map +1 -1
  293. package/dist/src/plugin/ui/quota-status.d.ts +4 -4
  294. package/dist/src/plugin/ui/quota-status.d.ts.map +1 -1
  295. package/dist/src/plugin/ui/quota-status.js +51 -49
  296. package/dist/src/plugin/ui/quota-status.js.map +1 -1
  297. package/dist/src/plugin/ui/select.d.ts.map +1 -1
  298. package/dist/src/plugin/ui/select.js +22 -12
  299. package/dist/src/plugin/ui/select.js.map +1 -1
  300. package/dist/src/plugin/version.d.ts +1 -1
  301. package/dist/src/plugin/version.js +1 -1
  302. package/dist/src/rpc/notifications.d.ts +6 -0
  303. package/dist/src/rpc/notifications.d.ts.map +1 -0
  304. package/dist/src/rpc/notifications.js +46 -0
  305. package/dist/src/rpc/notifications.js.map +1 -0
  306. package/dist/src/rpc/port-file.d.ts +13 -0
  307. package/dist/src/rpc/port-file.d.ts.map +1 -0
  308. package/dist/src/rpc/port-file.js +109 -0
  309. package/dist/src/rpc/port-file.js.map +1 -0
  310. package/dist/src/rpc/protocol.d.ts +22 -0
  311. package/dist/src/rpc/protocol.d.ts.map +1 -0
  312. package/dist/src/rpc/protocol.js +1 -0
  313. package/dist/src/rpc/protocol.js.map +1 -0
  314. package/dist/src/rpc/rpc-client.d.ts +10 -0
  315. package/dist/src/rpc/rpc-client.d.ts.map +1 -0
  316. package/dist/src/rpc/rpc-client.js +58 -0
  317. package/dist/src/rpc/rpc-client.js.map +1 -0
  318. package/dist/src/rpc/rpc-dir.d.ts +4 -0
  319. package/dist/src/rpc/rpc-dir.d.ts.map +1 -0
  320. package/dist/src/rpc/rpc-dir.js +19 -0
  321. package/dist/src/rpc/rpc-dir.js.map +1 -0
  322. package/dist/src/rpc/rpc-server.d.ts +13 -0
  323. package/dist/src/rpc/rpc-server.d.ts.map +1 -0
  324. package/dist/src/rpc/rpc-server.js +231 -0
  325. package/dist/src/rpc/rpc-server.js.map +1 -0
  326. package/dist/src/sidebar-state.d.ts +240 -0
  327. package/dist/src/sidebar-state.d.ts.map +1 -0
  328. package/dist/src/sidebar-state.js +520 -0
  329. package/dist/src/sidebar-state.js.map +1 -0
  330. package/dist/src/tui/command-dialogs.d.ts +61 -0
  331. package/dist/src/tui/command-dialogs.d.ts.map +1 -0
  332. package/dist/src/tui/command-dialogs.js +497 -0
  333. package/dist/src/tui/command-dialogs.js.map +1 -0
  334. package/dist/src/tui/file-logger.d.ts +41 -0
  335. package/dist/src/tui/file-logger.d.ts.map +1 -0
  336. package/dist/src/tui/file-logger.js +114 -0
  337. package/dist/src/tui/file-logger.js.map +1 -0
  338. package/dist/src/tui-preferences.d.ts +40 -0
  339. package/dist/src/tui-preferences.d.ts.map +1 -0
  340. package/dist/src/tui-preferences.js +328 -0
  341. package/dist/src/tui-preferences.js.map +1 -0
  342. package/dist/src/tui.d.ts +68 -0
  343. package/dist/src/tui.d.ts.map +1 -0
  344. package/dist/src/tui.js +662 -0
  345. package/dist/src/tui.js.map +1 -0
  346. package/package.json +46 -22
  347. package/src/plugin/command-data.ts +651 -0
  348. package/src/rpc/port-file.ts +143 -0
  349. package/src/rpc/protocol.ts +31 -0
  350. package/src/rpc/rpc-client.ts +102 -0
  351. package/src/rpc/rpc-dir.ts +24 -0
  352. package/src/sidebar-state.ts +717 -0
  353. package/src/tui/command-dialogs.tsx +699 -0
  354. package/src/tui/entry.mjs +74 -0
  355. package/src/tui/file-logger.ts +145 -0
  356. package/src/tui-compiled/plugin/command-data.ts +651 -0
  357. package/src/tui-compiled/rpc/port-file.ts +143 -0
  358. package/src/tui-compiled/rpc/protocol.ts +31 -0
  359. package/src/tui-compiled/rpc/rpc-client.ts +102 -0
  360. package/src/tui-compiled/rpc/rpc-dir.ts +24 -0
  361. package/src/tui-compiled/sidebar-state.ts +717 -0
  362. package/src/tui-compiled/tui/command-dialogs.tsx +659 -0
  363. package/src/tui-compiled/tui/file-logger.ts +145 -0
  364. package/src/tui-compiled/tui-preferences.ts +416 -0
  365. package/src/tui-compiled/tui.tsx +1150 -0
  366. package/src/tui-preferences.ts +416 -0
  367. package/src/tui.tsx +1117 -0
  368. package/dist/src/plugin.d.ts +0 -48
  369. package/dist/src/plugin.d.ts.map +0 -1
  370. package/dist/src/plugin.js +0 -3311
  371. package/dist/src/plugin.js.map +0 -1
@@ -0,0 +1,651 @@
1
+ /**
2
+ * Privacy-safe data service for the data-first slash-command dialogs.
3
+ *
4
+ * `/antigravity-quota` (this task) and the future `/antigravity-account`
5
+ * dialogs (Tasks 10-11) read from a single shared service so they
6
+ * never touch raw account storage, never see the email PII field, and
7
+ * never run quota network I/O during the dialog's open path — the
8
+ * Refresh action is the only path that performs a live fetch.
9
+ *
10
+ * Why this is a separate module:
11
+ *
12
+ * - `commands.ts` owns the slash-command orchestration; mixing the row
13
+ * projection + quota refresh logic in there would balloon the surface
14
+ * for what is fundamentally a small read/refresh service.
15
+ * - The row type (`CommandAccountRow`) is the projection that crosses
16
+ * the PII firewall into the dialog payload. Defining it here (next
17
+ * to the code that constructs it) keeps the firewall reviewable.
18
+ * - Tests can pin the read-vs-refresh boundary, the email redaction, and
19
+ * the refresh-token-keyed persistence in one file rather than chasing
20
+ * them across the dispatcher + RPC + apply layers.
21
+ *
22
+ * Cache-only opening contract (Task 9 operator requirement):
23
+ *
24
+ * `listAccounts()` is a pure read of the live AccountManager view
25
+ * the auth-loader materialized at session start. It performs
26
+ * ZERO quota manager calls — opening the dialog must be instant even
27
+ * when the network is unreachable, and quota refresh must remain an
28
+ * explicit user-driven action so the cached percentages never quietly
29
+ * rewrite themselves behind the user's back.
30
+ *
31
+ * Refresh-token-keyed persistence (Task 9 plan trap):
32
+ *
33
+ * `refreshQuota()` runs the shared quota manager across every enabled
34
+ * account and folds the results back into the live AccountManager +
35
+ * storage. Concurrent OAuth can renumber the flat `accounts[]` array
36
+ * between read and write, so we re-read under the lock and key the
37
+ * update by `refreshToken` (the canonical identity) rather than the
38
+ * array index. This way a successful OAuth add that lands between
39
+ * the read and the write cannot cause the refresh to overwrite the
40
+ * wrong account.
41
+ */
42
+
43
+ import {
44
+ buildSidebarMachineStateFromAccounts,
45
+ type SidebarAccountRedactionInput,
46
+ setSidebarMachineState,
47
+ } from '../sidebar-state'
48
+
49
+ /**
50
+ * Local copies of the few core types the data service touches.
51
+ *
52
+ * The shipped TUI tree cannot depend on the `@cortexkit/antigravity-auth-core`
53
+ * barrel (only subpath imports like `./file-lock` are allowed) — the
54
+ * compiled `command-data.ts` is copied verbatim into `tui-compiled/`,
55
+ * and the type-import would otherwise leak the full core surface into
56
+ * the dialog render path. Declaring the structural shape locally keeps
57
+ * the compiled tree free of the barrel while preserving duck-typed
58
+ * compatibility with the production quota manager.
59
+ */
60
+ type CommandDataAccountMetadata = {
61
+ email?: string
62
+ refreshToken: string
63
+ projectId?: string
64
+ managedProjectId?: string
65
+ addedAt: number
66
+ lastUsed: number
67
+ enabled?: boolean
68
+ label?: string
69
+ cachedQuota?: Partial<
70
+ Record<CommandDataQuotaGroup, CommandDataQuotaGroupSummary>
71
+ >
72
+ cachedQuotaUpdatedAt?: number
73
+ accountIneligible?: boolean
74
+ }
75
+
76
+ type CommandDataQuotaGroup =
77
+ | 'claude'
78
+ | 'gemini-pro'
79
+ | 'gemini-flash'
80
+ | 'gpt-oss'
81
+
82
+ type CommandDataQuotaGroupSummary = {
83
+ remainingFraction?: number
84
+ resetTime?: string
85
+ modelCount: number
86
+ }
87
+
88
+ type CommandDataAccountQuotaResult = {
89
+ index: number
90
+ status: 'ok' | 'disabled' | 'error'
91
+ error?: string
92
+ disabled?: boolean
93
+ quota?: {
94
+ groups: Partial<Record<CommandDataQuotaGroup, CommandDataQuotaGroupSummary>>
95
+ perModel?: Array<{
96
+ modelId: string
97
+ displayName?: string
98
+ group: CommandDataQuotaGroup | null
99
+ remainingFraction: number
100
+ resetTime?: string
101
+ }>
102
+ modelCount: number
103
+ error?: string
104
+ }
105
+ updatedAccount?: CommandDataAccountMetadata
106
+ }
107
+
108
+ type CommandDataAccountStorage = {
109
+ version: 4
110
+ activeIndex: number
111
+ activeIndexByFamily?: { claude?: number; gemini?: number }
112
+ accounts: CommandDataAccountMetadata[]
113
+ }
114
+
115
+ /**
116
+ * Privacy-safe per-account row shown in `/antigravity-*` data-first
117
+ * dialogs. Carries the cached quota percentages and the labels the
118
+ * dialog needs, but NEVER the email — the email stays in private
119
+ * account storage and is invisible to the sidebar and the dialog.
120
+ *
121
+ * `index` is the position in the live account array at the time of
122
+ * projection. It is informational only and is NOT a stable identity
123
+ * across refreshes — concurrent OAuth can renumber the array between
124
+ * the dialog opening and a refresh.
125
+ */
126
+ export interface CommandAccountRow {
127
+ /** Stable identity for dialog keying (`acct-<index>`). */
128
+ id: string
129
+ /** Position in the live account array at projection time. */
130
+ index: number
131
+ /** Privacy-safe ordinal display label (for example, `Account 1`). */
132
+ label: string
133
+ enabled: boolean
134
+ /** `true` when this row matches the harness-active account. */
135
+ current: boolean
136
+ quota: Array<{
137
+ key: 'claude' | 'gemini-pro' | 'gemini-flash' | 'gpt-oss'
138
+ label: string
139
+ remainingPercent: number | null
140
+ resetAt?: number
141
+ }>
142
+ }
143
+
144
+ /**
145
+ * Quota display label for each supported quota group. Kept here so the
146
+ * dialog and the quota manager agree on the same vocabulary.
147
+ */
148
+ const QUOTA_GROUP_LABELS: Record<
149
+ CommandAccountRow['quota'][number]['key'],
150
+ string
151
+ > = {
152
+ claude: 'Claude',
153
+ 'gemini-pro': 'Gemini Pro',
154
+ 'gemini-flash': 'Gemini Flash',
155
+ 'gpt-oss': 'GPT-OSS',
156
+ }
157
+
158
+ const SUPPORTED_QUOTA_KEYS = [
159
+ 'claude',
160
+ 'gemini-pro',
161
+ 'gemini-flash',
162
+ 'gpt-oss',
163
+ ] as const satisfies readonly CommandAccountRow['quota'][number]['key'][]
164
+
165
+ interface LiveAccountSnapshot {
166
+ index: number
167
+ refreshToken: string
168
+ label?: string
169
+ enabled: boolean
170
+ active: boolean
171
+ cachedQuota?: Partial<
172
+ Record<CommandDataQuotaGroup, CommandDataQuotaGroupSummary>
173
+ >
174
+ cachedQuotaUpdatedAt?: number
175
+ accountIneligible?: boolean
176
+ }
177
+
178
+ function toCommandAccountRow(entry: LiveAccountSnapshot): CommandAccountRow {
179
+ const cached = entry.cachedQuota
180
+ const quota: CommandAccountRow['quota'] = []
181
+ for (const key of SUPPORTED_QUOTA_KEYS) {
182
+ const cachedEntry = cached?.[key]
183
+ if (!cachedEntry) continue
184
+ const fraction = cachedEntry.remainingFraction
185
+ const remainingPercent =
186
+ typeof fraction === 'number' && Number.isFinite(fraction)
187
+ ? Math.round(fraction * 100)
188
+ : null
189
+ let resetAt: number | undefined
190
+ if (
191
+ typeof cachedEntry.resetTime === 'string' &&
192
+ cachedEntry.resetTime.length > 0
193
+ ) {
194
+ const parsed = Date.parse(cachedEntry.resetTime)
195
+ if (Number.isFinite(parsed)) resetAt = parsed
196
+ }
197
+ quota.push({
198
+ key,
199
+ label: QUOTA_GROUP_LABELS[key],
200
+ remainingPercent,
201
+ resetAt,
202
+ })
203
+ }
204
+ const label = `Account ${entry.index + 1}`
205
+ return {
206
+ id: `acct-${entry.index}`,
207
+ index: entry.index,
208
+ label,
209
+ enabled: entry.enabled,
210
+ current: entry.active,
211
+ quota,
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Live AccountManager view the data service needs.
217
+ *
218
+ * - `getAccounts()` returns the in-memory snapshot — used for cache-only
219
+ * reads during dialog open and for re-reading the post-mutation state.
220
+ * `getAccountsForQuotaCheck()` returns the freshest `AccountMetadataV3`
221
+ * (the canonical shape the quota manager expects).
222
+ * - `updateQuotaCache(index, groups)` + `requestSaveToDisk()` fold the
223
+ * refreshed quota back into the live view and persist it. The service
224
+ * calls them together after the network fetch resolves.
225
+ * - `setAccountEnabled`, `setAccountCurrent`, `removeAccountByIndex`
226
+ * mirror the CLI menu's mutation primitives — the dialog actions
227
+ * (Task 10) reuse them so the TUI never invents its own mutation
228
+ * logic. Each method returns `true` when the live view changed.
229
+ * - `getRefreshTokenAt(index)` lets the service identify the canonical
230
+ * account identity before it reaches the locked storage mutator; a
231
+ * concurrent OAuth could renumber the flat array between the dialog
232
+ * opening and the apply, so keying by refresh token is mandatory.
233
+ * - `flushSaveToDisk()` drains the AccountManager's debounced save so a
234
+ * dialog-triggered mutation lands on disk before the dialog's response
235
+ * returns. Without it, the dialog could toast "Account removed" while
236
+ * the file still carries the old pool.
237
+ * - `activeIndex()` returns the position the harness considers active;
238
+ * the service uses it to mark the matching row as `current: true`.
239
+ */
240
+ export interface CommandDataAccountManagerView {
241
+ getAccounts(): LiveAccountSnapshot[]
242
+ getAccountsForQuotaCheck(): CommandDataAccountMetadata[]
243
+ updateQuotaCache(
244
+ index: number,
245
+ groups: Partial<
246
+ Record<CommandDataQuotaGroup, CommandDataQuotaGroupSummary>
247
+ >,
248
+ ): void
249
+ requestSaveToDisk(): void
250
+ flushSaveToDisk(): Promise<void>
251
+ activeIndex(): number
252
+ /** Enable/disable the account at `index`. Returns true when it changed. */
253
+ setAccountEnabled(index: number, enabled: boolean): boolean
254
+ /** Pin `index` as the active account for every family the dialog cares about. */
255
+ setAccountCurrent(index: number): boolean
256
+ /** Remove the account at `index` from the live view. Returns true when it changed. */
257
+ removeAccountByIndex(index: number): boolean
258
+ /** Canonical refresh token for the account at `index`, or undefined. */
259
+ getRefreshTokenAt(index: number): string | undefined
260
+ }
261
+
262
+ /**
263
+ * Storage adapter the data service uses for re-read-under-lock writes.
264
+ * `mutate` runs the supplied callback against the latest snapshot under
265
+ * the file lock — the callback receives the current storage and may
266
+ * return a replacement; returning the input unchanged is a no-op.
267
+ *
268
+ * The promise resolves with the (possibly mutated) storage snapshot so
269
+ * the data service can await it. A rejected promise signals a failed
270
+ * write — `AccountStorageUnreadableError`, lock contention, or any
271
+ * other I/O failure — which the data service surfaces to the dialog
272
+ * as a friendly error toast.
273
+ */
274
+ export interface CommandDataStorage {
275
+ mutate(
276
+ mutator: (
277
+ current: CommandDataAccountStorage,
278
+ ) =>
279
+ | CommandDataAccountStorage
280
+ | undefined
281
+ | Promise<CommandDataAccountStorage | undefined>,
282
+ ): Promise<CommandDataAccountStorage | undefined> | undefined
283
+ }
284
+
285
+ /**
286
+ * Options for `createCommandDataService`. Each field is required so
287
+ * production wiring is explicit — a missing dependency is a startup
288
+ * error, not a silent no-op at dialog-open time.
289
+ */
290
+ export interface CommandDataServiceOptions {
291
+ accountManagerView: CommandDataAccountManagerView
292
+ quotaManager: {
293
+ refreshAccounts(
294
+ accounts: CommandDataAccountMetadata[],
295
+ options: {
296
+ indexFor?: (account: CommandDataAccountMetadata) => number
297
+ force?: boolean
298
+ },
299
+ ): Promise<CommandDataAccountQuotaResult[]>
300
+ }
301
+ /** Path to the label-only sidebar state file. */
302
+ sidebarStateFile: string
303
+ /**
304
+ * Optional storage adapter. When provided, the service persists the
305
+ * refreshed quota to disk via the lock-held mutator (best-effort —
306
+ * a lock contention never breaks the dialog response). When omitted,
307
+ * the service still folds results into the live AccountManager view;
308
+ * the auth-loader is responsible for the on-disk write.
309
+ */
310
+ storage?: CommandDataStorage
311
+ /** Clock for `cachedQuotaUpdatedAt`. Tests inject a fixed clock. */
312
+ now?: () => number
313
+ }
314
+
315
+ /**
316
+ * Public surface of the command-data service. Each method is the
317
+ * smallest possible projection so the dialog layer never has to know
318
+ * how the underlying quota manager or storage adapter is wired.
319
+ */
320
+ export interface CommandDataService {
321
+ /**
322
+ * Cache-only snapshot. Performs zero quota manager calls — safe to
323
+ * call as part of the dialog's open path.
324
+ */
325
+ listAccounts(): Promise<CommandAccountRow[]>
326
+ /**
327
+ * Force-refresh quota through the shared quota manager, persist by
328
+ * refresh token, bump `cachedQuotaUpdatedAt`, and push a label-only
329
+ * sidebar snapshot. Returns the freshly persisted rows so the
330
+ * dialog can re-render in place.
331
+ */
332
+ refreshQuota(): Promise<CommandAccountRow[]>
333
+ /**
334
+ * Pin `index` as the active account for every family the dialog
335
+ * tracks (claude + gemini). Mutates the live AccountManager AND the
336
+ * locked storage so the new active index survives a restart. Returns
337
+ * the freshly projected rows so the dialog can re-render in place.
338
+ * Returns `null` when the index is out of range or the account has
339
+ * no refresh token — the dialog surfaces the null as a toast.
340
+ */
341
+ setCurrentAccount(index: number): Promise<CommandAccountRow[] | null>
342
+ /**
343
+ * Flip the `enabled` flag on the account at `index`. Mirrors the CLI
344
+ * menu's "manage" toggle so the on-disk state matches what the CLI
345
+ * would produce. Returns the freshly projected rows (or `null` when
346
+ * the index is invalid or the account is ineligible).
347
+ */
348
+ toggleAccountEnabled(index: number): Promise<CommandAccountRow[] | null>
349
+ /**
350
+ * Remove the account at `index` from both the live view and the
351
+ * locked storage. Removal renumbers the flat `accounts[]` array so
352
+ * the returned rows use the freshest indices; callers MUST re-key
353
+ * their transient dialog IDs (`acct-${index}`) by the row's `id`
354
+ * field after this method returns.
355
+ *
356
+ * Returns `null` when the index is out of range — the dialog
357
+ * surfaces the null as a toast.
358
+ */
359
+ removeAccount(index: number): Promise<CommandAccountRow[] | null>
360
+ }
361
+
362
+ /**
363
+ * Build the data service.
364
+ *
365
+ * The factory form (instead of a module-level singleton) keeps the
366
+ * service unit-testable: each test constructs its own dependencies
367
+ * (storage stub, quota manager stub, fixed clock) without touching
368
+ * the production quota path.
369
+ */
370
+ export function createCommandDataService(
371
+ options: CommandDataServiceOptions,
372
+ ): CommandDataService {
373
+ const {
374
+ accountManagerView,
375
+ quotaManager,
376
+ sidebarStateFile,
377
+ storage,
378
+ now = () => Date.now(),
379
+ } = options
380
+
381
+ const projectRows = (): CommandAccountRow[] =>
382
+ accountManagerView.getAccounts().map(toCommandAccountRow)
383
+
384
+ const writeSidebar = (rows: CommandAccountRow[]): void => {
385
+ const accounts: SidebarAccountRedactionInput[] = rows.map((row) => {
386
+ const claude = row.quota.find((q) => q.key === 'claude')
387
+ const geminiPro = row.quota.find((q) => q.key === 'gemini-pro')
388
+ const geminiFlash = row.quota.find((q) => q.key === 'gemini-flash')
389
+ const gptOss = row.quota.find((q) => q.key === 'gpt-oss')
390
+ const toFraction = (
391
+ q: { remainingPercent: number | null } | undefined,
392
+ ): { remainingFraction?: number; resetTime?: string } | undefined => {
393
+ if (!q || q.remainingPercent == null) return undefined
394
+ return { remainingFraction: q.remainingPercent / 100 }
395
+ }
396
+ return {
397
+ index: row.index,
398
+ label: row.label,
399
+ enabled: row.enabled,
400
+ current: row.current,
401
+ cachedQuota: {
402
+ claude: toFraction(claude),
403
+ 'gemini-pro': toFraction(geminiPro),
404
+ 'gemini-flash': toFraction(geminiFlash),
405
+ 'gpt-oss': toFraction(gptOss),
406
+ },
407
+ }
408
+ })
409
+ // Fire-and-forget — the sidebar writer is fenced by its own queue,
410
+ // so a transient lock contention cannot block the dialog response.
411
+ void setSidebarMachineState(
412
+ buildSidebarMachineStateFromAccounts(accounts, { checkedAt: now() }),
413
+ { stateFile: sidebarStateFile },
414
+ ).catch(() => {
415
+ // Lock contention is the only realistic failure mode; the next
416
+ // periodic writer will catch up.
417
+ })
418
+ }
419
+
420
+ return {
421
+ async listAccounts() {
422
+ return projectRows()
423
+ },
424
+
425
+ async refreshQuota() {
426
+ const accountsForQuota = accountManagerView.getAccountsForQuotaCheck()
427
+ if (accountsForQuota.length === 0) {
428
+ // Empty pool: still push a clean sidebar so the TUI drops any
429
+ // stale snapshot left over from a previous session.
430
+ writeSidebar([])
431
+ return []
432
+ }
433
+
434
+ const results = await quotaManager.refreshAccounts(accountsForQuota, {
435
+ indexFor: (account) => accountsForQuota.indexOf(account),
436
+ force: true,
437
+ })
438
+
439
+ const refreshedAt = now()
440
+ const updates: Array<{
441
+ refreshToken: string
442
+ groups?: Partial<
443
+ Record<CommandDataQuotaGroup, CommandDataQuotaGroupSummary>
444
+ >
445
+ }> = []
446
+ for (const result of results) {
447
+ const refreshToken =
448
+ result.updatedAccount?.refreshToken ??
449
+ accountsForQuota[result.index]?.refreshToken
450
+ if (!refreshToken) continue
451
+ const groups =
452
+ result.status === 'ok' && result.quota?.groups
453
+ ? result.quota.groups
454
+ : undefined
455
+ updates.push({ refreshToken, groups })
456
+ }
457
+
458
+ // Resolve live indexes only after the network request. Numeric indexes
459
+ // from the quota result refer to the original input array and may now
460
+ // identify a different account after a concurrent add/remove.
461
+ const liveIndexByRefreshToken = new Map<string, number>()
462
+ for (const entry of accountManagerView.getAccounts()) {
463
+ liveIndexByRefreshToken.set(entry.refreshToken, entry.index)
464
+ }
465
+ let liveQuotaChanged = false
466
+ for (const update of updates) {
467
+ const liveIndex = liveIndexByRefreshToken.get(update.refreshToken)
468
+ if (liveIndex === undefined || !update.groups) continue
469
+ accountManagerView.updateQuotaCache(liveIndex, update.groups)
470
+ liveQuotaChanged = true
471
+ }
472
+ if (liveQuotaChanged) accountManagerView.requestSaveToDisk()
473
+
474
+ // Persist by canonical refresh token against the latest locked snapshot.
475
+ // A removed account is skipped rather than falling back to its old index.
476
+ if (storage) {
477
+ const updateByRefreshToken = new Map(
478
+ updates.map((update) => [update.refreshToken, update]),
479
+ )
480
+ const writeResult = storage.mutate((current) => ({
481
+ ...current,
482
+ accounts: current.accounts.map((entry) => {
483
+ const update = updateByRefreshToken.get(entry.refreshToken)
484
+ if (!update) return entry
485
+ if (update.groups) {
486
+ return {
487
+ ...entry,
488
+ cachedQuota: update.groups,
489
+ cachedQuotaUpdatedAt: refreshedAt,
490
+ }
491
+ }
492
+ // Error result keeps the previous cached percentage and only
493
+ // records that a refresh was attempted.
494
+ return {
495
+ ...entry,
496
+ cachedQuotaUpdatedAt: refreshedAt,
497
+ }
498
+ }),
499
+ }))
500
+ await Promise.resolve(writeResult).catch(() => {
501
+ // The live AccountManager already carries successful refreshes; its
502
+ // next save can reconcile a transient storage-lock failure.
503
+ })
504
+ }
505
+
506
+ // Re-read the live view post-mutate so the rows reflect the
507
+ // freshly persisted percentages.
508
+ const rows = projectRows()
509
+ writeSidebar(rows)
510
+ return rows
511
+ },
512
+
513
+ async setCurrentAccount(index) {
514
+ return mutateLiveAndStorage({ action: 'setCurrent', index })
515
+ },
516
+
517
+ async toggleAccountEnabled(index) {
518
+ return mutateLiveAndStorage({ action: 'toggleEnabled', index })
519
+ },
520
+
521
+ async removeAccount(index) {
522
+ return mutateLiveAndStorage({ action: 'remove', index })
523
+ },
524
+ }
525
+
526
+ async function mutateLiveAndStorage(args: {
527
+ action: 'setCurrent' | 'toggleEnabled' | 'remove'
528
+ index: number
529
+ }): Promise<CommandAccountRow[] | null> {
530
+ const { index, action } = args
531
+ const target = accountManagerView.getAccounts()[index]
532
+ if (!target) return null
533
+ const refreshToken =
534
+ accountManagerView.getRefreshTokenAt(index) ?? target.refreshToken
535
+ if (!refreshToken) return null
536
+ if (
537
+ action === 'toggleEnabled' &&
538
+ target.enabled === false &&
539
+ target.accountIneligible === true
540
+ ) {
541
+ throw new Error(
542
+ 'This account is ineligible and cannot be enabled until eligibility is rechecked.',
543
+ )
544
+ }
545
+
546
+ if (!storage) {
547
+ throw new Error(
548
+ 'CommandDataService is missing a locked-storage adapter; account mutations are disabled.',
549
+ )
550
+ }
551
+
552
+ let foundInStorage = false
553
+ let desiredEnabled: boolean | undefined
554
+ let previousEnabled: boolean | undefined
555
+ await storage.mutate((current) => {
556
+ const tokenIdx = current.accounts.findIndex(
557
+ (entry) => entry.refreshToken === refreshToken,
558
+ )
559
+ if (tokenIdx === -1) return current
560
+ foundInStorage = true
561
+
562
+ if (action === 'setCurrent') {
563
+ return {
564
+ ...current,
565
+ activeIndex: tokenIdx,
566
+ activeIndexByFamily: { claude: tokenIdx, gemini: tokenIdx },
567
+ }
568
+ }
569
+
570
+ if (action === 'toggleEnabled') {
571
+ const entry = current.accounts[tokenIdx]
572
+ if (!entry) return current
573
+ previousEnabled = entry.enabled !== false
574
+ desiredEnabled = entry.enabled === false
575
+ if (desiredEnabled && entry.accountIneligible === true) {
576
+ throw new Error(
577
+ 'This account is ineligible and cannot be enabled until eligibility is rechecked.',
578
+ )
579
+ }
580
+ return {
581
+ ...current,
582
+ accounts: current.accounts.map((account) =>
583
+ account.refreshToken === refreshToken
584
+ ? { ...account, enabled: desiredEnabled }
585
+ : account,
586
+ ),
587
+ }
588
+ }
589
+
590
+ return {
591
+ ...current,
592
+ accounts: current.accounts.filter(
593
+ (account) => account.refreshToken !== refreshToken,
594
+ ),
595
+ activeIndex: 0,
596
+ activeIndexByFamily: { claude: 0, gemini: 0 },
597
+ }
598
+ })
599
+
600
+ if (!foundInStorage) return null
601
+
602
+ // Re-resolve the live index by canonical identity after the awaited disk
603
+ // transaction. A concurrent OAuth add/remove may have shifted every index.
604
+ const liveIndex = accountManagerView
605
+ .getAccounts()
606
+ .findIndex((account) => account.refreshToken === refreshToken)
607
+ let applied = action === 'remove' && liveIndex === -1
608
+ if (liveIndex !== -1) {
609
+ if (action === 'setCurrent') {
610
+ applied = accountManagerView.setAccountCurrent(liveIndex)
611
+ } else if (action === 'toggleEnabled') {
612
+ applied = accountManagerView.setAccountEnabled(
613
+ liveIndex,
614
+ desiredEnabled === true,
615
+ )
616
+ if (!applied) {
617
+ applied =
618
+ accountManagerView.getAccounts()[liveIndex]?.enabled ===
619
+ desiredEnabled
620
+ }
621
+ } else {
622
+ applied = accountManagerView.removeAccountByIndex(liveIndex)
623
+ }
624
+ }
625
+
626
+ if (!applied && action !== 'remove') {
627
+ if (action === 'toggleEnabled' && previousEnabled !== undefined) {
628
+ await storage.mutate((current) => ({
629
+ ...current,
630
+ accounts: current.accounts.map((account) =>
631
+ account.refreshToken === refreshToken
632
+ ? { ...account, enabled: previousEnabled }
633
+ : account,
634
+ ),
635
+ }))
636
+ }
637
+ throw new Error(
638
+ 'The account changed while the operation was being applied; reopen the dialog and try again.',
639
+ )
640
+ }
641
+
642
+ await accountManagerView.flushSaveToDisk().catch(() => {
643
+ // The locked storage mutation already committed; a later periodic flush
644
+ // can reconcile transient AccountManager lock contention.
645
+ })
646
+
647
+ const rows = projectRows()
648
+ writeSidebar(rows)
649
+ return rows
650
+ }
651
+ }