quotacap 0.0.20 → 0.0.23

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 (356) hide show
  1. package/README.md +63 -24
  2. package/dist/adapters/agy.d.ts +3 -0
  3. package/dist/adapters/agy.js +81 -0
  4. package/dist/adapters/claude.d.ts +7 -5
  5. package/dist/adapters/claude.js +6 -6
  6. package/dist/adapters/codex.d.ts +3 -3
  7. package/dist/adapters/codex.js +128 -49
  8. package/dist/adapters/grok.d.ts +3 -3
  9. package/dist/adapters/grok.js +114 -70
  10. package/dist/adapters/index.d.ts +5 -2
  11. package/dist/adapters/index.js +36 -9
  12. package/dist/adapters/kimi.d.ts +3 -3
  13. package/dist/adapters/kimi.js +64 -86
  14. package/dist/adapters/manual.d.ts +4 -4
  15. package/dist/adapters/manual.js +2 -2
  16. package/dist/adapters/pty.d.ts +41 -0
  17. package/dist/adapters/pty.js +579 -0
  18. package/dist/adapters/types.d.ts +7 -3
  19. package/dist/advisory/engine.d.ts +19 -2
  20. package/dist/advisory/engine.js +130 -10
  21. package/dist/advisory/snapshot.d.ts +22 -0
  22. package/dist/advisory/snapshot.js +213 -0
  23. package/dist/advisory/types.d.ts +53 -4
  24. package/dist/advisory/validate.d.ts +5 -0
  25. package/dist/advisory/validate.js +18 -0
  26. package/dist/cli/advise.d.ts +3 -0
  27. package/dist/cli/advise.js +128 -0
  28. package/dist/cli/clients.d.ts +28 -0
  29. package/dist/cli/clients.js +11 -0
  30. package/dist/cli/index.js +16 -91
  31. package/dist/cli/ingest.d.ts +3 -0
  32. package/dist/cli/ingest.js +31 -0
  33. package/dist/cli/init.d.ts +2 -0
  34. package/dist/cli/init.js +29 -0
  35. package/dist/cli/runtime.d.ts +33 -0
  36. package/dist/cli/runtime.js +252 -0
  37. package/dist/cli/snapshot-source.d.ts +26 -0
  38. package/dist/cli/snapshot-source.js +232 -0
  39. package/dist/cli/status.d.ts +3 -0
  40. package/dist/cli/status.js +150 -0
  41. package/dist/cli/takeover.d.ts +64 -0
  42. package/dist/cli/takeover.js +175 -0
  43. package/dist/cli/update.d.ts +61 -0
  44. package/dist/cli/update.js +392 -0
  45. package/dist/config.d.ts +27 -0
  46. package/dist/config.js +154 -1
  47. package/dist/daemon.d.ts +4 -29
  48. package/dist/daemon.js +12 -67
  49. package/dist/format/markdown.d.ts +3 -0
  50. package/dist/format/markdown.js +24 -0
  51. package/dist/format/rows.d.ts +27 -0
  52. package/dist/format/rows.js +241 -0
  53. package/dist/format/terminal.d.ts +12 -0
  54. package/dist/format/terminal.js +144 -0
  55. package/dist/http/server.d.ts +32 -3
  56. package/dist/http/server.js +276 -106
  57. package/dist/mcp/server.d.ts +0 -6
  58. package/dist/mcp/server.js +78 -25
  59. package/dist/runtime/client.d.ts +24 -0
  60. package/dist/runtime/client.js +72 -0
  61. package/dist/runtime/owner.d.ts +35 -0
  62. package/dist/runtime/owner.js +393 -0
  63. package/dist/runtime/poll.d.ts +50 -0
  64. package/dist/runtime/poll.js +229 -0
  65. package/dist/runtime/service.d.ts +24 -0
  66. package/dist/runtime/service.js +268 -0
  67. package/dist/runtime/spawn.d.ts +17 -0
  68. package/dist/runtime/spawn.js +97 -0
  69. package/dist/runtime/token.d.ts +3 -0
  70. package/dist/runtime/token.js +64 -0
  71. package/dist/runtime/updates.d.ts +74 -0
  72. package/dist/runtime/updates.js +445 -0
  73. package/dist/runtime/versions.d.ts +2 -0
  74. package/dist/runtime/versions.js +26 -0
  75. package/dist/service/index.d.ts +9 -0
  76. package/dist/service/index.js +103 -0
  77. package/dist/service/macos.d.ts +100 -0
  78. package/dist/service/macos.js +602 -0
  79. package/dist/service/systemd.d.ts +40 -0
  80. package/dist/service/systemd.js +387 -0
  81. package/dist/src/adapters/agy.d.ts +3 -0
  82. package/dist/src/adapters/agy.js +81 -0
  83. package/dist/src/adapters/claude.d.ts +7 -5
  84. package/dist/src/adapters/claude.js +6 -6
  85. package/dist/src/adapters/codex.d.ts +3 -3
  86. package/dist/src/adapters/codex.js +128 -49
  87. package/dist/src/adapters/grok.d.ts +3 -3
  88. package/dist/src/adapters/grok.js +114 -70
  89. package/dist/src/adapters/index.d.ts +5 -2
  90. package/dist/src/adapters/index.js +36 -9
  91. package/dist/src/adapters/kimi.d.ts +3 -3
  92. package/dist/src/adapters/kimi.js +64 -86
  93. package/dist/src/adapters/manual.d.ts +4 -4
  94. package/dist/src/adapters/manual.js +2 -2
  95. package/dist/src/adapters/pty.d.ts +41 -0
  96. package/dist/src/adapters/pty.js +579 -0
  97. package/dist/src/adapters/types.d.ts +7 -3
  98. package/dist/src/advisory/engine.d.ts +19 -2
  99. package/dist/src/advisory/engine.js +130 -10
  100. package/dist/src/advisory/snapshot.d.ts +22 -0
  101. package/dist/src/advisory/snapshot.js +213 -0
  102. package/dist/src/advisory/types.d.ts +53 -4
  103. package/dist/src/advisory/validate.d.ts +5 -0
  104. package/dist/src/advisory/validate.js +18 -0
  105. package/dist/src/cli/advise.d.ts +3 -0
  106. package/dist/src/cli/advise.js +128 -0
  107. package/dist/src/cli/clients.d.ts +28 -0
  108. package/dist/src/cli/clients.js +11 -0
  109. package/dist/src/cli/index.js +16 -91
  110. package/dist/src/cli/ingest.d.ts +3 -0
  111. package/dist/src/cli/ingest.js +31 -0
  112. package/dist/src/cli/init.d.ts +2 -0
  113. package/dist/src/cli/init.js +29 -0
  114. package/dist/src/cli/runtime.d.ts +33 -0
  115. package/dist/src/cli/runtime.js +252 -0
  116. package/dist/src/cli/snapshot-source.d.ts +26 -0
  117. package/dist/src/cli/snapshot-source.js +232 -0
  118. package/dist/src/cli/status.d.ts +3 -0
  119. package/dist/src/cli/status.js +150 -0
  120. package/dist/src/cli/takeover.d.ts +64 -0
  121. package/dist/src/cli/takeover.js +175 -0
  122. package/dist/src/cli/update.d.ts +61 -0
  123. package/dist/src/cli/update.js +392 -0
  124. package/dist/src/config.d.ts +27 -0
  125. package/dist/src/config.js +154 -1
  126. package/dist/src/daemon.d.ts +4 -29
  127. package/dist/src/daemon.js +12 -67
  128. package/dist/src/format/markdown.d.ts +3 -0
  129. package/dist/src/format/markdown.js +24 -0
  130. package/dist/src/format/rows.d.ts +27 -0
  131. package/dist/src/format/rows.js +241 -0
  132. package/dist/src/format/terminal.d.ts +12 -0
  133. package/dist/src/format/terminal.js +144 -0
  134. package/dist/src/http/server.d.ts +32 -3
  135. package/dist/src/http/server.js +276 -106
  136. package/dist/src/mcp/server.d.ts +0 -6
  137. package/dist/src/mcp/server.js +78 -25
  138. package/dist/src/runtime/client.d.ts +24 -0
  139. package/dist/src/runtime/client.js +72 -0
  140. package/dist/src/runtime/owner.d.ts +35 -0
  141. package/dist/src/runtime/owner.js +393 -0
  142. package/dist/src/runtime/poll.d.ts +50 -0
  143. package/dist/src/runtime/poll.js +229 -0
  144. package/dist/src/runtime/service.d.ts +24 -0
  145. package/dist/src/runtime/service.js +268 -0
  146. package/dist/src/runtime/spawn.d.ts +17 -0
  147. package/dist/src/runtime/spawn.js +97 -0
  148. package/dist/src/runtime/token.d.ts +3 -0
  149. package/dist/src/runtime/token.js +64 -0
  150. package/dist/src/runtime/updates.d.ts +74 -0
  151. package/dist/src/runtime/updates.js +445 -0
  152. package/dist/src/runtime/versions.d.ts +2 -0
  153. package/dist/src/runtime/versions.js +26 -0
  154. package/dist/src/service/index.d.ts +9 -0
  155. package/dist/src/service/index.js +103 -0
  156. package/dist/src/service/macos.d.ts +100 -0
  157. package/dist/src/service/macos.js +602 -0
  158. package/dist/src/service/systemd.d.ts +40 -0
  159. package/dist/src/service/systemd.js +387 -0
  160. package/dist/src/store/attempts.d.ts +12 -0
  161. package/dist/src/store/attempts.js +29 -0
  162. package/dist/src/store/db.d.ts +1 -1
  163. package/dist/src/store/db.js +58 -2
  164. package/dist/src/store/quotas.js +52 -10
  165. package/dist/src/version.js +1 -1
  166. package/dist/src/webAssets.js +1 -1
  167. package/dist/src/webHtml.js +1 -1
  168. package/dist/store/attempts.d.ts +12 -0
  169. package/dist/store/attempts.js +29 -0
  170. package/dist/store/db.d.ts +1 -1
  171. package/dist/store/db.js +58 -2
  172. package/dist/store/quotas.js +52 -10
  173. package/dist/tests/adapters/agy.test.js +337 -0
  174. package/dist/tests/adapters/claude.test.js +41 -1
  175. package/dist/tests/adapters/codex.test.js +231 -91
  176. package/dist/tests/adapters/credential-free.test.js +211 -0
  177. package/dist/tests/adapters/grok.test.js +249 -135
  178. package/dist/tests/adapters/kimi.test.js +219 -104
  179. package/dist/tests/adapters/pty-perms.test.d.ts +1 -0
  180. package/dist/tests/adapters/pty-perms.test.js +78 -0
  181. package/dist/tests/adapters/pty.test.d.ts +1 -0
  182. package/dist/tests/adapters/pty.test.js +240 -0
  183. package/dist/tests/adapters/registry.test.js +5 -5
  184. package/dist/tests/advisory/engine.test.js +194 -6
  185. package/dist/tests/advisory/pace.test.d.ts +1 -0
  186. package/dist/tests/advisory/pace.test.js +60 -0
  187. package/dist/tests/advisory/snapshot.test.d.ts +1 -0
  188. package/dist/tests/advisory/snapshot.test.js +65 -0
  189. package/dist/tests/advisory/validate.test.d.ts +1 -0
  190. package/dist/tests/advisory/validate.test.js +19 -0
  191. package/dist/tests/cli/advise.test.d.ts +1 -0
  192. package/dist/tests/cli/advise.test.js +367 -0
  193. package/dist/tests/cli/cli.test.js +39 -19
  194. package/dist/tests/cli/helpers.d.ts +9 -0
  195. package/dist/tests/cli/helpers.js +93 -0
  196. package/dist/tests/cli/ingest.test.d.ts +1 -0
  197. package/dist/tests/cli/ingest.test.js +188 -0
  198. package/dist/tests/cli/init.test.d.ts +1 -0
  199. package/dist/tests/cli/init.test.js +55 -0
  200. package/dist/tests/cli/launcher.test.d.ts +1 -0
  201. package/dist/tests/cli/launcher.test.js +271 -0
  202. package/dist/tests/cli/parity.test.d.ts +1 -0
  203. package/dist/tests/cli/parity.test.js +225 -0
  204. package/dist/tests/cli/snapshot-source.test.d.ts +1 -0
  205. package/dist/tests/cli/snapshot-source.test.js +305 -0
  206. package/dist/tests/cli/status.test.d.ts +1 -0
  207. package/dist/tests/cli/status.test.js +536 -0
  208. package/dist/tests/cli/takeover.test.d.ts +1 -0
  209. package/dist/tests/cli/takeover.test.js +325 -0
  210. package/dist/tests/cli/update.test.d.ts +1 -0
  211. package/dist/tests/cli/update.test.js +382 -0
  212. package/dist/tests/daemon.test.js +140 -38
  213. package/dist/tests/fixtures/stable-state.d.ts +22 -0
  214. package/dist/tests/fixtures/stable-state.js +152 -0
  215. package/dist/tests/fixtures/stable-state.test.d.ts +1 -0
  216. package/dist/tests/fixtures/stable-state.test.js +17 -0
  217. package/dist/tests/format/markdown.test.d.ts +1 -0
  218. package/dist/tests/format/markdown.test.js +49 -0
  219. package/dist/tests/format/terminal.test.d.ts +1 -0
  220. package/dist/tests/format/terminal.test.js +476 -0
  221. package/dist/tests/http/api.test.js +224 -2
  222. package/dist/tests/integration.test.js +20 -7
  223. package/dist/tests/mcp/protocol.test.d.ts +1 -0
  224. package/dist/tests/mcp/protocol.test.js +134 -0
  225. package/dist/tests/mcp/quotas.test.js +38 -6
  226. package/dist/tests/mcp/tools.test.d.ts +1 -0
  227. package/dist/tests/mcp/tools.test.js +291 -0
  228. package/dist/tests/runtime/cancel.test.d.ts +1 -0
  229. package/dist/tests/runtime/cancel.test.js +264 -0
  230. package/dist/tests/runtime/cli.test.d.ts +1 -0
  231. package/dist/tests/runtime/cli.test.js +426 -0
  232. package/dist/tests/runtime/config.test.d.ts +1 -0
  233. package/dist/tests/runtime/config.test.js +144 -0
  234. package/dist/tests/runtime/http.test.d.ts +1 -0
  235. package/dist/tests/runtime/http.test.js +346 -0
  236. package/dist/tests/runtime/lifecycle.test.d.ts +1 -0
  237. package/dist/tests/runtime/lifecycle.test.js +303 -0
  238. package/dist/tests/runtime/ownership.test.d.ts +1 -0
  239. package/dist/tests/runtime/ownership.test.js +355 -0
  240. package/dist/tests/runtime/poll.test.d.ts +1 -0
  241. package/dist/tests/runtime/poll.test.js +438 -0
  242. package/dist/tests/runtime/status.test.d.ts +1 -0
  243. package/dist/tests/runtime/status.test.js +232 -0
  244. package/dist/tests/runtime/updates.test.d.ts +1 -0
  245. package/dist/tests/runtime/updates.test.js +509 -0
  246. package/dist/tests/service/macos.test.d.ts +1 -0
  247. package/dist/tests/service/macos.test.js +450 -0
  248. package/dist/tests/service/systemd.test.d.ts +1 -0
  249. package/dist/tests/service/systemd.test.js +330 -0
  250. package/dist/tests/smoke/dashboard.e2e.d.ts +1 -0
  251. package/dist/tests/smoke/dashboard.e2e.js +142 -0
  252. package/dist/tests/smoke/playwright.config.d.ts +2 -0
  253. package/dist/tests/smoke/playwright.config.js +11 -0
  254. package/dist/tests/store/attempts.test.d.ts +1 -0
  255. package/dist/tests/store/attempts.test.js +29 -0
  256. package/dist/tests/store/db.test.js +61 -6
  257. package/dist/tests/store/migration.test.d.ts +1 -0
  258. package/dist/tests/store/migration.test.js +41 -0
  259. package/dist/tests/web/advice-drawer.test.d.ts +1 -0
  260. package/dist/tests/web/advice-drawer.test.js +45 -0
  261. package/dist/tests/web/dashboard.e2e.d.ts +1 -0
  262. package/dist/tests/web/dashboard.e2e.js +670 -0
  263. package/dist/tests/web/header.test.d.ts +1 -0
  264. package/dist/tests/web/header.test.js +82 -0
  265. package/dist/tests/web/names.test.d.ts +1 -0
  266. package/dist/tests/web/names.test.js +19 -0
  267. package/dist/tests/web/onboarding.test.d.ts +1 -0
  268. package/dist/tests/web/onboarding.test.js +21 -0
  269. package/dist/tests/web/pacing.test.d.ts +1 -0
  270. package/dist/tests/web/pacing.test.js +62 -0
  271. package/dist/tests/web/playwright.config.d.ts +2 -0
  272. package/dist/tests/web/playwright.config.js +10 -0
  273. package/dist/tests/web/provider-drawer.test.d.ts +1 -0
  274. package/dist/tests/web/provider-drawer.test.js +68 -0
  275. package/dist/tests/web/recommendation.test.d.ts +1 -0
  276. package/dist/tests/web/recommendation.test.js +38 -0
  277. package/dist/tests/web/reset-rail.test.d.ts +1 -0
  278. package/dist/tests/web/reset-rail.test.js +63 -0
  279. package/dist/tests/web/responsive.test.d.ts +1 -0
  280. package/dist/tests/web/responsive.test.js +21 -0
  281. package/dist/tests/web/settings.test.d.ts +1 -0
  282. package/dist/tests/web/settings.test.js +32 -0
  283. package/dist/tests/web/state-catalog.test.d.ts +1 -0
  284. package/dist/tests/web/state-catalog.test.js +206 -0
  285. package/dist/tests/web/state.test.d.ts +1 -0
  286. package/dist/tests/web/state.test.js +27 -0
  287. package/dist/tests/web/stub-server.d.ts +37 -0
  288. package/dist/tests/web/stub-server.js +132 -0
  289. package/dist/tests/web/theme.test.d.ts +1 -0
  290. package/dist/tests/web/theme.test.js +12 -0
  291. package/dist/version.js +1 -1
  292. package/dist/web/src/App.d.ts +1 -0
  293. package/dist/web/src/App.js +106 -78
  294. package/dist/web/src/Theme.d.ts +11 -0
  295. package/dist/web/src/Theme.js +53 -0
  296. package/dist/web/src/api.d.ts +27 -2
  297. package/dist/web/src/api.js +102 -6
  298. package/dist/web/src/components/AdviceDrawer.d.ts +9 -0
  299. package/dist/web/src/components/AdviceDrawer.js +77 -0
  300. package/dist/web/src/components/FaultBanner.d.ts +17 -0
  301. package/dist/web/src/components/FaultBanner.js +60 -0
  302. package/dist/web/src/components/Header.d.ts +22 -0
  303. package/dist/web/src/components/Header.js +50 -0
  304. package/dist/web/src/components/PaceBar.d.ts +51 -0
  305. package/dist/web/src/components/PaceBar.js +251 -0
  306. package/dist/web/src/components/PacingLegend.d.ts +3 -0
  307. package/dist/web/src/components/PacingLegend.js +43 -0
  308. package/dist/web/src/components/ProviderCard.d.ts +8 -0
  309. package/dist/web/src/components/ProviderCard.js +27 -0
  310. package/dist/web/src/components/ProviderDrawer.d.ts +52 -0
  311. package/dist/web/src/components/ProviderDrawer.js +270 -0
  312. package/dist/web/src/components/ProviderIcon.d.ts +7 -0
  313. package/dist/web/src/components/ProviderIcon.js +27 -0
  314. package/dist/web/src/components/ProviderRow.d.ts +8 -0
  315. package/dist/web/src/components/ProviderRow.js +14 -0
  316. package/dist/web/src/components/Recommendation.d.ts +27 -0
  317. package/dist/web/src/components/Recommendation.js +77 -0
  318. package/dist/web/src/components/ResetRail.d.ts +38 -0
  319. package/dist/web/src/components/ResetRail.js +188 -0
  320. package/dist/web/src/components/SubscriptionList.d.ts +19 -0
  321. package/dist/web/src/components/SubscriptionList.js +107 -0
  322. package/dist/web/src/components/settings/CliIntegrationPanel.d.ts +2 -0
  323. package/dist/web/src/components/settings/CliIntegrationPanel.js +49 -0
  324. package/dist/web/src/components/settings/DaemonPanel.d.ts +6 -0
  325. package/dist/web/src/components/settings/DaemonPanel.js +45 -0
  326. package/dist/web/src/components/settings/ProvidersPanel.d.ts +14 -0
  327. package/dist/web/src/components/settings/ProvidersPanel.js +69 -0
  328. package/dist/web/src/components/settings/SettingsDrawer.d.ts +9 -0
  329. package/dist/web/src/components/settings/SettingsDrawer.js +55 -0
  330. package/dist/web/src/names.d.ts +3 -0
  331. package/dist/web/src/names.js +19 -0
  332. package/dist/web/src/pages/Onboarding.d.ts +17 -0
  333. package/dist/web/src/pages/Onboarding.js +96 -0
  334. package/dist/web/src/router.d.ts +11 -0
  335. package/dist/web/src/router.js +31 -0
  336. package/dist/web/src/state.d.ts +104 -0
  337. package/dist/web/src/state.js +80 -0
  338. package/dist/webAssets.js +1 -1
  339. package/dist/webHtml.js +1 -1
  340. package/package.json +27 -9
  341. package/web/dist/assets/index-BEBqgvq8.css +1 -0
  342. package/web/dist/assets/index-DZfttQgo.js +12 -0
  343. package/web/dist/index.html +19 -2
  344. package/dist/adapters/core.d.ts +0 -9
  345. package/dist/adapters/core.js +0 -90
  346. package/dist/format/table.d.ts +0 -2
  347. package/dist/format/table.js +0 -56
  348. package/dist/src/adapters/core.d.ts +0 -9
  349. package/dist/src/adapters/core.js +0 -90
  350. package/dist/src/format/table.d.ts +0 -2
  351. package/dist/src/format/table.js +0 -56
  352. package/dist/tests/adapters/core.test.js +0 -110
  353. package/dist/tests/format/table.test.js +0 -98
  354. package/web/dist/assets/index-Dpiqzcc8.js +0 -40
  355. /package/dist/tests/adapters/{core.test.d.ts → agy.test.d.ts} +0 -0
  356. /package/dist/tests/{format/table.test.d.ts → adapters/credential-free.test.d.ts} +0 -0
