quotacap 0.0.23 → 0.0.25

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 (313) hide show
  1. package/README.md +29 -6
  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/cli/takeover.d.ts +1 -0
  22. package/dist/cli/takeover.js +3 -2
  23. package/dist/cli/update.js +5 -2
  24. package/dist/config.d.ts +36 -0
  25. package/dist/config.js +180 -1
  26. package/dist/diagnostics/failure.d.ts +26 -0
  27. package/dist/diagnostics/failure.js +417 -0
  28. package/dist/format/issues.d.ts +2 -0
  29. package/dist/format/issues.js +17 -0
  30. package/dist/format/markdown.js +1 -1
  31. package/dist/format/rows.js +2 -0
  32. package/dist/format/terminal.js +6 -6
  33. package/dist/http/server.d.ts +2 -0
  34. package/dist/http/server.js +75 -2
  35. package/dist/mcp/server.js +2 -0
  36. package/dist/runtime/client.d.ts +1 -0
  37. package/dist/runtime/client.js +21 -1
  38. package/dist/runtime/poll.d.ts +12 -4
  39. package/dist/runtime/poll.js +49 -39
  40. package/dist/runtime/service.js +18 -5
  41. package/dist/runtime/spawn.js +14 -4
  42. package/dist/service/index.js +4 -1
  43. package/dist/service/macos.d.ts +11 -0
  44. package/dist/service/macos.js +35 -4
  45. package/dist/service/systemd.d.ts +2 -0
  46. package/dist/service/systemd.js +12 -4
  47. package/dist/store/attempts.d.ts +6 -0
  48. package/dist/store/attempts.js +27 -20
  49. package/dist/store/db.js +6 -1
  50. package/dist/version.js +1 -1
  51. package/dist/web/src/App.js +1 -1
  52. package/dist/web/src/api.d.ts +2 -0
  53. package/dist/web/src/api.js +20 -0
  54. package/dist/web/src/components/AdviceDrawer.js +3 -4
  55. package/dist/web/src/components/FaultBanner.js +2 -3
  56. package/dist/web/src/components/ProviderCard.js +1 -2
  57. package/dist/web/src/components/ProviderDrawer.d.ts +8 -1
  58. package/dist/web/src/components/ProviderDrawer.js +103 -7
  59. package/dist/web/src/components/ProviderIcon.js +1 -0
  60. package/dist/web/src/components/ProviderRow.js +1 -2
  61. package/dist/web/src/components/Recommendation.js +2 -3
  62. package/dist/web/src/components/ResetRail.js +5 -6
  63. package/dist/web/src/components/SubscriptionList.js +1 -2
  64. package/dist/web/src/components/settings/ProvidersPanel.js +1 -2
  65. package/dist/web/src/pages/Onboarding.js +4 -5
  66. package/dist/web/src/state.d.ts +11 -0
  67. package/dist/web/src/state.js +18 -0
  68. package/dist/webAssets.js +1 -1
  69. package/dist/webHtml.js +1 -1
  70. package/package.json +3 -1
  71. package/web/dist/assets/index-ClNhsr1-.js +12 -0
  72. package/web/dist/assets/index-TJk_KDbS.css +1 -0
  73. package/web/dist/index.html +2 -2
  74. package/dist/src/adapters/agy.d.ts +0 -3
  75. package/dist/src/adapters/agy.js +0 -81
  76. package/dist/src/adapters/claude.d.ts +0 -9
  77. package/dist/src/adapters/claude.js +0 -30
  78. package/dist/src/adapters/codex.d.ts +0 -7
  79. package/dist/src/adapters/codex.js +0 -140
  80. package/dist/src/adapters/grok.d.ts +0 -7
  81. package/dist/src/adapters/grok.js +0 -126
  82. package/dist/src/adapters/index.d.ts +0 -21
  83. package/dist/src/adapters/index.js +0 -66
  84. package/dist/src/adapters/kimi.d.ts +0 -7
  85. package/dist/src/adapters/kimi.js +0 -76
  86. package/dist/src/adapters/manual.d.ts +0 -8
  87. package/dist/src/adapters/manual.js +0 -28
  88. package/dist/src/adapters/parse.d.ts +0 -1
  89. package/dist/src/adapters/parse.js +0 -81
  90. package/dist/src/adapters/pty.d.ts +0 -41
  91. package/dist/src/adapters/pty.js +0 -579
  92. package/dist/src/adapters/types.d.ts +0 -21
  93. package/dist/src/adapters/types.js +0 -1
  94. package/dist/src/advisory/engine.d.ts +0 -28
  95. package/dist/src/advisory/engine.js +0 -149
  96. package/dist/src/advisory/snapshot.d.ts +0 -22
  97. package/dist/src/advisory/snapshot.js +0 -213
  98. package/dist/src/advisory/types.d.ts +0 -63
  99. package/dist/src/advisory/types.js +0 -1
  100. package/dist/src/advisory/validate.d.ts +0 -5
  101. package/dist/src/advisory/validate.js +0 -18
  102. package/dist/src/cli/advise.d.ts +0 -3
  103. package/dist/src/cli/advise.js +0 -128
  104. package/dist/src/cli/clients.d.ts +0 -28
  105. package/dist/src/cli/clients.js +0 -11
  106. package/dist/src/cli/index.d.ts +0 -2
  107. package/dist/src/cli/index.js +0 -27
  108. package/dist/src/cli/ingest.d.ts +0 -3
  109. package/dist/src/cli/ingest.js +0 -31
  110. package/dist/src/cli/init.d.ts +0 -2
  111. package/dist/src/cli/init.js +0 -29
  112. package/dist/src/cli/runtime.d.ts +0 -33
  113. package/dist/src/cli/runtime.js +0 -252
  114. package/dist/src/cli/snapshot-source.d.ts +0 -26
  115. package/dist/src/cli/snapshot-source.js +0 -232
  116. package/dist/src/cli/status.d.ts +0 -3
  117. package/dist/src/cli/status.js +0 -150
  118. package/dist/src/cli/takeover.d.ts +0 -64
  119. package/dist/src/cli/takeover.js +0 -175
  120. package/dist/src/cli/update.d.ts +0 -61
  121. package/dist/src/cli/update.js +0 -392
  122. package/dist/src/config.d.ts +0 -47
  123. package/dist/src/config.js +0 -185
  124. package/dist/src/daemon.d.ts +0 -4
  125. package/dist/src/daemon.js +0 -15
  126. package/dist/src/format/markdown.d.ts +0 -3
  127. package/dist/src/format/markdown.js +0 -24
  128. package/dist/src/format/rows.d.ts +0 -27
  129. package/dist/src/format/rows.js +0 -241
  130. package/dist/src/format/terminal.d.ts +0 -12
  131. package/dist/src/format/terminal.js +0 -144
  132. package/dist/src/http/server.d.ts +0 -33
  133. package/dist/src/http/server.js +0 -314
  134. package/dist/src/mcp/server.d.ts +0 -46
  135. package/dist/src/mcp/server.js +0 -163
  136. package/dist/src/runtime/client.d.ts +0 -24
  137. package/dist/src/runtime/client.js +0 -72
  138. package/dist/src/runtime/owner.d.ts +0 -35
  139. package/dist/src/runtime/owner.js +0 -393
  140. package/dist/src/runtime/poll.d.ts +0 -50
  141. package/dist/src/runtime/poll.js +0 -229
  142. package/dist/src/runtime/service.d.ts +0 -24
  143. package/dist/src/runtime/service.js +0 -268
  144. package/dist/src/runtime/spawn.d.ts +0 -17
  145. package/dist/src/runtime/spawn.js +0 -97
  146. package/dist/src/runtime/token.d.ts +0 -3
  147. package/dist/src/runtime/token.js +0 -64
  148. package/dist/src/runtime/updates.d.ts +0 -74
  149. package/dist/src/runtime/updates.js +0 -445
  150. package/dist/src/runtime/versions.d.ts +0 -2
  151. package/dist/src/runtime/versions.js +0 -26
  152. package/dist/src/service/index.d.ts +0 -9
  153. package/dist/src/service/index.js +0 -103
  154. package/dist/src/service/macos.d.ts +0 -100
  155. package/dist/src/service/macos.js +0 -602
  156. package/dist/src/service/systemd.d.ts +0 -40
  157. package/dist/src/service/systemd.js +0 -387
  158. package/dist/src/store/attempts.d.ts +0 -12
  159. package/dist/src/store/attempts.js +0 -29
  160. package/dist/src/store/db.d.ts +0 -2
  161. package/dist/src/store/db.js +0 -74
  162. package/dist/src/store/quotas.d.ts +0 -6
  163. package/dist/src/store/quotas.js +0 -98
  164. package/dist/src/version.d.ts +0 -1
  165. package/dist/src/version.js +0 -2
  166. package/dist/src/webAssets.d.ts +0 -1
  167. package/dist/src/webAssets.js +0 -2
  168. package/dist/src/webHtml.d.ts +0 -1
  169. package/dist/src/webHtml.js +0 -2
  170. package/dist/tests/adapters/agy.test.d.ts +0 -1
  171. package/dist/tests/adapters/agy.test.js +0 -337
  172. package/dist/tests/adapters/claude.test.d.ts +0 -1
  173. package/dist/tests/adapters/claude.test.js +0 -83
  174. package/dist/tests/adapters/codex.test.d.ts +0 -1
  175. package/dist/tests/adapters/codex.test.js +0 -246
  176. package/dist/tests/adapters/credential-free.test.d.ts +0 -1
  177. package/dist/tests/adapters/credential-free.test.js +0 -211
  178. package/dist/tests/adapters/grok.test.d.ts +0 -1
  179. package/dist/tests/adapters/grok.test.js +0 -269
  180. package/dist/tests/adapters/kimi.test.d.ts +0 -1
  181. package/dist/tests/adapters/kimi.test.js +0 -235
  182. package/dist/tests/adapters/manual.test.d.ts +0 -1
  183. package/dist/tests/adapters/manual.test.js +0 -28
  184. package/dist/tests/adapters/pty-perms.test.d.ts +0 -1
  185. package/dist/tests/adapters/pty-perms.test.js +0 -78
  186. package/dist/tests/adapters/pty.test.d.ts +0 -1
  187. package/dist/tests/adapters/pty.test.js +0 -240
  188. package/dist/tests/adapters/registry.test.d.ts +0 -1
  189. package/dist/tests/adapters/registry.test.js +0 -25
  190. package/dist/tests/advisory/engine.test.d.ts +0 -1
  191. package/dist/tests/advisory/engine.test.js +0 -222
  192. package/dist/tests/advisory/pace.test.d.ts +0 -1
  193. package/dist/tests/advisory/pace.test.js +0 -60
  194. package/dist/tests/advisory/snapshot.test.d.ts +0 -1
  195. package/dist/tests/advisory/snapshot.test.js +0 -65
  196. package/dist/tests/advisory/validate.test.d.ts +0 -1
  197. package/dist/tests/advisory/validate.test.js +0 -19
  198. package/dist/tests/bootstrap.test.d.ts +0 -1
  199. package/dist/tests/bootstrap.test.js +0 -14
  200. package/dist/tests/cli/advise.test.d.ts +0 -1
  201. package/dist/tests/cli/advise.test.js +0 -367
  202. package/dist/tests/cli/cli.test.d.ts +0 -1
  203. package/dist/tests/cli/cli.test.js +0 -46
  204. package/dist/tests/cli/helpers.d.ts +0 -9
  205. package/dist/tests/cli/helpers.js +0 -93
  206. package/dist/tests/cli/ingest.test.d.ts +0 -1
  207. package/dist/tests/cli/ingest.test.js +0 -188
  208. package/dist/tests/cli/init.test.d.ts +0 -1
  209. package/dist/tests/cli/init.test.js +0 -55
  210. package/dist/tests/cli/launcher.test.d.ts +0 -1
  211. package/dist/tests/cli/launcher.test.js +0 -271
  212. package/dist/tests/cli/parity.test.d.ts +0 -1
  213. package/dist/tests/cli/parity.test.js +0 -225
  214. package/dist/tests/cli/snapshot-source.test.d.ts +0 -1
  215. package/dist/tests/cli/snapshot-source.test.js +0 -305
  216. package/dist/tests/cli/status.test.d.ts +0 -1
  217. package/dist/tests/cli/status.test.js +0 -536
  218. package/dist/tests/cli/takeover.test.d.ts +0 -1
  219. package/dist/tests/cli/takeover.test.js +0 -325
  220. package/dist/tests/cli/update.test.d.ts +0 -1
  221. package/dist/tests/cli/update.test.js +0 -382
  222. package/dist/tests/daemon.test.d.ts +0 -1
  223. package/dist/tests/daemon.test.js +0 -164
  224. package/dist/tests/fixtures/stable-state.d.ts +0 -22
  225. package/dist/tests/fixtures/stable-state.js +0 -152
  226. package/dist/tests/fixtures/stable-state.test.d.ts +0 -1
  227. package/dist/tests/fixtures/stable-state.test.js +0 -17
  228. package/dist/tests/format/markdown.test.d.ts +0 -1
  229. package/dist/tests/format/markdown.test.js +0 -49
  230. package/dist/tests/format/terminal.test.d.ts +0 -1
  231. package/dist/tests/format/terminal.test.js +0 -476
  232. package/dist/tests/http/api.test.d.ts +0 -1
  233. package/dist/tests/http/api.test.js +0 -236
  234. package/dist/tests/integration.test.d.ts +0 -1
  235. package/dist/tests/integration.test.js +0 -61
  236. package/dist/tests/mcp/protocol.test.d.ts +0 -1
  237. package/dist/tests/mcp/protocol.test.js +0 -134
  238. package/dist/tests/mcp/quotas.test.d.ts +0 -1
  239. package/dist/tests/mcp/quotas.test.js +0 -56
  240. package/dist/tests/mcp/server.test.d.ts +0 -1
  241. package/dist/tests/mcp/server.test.js +0 -7
  242. package/dist/tests/mcp/tools.test.d.ts +0 -1
  243. package/dist/tests/mcp/tools.test.js +0 -291
  244. package/dist/tests/runtime/cancel.test.d.ts +0 -1
  245. package/dist/tests/runtime/cancel.test.js +0 -264
  246. package/dist/tests/runtime/cli.test.d.ts +0 -1
  247. package/dist/tests/runtime/cli.test.js +0 -426
  248. package/dist/tests/runtime/config.test.d.ts +0 -1
  249. package/dist/tests/runtime/config.test.js +0 -144
  250. package/dist/tests/runtime/http.test.d.ts +0 -1
  251. package/dist/tests/runtime/http.test.js +0 -346
  252. package/dist/tests/runtime/lifecycle.test.d.ts +0 -1
  253. package/dist/tests/runtime/lifecycle.test.js +0 -303
  254. package/dist/tests/runtime/ownership.test.d.ts +0 -1
  255. package/dist/tests/runtime/ownership.test.js +0 -355
  256. package/dist/tests/runtime/poll.test.d.ts +0 -1
  257. package/dist/tests/runtime/poll.test.js +0 -438
  258. package/dist/tests/runtime/status.test.d.ts +0 -1
  259. package/dist/tests/runtime/status.test.js +0 -232
  260. package/dist/tests/runtime/updates.test.d.ts +0 -1
  261. package/dist/tests/runtime/updates.test.js +0 -509
  262. package/dist/tests/service/macos.test.d.ts +0 -1
  263. package/dist/tests/service/macos.test.js +0 -450
  264. package/dist/tests/service/systemd.test.d.ts +0 -1
  265. package/dist/tests/service/systemd.test.js +0 -330
  266. package/dist/tests/smoke/dashboard.e2e.d.ts +0 -1
  267. package/dist/tests/smoke/dashboard.e2e.js +0 -142
  268. package/dist/tests/smoke/playwright.config.d.ts +0 -2
  269. package/dist/tests/smoke/playwright.config.js +0 -11
  270. package/dist/tests/store/attempts.test.d.ts +0 -1
  271. package/dist/tests/store/attempts.test.js +0 -29
  272. package/dist/tests/store/db.test.d.ts +0 -1
  273. package/dist/tests/store/db.test.js +0 -96
  274. package/dist/tests/store/migration.test.d.ts +0 -1
  275. package/dist/tests/store/migration.test.js +0 -41
  276. package/dist/tests/web/advice-drawer.test.d.ts +0 -1
  277. package/dist/tests/web/advice-drawer.test.js +0 -45
  278. package/dist/tests/web/build.test.d.ts +0 -1
  279. package/dist/tests/web/build.test.js +0 -8
  280. package/dist/tests/web/dashboard.e2e.d.ts +0 -1
  281. package/dist/tests/web/dashboard.e2e.js +0 -670
  282. package/dist/tests/web/header.test.d.ts +0 -1
  283. package/dist/tests/web/header.test.js +0 -82
  284. package/dist/tests/web/names.test.d.ts +0 -1
  285. package/dist/tests/web/names.test.js +0 -19
  286. package/dist/tests/web/onboarding.test.d.ts +0 -1
  287. package/dist/tests/web/onboarding.test.js +0 -21
  288. package/dist/tests/web/pacing.test.d.ts +0 -1
  289. package/dist/tests/web/pacing.test.js +0 -62
  290. package/dist/tests/web/playwright.config.d.ts +0 -2
  291. package/dist/tests/web/playwright.config.js +0 -10
  292. package/dist/tests/web/provider-drawer.test.d.ts +0 -1
  293. package/dist/tests/web/provider-drawer.test.js +0 -68
  294. package/dist/tests/web/recommendation.test.d.ts +0 -1
  295. package/dist/tests/web/recommendation.test.js +0 -38
  296. package/dist/tests/web/reset-rail.test.d.ts +0 -1
  297. package/dist/tests/web/reset-rail.test.js +0 -63
  298. package/dist/tests/web/responsive.test.d.ts +0 -1
  299. package/dist/tests/web/responsive.test.js +0 -21
  300. package/dist/tests/web/settings.test.d.ts +0 -1
  301. package/dist/tests/web/settings.test.js +0 -32
  302. package/dist/tests/web/state-catalog.test.d.ts +0 -1
  303. package/dist/tests/web/state-catalog.test.js +0 -206
  304. package/dist/tests/web/state.test.d.ts +0 -1
  305. package/dist/tests/web/state.test.js +0 -27
  306. package/dist/tests/web/stub-server.d.ts +0 -37
  307. package/dist/tests/web/stub-server.js +0 -132
  308. package/dist/tests/web/theme.test.d.ts +0 -1
  309. package/dist/tests/web/theme.test.js +0 -12
  310. package/dist/web/src/names.d.ts +0 -3
  311. package/dist/web/src/names.js +0 -19
  312. package/web/dist/assets/index-BEBqgvq8.css +0 -1
  313. package/web/dist/assets/index-DZfttQgo.js +0 -12
