quotacap 0.0.23 → 0.0.24

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 (306) hide show
  1. package/README.md +27 -4
  2. package/dist/adapters/index.js +3 -0
  3. package/dist/adapters/muse.d.ts +8 -0
  4. package/dist/adapters/muse.js +96 -0
  5. package/dist/adapters/pty.d.ts +35 -0
  6. package/dist/adapters/pty.js +282 -46
  7. package/dist/advisory/provider-names.d.ts +14 -0
  8. package/dist/advisory/provider-names.js +98 -0
  9. package/dist/advisory/snapshot.d.ts +1 -0
  10. package/dist/advisory/snapshot.js +7 -0
  11. package/dist/advisory/types.d.ts +5 -0
  12. package/dist/advisory/validation.d.ts +9 -0
  13. package/dist/advisory/validation.js +39 -0
  14. package/dist/cli/advise.js +2 -1
  15. package/dist/cli/clients.js +2 -0
  16. package/dist/cli/providers.d.ts +3 -0
  17. package/dist/cli/providers.js +174 -0
  18. package/dist/cli/snapshot-source.d.ts +3 -1
  19. package/dist/cli/snapshot-source.js +44 -5
  20. package/dist/cli/status.js +8 -0
  21. package/dist/config.d.ts +11 -0
  22. package/dist/config.js +78 -1
  23. package/dist/diagnostics/failure.d.ts +26 -0
  24. package/dist/diagnostics/failure.js +417 -0
  25. package/dist/format/issues.d.ts +2 -0
  26. package/dist/format/issues.js +17 -0
  27. package/dist/format/markdown.js +1 -1
  28. package/dist/format/rows.js +2 -0
  29. package/dist/format/terminal.js +6 -6
  30. package/dist/http/server.d.ts +2 -0
  31. package/dist/http/server.js +75 -2
  32. package/dist/mcp/server.js +2 -0
  33. package/dist/runtime/client.d.ts +1 -0
  34. package/dist/runtime/client.js +21 -1
  35. package/dist/runtime/poll.d.ts +12 -4
  36. package/dist/runtime/poll.js +49 -39
  37. package/dist/runtime/service.js +8 -2
  38. package/dist/runtime/spawn.js +14 -4
  39. package/dist/service/macos.js +1 -1
  40. package/dist/store/attempts.d.ts +6 -0
  41. package/dist/store/attempts.js +27 -20
  42. package/dist/store/db.js +6 -1
  43. package/dist/version.js +1 -1
  44. package/dist/web/src/App.js +1 -1
  45. package/dist/web/src/api.d.ts +2 -0
  46. package/dist/web/src/api.js +20 -0
  47. package/dist/web/src/components/AdviceDrawer.js +3 -4
  48. package/dist/web/src/components/FaultBanner.js +2 -3
  49. package/dist/web/src/components/ProviderCard.js +1 -2
  50. package/dist/web/src/components/ProviderDrawer.d.ts +8 -1
  51. package/dist/web/src/components/ProviderDrawer.js +103 -7
  52. package/dist/web/src/components/ProviderIcon.js +1 -0
  53. package/dist/web/src/components/ProviderRow.js +1 -2
  54. package/dist/web/src/components/Recommendation.js +2 -3
  55. package/dist/web/src/components/ResetRail.js +4 -5
  56. package/dist/web/src/components/SubscriptionList.js +1 -2
  57. package/dist/web/src/components/settings/ProvidersPanel.js +1 -2
  58. package/dist/web/src/pages/Onboarding.js +4 -5
  59. package/dist/web/src/state.d.ts +11 -0
  60. package/dist/web/src/state.js +18 -0
  61. package/dist/webAssets.js +1 -1
  62. package/dist/webHtml.js +1 -1
  63. package/package.json +3 -1
  64. package/web/dist/assets/index-CzObzbxb.js +12 -0
  65. package/web/dist/assets/index-VioQmN7J.css +1 -0
  66. package/web/dist/index.html +2 -2
  67. package/dist/src/adapters/agy.d.ts +0 -3
  68. package/dist/src/adapters/agy.js +0 -81
  69. package/dist/src/adapters/claude.d.ts +0 -9
  70. package/dist/src/adapters/claude.js +0 -30
  71. package/dist/src/adapters/codex.d.ts +0 -7
  72. package/dist/src/adapters/codex.js +0 -140
  73. package/dist/src/adapters/grok.d.ts +0 -7
  74. package/dist/src/adapters/grok.js +0 -126
  75. package/dist/src/adapters/index.d.ts +0 -21
  76. package/dist/src/adapters/index.js +0 -66
  77. package/dist/src/adapters/kimi.d.ts +0 -7
  78. package/dist/src/adapters/kimi.js +0 -76
  79. package/dist/src/adapters/manual.d.ts +0 -8
  80. package/dist/src/adapters/manual.js +0 -28
  81. package/dist/src/adapters/parse.d.ts +0 -1
  82. package/dist/src/adapters/parse.js +0 -81
  83. package/dist/src/adapters/pty.d.ts +0 -41
  84. package/dist/src/adapters/pty.js +0 -579
  85. package/dist/src/adapters/types.d.ts +0 -21
  86. package/dist/src/adapters/types.js +0 -1
  87. package/dist/src/advisory/engine.d.ts +0 -28
  88. package/dist/src/advisory/engine.js +0 -149
  89. package/dist/src/advisory/snapshot.d.ts +0 -22
  90. package/dist/src/advisory/snapshot.js +0 -213
  91. package/dist/src/advisory/types.d.ts +0 -63
  92. package/dist/src/advisory/types.js +0 -1
  93. package/dist/src/advisory/validate.d.ts +0 -5
  94. package/dist/src/advisory/validate.js +0 -18
  95. package/dist/src/cli/advise.d.ts +0 -3
  96. package/dist/src/cli/advise.js +0 -128
  97. package/dist/src/cli/clients.d.ts +0 -28
  98. package/dist/src/cli/clients.js +0 -11
  99. package/dist/src/cli/index.d.ts +0 -2
  100. package/dist/src/cli/index.js +0 -27
  101. package/dist/src/cli/ingest.d.ts +0 -3
  102. package/dist/src/cli/ingest.js +0 -31
  103. package/dist/src/cli/init.d.ts +0 -2
  104. package/dist/src/cli/init.js +0 -29
  105. package/dist/src/cli/runtime.d.ts +0 -33
  106. package/dist/src/cli/runtime.js +0 -252
  107. package/dist/src/cli/snapshot-source.d.ts +0 -26
  108. package/dist/src/cli/snapshot-source.js +0 -232
  109. package/dist/src/cli/status.d.ts +0 -3
  110. package/dist/src/cli/status.js +0 -150
  111. package/dist/src/cli/takeover.d.ts +0 -64
  112. package/dist/src/cli/takeover.js +0 -175
  113. package/dist/src/cli/update.d.ts +0 -61
  114. package/dist/src/cli/update.js +0 -392
  115. package/dist/src/config.d.ts +0 -47
  116. package/dist/src/config.js +0 -185
  117. package/dist/src/daemon.d.ts +0 -4
  118. package/dist/src/daemon.js +0 -15
  119. package/dist/src/format/markdown.d.ts +0 -3
  120. package/dist/src/format/markdown.js +0 -24
  121. package/dist/src/format/rows.d.ts +0 -27
  122. package/dist/src/format/rows.js +0 -241
  123. package/dist/src/format/terminal.d.ts +0 -12
  124. package/dist/src/format/terminal.js +0 -144
  125. package/dist/src/http/server.d.ts +0 -33
  126. package/dist/src/http/server.js +0 -314
  127. package/dist/src/mcp/server.d.ts +0 -46
  128. package/dist/src/mcp/server.js +0 -163
  129. package/dist/src/runtime/client.d.ts +0 -24
  130. package/dist/src/runtime/client.js +0 -72
  131. package/dist/src/runtime/owner.d.ts +0 -35
  132. package/dist/src/runtime/owner.js +0 -393
  133. package/dist/src/runtime/poll.d.ts +0 -50
  134. package/dist/src/runtime/poll.js +0 -229
  135. package/dist/src/runtime/service.d.ts +0 -24
  136. package/dist/src/runtime/service.js +0 -268
  137. package/dist/src/runtime/spawn.d.ts +0 -17
  138. package/dist/src/runtime/spawn.js +0 -97
  139. package/dist/src/runtime/token.d.ts +0 -3
  140. package/dist/src/runtime/token.js +0 -64
  141. package/dist/src/runtime/updates.d.ts +0 -74
  142. package/dist/src/runtime/updates.js +0 -445
  143. package/dist/src/runtime/versions.d.ts +0 -2
  144. package/dist/src/runtime/versions.js +0 -26
  145. package/dist/src/service/index.d.ts +0 -9
  146. package/dist/src/service/index.js +0 -103
  147. package/dist/src/service/macos.d.ts +0 -100
  148. package/dist/src/service/macos.js +0 -602
  149. package/dist/src/service/systemd.d.ts +0 -40
  150. package/dist/src/service/systemd.js +0 -387
  151. package/dist/src/store/attempts.d.ts +0 -12
  152. package/dist/src/store/attempts.js +0 -29
  153. package/dist/src/store/db.d.ts +0 -2
  154. package/dist/src/store/db.js +0 -74
  155. package/dist/src/store/quotas.d.ts +0 -6
  156. package/dist/src/store/quotas.js +0 -98
  157. package/dist/src/version.d.ts +0 -1
  158. package/dist/src/version.js +0 -2
  159. package/dist/src/webAssets.d.ts +0 -1
  160. package/dist/src/webAssets.js +0 -2
  161. package/dist/src/webHtml.d.ts +0 -1
  162. package/dist/src/webHtml.js +0 -2
  163. package/dist/tests/adapters/agy.test.d.ts +0 -1
  164. package/dist/tests/adapters/agy.test.js +0 -337
  165. package/dist/tests/adapters/claude.test.d.ts +0 -1
  166. package/dist/tests/adapters/claude.test.js +0 -83
  167. package/dist/tests/adapters/codex.test.d.ts +0 -1
  168. package/dist/tests/adapters/codex.test.js +0 -246
  169. package/dist/tests/adapters/credential-free.test.d.ts +0 -1
  170. package/dist/tests/adapters/credential-free.test.js +0 -211
  171. package/dist/tests/adapters/grok.test.d.ts +0 -1
  172. package/dist/tests/adapters/grok.test.js +0 -269
  173. package/dist/tests/adapters/kimi.test.d.ts +0 -1
  174. package/dist/tests/adapters/kimi.test.js +0 -235
  175. package/dist/tests/adapters/manual.test.d.ts +0 -1
  176. package/dist/tests/adapters/manual.test.js +0 -28
  177. package/dist/tests/adapters/pty-perms.test.d.ts +0 -1
  178. package/dist/tests/adapters/pty-perms.test.js +0 -78
  179. package/dist/tests/adapters/pty.test.d.ts +0 -1
  180. package/dist/tests/adapters/pty.test.js +0 -240
  181. package/dist/tests/adapters/registry.test.d.ts +0 -1
  182. package/dist/tests/adapters/registry.test.js +0 -25
  183. package/dist/tests/advisory/engine.test.d.ts +0 -1
  184. package/dist/tests/advisory/engine.test.js +0 -222
  185. package/dist/tests/advisory/pace.test.d.ts +0 -1
  186. package/dist/tests/advisory/pace.test.js +0 -60
  187. package/dist/tests/advisory/snapshot.test.d.ts +0 -1
  188. package/dist/tests/advisory/snapshot.test.js +0 -65
  189. package/dist/tests/advisory/validate.test.d.ts +0 -1
  190. package/dist/tests/advisory/validate.test.js +0 -19
  191. package/dist/tests/bootstrap.test.d.ts +0 -1
  192. package/dist/tests/bootstrap.test.js +0 -14
  193. package/dist/tests/cli/advise.test.d.ts +0 -1
  194. package/dist/tests/cli/advise.test.js +0 -367
  195. package/dist/tests/cli/cli.test.d.ts +0 -1
  196. package/dist/tests/cli/cli.test.js +0 -46
  197. package/dist/tests/cli/helpers.d.ts +0 -9
  198. package/dist/tests/cli/helpers.js +0 -93
  199. package/dist/tests/cli/ingest.test.d.ts +0 -1
  200. package/dist/tests/cli/ingest.test.js +0 -188
  201. package/dist/tests/cli/init.test.d.ts +0 -1
  202. package/dist/tests/cli/init.test.js +0 -55
  203. package/dist/tests/cli/launcher.test.d.ts +0 -1
  204. package/dist/tests/cli/launcher.test.js +0 -271
  205. package/dist/tests/cli/parity.test.d.ts +0 -1
  206. package/dist/tests/cli/parity.test.js +0 -225
  207. package/dist/tests/cli/snapshot-source.test.d.ts +0 -1
  208. package/dist/tests/cli/snapshot-source.test.js +0 -305
  209. package/dist/tests/cli/status.test.d.ts +0 -1
  210. package/dist/tests/cli/status.test.js +0 -536
  211. package/dist/tests/cli/takeover.test.d.ts +0 -1
  212. package/dist/tests/cli/takeover.test.js +0 -325
  213. package/dist/tests/cli/update.test.d.ts +0 -1
  214. package/dist/tests/cli/update.test.js +0 -382
  215. package/dist/tests/daemon.test.d.ts +0 -1
  216. package/dist/tests/daemon.test.js +0 -164
  217. package/dist/tests/fixtures/stable-state.d.ts +0 -22
  218. package/dist/tests/fixtures/stable-state.js +0 -152
  219. package/dist/tests/fixtures/stable-state.test.d.ts +0 -1
  220. package/dist/tests/fixtures/stable-state.test.js +0 -17
  221. package/dist/tests/format/markdown.test.d.ts +0 -1
  222. package/dist/tests/format/markdown.test.js +0 -49
  223. package/dist/tests/format/terminal.test.d.ts +0 -1
  224. package/dist/tests/format/terminal.test.js +0 -476
  225. package/dist/tests/http/api.test.d.ts +0 -1
  226. package/dist/tests/http/api.test.js +0 -236
  227. package/dist/tests/integration.test.d.ts +0 -1
  228. package/dist/tests/integration.test.js +0 -61
  229. package/dist/tests/mcp/protocol.test.d.ts +0 -1
  230. package/dist/tests/mcp/protocol.test.js +0 -134
  231. package/dist/tests/mcp/quotas.test.d.ts +0 -1
  232. package/dist/tests/mcp/quotas.test.js +0 -56
  233. package/dist/tests/mcp/server.test.d.ts +0 -1
  234. package/dist/tests/mcp/server.test.js +0 -7
  235. package/dist/tests/mcp/tools.test.d.ts +0 -1
  236. package/dist/tests/mcp/tools.test.js +0 -291
  237. package/dist/tests/runtime/cancel.test.d.ts +0 -1
  238. package/dist/tests/runtime/cancel.test.js +0 -264
  239. package/dist/tests/runtime/cli.test.d.ts +0 -1
  240. package/dist/tests/runtime/cli.test.js +0 -426
  241. package/dist/tests/runtime/config.test.d.ts +0 -1
  242. package/dist/tests/runtime/config.test.js +0 -144
  243. package/dist/tests/runtime/http.test.d.ts +0 -1
  244. package/dist/tests/runtime/http.test.js +0 -346
  245. package/dist/tests/runtime/lifecycle.test.d.ts +0 -1
  246. package/dist/tests/runtime/lifecycle.test.js +0 -303
  247. package/dist/tests/runtime/ownership.test.d.ts +0 -1
  248. package/dist/tests/runtime/ownership.test.js +0 -355
  249. package/dist/tests/runtime/poll.test.d.ts +0 -1
  250. package/dist/tests/runtime/poll.test.js +0 -438
  251. package/dist/tests/runtime/status.test.d.ts +0 -1
  252. package/dist/tests/runtime/status.test.js +0 -232
  253. package/dist/tests/runtime/updates.test.d.ts +0 -1
  254. package/dist/tests/runtime/updates.test.js +0 -509
  255. package/dist/tests/service/macos.test.d.ts +0 -1
  256. package/dist/tests/service/macos.test.js +0 -450
  257. package/dist/tests/service/systemd.test.d.ts +0 -1
  258. package/dist/tests/service/systemd.test.js +0 -330
  259. package/dist/tests/smoke/dashboard.e2e.d.ts +0 -1
  260. package/dist/tests/smoke/dashboard.e2e.js +0 -142
  261. package/dist/tests/smoke/playwright.config.d.ts +0 -2
  262. package/dist/tests/smoke/playwright.config.js +0 -11
  263. package/dist/tests/store/attempts.test.d.ts +0 -1
  264. package/dist/tests/store/attempts.test.js +0 -29
  265. package/dist/tests/store/db.test.d.ts +0 -1
  266. package/dist/tests/store/db.test.js +0 -96
  267. package/dist/tests/store/migration.test.d.ts +0 -1
  268. package/dist/tests/store/migration.test.js +0 -41
  269. package/dist/tests/web/advice-drawer.test.d.ts +0 -1
  270. package/dist/tests/web/advice-drawer.test.js +0 -45
  271. package/dist/tests/web/build.test.d.ts +0 -1
  272. package/dist/tests/web/build.test.js +0 -8
  273. package/dist/tests/web/dashboard.e2e.d.ts +0 -1
  274. package/dist/tests/web/dashboard.e2e.js +0 -670
  275. package/dist/tests/web/header.test.d.ts +0 -1
  276. package/dist/tests/web/header.test.js +0 -82
  277. package/dist/tests/web/names.test.d.ts +0 -1
  278. package/dist/tests/web/names.test.js +0 -19
  279. package/dist/tests/web/onboarding.test.d.ts +0 -1
  280. package/dist/tests/web/onboarding.test.js +0 -21
  281. package/dist/tests/web/pacing.test.d.ts +0 -1
  282. package/dist/tests/web/pacing.test.js +0 -62
  283. package/dist/tests/web/playwright.config.d.ts +0 -2
  284. package/dist/tests/web/playwright.config.js +0 -10
  285. package/dist/tests/web/provider-drawer.test.d.ts +0 -1
  286. package/dist/tests/web/provider-drawer.test.js +0 -68
  287. package/dist/tests/web/recommendation.test.d.ts +0 -1
  288. package/dist/tests/web/recommendation.test.js +0 -38
  289. package/dist/tests/web/reset-rail.test.d.ts +0 -1
  290. package/dist/tests/web/reset-rail.test.js +0 -63
  291. package/dist/tests/web/responsive.test.d.ts +0 -1
  292. package/dist/tests/web/responsive.test.js +0 -21
  293. package/dist/tests/web/settings.test.d.ts +0 -1
  294. package/dist/tests/web/settings.test.js +0 -32
  295. package/dist/tests/web/state-catalog.test.d.ts +0 -1
  296. package/dist/tests/web/state-catalog.test.js +0 -206
  297. package/dist/tests/web/state.test.d.ts +0 -1
  298. package/dist/tests/web/state.test.js +0 -27
  299. package/dist/tests/web/stub-server.d.ts +0 -37
  300. package/dist/tests/web/stub-server.js +0 -132
  301. package/dist/tests/web/theme.test.d.ts +0 -1
  302. package/dist/tests/web/theme.test.js +0 -12
  303. package/dist/web/src/names.d.ts +0 -3
  304. package/dist/web/src/names.js +0 -19
  305. package/web/dist/assets/index-BEBqgvq8.css +0 -1
  306. package/web/dist/assets/index-DZfttQgo.js +0 -12