@@ -1,82 +1,126 @@
1
1
  import os from "node:os";
2
- import path from "node:path";
3
- import { readJsonFile, getJson, postForm, persistCreds } from "./core.js";
4
- const BILLING_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
5
- const REFRESH_URL = "https://auth.x.ai/oauth2/token";
6
- const CLIENT_VERSION = "1.0.0";
7
- export function parseGrokUsage(body, now = new Date()) {
8
- const cfg = body?.config;
9
- if (!cfg)
10
- throw new Error("grok: no config in billing response");
11
- const pct = cfg.creditUsagePercent;
12
- if (typeof pct !== "number")
13
- throw new Error("grok: creditUsagePercent missing");
14
- const period = cfg.currentPeriod ?? {};
2
+ import { runPty, stripAnsi } from "./pty.js";
3
+ import { adapterSignal } from "../runtime/spawn.js";
4
+ const FULL_MONTHS = {
5
+ january: 0, february: 1, march: 2, april: 3, may: 4, june: 5,
6
+ july: 6, august: 7, september: 8, october: 9, november: 10, december: 11,
7
+ };
8
+ const ABBR_MONTHS = {
9
+ jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5,
10
+ jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11,
11
+ };
12
+ function parseGrokReset(raw, now) {
13
+ const m = raw.trim().match(/^([A-Za-z]+)\s+(\d{1,2}),\s*(\d{1,2}):(\d{2})$/);
14
+ if (!m)
15
+ return null;
16
+ const monStr = m[1].toLowerCase();
17
+ const day = parseInt(m[2], 10);
18
+ const hours = parseInt(m[3], 10);
19
+ const mins = parseInt(m[4], 10);
20
+ let month = FULL_MONTHS[monStr];
21
+ if (month === undefined)
22
+ month = ABBR_MONTHS[monStr.slice(0, 3)];
23
+ if (month === undefined || !Number.isFinite(day) || !Number.isFinite(hours) || !Number.isFinite(mins))
24
+ return null;
25
+ if (day < 1 || day > 31 || hours < 0 || hours > 23 || mins < 0 || mins > 59)
26
+ return null;
27
+ let year = now.getFullYear();
28
+ let dt = new Date(year, month, day, hours, mins, 0, 0);
29
+ if (Number.isNaN(dt.getTime()))
30
+ return null;
31
+ if (dt.getTime() < now.getTime()) {
32
+ dt = new Date(year + 1, month, day, hours, mins, 0, 0);
33
+ if (Number.isNaN(dt.getTime()))
34
+ return null;
35
+ }
36
+ return dt.toISOString();
37
+ }
38
+ export function parseGrokTui(text, now = new Date()) {
39
+ const cleaned = stripAnsi(text);
40
+ const planMatch = cleaned.match(/Weekly lim[i!l]t\s*\(([^)]+)\)/i);
41
+ let plan = "unknown";
42
+ if (planMatch) {
43
+ plan = planMatch[1].trim();
44
+ }
45
+ else {
46
+ const tier = cleaned.match(/\b(SuperGrok|Grok\s+Pro|Grok\s+Enterprise|Grok\s+Basic)\b/i);
47
+ if (tier)
48
+ plan = tier[1].trim();
49
+ }
50
+ const pctRe = /Weekly lim[i!l]t[^\n%]*?(\d+)%/i;
51
+ const mPct = cleaned.match(pctRe);
52
+ let usedPct = null;
53
+ if (mPct) {
54
+ const v = parseInt(mPct[1], 10);
55
+ if (Number.isFinite(v) && v >= 0 && v <= 100)
56
+ usedPct = v;
57
+ }
58
+ if (usedPct === null) {
59
+ const barRe = /[█░▓▓]+\s*(\d+)%/;
60
+ const b = cleaned.match(barRe);
61
+ if (b) {
62
+ const v = parseInt(b[1], 10);
63
+ if (Number.isFinite(v) && v >= 0 && v <= 100)
64
+ usedPct = v;
65
+ }
66
+ }
67
+ if (usedPct === null)
68
+ throw new Error("grok: weekly percent not found in TUI output");
69
+ let creditsUsd;
70
+ const mCred = cleaned.match(/Credits:\s*\$([0-9]+(?:\.[0-9]+)?)/i);
71
+ if (mCred) {
72
+ const v = parseFloat(mCred[1]);
73
+ if (Number.isFinite(v) && v >= 0)
74
+ creditsUsd = v;
75
+ }
76
+ const mReset = cleaned.match(/Resets:\s*([A-Za-z]+\s+\d+,\s*\d+:\d+)/i);
77
+ let resetsAt = null;
78
+ let estimated = false;
79
+ if (mReset) {
80
+ const resetsRaw = mReset[1].trim();
81
+ resetsAt = parseGrokReset(resetsRaw, now);
82
+ if (!resetsAt)
83
+ throw new Error(`grok: bad resets timestamp "${resetsRaw}"`);
84
+ }
85
+ else if (cleaned.match(/Resets:/i)) {
86
+ throw new Error("grok: bad resets timestamp");
87
+ }
88
+ else {
89
+ resetsAt = new Date(now.getTime() + 7 * 86400000).toISOString();
90
+ estimated = true;
91
+ }
92
+ const periodStart = new Date(new Date(resetsAt).getTime() - 7 * 86400000).toISOString();
15
93
  return {
16
94
  provider: "grok",
17
- plan: body?.subscriptionTier ?? "unknown",
18
- usedPct: pct,
19
- resetsAt: period.end ?? new Date(now.getTime() + 7 * 86400000).toISOString(),
20
- periodStart: period.start ?? new Date(now.getTime() - 7 * 86400000).toISOString(),
21
- raw: JSON.stringify(body),
22
- source: "api",
95
+ plan,
96
+ usedPct,
97
+ resetsAt,
98
+ periodStart,
99
+ source: "tui",
23
100
  fetchedAt: now.toISOString(),
101
+ creditsUsd,
102
+ resetsAtEstimated: estimated || undefined,
103
+ raw: cleaned.slice(0, 4096),
24
104
  };
25
105
  }