@@ -3,6 +3,7 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { createRequire } from "node:module";
5
5
  import { registerTrackedChild } from "../runtime/spawn.js";
6
+ import { diagnosticError, DiagnosticError } from "../diagnostics/failure.js";
6
7
  export function stripAnsi(s) {
7
8
  return s
8
9
  .replace(/\x1b\[[^A-Za-z]*[A-Za-z]/g, "")
@@ -18,6 +19,51 @@ function regexTest(re, s) {
18
19
  const fresh = new RegExp(re.source, re.flags.replace("g", ""));
19
20
  return fresh.test(s);
20
21
  }
22
+ /**
23
+ * Canned answers to terminal capability queries, table-driven so a future
24
+ * query type is a one-line row. Replies are static: colour answers never
25
+ * affect parsing because styling is stripped before matching.
26
+ */
27
+ export const TERMINAL_QUERY_RESPONSES = [
28
+ // DSR (device status report): cursor position request. Unanswered, some
29
+ // TUIs exit outright ("cursor position could not be read").
30
+ { match: /\x1b\[[0-9;]*6n/, reply: () => "\x1b[1;1R" },
31
+ // DA1 (primary device attributes): `CSI c` is exclusively a DA request.
32
+ { match: /\x1b\[[0-9;]*c/, reply: () => "\x1b[?1;2c" },
33
+ // OSC 4/10/11 colour queries, BEL- or ST-terminated. The palette index is
34
+ // echoed back; 16 OSC 4 queries were measured in a single Muse startup.
35
+ { match: /\x1b\]4;(\d+);\?(?:\x07|\x1b\\)/, reply: (m) => `\x1b]4;${m[1]};rgb:0000/0000/0000\x07` },
36
+ { match: /\x1b\]10;\?(?:\x07|\x1b\\)/, reply: () => "\x1b]10;rgb:ffff/ffff/ffff\x07" },
37
+ { match: /\x1b\]11;\?(?:\x07|\x1b\\)/, reply: () => "\x1b]11;rgb:0000/0000/0000\x07" },
38
+ ];
39
+ /**
40
+ * Stateful feeder for `TERMINAL_QUERY_RESPONSES`. Consumes each matched query
41
+ * from an internal buffer, so a query split across chunks is answered once it
42
+ * completes and never answered twice. Plain output passes through silently.
43
+ */
44
+ export function createTerminalQueryResponder(write) {
45
+ let buf = "";
46
+ return (chunk) => {
47
+ buf += chunk;
48
+ let progress = true;
49
+ while (progress) {
50
+ progress = false;
51
+ for (const rule of TERMINAL_QUERY_RESPONSES) {
52
+ const m = rule.match.exec(buf);
53
+ if (m?.index !== undefined) {
54
+ write(rule.reply(m));
55
+ buf = buf.slice(0, m.index) + buf.slice(m.index + m[0].length);
56
+ progress = true;
57
+ break;
58
+ }
59
+ }
60
+ }
61
+ // Bounded tail: a partial query can only straddle the end of the stream,
62
+ // and the longest query is well under 64 bytes.
63
+ if (buf.length > 64)
64
+ buf = buf.slice(-64);
65
+ };
66
+ }
21
67
  /**
22
68
  * Lazy replacement for the removed postinstall: best-effort `chmod 0o755`
23
69
  * over every prebuilds entry `spawn-helper` under the given node-pty roots.
@@ -143,8 +189,8 @@ function getPty() {
143
189
  catch { }
144
190
  }
145
191
  const repairNote = describeSpawnHelperRepairs(repairs);
146
- throw new Error("pty: node-pty not available — install with build tools (Xcode on macOS, build-essential + python3 on Linux) or use exec-based adapters only; for compiled binaries, ensure the pty sidecar is installed alongside the binary (see install.sh)" +
147
- (repairNote ? `; ${repairNote}` : ""));
192
+ throw diagnosticError(new Error("pty: node-pty not available — install with build tools (Xcode on macOS, build-essential + python3 on Linux) or use exec-based adapters only; for compiled binaries, ensure the pty sidecar is installed alongside the binary (see install.sh)" +
193
+ (repairNote ? `; ${repairNote}` : "")), { source: "pty" });
148
194
  }
149
195
  function isBunRuntime() {
150
196
  return typeof globalThis.Bun !== "undefined" && typeof globalThis.Bun.spawn === "function";
@@ -155,9 +201,9 @@ async function runPtyBun(opts) {
155
201
  const rows = opts.rows ?? 50;
156
202
  const readyTimeoutMs = opts.readyTimeoutMs ?? 6000;
157
203
  if (opts.timeoutMs <= 0)
158
- throw new Error("pty: timeoutMs must be > 0");
204
+ throw diagnosticError(new Error("pty: timeoutMs must be > 0"), { source: "pty" });
159
205
  if (opts.signal?.aborted)
160
- throw new Error("pty aborted");
206
+ throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort" });
161
207
  let aborted = false;
162
208
  const onAbort = () => {
163
209
  aborted = true;
@@ -166,7 +212,7 @@ async function runPtyBun(opts) {
166
212
  opts.signal.addEventListener("abort", onAbort, { once: true });
167
213
  const checkAborted = () => {
168
214
  if (aborted)
169
- throw new Error("pty aborted");
215
+ throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort", stdout: transcript });
170
216
  };
171
217
  const teardownAbort = () => {
172
218
  if (opts.signal)
@@ -181,6 +227,7 @@ async function runPtyBun(opts) {
181
227
  // through proc.terminal.write, and merged output arrives in `data`.
182
228
  // proc.stdin/stdout/stderr are null in this mode.
183
229
  const decoder = new TextDecoder();
230
+ let feedQueries = null;
184
231
  const proc = BunGlobal.spawn([opts.file, ...(opts.args ?? [])], {
185
232
  cwd: opts.cwd ?? process.cwd(),
186
233
  env: {
@@ -193,7 +240,9 @@ async function runPtyBun(opts) {
193
240
  rows,
194
241
  data(_term, chunk) {
195
242
  try {
196
- transcript += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
243
+ const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
244
+ transcript += text;
245
+ feedQueries?.(text);
197
246
  }
198
247
  catch { }
199
248
  },
@@ -206,7 +255,15 @@ async function runPtyBun(opts) {
206
255
  proc.kill();
207
256
  }
208
257
  catch { }
209
- throw new Error("pty: Bun runtime lacks terminal (PTY) support (Bun.spawn terminal option unavailable) — rebuild the standalone binary with a newer Bun version");
258
+ throw diagnosticError(new Error("pty: Bun runtime lacks terminal (PTY) support (Bun.spawn terminal option unavailable) — rebuild the standalone binary with a newer Bun version"), { source: "pty" });
259
+ }
260
+ if (opts.respondToQueries) {
261
+ feedQueries = createTerminalQueryResponder((s) => {
262
+ try {
263
+ proc.terminal.write(s);
264
+ }
265
+ catch { }
266
+ });
210
267
  }
211
268
  // Poll for exit
212
269
  const exitPoll = (async () => {
@@ -220,12 +277,20 @@ async function runPtyBun(opts) {
220
277
  void exitPoll;
221
278
  const checkCap = () => {
222
279
  if (Buffer.byteLength(transcript, "utf8") > maxBytes) {
223
- throw new Error(`pty transcript exceeds ${maxBytes} bytes`);
280
+ throw diagnosticError(new Error(`pty transcript exceeds ${maxBytes} bytes`), {
281
+ source: "pty",
282
+ checkpoint: "transcript overflow",
283
+ stdout: transcript,
284
+ });
224
285
  }
225
286
  };
226
287
  const checkAbort = (clean) => {
227
288
  if (opts.abortOn && regexTest(opts.abortOn, clean)) {
228
- throw new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`);
289
+ throw diagnosticError(new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`), {
290
+ source: "pty",
291
+ checkpoint: "trust prompt",
292
+ stdout: transcript,
293
+ });
229
294
  }
230
295
  };
231
296
  const kill = async () => {
@@ -267,8 +332,14 @@ async function runPtyBun(opts) {
267
332
  while (Date.now() < deadline) {
268
333
  checkCap();
269
334
  checkAborted();
270
- if (exited)
271
- throw new Error(`pty exited before ready (code ${exitCode})`);
335
+ if (exited) {
336
+ throw diagnosticError(new Error(`pty exited before ready (code ${exitCode})`), {
337
+ source: "pty",
338
+ checkpoint: "before ready",
339
+ exitCode,
340
+ stdout: transcript,
341
+ });
342
+ }
272
343
  const clean = stripAnsi(transcript);
273
344
  checkAbort(clean);
274
345
  if (regexTest(opts.readyRegex, clean)) {
@@ -279,13 +350,24 @@ async function runPtyBun(opts) {
279
350
  }
280
351
  if (!matched) {
281
352
  await kill();
282
- throw new Error(`pty ready timeout after ${readyTimeoutMs}ms`);
353
+ throw diagnosticError(new Error(`pty ready timeout after ${readyTimeoutMs}ms`), {
354
+ source: "pty",
355
+ checkpoint: "ready timeout",
356
+ durationMs: readyTimeoutMs,
357
+ stdout: transcript,
358
+ });
283
359
  }
284
- await delay(200);
360
+ await delay(opts.settleDelayMs ?? 200);
285
361
  checkCap();
286
362
  checkAborted();
287
- if (exited)
288
- throw new Error(`pty exited before input (code ${exitCode})`);
363
+ if (exited) {
364
+ throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
365
+ source: "pty",
366
+ checkpoint: "before input",
367
+ exitCode,
368
+ stdout: transcript,
369
+ });
370
+ }
289
371
  const postReadyClean = stripAnsi(transcript);
290
372
  checkAbort(postReadyClean);
291
373
  }
@@ -293,18 +375,57 @@ async function runPtyBun(opts) {
293
375
  await delay(opts.settleDelayMs);
294
376
  checkCap();
295
377
  checkAborted();
296
- if (exited)
297
- throw new Error(`pty exited during settle (code ${exitCode})`);
378
+ if (exited) {
379
+ throw diagnosticError(new Error(`pty exited during settle (code ${exitCode})`), {
380
+ source: "pty",
381
+ checkpoint: "during settle",
382
+ exitCode,
383
+ stdout: transcript,
384
+ });
385
+ }
298
386
  const c = stripAnsi(transcript);
299
387
  checkAbort(c);
300
388
  }
301
- if (exited)
302
- throw new Error(`pty exited before input (code ${exitCode})`);
389
+ if (exited) {
390
+ throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
391
+ source: "pty",
392
+ checkpoint: "before input",
393
+ exitCode,
394
+ stdout: transcript,
395
+ });
396
+ }
303
397
  try {
304
398
  proc.terminal.write(opts.input);
305
399
  }
306
400
  catch (e) {
307
- throw new Error(`pty write failed: ${e.message}`);
401
+ throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
402
+ source: "pty",
403
+ checkpoint: "write",
404
+ stdout: transcript,
405
+ });
406
+ }
407
+ if (opts.submitInput !== undefined && opts.submitAfterMs !== undefined) {
408
+ await delay(opts.submitAfterMs);
409
+ checkCap();
410
+ checkAborted();
411
+ if (exited) {
412
+ throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
413
+ source: "pty",
414
+ checkpoint: "before input",
415
+ exitCode,
416
+ stdout: transcript,
417
+ });
418
+ }
419
+ try {
420
+ proc.terminal.write(opts.submitInput);
421
+ }
422
+ catch (e) {
423
+ throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
424
+ source: "pty",
425
+ checkpoint: "write",
426
+ stdout: transcript,
427
+ });
428
+ }
308
429
  }
309
430
  if (opts.completionRegex) {
310
431
  const deadline = Date.now() + opts.timeoutMs;
@@ -320,7 +441,12 @@ async function runPtyBun(opts) {
320
441
  }
321
442
  if (exited) {
322
443
  if (!regexTest(opts.completionRegex, clean)) {
323
- throw new Error(`pty exited before completion (code ${exitCode})`);
444
+ throw diagnosticError(new Error(`pty exited before completion (code ${exitCode})`), {
445
+ source: "pty",
446
+ checkpoint: "before completion",
447
+ exitCode,
448
+ stdout: transcript,
449
+ });
324
450
  }
325
451
  done = true;
326
452
  break;
@@ -329,7 +455,12 @@ async function runPtyBun(opts) {
329
455
  }
330
456
  if (!done) {
331
457
  await kill();
332
- throw new Error(`pty completion timeout after ${opts.timeoutMs}ms`);
458
+ throw diagnosticError(new Error(`pty completion timeout after ${opts.timeoutMs}ms`), {
459
+ source: "pty",
460
+ checkpoint: "completion timeout",
461
+ durationMs: opts.timeoutMs,
462
+ stdout: transcript,
463
+ });
333
464
  }
334
465
  }
335
466
  else {
@@ -372,7 +503,9 @@ async function runPtyBun(opts) {
372
503
  catch { }
373
504
  unregisterChild();
374
505
  teardownAbort();
375
- throw e;
506
+ if (e instanceof DiagnosticError)
507
+ throw e;
508
+ throw diagnosticError(e, { source: "pty", stdout: transcript, exitCode });
376
509
  }
377
510
  }
378
511
  async function runPtyNode(opts) {
@@ -381,9 +514,9 @@ async function runPtyNode(opts) {
381
514
  const rows = opts.rows ?? 50;
382
515
  const readyTimeoutMs = opts.readyTimeoutMs ?? 6000;
383
516
  if (opts.timeoutMs <= 0)
384
- throw new Error("pty: timeoutMs must be > 0");
517
+ throw diagnosticError(new Error("pty: timeoutMs must be > 0"), { source: "pty" });
385
518
  if (opts.signal?.aborted)
386
- throw new Error("pty aborted");
519
+ throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort" });
387
520
  let aborted = false;
388
521
  const onAbort = () => {
389
522
  aborted = true;
@@ -392,7 +525,7 @@ async function runPtyNode(opts) {
392
525
  opts.signal.addEventListener("abort", onAbort, { once: true });
393
526
  const checkAborted = () => {
394
527
  if (aborted)
395
- throw new Error("pty aborted");
528
+ throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort", stdout: transcript });
396
529
  };
397
530
  const teardownAbort = () => {
398
531
  if (opts.signal)
@@ -417,7 +550,10 @@ async function runPtyNode(opts) {
417
550
  });
418
551
  }
419
552
  catch (e) {
420
- throw new Error(`pty spawn failed for ${opts.file}: ${e.message}`);
553
+ throw diagnosticError(new Error(`pty spawn failed for ${opts.file}: ${e.message}`), {
554
+ source: "pty",
555
+ checkpoint: "spawn",
556
+ });
421
557
  }
422
558
  const unregisterChild = registerTrackedChild(() => {
423
559
  try {
@@ -425,9 +561,21 @@ async function runPtyNode(opts) {
425
561
  }
426
562
  catch { }
427
563
  }, opts.label ?? opts.file);
564
+ const feedQueries = opts.respondToQueries
565
+ ? createTerminalQueryResponder((s) => {
566
+ try {
567
+ ptyProcess.write(s);
568
+ }
569
+ catch { }
570
+ })
571
+ : null;
428
572
  const disposables = [];
429
573
  disposables.push(ptyProcess.onData((data) => {
430
574
  transcript += data;
575
+ try {
576
+ feedQueries?.(data);
577
+ }
578
+ catch { }
431
579
  }));
432
580
  disposables.push(ptyProcess.onExit(({ exitCode: c }) => {
433
581
  exited = true;
@@ -465,12 +613,20 @@ async function runPtyNode(opts) {
465
613
  };
466
614
  const checkCap = () => {
467
615
  if (Buffer.byteLength(transcript, "utf8") > maxBytes) {
468
- throw new Error(`pty transcript exceeds ${maxBytes} bytes`);
616
+ throw diagnosticError(new Error(`pty transcript exceeds ${maxBytes} bytes`), {
617
+ source: "pty",
618
+ checkpoint: "transcript overflow",
619
+ stdout: transcript,
620
+ });
469
621
  }
470
622
  };
471
623
  const checkAbort = (clean) => {
472
624
  if (opts.abortOn && regexTest(opts.abortOn, clean)) {
473
- throw new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`);
625
+ throw diagnosticError(new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`), {
626
+ source: "pty",
627
+ checkpoint: "trust prompt",
628
+ stdout: transcript,
629
+ });
474
630
  }
475
631
  };
476
632
  try {
@@ -480,8 +636,14 @@ async function runPtyNode(opts) {
480
636
  while (Date.now() < deadline) {
481
637
  checkCap();
482
638
  checkAborted();
483
- if (exited)
484
- throw new Error(`pty exited before ready (code ${exitCode})`);
639
+ if (exited) {
640
+ throw diagnosticError(new Error(`pty exited before ready (code ${exitCode})`), {
641
+ source: "pty",
642
+ checkpoint: "before ready",
643
+ exitCode,
644
+ stdout: transcript,
645
+ });
646
+ }
485
647
  const clean = stripAnsi(transcript);
486
648
  checkAbort(clean);
487
649
  if (regexTest(opts.readyRegex, clean)) {
@@ -492,13 +654,24 @@ async function runPtyNode(opts) {
492
654
  }
493
655
  if (!matched) {
494
656
  await kill();
495
- throw new Error(`pty ready timeout after ${readyTimeoutMs}ms`);
657
+ throw diagnosticError(new Error(`pty ready timeout after ${readyTimeoutMs}ms`), {
658
+ source: "pty",
659
+ checkpoint: "ready timeout",
660
+ durationMs: readyTimeoutMs,
661
+ stdout: transcript,
662
+ });
496
663
  }
497
- await delay(200);
664
+ await delay(opts.settleDelayMs ?? 200);
498
665
  checkCap();
499
666
  checkAborted();
500
- if (exited)
501
- throw new Error(`pty exited before input (code ${exitCode})`);
667
+ if (exited) {
668
+ throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
669
+ source: "pty",
670
+ checkpoint: "before input",
671
+ exitCode,
672
+ stdout: transcript,
673
+ });
674
+ }
502
675
  const postReadyClean = stripAnsi(transcript);
503
676
  checkAbort(postReadyClean);
504
677
  }
@@ -506,14 +679,58 @@ async function runPtyNode(opts) {
506
679
  await delay(opts.settleDelayMs);
507
680
  checkCap();
508
681
  checkAborted();
509
- if (exited)
510
- throw new Error(`pty exited during settle (code ${exitCode})`);
682
+ if (exited) {
683
+ throw diagnosticError(new Error(`pty exited during settle (code ${exitCode})`), {
684
+ source: "pty",
685
+ checkpoint: "during settle",
686
+ exitCode,
687
+ stdout: transcript,
688
+ });
689
+ }
511
690
  const c = stripAnsi(transcript);
512
691
  checkAbort(c);
513
692
  }
514
- if (exited)
515
- throw new Error(`pty exited before input (code ${exitCode})`);
516
- ptyProcess.write(opts.input);
693
+ if (exited) {
694
+ throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
695
+ source: "pty",
696
+ checkpoint: "before input",
697
+ exitCode,
698
+ stdout: transcript,
699
+ });
700
+ }
701
+ try {
702
+ ptyProcess.write(opts.input);
703
+ }
704
+ catch (e) {
705
+ throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
706
+ source: "pty",
707
+ checkpoint: "write",
708
+ stdout: transcript,
709
+ });
710
+ }
711
+ if (opts.submitInput !== undefined && opts.submitAfterMs !== undefined) {
712
+ await delay(opts.submitAfterMs);
713
+ checkCap();
714
+ checkAborted();
715
+ if (exited) {
716
+ throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
717
+ source: "pty",
718
+ checkpoint: "before input",
719
+ exitCode,
720
+ stdout: transcript,
721
+ });
722
+ }
723
+ try {
724
+ ptyProcess.write(opts.submitInput);
725
+ }
726
+ catch (e) {
727
+ throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
728
+ source: "pty",
729
+ checkpoint: "write",
730
+ stdout: transcript,
731
+ });
732
+ }
733
+ }
517
734
  if (opts.completionRegex) {
518
735
  const deadline = Date.now() + opts.timeoutMs;
519
736
  let done = false;
@@ -528,7 +745,12 @@ async function runPtyNode(opts) {
528
745
  }
529
746
  if (exited) {
530
747
  if (!regexTest(opts.completionRegex, clean)) {
531
- throw new Error(`pty exited before completion (code ${exitCode})`);
748
+ throw diagnosticError(new Error(`pty exited before completion (code ${exitCode})`), {
749
+ source: "pty",
750
+ checkpoint: "before completion",
751
+ exitCode,
752
+ stdout: transcript,
753
+ });
532
754
  }
533
755
  done = true;
534
756
  break;
@@ -537,7 +759,12 @@ async function runPtyNode(opts) {
537
759
  }
538
760
  if (!done) {
539
761
  await kill();
540
- throw new Error(`pty completion timeout after ${opts.timeoutMs}ms`);
762
+ throw diagnosticError(new Error(`pty completion timeout after ${opts.timeoutMs}ms`), {
763
+ source: "pty",
764
+ checkpoint: "completion timeout",
765
+ durationMs: opts.timeoutMs,
766
+ stdout: transcript,
767
+ });
541
768
  }
542
769
  }
543
770
  else {
@@ -563,7 +790,9 @@ async function runPtyNode(opts) {
563
790
  }
564
791
  catch (e) {
565
792
  await kill();
566
- throw e;
793
+ if (e instanceof DiagnosticError)
794
+ throw e;
795
+ throw diagnosticError(e, { source: "pty", stdout: transcript, exitCode });
567
796
  }
568
797
  finally {
569
798
  cleanup();
@@ -572,8 +801,15 @@ async function runPtyNode(opts) {
572
801
  }
573
802
  }
574
803
  export async function runPty(opts) {
575
- if (isBunRuntime()) {
576
- return runPtyBun(opts);
804
+ try {
805
+ if (isBunRuntime()) {
806
+ return await runPtyBun(opts);
807
+ }
808
+ return await runPtyNode(opts);
809
+ }
810
+ catch (e) {
811
+ if (e instanceof DiagnosticError)
812
+ throw e;
813
+ throw diagnosticError(e, { source: "pty" });
577
814
  }
578
- return runPtyNode(opts);
579
815
  }
@@ -0,0 +1,14 @@
1
+ export interface ProviderIdentity {
2
+ displayName: string;
3
+ builtinName: string | null;
4
+ vendor: string | null;
5
+ harness: string | null;
6
+ description: string | null;
7
+ }
8
+ export declare const REGISTRY: Map<string, {
9
+ displayName: string;
10
+ vendor: string | null;
11
+ harness: string | null;
12
+ description: string;
13
+ }>;
14
+ export declare function providerIdentity(id: string, overrides?: Record<string, string>): ProviderIdentity;
@@ -0,0 +1,98 @@
1
+ // One server-side home for provider identity. Unknown ids (manual pastes)
2
+ // degrade to the raw id with null metadata instead of throwing. Map, not a
3
+ // plain object: Object.prototype keys such as __proto__ and constructor would
4
+ // otherwise resolve as inherited values, not literals.
5
+ export const REGISTRY = new Map([
6
+ [
7
+ "claude",
8
+ {
9
+ displayName: "Claude",
10
+ vendor: "Anthropic",
11
+ harness: "Claude Code",
12
+ description: "Anthropic's Claude Code subscription, metered as a weekly window with a shorter session window inside it.",
13
+ },
14
+ ],
15
+ [
16
+ "codex",
17
+ {
18
+ displayName: "Codex",
19
+ vendor: "OpenAI",
20
+ harness: "Codex CLI",
21
+ description: "OpenAI's Codex CLI subscription, metered as weekly and 5-hour windows.",
22
+ },
23
+ ],
24
+ [
25
+ "kimi",
26
+ {
27
+ displayName: "Kimi",
28
+ vendor: "Moonshot AI",
29
+ harness: "Kimi Code",
30
+ description: "Moonshot's Kimi Code subscription, metered as weekly and 5-hour windows.",
31
+ },
32
+ ],
33
+ [
34
+ "grok",
35
+ {
36
+ displayName: "Grok",
37
+ vendor: "xAI",
38
+ harness: "Grok CLI",
39
+ description: "xAI's Grok CLI subscription, metered weekly, with a separate pay-as-you-go credit balance.",
40
+ },
41
+ ],
42
+ [
43
+ "agy",
44
+ {
45
+ displayName: "Antigravity",
46
+ vendor: "Google",
47
+ harness: "Antigravity",
48
+ description: "The Gemini model pool in Google's Antigravity, metered weekly with a 5-hour window inside it.",
49
+ },
50
+ ],
51
+ [
52
+ "agy:3p",
53
+ {
54
+ displayName: "Antigravity 3P",
55
+ vendor: "Google",
56
+ harness: "Antigravity",
57
+ description: "The third-party model pool in Google's Antigravity — Claude and GPT models billed against your Google plan and metered separately from Gemini.",
58
+ },
59
+ ],
60
+ [
61
+ "muse",
62
+ {
63
+ displayName: "Muse",
64
+ vendor: "Meta",
65
+ harness: "Muse Code",
66
+ description: "Meta's Muse Code subscription, running Muse Spark models, metered as a weekly window with a shorter current window inside it.",
67
+ },
68
+ ],
69
+ [
70
+ "manual",
71
+ {
72
+ displayName: "Manual",
73
+ vendor: null,
74
+ harness: null,
75
+ description: "A quota you pasted in by hand. QuotaCap does not poll it.",
76
+ },
77
+ ],
78
+ ]);
79
+ export function providerIdentity(id, overrides) {
80
+ const entry = REGISTRY.get(id);
81
+ const override = overrides && typeof overrides[id] === "string" && overrides[id].trim().length > 0
82
+ ? overrides[id].trim()
83
+ : undefined;
84
+ if (!entry) {
85
+ return {
86
+ displayName: override ?? id,
87
+ builtinName: null,
88
+ vendor: null,
89
+ harness: null,
90
+ description: null,
91
+ };
92
+ }
93
+ return {
94
+ ...entry,
95
+ displayName: override ?? entry.displayName,
96
+ builtinName: entry.displayName,
97
+ };
98
+ }
@@ -2,6 +2,7 @@ import type { StateSnapshot, Recommendation } from "./types.js";
2
2
  export declare const STALE_MS: number;
3
3
  export interface SnapshotOptions {
4
4
  enabledProviders: string[];
5
+ providerNames?: Record<string, string>;
5
6
  now?: Date;
6
7
  runtime: {
7
8
  available: boolean;
@@ -1,6 +1,7 @@
1
1
  import { getAllLatest, getBurnRates } from "../store/quotas.js";
2
2
  import { getAttempts } from "../store/attempts.js";
3
3
  import { recommend, averagePace, computeAdvisory } from "./engine.js";
4
+ import { providerIdentity } from "./provider-names.js";
4
5
  export const STALE_MS = 60 * 60 * 1000;
5
6
  function normalizeAdvisory(adv) {
6
7
  if (!adv)
@@ -141,6 +142,7 @@ export function buildSnapshot(db, opts) {
141
142
  }
142
143
  providerSnapshots.push({
143
144
  id,
145
+ ...providerIdentity(id, opts.providerNames),
144
146
  enabled,
145
147
  quota,
146
148
  lastAttempt: attempt,
@@ -202,6 +204,11 @@ export function projectQuotasResponse(s) {
202
204
  .filter((p) => p.quota !== null)
203
205
  .map((p) => ({
204
206
  ...p.quota,
207
+ displayName: p.displayName,
208
+ builtinName: p.builtinName ?? null,
209
+ vendor: p.vendor,
210
+ harness: p.harness,
211
+ description: p.description,
205
212
  stale: p.stale,
206
213
  ageMs: p.ageMs,
207
214
  evidence: p.evidence,