package/README.md CHANGED
@@ -19,10 +19,11 @@ QuotaCap helps you get more from the AI coding subscriptions you already pay for
19
19
  | Provider | Mechanism | Source |
20
20
  |---|---|---|
21
21
  | Claude Code | `exec` — `claude -p /usage --output-format json` | Live |
22
- | Antigravity | `exec` — `agy -p /usage --output-format json` (two rows: `agy` Gemini, `agy:3p` 3p) | Live |
22
+ | Antigravity | `exec` — `agy -p /usage --output-format json` (two rows: Antigravity for Gemini, Antigravity 3P for third-party models) | Live |
23
23
  | Codex | `pty` — `codex --no-alt-screen` then `/status`, parse `Weekly/5h limit: X% left` | Live |
24
24
  | Kimi Code | `pty` — `kimi` then `/usage`, parse `Weekly/5h limit: Y% used` | Live |
25
25
  | Grok | `pty` — `grok` then `/usage`, parse `Weekly limit (plan)` + `Credits: $X` | Live |
26
+ | Muse Code | `pty` — `muse --trust-workspace` then `/usage`, parse `Subscription · Muse Code <plan>` + `Weekly/Current N% used` | Live |
26
27
 
27
28
  Exec adapters run via `execFile` with an argv list. PTY adapters run via `node-pty` (`src/adapters/pty.ts`). They are TUI-fragile: a vendor text change breaks the parser and the row degrades fail-closed until the regex is fixed. Poll latency is 2–10 s per PTY provider (settle plus completion). It dominates `POST /api/refresh` and the first poll, not the steady-state 15 m timer.
