@cortexkit/opencode-antigravity-auth 3.0.5 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/README.md +38 -352
  2. package/dist/cli.js +9199 -8799
  3. package/dist/cli.js.map +4 -4
  4. package/dist/cli.meta.json +1286 -1064
  5. package/dist/index.js +13641 -14559
  6. package/dist/index.js.map +4 -4
  7. package/dist/index.meta.json +1333 -1342
  8. package/dist/server.js +10618 -11080
  9. package/dist/server.js.map +4 -4
  10. package/dist/server.meta.json +1 -1
  11. package/dist/src/auth-method-labels.d.ts +3 -0
  12. package/dist/src/auth-method-labels.d.ts.map +1 -0
  13. package/dist/src/auth-method-labels.js +5 -0
  14. package/dist/src/auth-method-labels.js.map +1 -0
  15. package/dist/src/cli.d.ts +3 -1
  16. package/dist/src/cli.d.ts.map +1 -1
  17. package/dist/src/cli.js +98 -12
  18. package/dist/src/cli.js.map +1 -1
  19. package/dist/src/common-auth-embedded/claustrum/consumer.d.ts +33 -3
  20. package/dist/src/common-auth-embedded/claustrum/consumer.js +96 -1
  21. package/dist/src/common-auth-embedded/claustrum/custody.d.ts +4 -1
  22. package/dist/src/common-auth-embedded/claustrum/enrollment.d.ts +6 -1
  23. package/dist/src/common-auth-embedded/claustrum/enrollment.js +6 -1
  24. package/dist/src/common-auth-embedded/claustrum/index.d.ts +2 -1
  25. package/dist/src/common-auth-embedded/claustrum/index.js +5 -3
  26. package/dist/src/common-auth-embedded/claustrum/insula.d.ts +69 -0
  27. package/dist/src/common-auth-embedded/claustrum/insula.js +152 -0
  28. package/dist/src/common-auth-embedded/claustrum/roster.d.ts +13 -0
  29. package/dist/src/common-auth-embedded/claustrum/roster.js +33 -3
  30. package/dist/src/common-auth-embedded/fs/refresh-file-lock.js +20 -11
  31. package/dist/src/common-auth-embedded/quota/codec.d.ts +2 -0
  32. package/dist/src/common-auth-embedded/quota/codec.js +2 -1
  33. package/dist/src/common-auth-embedded/quota/format.d.ts +26 -0
  34. package/dist/src/common-auth-embedded/quota/format.js +78 -42
  35. package/dist/src/common-auth-embedded/quota/index.d.ts +4 -4
  36. package/dist/src/common-auth-embedded/quota/index.js +2 -2
  37. package/dist/src/common-auth-embedded/quota/map.d.ts +11 -0
  38. package/dist/src/common-auth-embedded/quota/map.js +36 -1
  39. package/dist/src/common-auth-embedded/quota/merge.d.ts +3 -1
  40. package/dist/src/common-auth-embedded/quota/merge.js +21 -5
  41. package/dist/src/common-auth-embedded/quota/projection.d.ts +2 -1
  42. package/dist/src/common-auth-embedded/quota/projection.js +3 -1
  43. package/dist/src/common-auth-embedded/setup/command-runner.js +49 -0
  44. package/dist/src/common-auth-embedded/setup/command.d.ts +3 -0
  45. package/dist/src/common-auth-embedded/setup/command.js +215 -0
  46. package/dist/src/common-auth-embedded/setup/config-write.js +60 -0
  47. package/dist/src/common-auth-embedded/setup/detect.d.ts +6 -0
  48. package/dist/src/common-auth-embedded/setup/detect.js +224 -0
  49. package/dist/src/common-auth-embedded/setup/enroll.js +70 -0
  50. package/dist/src/common-auth-embedded/setup/index.d.ts +5 -0
  51. package/dist/src/common-auth-embedded/setup/index.js +4 -0
  52. package/dist/src/common-auth-embedded/setup/paths.js +33 -0
  53. package/dist/src/common-auth-embedded/setup/process-fence.d.ts +43 -0
  54. package/dist/src/common-auth-embedded/setup/process-fence.js +270 -0
  55. package/dist/src/common-auth-embedded/setup/prompts.js +26 -0
  56. package/dist/src/common-auth-embedded/setup/register.d.ts +11 -0
  57. package/dist/src/common-auth-embedded/setup/register.js +130 -0
  58. package/dist/src/common-auth-embedded/setup/types.d.ts +136 -0
  59. package/dist/src/common-auth-embedded/source-output.json +228 -80
  60. package/dist/src/common-auth-embedded/store/schema.d.ts +2 -0
  61. package/dist/src/common-auth-embedded/store/schema.js +3 -2
  62. package/dist/src/ga/rpc/protocol.d.ts +4 -7
  63. package/dist/src/ga/rpc/protocol.d.ts.map +1 -1
  64. package/dist/src/ga/rpc/protocol.js +5 -13
  65. package/dist/src/ga/rpc/protocol.js.map +1 -1
  66. package/dist/src/ga/server/auth.d.ts +8 -3
  67. package/dist/src/ga/server/auth.d.ts.map +1 -1
  68. package/dist/src/ga/server/auth.js +25 -9
  69. package/dist/src/ga/server/auth.js.map +1 -1
  70. package/dist/src/ga/server/index.d.ts +39 -12
  71. package/dist/src/ga/server/index.d.ts.map +1 -1
  72. package/dist/src/ga/server/index.js +214 -53
  73. package/dist/src/ga/server/index.js.map +1 -1
  74. package/dist/src/ga/server/pool-login.d.ts +22 -0
  75. package/dist/src/ga/server/pool-login.d.ts.map +1 -0
  76. package/dist/src/ga/server/pool-login.js +150 -0
  77. package/dist/src/ga/server/pool-login.js.map +1 -0
  78. package/dist/src/ga/tui/host-ga.d.ts +2 -5
  79. package/dist/src/ga/tui/host-ga.d.ts.map +1 -1
  80. package/dist/src/ga/tui/host-ga.js +17 -49
  81. package/dist/src/ga/tui/host-ga.js.map +1 -1
  82. package/dist/src/plugin/accounts.d.ts +1 -1
  83. package/dist/src/plugin/accounts.d.ts.map +1 -1
  84. package/dist/src/plugin/accounts.js +1 -0
  85. package/dist/src/plugin/accounts.js.map +1 -1
  86. package/dist/src/plugin/background-vault-quota-refresh.d.ts +2 -0
  87. package/dist/src/plugin/background-vault-quota-refresh.d.ts.map +1 -1
  88. package/dist/src/plugin/background-vault-quota-refresh.js +125 -10
  89. package/dist/src/plugin/background-vault-quota-refresh.js.map +1 -1
  90. package/dist/src/plugin/command-apply.d.ts +2 -2
  91. package/dist/src/plugin/command-apply.d.ts.map +1 -1
  92. package/dist/src/plugin/command-apply.js +3 -10
  93. package/dist/src/plugin/command-apply.js.map +1 -1
  94. package/dist/src/plugin/commands.d.ts +1 -0
  95. package/dist/src/plugin/commands.d.ts.map +1 -1
  96. package/dist/src/plugin/commands.js +2 -2
  97. package/dist/src/plugin/commands.js.map +1 -1
  98. package/dist/src/plugin/config/operator-settings-schema.d.ts +0 -4
  99. package/dist/src/plugin/config/operator-settings-schema.d.ts.map +1 -1
  100. package/dist/src/plugin/config/operator-settings-schema.js +0 -5
  101. package/dist/src/plugin/config/operator-settings-schema.js.map +1 -1
  102. package/dist/src/plugin/config/schema.d.ts +0 -7
  103. package/dist/src/plugin/config/schema.d.ts.map +1 -1
  104. package/dist/src/plugin/config/schema.js +0 -43
  105. package/dist/src/plugin/config/schema.js.map +1 -1
  106. package/dist/src/plugin/config/writer.d.ts +1 -1
  107. package/dist/src/plugin/config/writer.js +1 -1
  108. package/dist/src/plugin/fetch/retry-state.d.ts +19 -3
  109. package/dist/src/plugin/fetch/retry-state.d.ts.map +1 -1
  110. package/dist/src/plugin/fetch/retry-state.js +0 -6
  111. package/dist/src/plugin/fetch/retry-state.js.map +1 -1
  112. package/dist/src/plugin/fetch-interceptor.d.ts.map +1 -1
  113. package/dist/src/plugin/fetch-interceptor.js +0 -3
  114. package/dist/src/plugin/fetch-interceptor.js.map +1 -1
  115. package/dist/src/plugin/fetch-routing.d.ts +0 -16
  116. package/dist/src/plugin/fetch-routing.d.ts.map +1 -1
  117. package/dist/src/plugin/fetch-routing.js +0 -49
  118. package/dist/src/plugin/fetch-routing.js.map +1 -1
  119. package/dist/src/plugin/ga-location-services.d.ts +14 -9
  120. package/dist/src/plugin/ga-location-services.d.ts.map +1 -1
  121. package/dist/src/plugin/ga-location-services.js +120 -40
  122. package/dist/src/plugin/ga-location-services.js.map +1 -1
  123. package/dist/src/plugin/google-origin.d.ts +7 -0
  124. package/dist/src/plugin/google-origin.d.ts.map +1 -0
  125. package/dist/src/plugin/google-origin.js +14 -0
  126. package/dist/src/plugin/google-origin.js.map +1 -0
  127. package/dist/src/plugin/index.d.ts.map +1 -1
  128. package/dist/src/plugin/index.js +15 -4
  129. package/dist/src/plugin/index.js.map +1 -1
  130. package/dist/src/plugin/oauth-methods.d.ts.map +1 -1
  131. package/dist/src/plugin/oauth-methods.js +0 -33
  132. package/dist/src/plugin/oauth-methods.js.map +1 -1
  133. package/dist/src/plugin/operator-settings.d.ts +1 -1
  134. package/dist/src/plugin/operator-settings.d.ts.map +1 -1
  135. package/dist/src/plugin/operator-settings.js +1 -8
  136. package/dist/src/plugin/operator-settings.js.map +1 -1
  137. package/dist/src/plugin/quota.d.ts +11 -7
  138. package/dist/src/plugin/quota.d.ts.map +1 -1
  139. package/dist/src/plugin/quota.js +128 -102
  140. package/dist/src/plugin/quota.js.map +1 -1
  141. package/dist/src/plugin/request.d.ts +5 -3
  142. package/dist/src/plugin/request.d.ts.map +1 -1
  143. package/dist/src/plugin/request.js +87 -168
  144. package/dist/src/plugin/request.js.map +1 -1
  145. package/dist/src/plugin/server.d.ts.map +1 -1
  146. package/dist/src/plugin/server.js +60 -53
  147. package/dist/src/plugin/server.js.map +1 -1
  148. package/dist/src/plugin/shared/advisory-repository.d.ts +20 -0
  149. package/dist/src/plugin/shared/advisory-repository.d.ts.map +1 -0
  150. package/dist/src/plugin/shared/advisory-repository.js +91 -0
  151. package/dist/src/plugin/shared/advisory-repository.js.map +1 -0
  152. package/dist/src/plugin/shared/request-services.d.ts +19 -25
  153. package/dist/src/plugin/shared/request-services.d.ts.map +1 -1
  154. package/dist/src/plugin/shared/request-services.js +105 -188
  155. package/dist/src/plugin/shared/request-services.js.map +1 -1
  156. package/dist/src/plugin/shared/runtime.js +4 -2
  157. package/dist/src/plugin/shared/runtime.js.map +1 -1
  158. package/dist/src/plugin/shared/vault-request-credentials.d.ts +4 -1
  159. package/dist/src/plugin/shared/vault-request-credentials.d.ts.map +1 -1
  160. package/dist/src/plugin/shared/vault-request-credentials.js +41 -7
  161. package/dist/src/plugin/shared/vault-request-credentials.js.map +1 -1
  162. package/dist/src/plugin/storage.d.ts +2 -2
  163. package/dist/src/plugin/storage.d.ts.map +1 -1
  164. package/dist/src/plugin/storage.js.map +1 -1
  165. package/dist/src/plugin/vault-custody.d.ts.map +1 -1
  166. package/dist/src/plugin/vault-custody.js +147 -35
  167. package/dist/src/plugin/vault-custody.js.map +1 -1
  168. package/dist/src/plugin/vault-quota-outcome.d.ts +19 -0
  169. package/dist/src/plugin/vault-quota-outcome.d.ts.map +1 -0
  170. package/dist/src/plugin/vault-quota-outcome.js +47 -0
  171. package/dist/src/plugin/vault-quota-outcome.js.map +1 -0
  172. package/dist/src/quota-presentation.d.ts +29 -1
  173. package/dist/src/quota-presentation.d.ts.map +1 -1
  174. package/dist/src/quota-presentation.js +52 -17
  175. package/dist/src/quota-presentation.js.map +1 -1
  176. package/dist/src/setup.d.ts +15 -0
  177. package/dist/src/setup.d.ts.map +1 -0
  178. package/dist/src/setup.js +320 -0
  179. package/dist/src/setup.js.map +1 -0
  180. package/dist/src/sidebar-projection.d.ts +1 -1
  181. package/dist/src/sidebar-projection.d.ts.map +1 -1
  182. package/dist/src/sidebar-state.d.ts +1 -1
  183. package/dist/src/sidebar-state.d.ts.map +1 -1
  184. package/dist/src/sidebar-state.js +19 -1
  185. package/dist/src/sidebar-state.js.map +1 -1
  186. package/dist/src/tui.d.ts.map +1 -1
  187. package/dist/src/tui.js +205 -110
  188. package/dist/src/tui.js.map +1 -1
  189. package/dist/tui-build-map.json +37 -37
  190. package/dist/tui-ga-build-map.json +33 -33
  191. package/dist/types/src/auth-method-labels.d.ts +3 -0
  192. package/dist/types/src/auth-method-labels.d.ts.map +1 -0
  193. package/dist/types/src/cli.d.ts +3 -1
  194. package/dist/types/src/cli.d.ts.map +1 -1
  195. package/dist/types/src/common-auth-embedded/auth-menu/terminal.d.ts +43 -0
  196. package/dist/types/src/common-auth-embedded/claustrum/consumer.d.ts +132 -0
  197. package/dist/types/src/common-auth-embedded/claustrum/custody.d.ts +178 -0
  198. package/dist/types/src/common-auth-embedded/claustrum/enrollment.d.ts +164 -0
  199. package/dist/types/src/common-auth-embedded/claustrum/errors.d.ts +16 -0
  200. package/dist/types/src/common-auth-embedded/claustrum/host-slot.d.ts +39 -0
  201. package/dist/types/src/common-auth-embedded/claustrum/index.d.ts +19 -0
  202. package/dist/types/src/common-auth-embedded/claustrum/insula.d.ts +69 -0
  203. package/dist/types/src/common-auth-embedded/claustrum/interlock.d.ts +27 -0
  204. package/dist/types/src/common-auth-embedded/claustrum/roster.d.ts +221 -0
  205. package/dist/types/src/common-auth-embedded/opencode2/errors.d.ts +25 -0
  206. package/dist/types/src/common-auth-embedded/opencode2/index.d.ts +8 -0
  207. package/dist/types/src/common-auth-embedded/opencode2/install.d.ts +55 -0
  208. package/dist/types/src/common-auth-embedded/opencode2/integration.d.ts +112 -0
  209. package/dist/types/src/common-auth-embedded/opencode2/sse.d.ts +12 -0
  210. package/dist/types/src/common-auth-embedded/opencode2/types.d.ts +413 -0
  211. package/dist/types/src/common-auth-embedded/quota/codec.d.ts +2 -0
  212. package/dist/types/src/common-auth-embedded/quota/format.d.ts +26 -0
  213. package/dist/types/src/common-auth-embedded/quota/index.d.ts +4 -4
  214. package/dist/types/src/common-auth-embedded/quota/map.d.ts +11 -0
  215. package/dist/types/src/common-auth-embedded/quota/merge.d.ts +3 -1
  216. package/dist/types/src/common-auth-embedded/quota/projection.d.ts +2 -1
  217. package/dist/types/src/common-auth-embedded/routing/admission.d.ts +93 -0
  218. package/dist/types/src/common-auth-embedded/routing/index.d.ts +8 -0
  219. package/dist/types/src/common-auth-embedded/routing/ordered.d.ts +47 -0
  220. package/dist/types/src/common-auth-embedded/routing/pins.d.ts +22 -0
  221. package/dist/types/src/common-auth-embedded/routing/sticky.d.ts +158 -0
  222. package/dist/types/src/common-auth-embedded/setup/command.d.ts +3 -0
  223. package/dist/types/src/common-auth-embedded/setup/detect.d.ts +6 -0
  224. package/dist/types/src/common-auth-embedded/setup/index.d.ts +5 -0
  225. package/dist/types/src/common-auth-embedded/setup/process-fence.d.ts +43 -0
  226. package/dist/types/src/common-auth-embedded/setup/register.d.ts +11 -0
  227. package/dist/types/src/common-auth-embedded/setup/types.d.ts +136 -0
  228. package/dist/types/src/common-auth-embedded/store/schema.d.ts +2 -0
  229. package/dist/types/src/ga/rpc/protocol.d.ts +4 -7
  230. package/dist/types/src/ga/rpc/protocol.d.ts.map +1 -1
  231. package/dist/types/src/ga/server/auth.d.ts +8 -3
  232. package/dist/types/src/ga/server/auth.d.ts.map +1 -1
  233. package/dist/types/src/ga/server/index.d.ts +39 -12
  234. package/dist/types/src/ga/server/index.d.ts.map +1 -1
  235. package/dist/types/src/ga/server/pool-login.d.ts +22 -0
  236. package/dist/types/src/ga/server/pool-login.d.ts.map +1 -0
  237. package/dist/types/src/plugin/accounts.d.ts +1 -1
  238. package/dist/types/src/plugin/accounts.d.ts.map +1 -1
  239. package/dist/types/src/plugin/background-vault-quota-refresh.d.ts +2 -0
  240. package/dist/types/src/plugin/background-vault-quota-refresh.d.ts.map +1 -1
  241. package/dist/types/src/plugin/command-apply.d.ts +2 -2
  242. package/dist/types/src/plugin/command-apply.d.ts.map +1 -1
  243. package/dist/types/src/plugin/commands.d.ts +1 -0
  244. package/dist/types/src/plugin/commands.d.ts.map +1 -1
  245. package/dist/types/src/plugin/config/operator-settings-schema.d.ts +0 -4
  246. package/dist/types/src/plugin/config/operator-settings-schema.d.ts.map +1 -1
  247. package/dist/types/src/plugin/config/schema.d.ts +0 -7
  248. package/dist/types/src/plugin/config/schema.d.ts.map +1 -1
  249. package/dist/types/src/plugin/config/writer.d.ts +1 -1
  250. package/dist/types/src/plugin/fetch/retry-state.d.ts +19 -3
  251. package/dist/types/src/plugin/fetch/retry-state.d.ts.map +1 -1
  252. package/dist/types/src/plugin/fetch-interceptor.d.ts.map +1 -1
  253. package/dist/types/src/plugin/fetch-routing.d.ts +0 -16
  254. package/dist/types/src/plugin/fetch-routing.d.ts.map +1 -1
  255. package/dist/types/src/plugin/ga-location-services.d.ts +14 -9
  256. package/dist/types/src/plugin/ga-location-services.d.ts.map +1 -1
  257. package/dist/types/src/plugin/google-origin.d.ts +7 -0
  258. package/dist/types/src/plugin/google-origin.d.ts.map +1 -0
  259. package/dist/types/src/plugin/index.d.ts.map +1 -1
  260. package/dist/types/src/plugin/oauth-methods.d.ts.map +1 -1
  261. package/dist/types/src/plugin/operator-settings.d.ts +1 -1
  262. package/dist/types/src/plugin/operator-settings.d.ts.map +1 -1
  263. package/dist/types/src/plugin/quota.d.ts +11 -7
  264. package/dist/types/src/plugin/quota.d.ts.map +1 -1
  265. package/dist/types/src/plugin/request.d.ts +5 -3
  266. package/dist/types/src/plugin/request.d.ts.map +1 -1
  267. package/dist/types/src/plugin/server.d.ts.map +1 -1
  268. package/dist/types/src/plugin/shared/advisory-repository.d.ts +20 -0
  269. package/dist/types/src/plugin/shared/advisory-repository.d.ts.map +1 -0
  270. package/dist/types/src/plugin/shared/request-services.d.ts +19 -25
  271. package/dist/types/src/plugin/shared/request-services.d.ts.map +1 -1
  272. package/dist/types/src/plugin/shared/vault-request-credentials.d.ts +4 -1
  273. package/dist/types/src/plugin/shared/vault-request-credentials.d.ts.map +1 -1
  274. package/dist/types/src/plugin/storage.d.ts +2 -2
  275. package/dist/types/src/plugin/storage.d.ts.map +1 -1
  276. package/dist/types/src/plugin/vault-custody.d.ts.map +1 -1
  277. package/dist/types/src/plugin/vault-quota-outcome.d.ts +19 -0
  278. package/dist/types/src/plugin/vault-quota-outcome.d.ts.map +1 -0
  279. package/dist/types/src/setup.d.ts +15 -0
  280. package/dist/types/src/setup.d.ts.map +1 -0
  281. package/dist/types/src/sidebar-state.d.ts +1 -1
  282. package/dist/types/src/sidebar-state.d.ts.map +1 -1
  283. package/package.json +5 -4
  284. package/src/common-auth-embedded/claustrum/consumer.d.ts +33 -3
  285. package/src/common-auth-embedded/claustrum/consumer.js +96 -1
  286. package/src/common-auth-embedded/claustrum/custody.d.ts +4 -1
  287. package/src/common-auth-embedded/claustrum/enrollment.d.ts +6 -1
  288. package/src/common-auth-embedded/claustrum/enrollment.js +6 -1
  289. package/src/common-auth-embedded/claustrum/index.d.ts +2 -1
  290. package/src/common-auth-embedded/claustrum/index.js +5 -3
  291. package/src/common-auth-embedded/claustrum/insula.d.ts +69 -0
  292. package/src/common-auth-embedded/claustrum/insula.js +152 -0
  293. package/src/common-auth-embedded/claustrum/roster.d.ts +13 -0
  294. package/src/common-auth-embedded/claustrum/roster.js +33 -3
  295. package/src/common-auth-embedded/fs/refresh-file-lock.js +20 -11
  296. package/src/common-auth-embedded/quota/codec.d.ts +2 -0
  297. package/src/common-auth-embedded/quota/codec.js +2 -1
  298. package/src/common-auth-embedded/quota/format.d.ts +26 -0
  299. package/src/common-auth-embedded/quota/format.js +78 -42
  300. package/src/common-auth-embedded/quota/index.d.ts +4 -4
  301. package/src/common-auth-embedded/quota/index.js +2 -2
  302. package/src/common-auth-embedded/quota/map.d.ts +11 -0
  303. package/src/common-auth-embedded/quota/map.js +36 -1
  304. package/src/common-auth-embedded/quota/merge.d.ts +3 -1
  305. package/src/common-auth-embedded/quota/merge.js +21 -5
  306. package/src/common-auth-embedded/quota/projection.d.ts +2 -1
  307. package/src/common-auth-embedded/quota/projection.js +3 -1
  308. package/src/common-auth-embedded/setup/command-runner.js +49 -0
  309. package/src/common-auth-embedded/setup/command.d.ts +3 -0
  310. package/src/common-auth-embedded/setup/command.js +215 -0
  311. package/src/common-auth-embedded/setup/config-write.js +60 -0
  312. package/src/common-auth-embedded/setup/detect.d.ts +6 -0
  313. package/src/common-auth-embedded/setup/detect.js +224 -0
  314. package/src/common-auth-embedded/setup/enroll.js +70 -0
  315. package/src/common-auth-embedded/setup/index.d.ts +5 -0
  316. package/src/common-auth-embedded/setup/index.js +4 -0
  317. package/src/common-auth-embedded/setup/paths.js +33 -0
  318. package/src/common-auth-embedded/setup/process-fence.d.ts +43 -0
  319. package/src/common-auth-embedded/setup/process-fence.js +270 -0
  320. package/src/common-auth-embedded/setup/prompts.js +26 -0
  321. package/src/common-auth-embedded/setup/register.d.ts +11 -0
  322. package/src/common-auth-embedded/setup/register.js +130 -0
  323. package/src/common-auth-embedded/setup/types.d.ts +136 -0
  324. package/src/common-auth-embedded/source-output.json +228 -80
  325. package/src/common-auth-embedded/store/schema.d.ts +2 -0
  326. package/src/common-auth-embedded/store/schema.js +3 -2
  327. package/src/sidebar-state.ts +19 -2
  328. package/src/tui-compiled/common-auth-embedded/quota/codec.js +2 -1
  329. package/src/tui-compiled/common-auth-embedded/quota/format.js +78 -42
  330. package/src/tui-compiled/common-auth-embedded/quota/index.js +2 -2
  331. package/src/tui-compiled/common-auth-embedded/quota/map.js +36 -1
  332. package/src/tui-compiled/common-auth-embedded/quota/merge.js +21 -5
  333. package/src/tui-compiled/common-auth-embedded/quota/projection.js +3 -1
  334. package/src/tui-compiled/quota-presentation.js +42 -9
  335. package/src/tui-compiled/sidebar-state.js +15 -1
  336. package/src/tui-compiled/tui.js +516 -334
  337. package/src/tui-ga-compiled/host-ga.js +11 -38
  338. package/src/tui-ga-compiled/shared/1ff34afa7c42d196-projection.js +3 -1
  339. package/src/tui-ga-compiled/shared/77138df80c7ae006-map.js +36 -1
  340. package/src/tui-ga-compiled/shared/98cc0c186c46c42d-quota-presentation.js +42 -9
  341. package/src/tui-ga-compiled/shared/aef2402f8fc342f0-format.js +78 -42
  342. package/src/tui-ga-compiled/shared/bf17cbee28e58197-index.js +2 -2
  343. package/src/tui-ga-compiled/shared/ca21dc417b36dc5d-codec.js +2 -1
  344. package/src/tui-ga-compiled/shared/ecc214300932dfb6-merge.js +21 -5
  345. package/src/tui-ga-raw/host-ga.tsx +31 -62
  346. package/src/tui-ga-raw/shared/1ff34afa7c42d196-projection.js +3 -1
  347. package/src/tui-ga-raw/shared/77138df80c7ae006-map.js +36 -1
  348. package/src/tui-ga-raw/shared/98cc0c186c46c42d-quota-presentation.js +42 -9
  349. package/src/tui-ga-raw/shared/aef2402f8fc342f0-format.js +78 -42
  350. package/src/tui-ga-raw/shared/bf17cbee28e58197-index.js +2 -2
  351. package/src/tui-ga-raw/shared/ca21dc417b36dc5d-codec.js +2 -1
  352. package/src/tui-ga-raw/shared/ecc214300932dfb6-merge.js +21 -5
  353. package/src/tui-raw/common-auth-embedded/quota/codec.js +2 -1
  354. package/src/tui-raw/common-auth-embedded/quota/format.js +78 -42
  355. package/src/tui-raw/common-auth-embedded/quota/index.js +2 -2
  356. package/src/tui-raw/common-auth-embedded/quota/map.js +36 -1
  357. package/src/tui-raw/common-auth-embedded/quota/merge.js +21 -5
  358. package/src/tui-raw/common-auth-embedded/quota/projection.js +3 -1
  359. package/src/tui-raw/quota-presentation.js +42 -9
  360. package/src/tui-raw/sidebar-state.js +15 -1
  361. package/src/tui-raw/tui.tsx +423 -218
  362. package/src/tui.tsx +423 -218
  363. package/dist/src/tui/vault-setup.d.ts +0 -98
  364. package/dist/src/tui/vault-setup.d.ts.map +0 -1
  365. package/dist/src/tui/vault-setup.js +0 -198
  366. package/dist/src/tui/vault-setup.js.map +0 -1
  367. package/dist/types/src/plugin/transform/model-resolver.d.ts +0 -2
  368. package/dist/types/src/plugin/transform/model-resolver.d.ts.map +0 -1
  369. package/dist/types/src/quota-presentation.d.ts +0 -18
  370. package/dist/types/src/quota-presentation.d.ts.map +0 -1