26
- function grokHome() {
27
- return process.env.GROK_HOME ?? path.join(os.homedir(), ".grok");
28
- }
29
- async function loadEntry() {
30
- const file = path.join(grokHome(), "auth.json");
31
- const auth = await readJsonFile(file);
32
- for (const [entryKey, entry] of Object.entries(auth)) {
33
- if (entry.refresh_token)
34
- return { file, entryKey, entry };
35
- }
36
- throw new Error("grok: no refresh_token in auth.json — run `grok login`");
37
- }
38
106
  export const grokAdapter = {
39
107
  id: "grok",
40
- requiresAuth: "~/.grok/auth.json (grok login)",
108
+ requiresAuth: "grok login (CLI owns credentials)",
41
109
  async poll() {
42
- const { file, entryKey, entry } = await loadEntry();
43
- const expiryMs = entry.expires_at ? Date.parse(String(entry.expires_at)) : NaN;
44
- const reuseToken = Boolean(entry.key) && Number.isFinite(expiryMs) && expiryMs - 60000 > Date.now();
45
- let token;
46
- if (reuseToken) {
47
- token = entry.key;
48
- }
49
- else {
50
- const tok = await postForm(REFRESH_URL, {
51
- grant_type: "refresh_token",
52
- client_id: entry.oidc_client_id ?? "",
53
- refresh_token: entry.refresh_token ?? "",
54
- });
55
- if (!tok.access_token)
56
- throw new Error("grok: refresh returned no access_token");
57
- await persistCreds(file, (cur) => {
58
- const next = { ...cur };
59
- const target = next[entryKey];
60
- if (target) {
61
- next[entryKey] = {
62
- ...target,
63
- key: tok.access_token ?? target.key,
64
- refresh_token: tok.refresh_token ?? target.refresh_token,
65
- expires_at: tok.expires_at ??
66
- (tok.expires_in ? new Date(Date.now() + Number(tok.expires_in) * 1000).toISOString() : target.expires_at),
67
- };
68
- }
69
- return next;
70
- });
71
- token = String(tok.access_token);
72
- }
73
- const body = await getJson(BILLING_URL, {
74
- Authorization: `Bearer ${token}`,
75
- Accept: "application/json",
76
- "x-grok-client-version": CLIENT_VERSION,
77
- "x-grok-client-surface": "grok-build",
78
- "X-XAI-Token-Auth": "xai-grok-cli",
110
+ const transcript = await runPty({
111
+ file: "grok",
112
+ args: [],
113
+ cwd: os.homedir(),
114
+ cols: 140,
115
+ rows: 50,
116
+ settleDelayMs: 5000,
117
+ input: "/usage\r",
118
+ completionRegex: /Weekly lim|Credits:/i,
119
+ timeoutMs: 14000,
120
+ maxBytes: 256 * 1024,
121
+ signal: adapterSignal("grok"),
122
+ label: "grok",
79
123
  });
80
- return parseGrokUsage(body);
124
+ return parseGrokTui(transcript);
81
125
  },
82
126
  };
@@ -1,9 +1,12 @@
1
1
  import type { Adapter } from "./types.js";
2
2
  export declare const adapters: Record<string, Adapter>;
3
- export declare function pollAll(enabled: string[]): Promise<({
3
+ export interface PollAllOptions {
4
+ timeouts?: Record<string, number>;
5
+ }
6
+ export declare function pollAll(enabled: string[], opts?: PollAllOptions): Promise<({
4
7
  provider: string;
5
8
  status: "fulfilled";
6
- value: import("./types.js").Quota;
9
+ value: unknown;
7
10
  reason?: undefined;
8
11
  } | {
9
12
  provider: string;
@@ -3,28 +3,55 @@ import { manualAdapter } from "./manual.js";
3
3
  import { codexAdapter } from "./codex.js";
4
4
  import { kimiAdapter } from "./kimi.js";
5
5
  import { grokAdapter } from "./grok.js";
6
+ import { agyAdapter } from "./agy.js";
7
+ import { installAdapterSignal, clearAdapterSignal } from "../runtime/spawn.js";
6
8
  export const adapters = {
7
9
  claude: claudeAdapter,
8
10
  manual: manualAdapter,
9
11
  codex: codexAdapter,
10
12
  kimi: kimiAdapter,
11
13
  grok: grokAdapter,
14
+ agy: agyAdapter,
12
15
  };
13
- function withTimeout(p, ms) {
14
- return new Promise((resolve, reject) => {
15
- const timer = setTimeout(() => reject(new Error(`timeout after ${ms}ms`)), ms);
16
- Promise.resolve(p).then((v) => { clearTimeout(timer); resolve(v); }, (e) => { clearTimeout(timer); reject(e); });
17
- });
18
- }
19
- export async function pollAll(enabled) {
16
+ const ADAPTER_TIMEOUTS = {
17
+ claude: 8000,
18
+ codex: 12000,
19
+ kimi: 8000,
20
+ grok: 14000,
21
+ agy: 20000,
22
+ };
23
+ export async function pollAll(enabled, opts) {
20
24
  const rawJobs = enabled.map(id => {
21
25
  const a = adapters[id];
22
26
  if (!a)
23
27
  return Promise.reject(new Error(`unknown adapter ${id}`));
24
28
  // manual adapter has no poll capability — skip without degraded
25
29
  if (id === "manual")
26
- return Promise.reject(new Error("manual skipped — use ingest"));
27
- return withTimeout(a.poll(), 8000);
30
+ return Promise.reject(new Error("manual skipped"));
31
+ const timeout = opts?.timeouts?.[id] ?? ADAPTER_TIMEOUTS[id] ?? 8000;
32
+ // One controller per adapter: a timeout aborts only that adapter's
33
+ // children; the signal clears when its job settles.
34
+ const controller = new AbortController();
35
+ installAdapterSignal(id, controller.signal);
36
+ let p;
37
+ try {
38
+ p = Promise.resolve(a.poll());
39
+ }
40
+ catch (e) {
41
+ clearAdapterSignal(id);
42
+ throw e;
43
+ }
44
+ const gated = new Promise((resolve, reject) => {
45
+ const timer = setTimeout(() => {
46
+ try {
47
+ controller.abort();
48
+ }
49
+ catch { }
50
+ reject(new Error(`timeout after ${timeout}ms`));
51
+ }, timeout);
52
+ p.then((v) => { clearTimeout(timer); resolve(v); }, (e) => { clearTimeout(timer); reject(e); });
53
+ });
54
+ return gated.finally(() => clearAdapterSignal(id));
28
55
  });
29
56
  const settled = await Promise.allSettled(rawJobs);
30
57
  return settled.map((s, i) => {
@@ -1,7 +1,7 @@
1
- import type { Quota } from "./types.js";
2
- export declare function parseKimiUsage(body: any, now?: Date): Quota;
1
+ import type { ParsedQuota } from "./types.js";
2
+ export declare function parseKimiTui(text: string, now?: Date): ParsedQuota;
3
3
  export declare const kimiAdapter: {
4
4
  id: string;
5
5
  requiresAuth: string;
6
- poll(): Promise<Quota>;
6
+ poll(): Promise<ParsedQuota>;
7
7
  };
@@ -1,98 +1,76 @@
1
1
  import os from "node:os";
2
- import path from "node:path";
3
- import { readJsonFile, getJson, postForm, persistCreds } from "./core.js";
4
- const USAGE_URL = "https://api.kimi.com/coding/v1/usages";
5
- const REFRESH_URL = "https://auth.kimi.com/api/oauth/token";
6
- const CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098";
7
- export function parseKimiUsage(body, now = new Date()) {
8
- const usage = body?.usage;
9
- if (!usage)
10
- throw new Error("kimi: no usage in response");
11
- const limit = Number(usage.limit);
12
- const used = Number(usage.used);
13
- if (!(limit > 0) || Number.isNaN(used))
14
- throw new Error("kimi: bad usage numbers");
15
- const resetsAt = usage.resetTime;
16
- if (Number.isNaN(new Date(resetsAt).getTime()))
17
- throw new Error("kimi: bad resetTime");
18
- const periodStart = new Date(new Date(resetsAt).getTime() - 7 * 86400000).toISOString();
19
- const level = body?.user?.membership?.level;
2
+ import { parseResetText } from "./parse.js";
3
+ import { runPty, stripAnsi } from "./pty.js";
4
+ import { adapterSignal } from "../runtime/spawn.js";
5
+ export function parseKimiTui(text, now = new Date()) {
6
+ const cleaned = stripAnsi(text);
7
+ const weeklyRe = /Weekly limit\s+[^0-9]*(\d+)%\s+used\s+resets\s+(in\s+[^\n│\r]+)/i;
8
+ const fiveRe = /5h limit\s+[^0-9]*(\d+)%\s+used\s+resets\s+(in\s+[^\n│\r]+)/i;
9
+ const w = cleaned.match(weeklyRe);
10
+ if (!w)
11
+ throw new Error("kimi: weekly limit not found in TUI output");
12
+ const f = cleaned.match(fiveRe);
13
+ if (!f)
14
+ throw new Error("kimi: 5h limit not found in TUI output");
15
+ const usedPct = parseInt(w[1], 10);
16
+ const sessionPct = parseInt(f[1], 10);
17
+ if (!Number.isFinite(usedPct) || usedPct < 0 || usedPct > 100)
18
+ throw new Error("kimi: bad weekly pct");
19
+ if (!Number.isFinite(sessionPct) || sessionPct < 0 || sessionPct > 100)
20
+ throw new Error("kimi: bad 5h pct");
21
+ const weeklyRaw = w[2].trim();
22
+ const fiveRaw = f[2].trim();
23
+ const weeklyIso = parseResetText(`resets ${weeklyRaw}`, now);
24
+ if (!weeklyIso)
25
+ throw new Error(`kimi: bad weekly reset "${weeklyRaw}"`);
26
+ const fiveIso = parseResetText(`resets ${fiveRaw}`, now);
27
+ if (!fiveIso)
28
+ throw new Error(`kimi: bad 5h reset "${fiveRaw}"`);
29
+ let plan = "unknown";
30
+ const paren = cleaned.match(/Weekly limit\s*\(([^)]+)\)/i);
31
+ if (paren) {
32
+ plan = paren[1].trim().toLowerCase();
33
+ }
34
+ else {
35
+ const lvl = cleaned.match(/level\s*[:\-]\s*([A-Za-z0-9_\-]+)/i);
36
+ if (lvl)
37
+ plan = lvl[1].trim().toLowerCase().replace(/^level_/i, "");
38
+ }
39
+ const periodStart = new Date(new Date(weeklyIso).getTime() - 7 * 86400000).toISOString();
20
40
  return {
21
41
  provider: "kimi",
22
- plan: level ? String(level).replace(/^LEVEL_/, "").toLowerCase() : "unknown",
23
- usedPct: Math.round((used / limit) * 100),
24
- resetsAt,
42
+ plan,
43
+ usedPct,
44
+ sessionPct,
45
+ resetsAt: weeklyIso,
25
46
  periodStart,
26
- raw: JSON.stringify(body),
27
- source: "api",
47
+ source: "tui",
28
48
  fetchedAt: now.toISOString(),
49
+ raw: cleaned.slice(0, 4096),
29
50
  };
30
51
  }
31
- function credsCandidates() {
32
- const home = process.env.KIMI_CODE_HOME ?? path.join(os.homedir(), ".kimi-code");
33
- const legacy = path.join(os.homedir(), ".kimi", "credentials", "kimi-code.json");
34
- return [path.join(home, "credentials", "kimi-code.json"), legacy];
35
- }
36
- async function loadCreds() {
37
- for (const file of credsCandidates()) {
38
- try {
39
- return { file, creds: await readJsonFile(file) };
40
- }
41
- catch {
42
- continue;
43
- }
44
- }
45
- throw new Error("kimi: no credentials file — run `kimi login`");
46
- }
47
- function isFreshExpiry(raw) {
48
- const v = Number(raw);
49
- if (!Number.isFinite(v) || v <= 0)
50
- return false;
51
- const ms = v < 1e12 ? v * 1000 : v; // CLI stores seconds; legacy rows from earlier quotas are ms
52
- return ms > Date.now();
53
- }
54
- async function refreshAndPersist(file, creds) {
55
- if (!creds.refresh_token)
56
- throw new Error("kimi: no refresh_token — run `kimi login`");
57
- const tok = await postForm(REFRESH_URL, {
58
- grant_type: "refresh_token",
59
- client_id: CLIENT_ID,
60
- refresh_token: creds.refresh_token,
61
- });
62
- if (!tok.access_token)
63
- throw new Error("kimi: refresh returned no access_token");
64
- await persistCreds(file, (cur) => ({
65
- ...cur,
66
- access_token: tok.access_token,
67
- refresh_token: tok.refresh_token ?? cur.refresh_token,
68
- expires_at: Math.floor(Date.now() / 1000) + (Number(tok.expires_in) || 3600),
69
- token_type: tok.token_type ?? cur.token_type,
70
- scope: tok.scope ?? cur.scope,
71
- }));
72
- return String(tok.access_token);
73
- }
74
52
  export const kimiAdapter = {
75
53
  id: "kimi",
76
- requiresAuth: "~/.kimi-code/credentials/kimi-code.json (kimi login)",
54
+ requiresAuth: "kimi login (CLI owns credentials)",
77
55
  async poll() {
78
- const { file, creds } = await loadCreds();
79
- let token = creds.access_token ?? "";
80
- let justRefreshed = false;
81
- if (!token)
82
- throw new Error("kimi: no access_token — run `kimi login`");
83
- if (!isFreshExpiry(creds.expires_at)) {
84
- token = await refreshAndPersist(file, creds);
85
- justRefreshed = true;
86
- }
87
- const call = (t) => getJson(USAGE_URL, { Authorization: `Bearer ${t}` }).then(parseKimiUsage);
88
- try {
89
- return await call(token);
90
- }
91
- catch (e) {
92
- if (e.status !== 401 || justRefreshed)
93
- throw e;
94
- const fresh = await refreshAndPersist(file, creds);
95
- return await call(fresh);
96
- }
56
+ // Use homedir as neutral cwd so quota modal does not depend on project path
57
+ // and workspace-trust prompts are minimized. Fail closed if trust prompt appears.
58
+ const transcript = await runPty({
59
+ file: "kimi",
60
+ args: [],
61
+ cols: 140,
62
+ rows: 35,
63
+ cwd: os.homedir(),
64
+ readyRegex: /Welcome to Kimi Code|context:/i,
65
+ readyTimeoutMs: 8000,
66
+ input: "/usage\r",
67
+ completionRegex: /Weekly limit[\s\S]*?5h limit|5h limit[\s\S]*?Weekly limit/i,
68
+ abortOn: /Trust this folder\?/i,
69
+ timeoutMs: 8000,
70
+ maxBytes: 256 * 1024,
71
+ signal: adapterSignal("kimi"),
72
+ label: "kimi",
73
+ });
74
+ return parseKimiTui(transcript);
97
75
  },
98
76
  };
@@ -1,8 +1,8 @@
1
- import type { Quota } from "./types.js";
2
- export declare function parseManualUsage(provider: string, text: string, now?: Date): Quota;
1
+ import type { ParsedQuota } from "./types.js";
2
+ export declare function parseManualUsage(provider: string, text: string, now?: Date): ParsedQuota;
3
3
  export declare const manualAdapter: {
4
4
  id: string;
5
5
  requiresAuth: string;
6
- ingest(provider: string, text: string): Promise<Quota>;
7
- poll(): Promise<Quota>;
6
+ ingest(provider: string, text: string): Promise<ParsedQuota>;
7
+ poll(): Promise<ParsedQuota>;
8
8
  };
@@ -11,9 +11,9 @@ export function parseManualUsage(provider, text, now = new Date()) {
11
11
  usedPct,
12
12
  resetsAt,
13
13
  periodStart,
14
- raw: text,
15
14
  source: "manual",
16
15
  fetchedAt: now.toISOString(),
16
+ raw: text,
17
17
  };
18
18
  }
19
19
  export const manualAdapter = {
@@ -23,6 +23,6 @@ export const manualAdapter = {
23
23
  return parseManualUsage(provider, text);
24
24
  },
25
25
  async poll() {
26
- throw new Error("manual has no poll — use ingest");
26
+ throw new Error("manual has no poll");
27
27
  },
28
28
  };
@@ -0,0 +1,41 @@
1
+ export declare function stripAnsi(s: string): string;
2
+ export interface PtyRunOptions {
3
+ file: string;
4
+ args?: string[];
5
+ cwd?: string;
6
+ env?: Record<string, string>;
7
+ cols?: number;
8
+ rows?: number;
9
+ readyRegex?: RegExp;
10
+ readyTimeoutMs?: number;
11
+ settleDelayMs?: number;
12
+ input: string;
13
+ completionRegex?: RegExp;
14
+ /** If matched, abort immediately with a clear error (e.g. trust prompts). */
15
+ abortOn?: RegExp;
16
+ timeoutMs: number;
17
+ maxBytes?: number;
18
+ /** Abort kills the child via the existing kill path and rejects `pty aborted`. */
19
+ signal?: AbortSignal;
20
+ /** Diagnostic label for the tracked child (adapter id). */
21
+ label?: string;
22
+ }
23
+ export interface SpawnHelperRepair {
24
+ helper: string;
25
+ ok: boolean;
26
+ error?: string;
27
+ }
28
+ /**
29
+ * Lazy replacement for the removed postinstall: best-effort `chmod 0o755`
30
+ * over every prebuilds entry `spawn-helper` under the given node-pty roots.
31
+ * Missing roots, missing prebuild dirs, and missing helpers are skipped
32
+ * silently; only attempted repairs are recorded.
33
+ */
34
+ export declare function repairSpawnHelpers(roots: string[], chmod?: (p: string, mode: number) => void): SpawnHelperRepair[];
35
+ /**
36
+ * Human diagnostic for failed repairs, or null when nothing failed.
37
+ * Load-bearing for root-owned trees (e.g. `sudo` global installs) where the
38
+ * invoking user cannot repair: without it the failure would go opaque again.
39
+ */
40
+ export declare function describeSpawnHelperRepairs(outcomes: SpawnHelperRepair[]): string | null;
41
+ export declare function runPty(opts: PtyRunOptions): Promise<string>;