28
29
 
@@ -46,12 +47,15 @@ The npm package runs the same CLI on Node. To run without a global install, repl
46
47
 
47
48
  ```bash
48
49
  quotacap # opens the dashboard, starting the service if needed
49
- quotacap status
50
+ quotacap status # summary table (pass --verbose for adapter failure guidance)
50
51
  quotacap advise
51
52
  quotacap update # upgrade to the latest release
53
+ quotacap providers list # view built-in and effective provider names
54
+ quotacap providers rename claude "Work" # customize provider display name
55
+ quotacap providers reset claude # restore built-in name (--all resets all)
52
56
  ```
53
57
 
54
- No setup ceremony: config is provisioned on first use, and bare `quotacap` is the dashboard launcher. `quotacap web --foreground` and `quotacap daemon` keep the old hold-the-terminal mode for SSH, containers, and supervisors.
58
+ No setup ceremony: config is provisioned on first use, and bare `quotacap` is the dashboard launcher. `quotacap status --verbose` displays actionable remediation guidance and sanitized failure details for failing adapters. `status` remains strictly read-only and never triggers background polling. `quotacap providers` manages custom display names (persisted to `~/.quotacap/config.json` under `providerNames`). `quotacap web --foreground` and `quotacap daemon` keep the old hold-the-terminal mode for SSH, containers, and supervisors.
55
59
 