@@ -133,6 +133,19 @@ export declare function recordVaultQuota(path: string, input: QuotaReceipt & {
133
133
  routeId: string;
134
134
  observation: QuotaObservation;
135
135
  }): Promise<boolean>;
136
+ /**
137
+ * Merge a quota observation that belongs to an account rather than to a
138
+ * served credential, such as a background reading from insula. The write
139
+ * lands only while the row is still bound to that exact known account, so a
140
+ * reading taken before a replacement never reaches the successor. Unlike
141
+ * `recordVaultQuota` it never binds an account: a row without a known account
142
+ * is refused. Returns whether the observation was kept.
143
+ */
144
+ export declare function recordVaultAccountQuota(path: string, input: {
145
+ routeId: string;
146
+ accountIdentity: string;
147
+ observation: QuotaObservation;
148
+ }): Promise<boolean>;
136
149
  /**
137
150
  * Discover and commit the roster. The lease covers the list call and the
138
151
  * commit, not just the write: an opaque view cannot tell a delayed old reply
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { acquireRefreshFileLock, withLock, writeJsonAtomic, } from '../fs/index.js';
3
3
  import { readPrivateText, windowsPrivateFiles } from '../fs/private-files.js';
4
- import { isQuotaMap, mergeQuotaObservation, } from '../quota/index.js';
4
+ import { decodeQuotaMap, mergeQuotaObservation, } from '../quota/index.js';
5
5
  import { ClaustrumConsumerError } from './errors.js';
6
6
  import { acceptAccount, declineAccount, isDeclined, } from './interlock.js';
7
7
  export const DEFAULT_ROUTE_PREFIX = 'vault:';
@@ -210,6 +210,7 @@ function optionalString(value) {
210
210
  return value === undefined || typeof value === 'string';
211
211
  }
212
212
  function decodeRow(value) {
213
+ const quota = isRecord(value) ? decodeQuotaMap(value.quota) : undefined;
213
214
  if (!isRecord(value) ||
214
215
  typeof value.routeId !== 'string' ||
215
216
  typeof value.credentialId !== 'string' ||
@@ -224,11 +225,14 @@ function decodeRow(value) {
224
225
  (value.aliases !== undefined &&
225
226
  (!Array.isArray(value.aliases) ||
226
227
  !value.aliases.every((alias) => typeof alias === 'string'))) ||
227
- (value.quota !== undefined && !isQuotaMap(value.quota)) ||
228
+ (value.quota !== undefined && quota === undefined) ||
228
229
  (value.stale !== undefined && value.stale !== true) ||
229
230
  (value.unclaimed !== undefined && value.unclaimed !== true))
230
231
  return undefined;
231
- return value;
232
+ return {
233
+ ...value,
234
+ ...(quota === undefined ? {} : { quota }),
235
+ };
232
236
  }
233
237
  const SKIPPED_REASONS = new Set([
234
238
  'empty credential id',
@@ -435,6 +439,32 @@ export function recordVaultQuota(path, input) {
435
439
  };
436
440
  });
437
441
  }
442
+ /**
443
+ * Merge a quota observation that belongs to an account rather than to a
444
+ * served credential, such as a background reading from insula. The write
445
+ * lands only while the row is still bound to that exact known account, so a
446
+ * reading taken before a replacement never reaches the successor. Unlike
447
+ * `recordVaultQuota` it never binds an account: a row without a known account
448
+ * is refused. Returns whether the observation was kept.
449
+ */
450
+ export function recordVaultAccountQuota(path, input) {
451
+ return mutateVaultRoster(path, (current) => {
452
+ const row = current?.rows.find((entry) => entry.routeId === input.routeId);
453
+ if (!current ||
454
+ !row ||
455
+ row.accountIdentity === undefined ||
456
+ row.accountIdentity !== input.accountIdentity)
457
+ return { result: false };
458
+ const quota = mergeQuotaObservation(row.quota, input.observation);
459
+ return {
460
+ next: {
461
+ ...current,
462
+ rows: current.rows.map((entry) => entry === row ? { ...entry, quota } : entry),
463
+ },
464
+ result: true,
465
+ };
466
+ });
467
+ }
438
468
  /**
439
469
  * Discover and commit the roster. The lease covers the list call and the
440
470
  * commit, not just the write: an opaque view cannot tell a delayed old reply
@@ -1,8 +1,25 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { mkdir, open, readFile, rename, rm, stat, writeFile, } from 'node:fs/promises';
2
+ import { mkdir, open, readFile, rename, rm, stat } from 'node:fs/promises';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { createPrivateBytes, createPrivateDirectoryExclusive, ensurePrivateDirectory, PrivateFileError, privateOperation, readPrivateText, verifyPrivate, windowsPrivateFiles, writePrivateAtomic, } from './private-files.js';
5
5
  import { LockOwnershipError, lockPathFor } from './with-lock.js';
6
+ /**
7
+ * Create a new owner-only file and write `text` into it, failing with EEXIST if
8
+ * the file already exists. Writing through the handle rather than path-based
9
+ * `writeFile(path, …, { flag: 'wx' })` matters: Bun's path form also truncates
10
+ * the file it has just created, and on some Linux filesystems (XFS under load)
11
+ * that redundant ftruncate stalls for hundreds of milliseconds, inside every
12
+ * lock acquisition.
13
+ */
14
+ async function createExclusiveText(path, text) {
15
+ const handle = await open(path, 'wx', 0o600);
16
+ try {
17
+ await handle.writeFile(text, 'utf8');
18
+ }
19
+ finally {
20
+ await handle.close();
21
+ }
22
+ }
6
23
  const setRefreshLockRenewalTimeout = globalThis.setTimeout.bind(globalThis);