56
60
  ## MCP
57
61
 
@@ -78,6 +82,7 @@ The local web server listens on `127.0.0.1:8787` (configured via `QUOTACAP_URL`
78
82
  | `GET` | `/api/recommendation` | None | Current advisory on which provider to use next |
79
83
  | `GET` | `/api/token` | Same-origin | Shared secret token for the dashboard |
80
84
  | `POST` | `/api/refresh` | `X-QuotaCap-Token` | Trigger an immediate adapter poll (debounced to 60s) |
85
+ | `PATCH` | `/api/providers/:id` | `X-QuotaCap-Token` | Set or clear custom provider display name override |
81
86
 
82
87
  ### Curl examples
83
88
 
@@ -94,11 +99,27 @@ curl -X POST http://localhost:8787/api/refresh \
94
99
  -H "X-QuotaCap-Token: $(cat ~/.quotacap/token)"
95
100
  ```
96
101
 
102
+ Set or clear a provider display name override:
103
+
104
+ ```bash
105
+ # Set custom display name
106
+ curl -X PATCH http://localhost:8787/api/providers/claude \
107
+ -H "X-QuotaCap-Token: $(cat ~/.quotacap/token)" \
108
+ -H "Content-Type: application/json" \
109
+ -d '{"displayName": "Work Claude"}'
110
+
111
+ # Reset to built-in name
112
+ curl -X PATCH http://localhost:8787/api/providers/claude \
113
+ -H "X-QuotaCap-Token: $(cat ~/.quotacap/token)" \
114
+ -H "Content-Type: application/json" \
115
+ -d '{"displayName": null}'
116
+ ```
117
+
97
118
  ## Security
98
119
 
99
120
  QuotaCap is a local daemon. It binds to `127.0.0.1` only (`src/cli/index.ts:56`). It does not listen on `0.0.0.0`. There is no LAN surface.
100
121
 
101
- It owns no tokens. It never reads `~/.codex/auth.json`, `~/.kimi-code/credentials/kimi-code.json`, `~/.kimi/credentials/kimi-code.json`, `~/.grok/auth.json`, or `~/.gemini/oauth_creds.json`. It never uses `refresh_token` or `grant_type=refresh_token`. It has no hardcoded client ids. Those OAuth paths and the `.qc-bak` and `.qc-lock` helpers were removed in #14. This is asserted by `tests/adapters/credential-free.test.ts`. Each CLI owns its own session. QuotaCap only spawns the CLI and reads its stdout via `exec` (`claude`, `agy`) or PTY (`codex`, `kimi`, `grok`).
122
+ It owns no tokens. It never reads `~/.codex/auth.json`, `~/.kimi-code/credentials/kimi-code.json`, `~/.kimi/credentials/kimi-code.json`, `~/.grok/auth.json`, or `~/.gemini/oauth_creds.json`. It never uses `refresh_token` or `grant_type=refresh_token`. It has no hardcoded client ids. Those OAuth paths and the `.qc-bak` and `.qc-lock` helpers were removed in #14. This is asserted by `tests/adapters/credential-free.test.ts`. Each CLI owns its own session. It never reads `~/.config/muse/auth.json`, `~/.local/share/muse/sessions/`, or `~/.config/muse/tui-history.jsonl`. Each CLI owns its own session. QuotaCap only spawns the CLI and reads its stdout via `exec` (`claude`, `agy`) or PTY (`codex`, `kimi`, `grok`, `muse`).
102
123
 
103
124
  It stores no `raw` provider payload. The `raw` column was dropped and migrated in `src/store/db.ts:37-48`. `GET /api/quotas` and MCP `get_quotas` never return `raw` (`tests/http/api.test.ts`). History and the token live under `~/.quotacap/` with `0700` on the directory and `0600` on files.
104
125
 
@@ -134,6 +155,8 @@ The same code is an npm package and a compiled binary.
134
155
 
135
156
  The Kimi adapter spawns `kimi` in your home directory so the quota modal does not depend on the project path. If Kimi shows `Trust this folder?`, QuotaCap fails closed with `untrusted workspace — run \`kimi\` there and select Trust this folder` and does not auto-trust; trust remains an explicit interactive decision.
136
157
 
158
+ The Muse adapter never trusts a directory of yours. It runs `muse --trust-workspace` with its working directory set to an empty folder QuotaCap owns (`~/.quotacap/muse-probe/`), so the flag has no project-local skills, rules, hooks or plugin config to load, and Muse's own flag does not persist trust. If a future Muse ignores the flag and shows the prompt anyway, the adapter aborts fail-closed rather than answering it.
159
+
137
160
  The compiled binaries are built with `bun build --compile`.
138
161
  Each `dist-bin/quotacap-<os>-<arch>.tar.gz` bundles a `pty` sidecar.
139
162
  The sidecar is `pty/node-pty` with the matching prebuild.
@@ -4,6 +4,7 @@ import { codexAdapter } from "./codex.js";
4
4
  import { kimiAdapter } from "./kimi.js";
5
5
  import { grokAdapter } from "./grok.js";
6
6
  import { agyAdapter } from "./agy.js";
7
+ import { museAdapter } from "./muse.js";
7
8
  import { installAdapterSignal, clearAdapterSignal } from "../runtime/spawn.js";
8
9
  export const adapters = {
9
10
  claude: claudeAdapter,
@@ -12,6 +13,7 @@ export const adapters = {
12
13
  kimi: kimiAdapter,
13
14
  grok: grokAdapter,
14
15
  agy: agyAdapter,
16
+ muse: museAdapter,
15
17
  };
16
18
  const ADAPTER_TIMEOUTS = {
17
19
  claude: 8000,
@@ -19,6 +21,7 @@ const ADAPTER_TIMEOUTS = {
19
21
  kimi: 8000,
20
22
  grok: 14000,
21
23
  agy: 20000,
24
+ muse: 14000,
22
25
  };
23
26
  export async function pollAll(enabled, opts) {
24
27
  const rawJobs = enabled.map(id => {
@@ -0,0 +1,8 @@
1
+ import type { ParsedQuota } from "./types.js";
2
+ export declare function museProbeDir(): string;
3
+ export declare function parseMuseTui(text: string, now?: Date): ParsedQuota;
4
+ export declare const museAdapter: {
5
+ id: string;
6
+ requiresAuth: string;
7
+ poll(): Promise<ParsedQuota>;
8
+ };
@@ -0,0 +1,96 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { parseResetText } from "./parse.js";
5
+ import { runPty, stripAnsi } from "./pty.js";
6
+ import { adapterSignal } from "../runtime/spawn.js";
7
+ // QuotaCap-owned empty probe dir, beside the config and database. Muse loads
8
+ // project-local skills, rules, hooks and plugin config from cwd, so the probe
9
+ // never runs in $HOME or a user project — an empty dir grants nothing.
10
+ export function museProbeDir() {
11
+ return path.join(process.env.QUOTACAP_HOME ?? os.homedir(), ".quotacap", "muse-probe");
12
+ }
13
+ export function parseMuseTui(text, now = new Date()) {
14
+ // After ANSI stripping the panel is ONE line (cursor-addressed, no
15
+ // newlines), so no pattern here may anchor on ^, $ or \n.
16
+ const cleaned = stripAnsi(text);
17
+ const weekly = cleaned.match(/Weekly\s+(\d+)%\s+used/i);
18
+ if (!weekly)
19
+ throw new Error("muse: weekly usage not found in TUI output");
20
+ const current = cleaned.match(/Current\s+(\d+)%\s+used/i);
21
+ if (!current)
22
+ throw new Error("muse: current-window usage not found in TUI output");
23
+ const usedPct = parseInt(weekly[1], 10);
24
+ const sessionPct = parseInt(current[1], 10);
25
+ if (!Number.isFinite(usedPct) || usedPct < 0 || usedPct > 100)
26
+ throw new Error("muse: bad weekly pct");
27
+ if (!Number.isFinite(sessionPct) || sessionPct < 0 || sessionPct > 100)
28
+ throw new Error("muse: bad current pct");
29
+ // First match wins; overdrawn transcripts carry two identical copies.
30
+ // Vendor casing preserved (grok precedent: SuperGrok); the Muse Code
31
+ // product prefix is stripped because the product is already the row label.
32
+ let plan = "unknown";
33
+ const planMatch = cleaned.match(/Subscription\s*·\s*Muse Code\s+(.+?)\s{2,}/i);
34
+ if (planMatch)
35
+ plan = planMatch[1].trim();
36
+ // The Current-window reset ("Resets at 3:51 PM") is a bare clock time with
37
+ // no date, and Quota has no session-reset field — discarded, as claude and
38
+ // kimi already do. A missing or unparseable Weekly reset throws rather than
39
+ // estimating: an invented reset would feed the advisory a false deadline.
40
+ const resetMatch = cleaned.match(/Weekly[^\n]*?Resets\s+([A-Za-z]{3}\s+\d{1,2}\s+at\s+\d{1,2}:\d{2}\s*[AP]M)/i);
41
+ if (!resetMatch)
42
+ throw new Error("muse: bad weekly reset");
43
+ const resetRaw = resetMatch[1].trim();
44
+ const resetsAt = parseResetText(`resets ${resetRaw}`, now);
45
+ if (!resetsAt)
46
+ throw new Error(`muse: bad weekly reset "${resetRaw}"`);
47
+ const periodStart = new Date(new Date(resetsAt).getTime() - 7 * 86400000).toISOString();
48
+ return {
49
+ provider: "muse",
50
+ plan,
51
+ usedPct,
52
+ sessionPct,
53
+ resetsAt,
54
+ periodStart,
55
+ source: "tui",
56
+ fetchedAt: now.toISOString(),
57
+ raw: cleaned.slice(0, 4096),
58
+ };
59
+ }
60
+ export const museAdapter = {
61
+ id: "muse",
62
+ requiresAuth: "muse login (CLI owns credentials)",
63
+ async poll() {
64
+ const cwd = museProbeDir();
65
+ fs.mkdirSync(cwd, { recursive: true });
66
+ const transcript = await runPty({
67
+ file: "muse",
68
+ args: ["--trust-workspace"],
69
+ cwd,
70
+ // Without this, a missing binary triggers a foreground 248 MB download
71
+ // on every poll; with it the launcher dies fast with a clear message.
72
+ env: { MUSE_NO_AUTO_UPDATE: "1" },
73
+ cols: 140,
74
+ rows: 50,
75
+ readyRegex: /muse-spark|Muse Code \d/,
76
+ readyTimeoutMs: 8000,
77
+ settleDelayMs: 1000,
78
+ // Two-phase submit: "/usage" first, CR 1500ms later. A single combined
79
+ // write is swallowed by the slash-command autocomplete and never runs.
80
+ input: "/usage",
81
+ submitInput: "\r",
82
+ submitAfterMs: 1500,
83
+ completionRegex: /Subscription/i,
84
+ // Trust prompt (in case a future Muse ignores the flag), an accidental
85
+ // model turn from a mistimed Enter, and the unavailable-subscription
86
+ // fast-fail — every one aborts to a degraded row, never a 14s timeout.
87
+ abortOn: /Do you trust this workspace|Working \(\d+s|Subscriptions aren't currently available|subscription_unavailable/,
88
+ timeoutMs: 14000,
89
+ respondToQueries: true,
90
+ maxBytes: 256 * 1024,
91
+ signal: adapterSignal("muse"),
92
+ label: "muse",
93
+ });
94
+ return parseMuseTui(transcript);
95
+ },
96
+ };
@@ -8,8 +8,26 @@ export interface PtyRunOptions {
8
8
  rows?: number;
9
9
  readyRegex?: RegExp;
10
10
  readyTimeoutMs?: number;
11
+ /**
12
+ * Settle delay after spawn (no readyRegex) or after readiness (with
13
+ * readyRegex). The post-ready default stays 200ms; pass an explicit value
14
+ * (e.g. muse's 1000ms) only when the TUI needs longer after signalling.
15
+ */
11
16
  settleDelayMs?: number;
12
17
  input: string;
18
+ /**
19
+ * Second-phase input, written `submitAfterMs` after `input`. Both must be
20
+ * set for the second write to happen; absent by default, which keeps the
21
+ * historical single-write behaviour for existing adapters.
22
+ */
23
+ submitInput?: string;
24
+ submitAfterMs?: number;
25
+ /**
26
+ * Opt-in terminal-query responder: answers DSR, DA1 and OSC 4/10/11
27
+ * capability queries the child emits. Off by default; TUIs that never
28
+ * query (codex, kimi, grok) are unaffected either way.
29
+ */
30
+ respondToQueries?: boolean;
13
31
  completionRegex?: RegExp;
14
32
  /** If matched, abort immediately with a clear error (e.g. trust prompts). */
15
33
  abortOn?: RegExp;
@@ -20,6 +38,23 @@ export interface PtyRunOptions {
20
38
  /** Diagnostic label for the tracked child (adapter id). */
21
39
  label?: string;
22
40
  }
41
+ export interface TerminalQueryRule {
42
+ /** Non-global pattern; must match the complete query including terminator. */
43
+ match: RegExp;
44
+ reply: (m: RegExpMatchArray) => string;
45
+ }
46
+ /**
47
+ * Canned answers to terminal capability queries, table-driven so a future
48
+ * query type is a one-line row. Replies are static: colour answers never
49
+ * affect parsing because styling is stripped before matching.
50
+ */
51
+ export declare const TERMINAL_QUERY_RESPONSES: TerminalQueryRule[];
52
+ /**
53
+ * Stateful feeder for `TERMINAL_QUERY_RESPONSES`. Consumes each matched query
54
+ * from an internal buffer, so a query split across chunks is answered once it
55
+ * completes and never answered twice. Plain output passes through silently.
56
+ */
57
+ export declare function createTerminalQueryResponder(write: (s: string) => void): (chunk: string) => void;
23
58
  export interface SpawnHelperRepair {
24
59
  helper: string;
25
60
  ok: boolean;