7
24
  const clearRefreshLockRenewalTimeout = globalThis.clearTimeout.bind(globalThis);
8
25
  // A concurrent contender renaming the freshly-created eviction-marker directory
@@ -152,11 +169,7 @@ export async function acquireRefreshFileLock(options) {
152
169
  if (windowsPrivateFiles)
153
170
  await createPrivateBytes(lockPath, bytes);
154
171
  else
155
- await writeFile(lockPath, bytes, {
156
- encoding: 'utf8',
157
- mode: 0o600,
158
- flag: 'wx',
159
- });
172
+ await createExclusiveText(lockPath, bytes);
160
173
  }
161
174
  async function leaseContention(error) {
162
175
  const code = error.code;
@@ -304,11 +317,7 @@ export async function acquireRefreshFileLock(options) {
304
317
  if (windowsPrivateFiles)
305
318
  await createPrivateBytes(evictOwnerPath, bytes);
306
319
  else
307
- await writeFile(evictOwnerPath, bytes, {
308
- encoding: 'utf8',
309
- mode: 0o600,
310
- flag: 'wx',
311
- });
320
+ await createExclusiveText(evictOwnerPath, bytes);
312
321
  }
313
322
  catch (error) {
314
323
  // A competing contender can rename our just-created marker directory
@@ -1,5 +1,7 @@
1
1
  export interface QuotaCodec {
2
2
  readonly validate: (value: unknown) => boolean;
3
+ /** Normalize persisted data before validation; older codecs may omit this. */
4
+ readonly decode?: (value: unknown) => unknown;
3
5
  readonly merge: (stored: unknown, observation: unknown) => unknown;
4
6
  }
5
7
  /**
@@ -1,4 +1,4 @@
1
- import { isQuotaMap } from './map.js';
1
+ import { decodeQuotaMap, isQuotaMap } from './map.js';
2
2
  import { mergeQuotaObservation } from './merge.js';
3
3
  /**
4
4
  * The quota codec the account-pool store (the `/store` subpath) takes when it
@@ -8,5 +8,6 @@ import { mergeQuotaObservation } from './merge.js';
8
8
  */
9
9
  export const quotaCodec = Object.freeze({
10
10
  validate: isQuotaMap,
11
+ decode: decodeQuotaMap,
11
12
  merge: mergeQuotaObservation,
12
13
  });
@@ -21,6 +21,26 @@ export interface QuotaTextOptions {
21
21
  /** Defaults to `QUOTA_STALE_AFTER_MS`. */
22
22
  staleAfterMs?: number;
23
23
  }
24
+ export interface QuotaWindowPart {
25
+ kind: 'window' | 'budget';
26
+ /** The display name of a quota window, or `credits` for the budget. */
27
+ name: string;
28
+ /** The shared figure wording, including `credits` for budget figures. */
29
+ figure: string;
30
+ /** The unrounded remaining percentage, clamped to 0–100, when known. */
31
+ remainingPercent?: number;
32
+ /** A bare relative reset span such as `4d`, only when the reset is future. */
33
+ resetIn?: string;
34
+ checkedAt: number;
35
+ /** Whether the reset followed this reading and has arrived by `now`. */
36
+ resetSinceReading: boolean;
37
+ }
38
+ export interface QuotaParts {
39
+ /** Reported windows in display order, followed by the budget when present. */
40
+ windows: QuotaWindowPart[];
41
+ /** The shared checked-age wording, present only when the reading is stale. */
42
+ age?: string;
43
+ }
24
44
  /**
25
45
  * The name a window is shown under. A general window (scope `all`) is named
26
46
  * by its stored length; a model-scoped window keeps the label the plugin
@@ -29,6 +49,12 @@ export interface QuotaTextOptions {
29
49
  * were recorded) falls back to its label.
30
50
  */
31
51
  export declare function quotaWindowName(limit: Pick<ProjectedLimit, 'scope' | 'label' | 'windowMinutes'>): string;
52
+ /**
53
+ * Structured quota wording for layouts that need one row per window. The
54
+ * windows use the same filtering and order as `quotaTextParts`; future resets
55
+ * and the optional age keep the shared formatter's wording.
56
+ */
57
+ export declare function quotaWindowParts(quota: ProjectedQuota | undefined, options: QuotaTextOptions): QuotaParts;
32
58
  /**
33
59
  * The pieces of a quota text, in order: one per reported window
34
60
  * (`5h 58% left, resets 2h`), then the credit budget when there is one, then
@@ -1,7 +1,7 @@
1
1
  // The one quota wording every surface shows: the command menu's dialogs on
2
2
  // OpenCode and Pi, the terminal auth menu, and the plugins' sidebars. Each of
3
- // them calls `formatQuota` (or `quotaTextParts` for one line per window), so
4
- // the wording cannot drift between plugins or between a dialog and a sidebar.
3
+ // them calls `formatQuota`, `quotaTextParts`, or `quotaWindowParts`, so the
4
+ // wording cannot drift between plugins or between a dialog and a sidebar.
5
5
  //
6
6
  // The rules: a window is named by its length (`5h`, `7d`), or, for a window
7
7
  // that binds only one model family, by the label the plugin stored it under.
@@ -22,18 +22,23 @@ export const NO_QUOTA_READING = 'no quota reading yet';
22
22
  */
23
23
  export const NO_LIMITS_REPORTED = 'no limits reported';
24
24
  /**
25
- * A length or a time span in its largest whole-ish unit: `45m`, `5h`, `3d`.
26
- * Spans under an hour are minutes, under a day hours, otherwise days, each
27
- * rounded to the nearest unit and never shown as zero.
25
+ * A time span in at most its two largest units: `45m`, `2h29m`, `5h`, `3d23h`,
26
+ * `7d`. Every unit is rounded down, so a reset is never shown later than it is;
27
+ * the one exception is a span under a minute, which reads `1m` rather than
28
+ * zero. A zero second unit is left out.
28
29
  */
29
30
  function shortSpan(ms) {
30
- const minutes = Math.max(1, Math.round(ms / 60_000));
31
+ const minutes = Math.max(1, Math.floor(ms / 60_000));
31
32
  if (minutes < 60)
32
33
  return `${minutes}m`;
33
- const hours = Math.round(minutes / 60);
34
- if (hours < 24)
35
- return `${hours}h`;
36
- return `${Math.max(1, Math.round(minutes / 1440))}d`;
34
+ if (minutes < 1440) {
35
+ const hours = Math.floor(minutes / 60);
36
+ const rest = minutes % 60;
37
+ return rest === 0 ? `${hours}h` : `${hours}h${rest}m`;
38
+ }
39
+ const days = Math.floor(minutes / 1440);
40
+ const hours = Math.floor((minutes % 1440) / 60);
41
+ return hours === 0 ? `${days}d` : `${days}d${hours}h`;
37
42
  }
38
43
  /** A stored window length in minutes as its exact name: `5h`, `7d`, `90m`. */
39
44
  function lengthName(minutes) {
@@ -55,17 +60,20 @@ export function quotaWindowName(limit) {
55
60
  return limit.label;
56
61
  return lengthName(limit.windowMinutes);
57
62
  }
63
+ function clampPercent(remaining) {
64
+ return Math.min(100, Math.max(0, remaining));
65
+ }
58
66
  function percentLeft(remaining) {
59
- return `${Math.round(Math.min(100, Math.max(0, remaining)))}% left`;
67
+ return `${Math.round(clampPercent(remaining))}% left`;
60
68
  }
61
- function resetText(resetsAt, now) {
62
- if (resetsAt === undefined)
63
- return '';
64
- const at = Date.parse(resetsAt);
65
- // A reset already passed says nothing about the future; leave it out.
66
- if (!Number.isFinite(at) || at <= now)
67
- return '';
68
- return `, resets ${shortSpan(at - now)}`;
69
+ function resetDetails(resetsAt, checkedAt, now) {
70
+ const at = resetsAt === undefined ? Number.NaN : Date.parse(resetsAt);
71
+ return {
72
+ ...(Number.isFinite(at) && at > now
73
+ ? { resetIn: shortSpan(at - now) }
74
+ : {}),
75
+ resetSinceReading: Number.isFinite(at) && at > checkedAt && at <= now,
76
+ };
69
77
  }
70
78
  /** Shortest window first, so `5h` reads before `7d`; unknown lengths last. */
71
79
  function displayOrder(left, right) {
@@ -85,42 +93,70 @@ function displayOrder(left, right) {
85
93
  return left.label < right.label ? -1 : left.label > right.label ? 1 : 0;
86
94
  }
87
95
  /**
88
- * The pieces of a quota text, in order: one per reported window
89
- * (`5h 58% left, resets 2h`), then the credit budget when there is one, then
90
- * `checked 3h ago` when the oldest piece shown is stale. Empty when nothing
91
- * was reported; `formatQuota` turns that into a sentence. A caller printing
92
- * one line per window (the terminal auth menu) uses these directly.
96
+ * Structured quota wording for layouts that need one row per window. The
97
+ * windows use the same filtering and order as `quotaTextParts`; future resets
98
+ * and the optional age keep the shared formatter's wording.
93
99
  */
94
- export function quotaTextParts(quota, options) {
100
+ export function quotaWindowParts(quota, options) {
95
101
  if (!quota)
96
- return [];
102
+ return { windows: [] };
97
103
  const { now } = options;
98
- const full = (options.form ?? 'full') === 'full';
99
- const parts = [];
100
- const times = [];
101
104
  const readings = quota.limits
102
105
  .filter((limit) => limit.kind === 'reading' && limit.remainingPercent !== undefined)
103
106
  .sort(displayOrder);
104
- for (const limit of readings) {
105
- times.push(limit.checkedAt);
106
- parts.push(`${quotaWindowName(limit)} ${percentLeft(limit.remainingPercent ?? 0)}${full ? resetText(limit.resetsAt, now) : ''}`);
107
- }
107
+ const windows = readings.map((limit) => {
108
+ const remainingPercent = clampPercent(limit.remainingPercent ?? 0);
109
+ return {
110
+ kind: 'window',
111
+ name: quotaWindowName(limit),
112
+ figure: percentLeft(limit.remainingPercent ?? 0),
113
+ remainingPercent,
114
+ checkedAt: limit.checkedAt,
115
+ ...resetDetails(limit.resetsAt, limit.checkedAt, now),
116
+ };
117
+ });
108
118
  const budget = quota.budget;
109
119
  if (budget) {
110
- times.push(budget.checkedAt);
120
+ const remainingPercent = budget.remainingPercent;
111
121
  const figure = budget.reached
112
122
  ? 'credits spent'
113
- : budget.remainingPercent !== undefined
114
- ? `credits ${percentLeft(budget.remainingPercent)}`
123
+ : remainingPercent !== undefined
124
+ ? `credits ${percentLeft(remainingPercent)}`
115
125
  : 'credits available';
116
- parts.push(`${figure}${full ? resetText(budget.resetsAt, now) : ''}`);
126
+ windows.push({
127
+ kind: 'budget',
128
+ name: 'credits',
129
+ figure,
130
+ ...(remainingPercent === undefined
131
+ ? {}
132
+ : { remainingPercent: clampPercent(remainingPercent) }),
133
+ checkedAt: budget.checkedAt,
134
+ ...resetDetails(budget.resetsAt, budget.checkedAt, now),
135
+ });
117
136
  }
118
- if (times.length > 0) {
119
- const age = now - Math.min(...times);
120
- if (age > (options.staleAfterMs ?? QUOTA_STALE_AFTER_MS))
121
- parts.push(`checked ${shortSpan(age)} ago`);
137
+ if (windows.length > 0) {
138
+ const age = now - Math.min(...windows.map((part) => part.checkedAt));
139
+ if (age > (options.staleAfterMs ?? QUOTA_STALE_AFTER_MS)) {
140
+ return { windows, age: `checked ${shortSpan(age)} ago` };
141
+ }
122
142
  }
123
- return parts;
143
+ return { windows };
144
+ }
145
+ /**
146
+ * The pieces of a quota text, in order: one per reported window
147
+ * (`5h 58% left, resets 2h`), then the credit budget when there is one, then
148
+ * `checked 3h ago` when the oldest piece shown is stale. Empty when nothing
149
+ * was reported; `formatQuota` turns that into a sentence. A caller printing
150
+ * one line per window (the terminal auth menu) uses these directly.
151
+ */
152
+ export function quotaTextParts(quota, options) {
153
+ const parts = quotaWindowParts(quota, options);
154
+ const full = (options.form ?? 'full') === 'full';
155
+ const text = parts.windows.map((part) => {
156
+ const reset = full && part.resetIn ? `, resets ${part.resetIn}` : '';
157
+ return `${part.kind === 'budget' ? '' : `${part.name} `}${part.figure}${reset}`;
158
+ });
159
+ return parts.age === undefined ? text : [...text, parts.age];
124
160
  }
125
161
  /**
126
162
  * A row's quota as one line: `5h 58% left · 7d 90% left` in the compact
@@ -1,9 +1,9 @@
1
1
  export type { QuotaCodec } from './codec.js';
2
2
  export { quotaCodec } from './codec.js';
3
- export type { QuotaTextForm, QuotaTextOptions } from './format.js';
4
- export { formatQuota, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, } from './format.js';
5
- export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
6
- export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, emptyQuotaMap, isQuotaMap, } from './map.js';
3
+ export type { QuotaParts, QuotaTextForm, QuotaTextOptions, QuotaWindowPart, } from './format.js';
4
+ export { formatQuota, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
5
+ export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
6
+ export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from './map.js';
7
7
  export type { ObservedBudget, ObservedPair, ObservedReading, QuotaObservation, } from './merge.js';
8
8
  export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from './merge.js';
9
9
  export type { ExhaustionReset, ProjectedBudget, ProjectedLimit, ProjectedQuota, } from './projection.js';
@@ -1,5 +1,5 @@
1
1
  export { quotaCodec } from './codec.js';
2
- export { formatQuota, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, } from './format.js';
3
- export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, emptyQuotaMap, isQuotaMap, } from './map.js';
2
+ export { formatQuota, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
3
+ export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from './map.js';
4
4
  export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from './merge.js';
5
5
  export { budgetExhaustedResetAt, futureResetAt, projectQuota, readsExhausted, } from './projection.js';
@@ -44,16 +44,27 @@ export interface CreditBudgetCleared {
44
44
  checkedAt: number;
45
45
  }
46
46
  export type CreditBudgetEntry = CreditBudgetReading | CreditBudgetCleared;
47
+ export interface QuotaCounter {
48
+ value: number;
49
+ checkedAt: number;
50
+ }
47
51
  export interface QuotaMap {
48
52
  limits: QuotaEntry[];
49
53
  /** Absent means no observation has reported on the budget yet. */
50
54
  budget?: CreditBudgetEntry;
55
+ /** Provider-named counts, each with its own observation time. */
56
+ counters?: Record<string, QuotaCounter>;
51
57
  }
52
58
  export declare function emptyQuotaMap(): QuotaMap;
53
59
  /** The time an entry speaks for: its reading, retirement or absence time. */
54
60
  export declare function entryTime(entry: QuotaEntry): number;
55
61
  export declare function limitKey(scope: string, label: string): string;
56
62
  export declare function isCreditBudgetEntry(value: unknown): value is CreditBudgetEntry;
63
+ export declare function isQuotaCounters(value: unknown): value is Record<string, QuotaCounter>;
64
+ /** Keep usable counters without letting one malformed count hide a quota map. */
65
+ export declare function readQuotaCounters(value: unknown): Record<string, QuotaCounter> | undefined;
66
+ /** Decode persisted quota, dropping malformed counters but not limit evidence. */
67
+ export declare function decodeQuotaMap(value: unknown): QuotaMap | undefined;
57
68
  /**
58
69
  * True when `value` is a well-formed quota map. Unrecognised top-level keys
59
70
  * are tolerated (and preserved by merge) so a newer writer's additions
@@ -83,6 +83,40 @@ export function isCreditBudgetEntry(value) {
83
83
  optional(value, 'remaining', isFiniteNumber) &&
84
84
  optional(value, 'unit', isString));
85
85
  }
86
+ function isQuotaCounter(value) {
87
+ return (isRecord(value) &&
88
+ isFiniteNumber(value.value) &&
89
+ Number.isInteger(value.value) &&
90
+ value.value >= 0 &&
91
+ isFiniteNumber(value.checkedAt));
92
+ }
93
+ export function isQuotaCounters(value) {
94
+ return isRecord(value) && Object.values(value).every(isQuotaCounter);
95
+ }
96
+ /** Keep usable counters without letting one malformed count hide a quota map. */
97
+ export function readQuotaCounters(value) {
98
+ if (!isRecord(value))
99
+ return undefined;
100
+ const entries = Object.entries(value).filter(([, entry]) => isQuotaCounter(entry));
101
+ return entries.length === 0
102
+ ? undefined
103
+ : Object.fromEntries(entries);
104
+ }
105
+ /** Decode persisted quota, dropping malformed counters but not limit evidence. */
106
+ export function decodeQuotaMap(value) {
107
+ // A valid map is returned as the same object, so stored quota keeps the
108
+ // identity it had before counters existed; only a map with a malformed
109
+ // counter is copied without it.
110
+ if (isQuotaMap(value))
111
+ return value;
112
+ if (!isRecord(value))
113
+ return undefined;
114
+ const { counters: rawCounters, ...rest } = value;
115
+ if (!isQuotaMap(rest))
116
+ return undefined;
117
+ const counters = readQuotaCounters(rawCounters);
118
+ return { ...rest, ...(counters === undefined ? {} : { counters }) };
119
+ }
86
120
  /**
87
121
  * True when `value` is a well-formed quota map. Unrecognised top-level keys
88
122
  * are tolerated (and preserved by merge) so a newer writer's additions
@@ -101,5 +135,6 @@ export function isQuotaMap(value) {
101
135
  return false;
102
136
  seen.add(key);
103
137
  }
104
- return optional(value, 'budget', isCreditBudgetEntry);
138
+ return (optional(value, 'budget', isCreditBudgetEntry) &&
139
+ optional(value, 'counters', isQuotaCounters));
105
140
  }
@@ -1,4 +1,4 @@
1
- import { type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaMap } from './map.js';
2
2
  export interface ObservedReading {
3
3
  /** Defaults to `all`. */
4
4
  scope?: string;
@@ -32,6 +32,8 @@ export interface QuotaObservation {
32
32
  coverage?: ObservedPair[];
33
33
  /** Absent means the observation says nothing about the budget. */
34
34
  budget?: ObservedBudget;
35
+ /** Each named count uses its own time, independently of limits and budget. */
36
+ counters?: Record<string, QuotaCounter>;
35
37
  }
36
38
  /** Thrown by `mergeQuotaObservation` when either argument is malformed. */
37
39
  export declare class QuotaCodecError extends TypeError {
@@ -11,7 +11,7 @@
11
11
  // no entry gets an absence record. A pair outside the coverage is never
12
12
  // touched, so a header-shaped partial observation (which covers only what it
13
13
  // carries) cannot erase a family limit or the budget.
14
- import { ALL_SCOPE, entryTime, isQuotaMap, limitKey, } from './map.js';
14
+ import { ALL_SCOPE, decodeQuotaMap, entryTime, isQuotaCounters, limitKey, } from './map.js';
15
15
  /** Thrown by `mergeQuotaObservation` when either argument is malformed. */
16
16
  export class QuotaCodecError extends TypeError {
17
17
  constructor(message) {
@@ -83,7 +83,8 @@ export function isQuotaObservation(value) {
83
83
  }
84
84
  if (!coverage.every(isObservedPair))
85
85
  return false;
86
- return value.budget === undefined || isObservedBudget(value.budget);
86
+ return ((value.budget === undefined || isObservedBudget(value.budget)) &&
87
+ (value.counters === undefined || isQuotaCounters(value.counters)));
87
88
  }
88
89
  function compareEntries(left, right) {
89
90
  if (left.scope !== right.scope)
@@ -99,6 +100,16 @@ function mergeBudget(stored, observed, checkedAt) {
99
100
  return stored;
100
101
  return { ...observed, checkedAt };
101
102
  }
103
+ function mergeCounters(stored, observed) {
104
+ const counters = new Map(Object.entries(stored ?? {}));
105
+ for (const [name, counter] of Object.entries(observed ?? {})) {
106
+ const existing = counters.get(name);
107
+ if (existing === undefined || counter.checkedAt >= existing.checkedAt) {
108
+ counters.set(name, counter);
109
+ }
110
+ }
111
+ return counters.size === 0 ? undefined : Object.fromEntries(counters);
112
+ }
102
113
  /**
103
114
  * Applies `observation` to `stored` (undefined for a row with no map yet) and
104
115
  * returns the new map. Pure: neither argument is modified. Throws
@@ -106,13 +117,16 @@ function mergeBudget(stored, observed, checkedAt) {
106
117
  * write rather than persisting a map it could not read back.
107
118
  */
108
119
  export function mergeQuotaObservation(stored, observation) {
109
- if (stored !== undefined && !isQuotaMap(stored)) {
120
+ // Decode the stored map the way reads do, so a malformed counter that reads
121
+ // already hide is dropped here too instead of refusing every later write.
122
+ const decoded = stored === undefined ? undefined : decodeQuotaMap(stored);
123
+ if (stored !== undefined && decoded === undefined) {
110
124
  throw new QuotaCodecError('stored quota map is malformed');
111
125
  }
112
126
  if (!isQuotaObservation(observation)) {
113
127
  throw new QuotaCodecError('quota observation is malformed');
114
128
  }
115
- const base = stored ?? { limits: [] };
129
+ const base = decoded ?? { limits: [] };
116
130
  const at = observation.checkedAt;
117
131
  const entries = new Map();
118
132
  for (const entry of base.limits) {
@@ -152,11 +166,13 @@ export function mergeQuotaObservation(stored, observation) {
152
166
  : { scope, label: pair.label, kind: 'retired', retiredAt: at });
153
167
  }
154
168
  // Keys this module does not recognise are carried over untouched.
155
- const { limits: _limits, budget: _budget, ...unknownKeys } = base;
169
+ const { limits: _limits, budget: _budget, counters: _counters, ...unknownKeys } = base;
156
170
  const budget = mergeBudget(base.budget, observation.budget, at);
171
+ const counters = mergeCounters(base.counters, observation.counters);
157
172
  return {
158
173
  ...unknownKeys,
159
174
  limits: [...entries.values()].sort(compareEntries),
160
175
  ...(budget === undefined ? {} : { budget }),
176
+ ...(counters === undefined ? {} : { counters }),
161
177
  };
162
178
  }
@@ -1,4 +1,4 @@
1
- import { type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaMap } from './map.js';
2
2
  export interface ProjectedLimit {
3
3
  scope: string;
4
4
  label: string;
@@ -30,6 +30,7 @@ export interface ProjectedQuota {
30
30
  checkedAt?: number;
31
31
  /** Present only when the stored budget is a reading, not cleared. */
32
32
  budget?: ProjectedBudget;
33
+ counters?: Record<string, QuotaCounter>;
33
34
  }
34
35
  /**
35
36
  * Resolves `map` for a request in `scope` (`all` or a model family). A family
@@ -6,7 +6,7 @@
6
6
  // scope, orders the result the way the primitives expect, and carries each
7
7
  // entry's (scope, label) so a decision names the limit it judged rather than
8
8
  // the position it occupied.
9
- import { ALL_SCOPE, } from './map.js';
9
+ import { ALL_SCOPE, readQuotaCounters, } from './map.js';
10
10
  function project(entry) {
11
11
  if (entry.kind === 'retired') {
12
12
  return {
@@ -94,11 +94,13 @@ export function projectQuota(map, scope = ALL_SCOPE) {
94
94
  ? Math.min(...stamped.map((limit) => limit.checkedAt))
95
95
  : undefined;
96
96
  const budget = map?.budget?.kind === 'reading' ? projectBudget(map.budget) : undefined;
97
+ const counters = readQuotaCounters(map?.counters);
97
98
  return {
98
99
  scope,
99
100
  limits,
100
101
  ...(checkedAt === undefined ? {} : { checkedAt }),
101
102
  ...(budget === undefined ? {} : { budget }),
103
+ ...(counters === undefined ? {} : { counters }),
102
104
  };
103
105
  }
104
106
  /**
@@ -0,0 +1,49 @@
1
+ import { execFile } from 'node:child_process';
2
+ export const defaultCommandRunner = {
3
+ run(file, args, options = {}) {
4
+ return new Promise((resolve) => {
5
+ const started = performance.now();
6
+ const timeoutMs = options.timeoutMs ?? 15_000;
7
+ execFile(file, [...args], {
8
+ env: options.env ?? process.env,
9
+ timeout: timeoutMs,
10
+ windowsHide: true,
11
+ windowsVerbatimArguments: options.windowsVerbatimArguments,
12
+ maxBuffer: 1024 * 1024,
13
+ }, (error, stdout, stderr) => {
14
+ const durationMs = performance.now() - started;
15
+ resolve({
16
+ exitCode: error
17
+ ? typeof error.code === 'number'
18
+ ? error.code
19
+ : 1
20
+ : 0,
21
+ stdout: String(stdout ?? ''),
22
+ stderr: String(stderr ?? ''),
23
+ signal: error?.signal ?? null,
24
+ // Buffer-limit and spawn errors have string codes, not timeout kills.
25
+ timedOut: error?.killed === true &&
26
+ typeof error.code !== 'string' &&
27
+ error.signal === 'SIGTERM' &&
28
+ durationMs >= timeoutMs &&
29
+ timeoutMs > 0,
30
+ durationMs,
31
+ });
32
+ });
33
+ });
34
+ },
35
+ };
36
+ /** Batch launchers need cmd.exe; no free-form user text is accepted as shell arguments. */
37
+ export function runResolvedCommand(runner, platform, file, args, env, timeoutMs = 15_000) {
38
+ if (platform === 'win32' && /\.(cmd|bat)$/i.test(file)) {
39
+ if (/["%!\r\n]/.test(file) ||
40
+ args.some((arg) => !/^[a-zA-Z0-9@:/._+-]+$/.test(arg)))
41
+ throw new Error('Unsafe batch launcher or argument; use an executable instead.');
42
+ return runner.run('cmd.exe', ['/d', '/s', '/c', `""${file}" ${args.join(' ')}"`], {
43
+ env,
44
+ timeoutMs,
45
+ windowsVerbatimArguments: true,
46
+ });
47
+ }
48
+ return runner.run(file, args, { env, timeoutMs });
49
+ }
@@ -0,0 +1,3 @@
1
+ import type { SetupIO, SetupPlugin, SetupResult } from './types.js';
2
+ export declare const AMBIGUOUS_OPENCODE_MESSAGE = "Multiple OpenCode generations found. Choose --opencode=1|2|both before running noninteractive setup.";
3
+ export declare function runSetup(plugin: SetupPlugin, argv?: readonly string[], io?: SetupIO): Promise<SetupResult>;