@ashlr/hub 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/README.md +3 -0
  3. package/dist/build-identity.json +2 -2
  4. package/dist/cli/index.js +11 -0
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/cli/local-runtime.js +40 -9
  7. package/dist/cli/local-runtime.js.map +1 -1
  8. package/dist/core/local-eval/classify.d.ts +57 -0
  9. package/dist/core/local-eval/classify.js +121 -0
  10. package/dist/core/local-eval/classify.js.map +1 -0
  11. package/dist/core/local-eval/configuration.d.ts +37 -0
  12. package/dist/core/local-eval/configuration.js +171 -0
  13. package/dist/core/local-eval/configuration.js.map +1 -0
  14. package/dist/core/local-eval/index.d.ts +15 -0
  15. package/dist/core/local-eval/index.js +16 -0
  16. package/dist/core/local-eval/index.js.map +1 -0
  17. package/dist/core/local-eval/main.d.ts +31 -0
  18. package/dist/core/local-eval/main.js +174 -0
  19. package/dist/core/local-eval/main.js.map +1 -0
  20. package/dist/core/local-eval/report.d.ts +25 -0
  21. package/dist/core/local-eval/report.js +157 -0
  22. package/dist/core/local-eval/report.js.map +1 -0
  23. package/dist/core/local-eval/runner.d.ts +55 -0
  24. package/dist/core/local-eval/runner.js +290 -0
  25. package/dist/core/local-eval/runner.js.map +1 -0
  26. package/dist/core/local-eval/tasks.d.ts +22 -0
  27. package/dist/core/local-eval/tasks.js +303 -0
  28. package/dist/core/local-eval/tasks.js.map +1 -0
  29. package/dist/core/local-eval/trace.d.ts +67 -0
  30. package/dist/core/local-eval/trace.js +309 -0
  31. package/dist/core/local-eval/trace.js.map +1 -0
  32. package/dist/core/local-eval/types.d.ts +262 -0
  33. package/dist/core/local-eval/types.js +23 -0
  34. package/dist/core/local-eval/types.js.map +1 -0
  35. package/dist/core/local-runtime/llama/anthropic-proxy-host.d.ts +68 -0
  36. package/dist/core/local-runtime/llama/anthropic-proxy-host.js +120 -0
  37. package/dist/core/local-runtime/llama/anthropic-proxy-host.js.map +1 -0
  38. package/dist/core/local-runtime/llama/anthropic-proxy-process.d.ts +15 -0
  39. package/dist/core/local-runtime/llama/anthropic-proxy-process.js +21 -0
  40. package/dist/core/local-runtime/llama/anthropic-proxy-process.js.map +1 -0
  41. package/dist/core/local-runtime/llama/anthropic-proxy.d.ts +2 -1
  42. package/dist/core/local-runtime/llama/anthropic-proxy.js +14 -6
  43. package/dist/core/local-runtime/llama/anthropic-proxy.js.map +1 -1
  44. package/dist/core/local-runtime/llama/anthropic-shim.d.ts +33 -0
  45. package/dist/core/local-runtime/llama/anthropic-shim.js +59 -0
  46. package/dist/core/local-runtime/llama/anthropic-shim.js.map +1 -1
  47. package/dist/core/local-runtime/llama/config.d.ts +101 -6
  48. package/dist/core/local-runtime/llama/config.js +174 -6
  49. package/dist/core/local-runtime/llama/config.js.map +1 -1
  50. package/dist/core/local-runtime/llama/index.d.ts +15 -0
  51. package/dist/core/local-runtime/llama/index.js +13 -0
  52. package/dist/core/local-runtime/llama/index.js.map +1 -1
  53. package/dist/core/local-runtime/llama/paths.d.ts +17 -0
  54. package/dist/core/local-runtime/llama/paths.js +25 -0
  55. package/dist/core/local-runtime/llama/paths.js.map +1 -1
  56. package/dist/core/local-runtime/llama/proxy-invocation.d.ts +101 -0
  57. package/dist/core/local-runtime/llama/proxy-invocation.js +134 -0
  58. package/dist/core/local-runtime/llama/proxy-invocation.js.map +1 -0
  59. package/dist/core/local-runtime/llama/proxy-launchd.d.ts +83 -0
  60. package/dist/core/local-runtime/llama/proxy-launchd.js +216 -0
  61. package/dist/core/local-runtime/llama/proxy-launchd.js.map +1 -0
  62. package/dist/core/local-runtime/llama/shapes.d.ts +2 -1
  63. package/dist/core/local-runtime/llama/shapes.js +2 -1
  64. package/dist/core/local-runtime/llama/shapes.js.map +1 -1
  65. package/dist/core/verse/adapters/claude.js +16 -0
  66. package/dist/core/verse/adapters/claude.js.map +1 -1
  67. package/dist/core/verse/adapters/codex.js +60 -2
  68. package/dist/core/verse/adapters/codex.js.map +1 -1
  69. package/dist/core/verse/adapters/grok.js +13 -0
  70. package/dist/core/verse/adapters/grok.js.map +1 -1
  71. package/dist/core/verse/control-api.d.ts +8 -9
  72. package/dist/core/verse/control-api.js +14 -114
  73. package/dist/core/verse/control-api.js.map +1 -1
  74. package/dist/core/verse/github-api.d.ts +50 -0
  75. package/dist/core/verse/github-api.js +211 -0
  76. package/dist/core/verse/github-api.js.map +1 -0
  77. package/dist/core/verse/github-proposal.d.ts +64 -0
  78. package/dist/core/verse/github-proposal.js +186 -0
  79. package/dist/core/verse/github-proposal.js.map +1 -0
  80. package/dist/core/verse/github-repo.d.ts +104 -0
  81. package/dist/core/verse/github-repo.js +476 -0
  82. package/dist/core/verse/github-repo.js.map +1 -0
  83. package/dist/core/verse/github-types.d.ts +174 -0
  84. package/dist/core/verse/github-types.js +31 -0
  85. package/dist/core/verse/github-types.js.map +1 -0
  86. package/dist/core/verse/mcp-cli-health.d.ts +149 -0
  87. package/dist/core/verse/mcp-cli-health.js +227 -0
  88. package/dist/core/verse/mcp-cli-health.js.map +1 -0
  89. package/dist/core/verse/mcp-control-api.d.ts +68 -0
  90. package/dist/core/verse/mcp-control-api.js +322 -0
  91. package/dist/core/verse/mcp-control-api.js.map +1 -0
  92. package/dist/core/verse/mcp-mutation.d.ts +187 -0
  93. package/dist/core/verse/mcp-mutation.js +471 -0
  94. package/dist/core/verse/mcp-mutation.js.map +1 -0
  95. package/dist/core/verse/mcp-scope.d.ts +114 -0
  96. package/dist/core/verse/mcp-scope.js +149 -0
  97. package/dist/core/verse/mcp-scope.js.map +1 -0
  98. package/dist/core/verse/mcp-seat-view.d.ts +197 -0
  99. package/dist/core/verse/mcp-seat-view.js +322 -0
  100. package/dist/core/verse/mcp-seat-view.js.map +1 -0
  101. package/dist/core/verse/path-guard.d.ts +115 -0
  102. package/dist/core/verse/path-guard.js +171 -0
  103. package/dist/core/verse/path-guard.js.map +1 -0
  104. package/dist/core/verse/seats.js +30 -2
  105. package/dist/core/verse/seats.js.map +1 -1
  106. package/dist/core/verse/session-engine.js +60 -1
  107. package/dist/core/verse/session-engine.js.map +1 -1
  108. package/dist/core/verse/session-store.js +20 -0
  109. package/dist/core/verse/session-store.js.map +1 -1
  110. package/dist/core/verse/types.d.ts +225 -1
  111. package/dist/core/verse/types.js +26 -0
  112. package/dist/core/verse/types.js.map +1 -1
  113. package/dist/core/verse/verse-api.d.ts +16 -1
  114. package/dist/core/verse/verse-api.js +338 -16
  115. package/dist/core/verse/verse-api.js.map +1 -1
  116. package/dist/core/verse/workspaces.d.ts +130 -0
  117. package/dist/core/verse/workspaces.js +501 -0
  118. package/dist/core/verse/workspaces.js.map +1 -0
  119. package/dist/core/web/api.js +23 -0
  120. package/dist/core/web/api.js.map +1 -1
  121. package/dist/core/web/public/next/assets/{App-LuILXjWG.js → App-CsbPYQqy.js} +2 -2
  122. package/dist/core/web/public/next/assets/{App-LuILXjWG.js.map → App-CsbPYQqy.js.map} +1 -1
  123. package/dist/core/web/public/next/assets/{ApprovalsSection-B_Xor8JH.js → ApprovalsSection-CBZquyLK.js} +2 -2
  124. package/dist/core/web/public/next/assets/{ApprovalsSection-B_Xor8JH.js.map → ApprovalsSection-CBZquyLK.js.map} +1 -1
  125. package/dist/core/web/public/next/assets/AutonomySection-CQVpEdCE.js +2 -0
  126. package/dist/core/web/public/next/assets/{AutonomySection-BEwITtPJ.js.map → AutonomySection-CQVpEdCE.js.map} +1 -1
  127. package/dist/core/web/public/next/assets/AutonomySection-DTN6m6QE.css +1 -0
  128. package/dist/core/web/public/next/assets/ChatSection-DiRvNX3Q.css +1 -0
  129. package/dist/core/web/public/next/assets/ChatSection-DmE9K0Tg.js +77 -0
  130. package/dist/core/web/public/next/assets/ChatSection-DmE9K0Tg.js.map +1 -0
  131. package/dist/core/web/public/next/assets/{ConfirmDialog-BqkZdPs_.js → ConfirmDialog-deO0C0iY.js} +2 -2
  132. package/dist/core/web/public/next/assets/{ConfirmDialog-BqkZdPs_.js.map → ConfirmDialog-deO0C0iY.js.map} +1 -1
  133. package/dist/core/web/public/next/assets/{Dialog-CPmedg4S.js → Dialog-CNd7q89m.js} +2 -2
  134. package/dist/core/web/public/next/assets/{Dialog-CPmedg4S.js.map → Dialog-CNd7q89m.js.map} +1 -1
  135. package/dist/core/web/public/next/assets/{DiffViewer-DmPJbeM_.js → DiffViewer-DTO0KRuG.js} +2 -2
  136. package/dist/core/web/public/next/assets/{DiffViewer-DmPJbeM_.js.map → DiffViewer-DTO0KRuG.js.map} +1 -1
  137. package/dist/core/web/public/next/assets/{Epistemic-M3SxLHjq.js → Epistemic-53PikaUI.js} +2 -2
  138. package/dist/core/web/public/next/assets/{Epistemic-M3SxLHjq.js.map → Epistemic-53PikaUI.js.map} +1 -1
  139. package/dist/core/web/public/next/assets/{Meter-D_X7c9U3.js → Meter-DRWZQgr1.js} +2 -2
  140. package/dist/core/web/public/next/assets/{Meter-D_X7c9U3.js.map → Meter-DRWZQgr1.js.map} +1 -1
  141. package/dist/core/web/public/next/assets/{MutationTokenDialog-Ba5ZHu-8.js → MutationTokenDialog-DYF8n_RD.js} +2 -2
  142. package/dist/core/web/public/next/assets/{MutationTokenDialog-Ba5ZHu-8.js.map → MutationTokenDialog-DYF8n_RD.js.map} +1 -1
  143. package/dist/core/web/public/next/assets/{RefreshIndicator-vHGpmyzb.js → RefreshIndicator-DXJQXKyA.js} +2 -2
  144. package/dist/core/web/public/next/assets/{RefreshIndicator-vHGpmyzb.js.map → RefreshIndicator-DXJQXKyA.js.map} +1 -1
  145. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-B5g6JEVk.js → ResourcePoolConsoleApp-hDSEFdLU.js} +2 -2
  146. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-B5g6JEVk.js.map → ResourcePoolConsoleApp-hDSEFdLU.js.map} +1 -1
  147. package/dist/core/web/public/next/assets/{RouteErrorBoundary-CroxuKoi.js → RouteErrorBoundary-CZd7ln8v.js} +2 -2
  148. package/dist/core/web/public/next/assets/{RouteErrorBoundary-CroxuKoi.js.map → RouteErrorBoundary-CZd7ln8v.js.map} +1 -1
  149. package/dist/core/web/public/next/assets/{SettingsSection-p-7FCGfn.js → SettingsSection-C9Qs9JWW.js} +2 -2
  150. package/dist/core/web/public/next/assets/{SettingsSection-p-7FCGfn.js.map → SettingsSection-C9Qs9JWW.js.map} +1 -1
  151. package/dist/core/web/public/next/assets/{Skeleton-4qq4HuJO.js → Skeleton-YkcOpgFb.js} +2 -2
  152. package/dist/core/web/public/next/assets/{Skeleton-4qq4HuJO.js.map → Skeleton-YkcOpgFb.js.map} +1 -1
  153. package/dist/core/web/public/next/assets/{StatusBadge-BbEdqEGn.js → StatusBadge-DJw0LSPg.js} +2 -2
  154. package/dist/core/web/public/next/assets/{StatusBadge-BbEdqEGn.js.map → StatusBadge-DJw0LSPg.js.map} +1 -1
  155. package/dist/core/web/public/next/assets/{TableView-nyYs7Sv1.js → TableView-Bj-w1O6o.js} +2 -2
  156. package/dist/core/web/public/next/assets/{TableView-nyYs7Sv1.js.map → TableView-Bj-w1O6o.js.map} +1 -1
  157. package/dist/core/web/public/next/assets/{UniverseConsoleApp-DjTpQUrA.js → UniverseConsoleApp-Bs2fxaen.js} +2 -2
  158. package/dist/core/web/public/next/assets/{UniverseConsoleApp-DjTpQUrA.js.map → UniverseConsoleApp-Bs2fxaen.js.map} +1 -1
  159. package/dist/core/web/public/next/assets/{UniverseView-BtApwayT.js → UniverseView-B6b2hw-8.js} +2 -2
  160. package/dist/core/web/public/next/assets/{UniverseView-BtApwayT.js.map → UniverseView-B6b2hw-8.js.map} +1 -1
  161. package/dist/core/web/public/next/assets/{UsageSection-Db99N6ao.js → UsageSection-C__ziNkV.js} +2 -2
  162. package/dist/core/web/public/next/assets/{UsageSection-Db99N6ao.js.map → UsageSection-C__ziNkV.js.map} +1 -1
  163. package/dist/core/web/public/next/assets/{VerseConsoleApp-COa3J2dO.js → VerseConsoleApp-Cu5aG6J6.js} +3 -3
  164. package/dist/core/web/public/next/assets/VerseConsoleApp-Cu5aG6J6.js.map +1 -0
  165. package/dist/core/web/public/next/assets/{global-CNVapoV1.js → global-BHjd6fVZ.js} +2 -2
  166. package/dist/core/web/public/next/assets/{global-CNVapoV1.js.map → global-BHjd6fVZ.js.map} +1 -1
  167. package/dist/core/web/public/next/assets/index-CJ-e8YA9.js +2 -0
  168. package/dist/core/web/public/next/assets/index-CJ-e8YA9.js.map +1 -0
  169. package/dist/core/web/public/next/assets/{index-BkUlX2Se.js → index-CpgkRaTi.js} +3 -3
  170. package/dist/core/web/public/next/assets/{index-BkUlX2Se.js.map → index-CpgkRaTi.js.map} +1 -1
  171. package/dist/core/web/public/next/assets/{queries-C9XS4SYq.js → queries-856qUN3Y.js} +2 -2
  172. package/dist/core/web/public/next/assets/{queries-C9XS4SYq.js.map → queries-856qUN3Y.js.map} +1 -1
  173. package/dist/core/web/public/next/assets/{use-guarded-action-Bql1nA-z.js → use-guarded-action-DwaESKVy.js} +2 -2
  174. package/dist/core/web/public/next/assets/{use-guarded-action-Bql1nA-z.js.map → use-guarded-action-DwaESKVy.js.map} +1 -1
  175. package/dist/core/web/public/next/assets/verse-model-Dllo8rRt.js +2 -0
  176. package/dist/core/web/public/next/assets/{verse-model-IrNQKtER.js.map → verse-model-Dllo8rRt.js.map} +1 -1
  177. package/dist/core/web/public/next/assets/workspace-model-JgmWDKt4.js +2 -0
  178. package/dist/core/web/public/next/assets/workspace-model-JgmWDKt4.js.map +1 -0
  179. package/dist/core/web/public/next/index.html +1 -1
  180. package/dist/release-dependency-inventory.json +1 -1
  181. package/docs/README.md +4 -0
  182. package/package.json +1 -1
  183. package/dist/cli/activation.d.ts +0 -41
  184. package/dist/cli/activation.js +0 -485
  185. package/dist/cli/activation.js.map +0 -1
  186. package/dist/core/fleet/generated-repair-resolution.d.ts +0 -36
  187. package/dist/core/fleet/generated-repair-resolution.js +0 -245
  188. package/dist/core/fleet/generated-repair-resolution.js.map +0 -1
  189. package/dist/core/local-runtime/llama/port.d.ts +0 -17
  190. package/dist/core/local-runtime/llama/port.js +0 -47
  191. package/dist/core/local-runtime/llama/port.js.map +0 -1
  192. package/dist/core/local-runtime/llama/proxy-host.d.ts +0 -54
  193. package/dist/core/local-runtime/llama/proxy-host.js +0 -159
  194. package/dist/core/local-runtime/llama/proxy-host.js.map +0 -1
  195. package/dist/core/local-runtime/llama/proxy-process.d.ts +0 -148
  196. package/dist/core/local-runtime/llama/proxy-process.js +0 -250
  197. package/dist/core/local-runtime/llama/proxy-process.js.map +0 -1
  198. package/dist/core/local-runtime/llama/proxy-record.d.ts +0 -40
  199. package/dist/core/local-runtime/llama/proxy-record.js +0 -133
  200. package/dist/core/local-runtime/llama/proxy-record.js.map +0 -1
  201. package/dist/core/local-runtime/llama/proxy-supervisor.d.ts +0 -118
  202. package/dist/core/local-runtime/llama/proxy-supervisor.js +0 -511
  203. package/dist/core/local-runtime/llama/proxy-supervisor.js.map +0 -1
  204. package/dist/core/web/public/next/assets/AutonomySection-BB8RD-tv.css +0 -1
  205. package/dist/core/web/public/next/assets/AutonomySection-BEwITtPJ.js +0 -2
  206. package/dist/core/web/public/next/assets/ChatSection-DrxqhBPK.css +0 -1
  207. package/dist/core/web/public/next/assets/ChatSection-bRAzlMth.js +0 -77
  208. package/dist/core/web/public/next/assets/ChatSection-bRAzlMth.js.map +0 -1
  209. package/dist/core/web/public/next/assets/VerseConsoleApp-COa3J2dO.js.map +0 -1
  210. package/dist/core/web/public/next/assets/index-YNTgDG2b.js +0 -2
  211. package/dist/core/web/public/next/assets/index-YNTgDG2b.js.map +0 -1
  212. package/dist/core/web/public/next/assets/verse-model-IrNQKtER.js +0 -2
  213. package/node_modules/which/CHANGELOG.md +0 -166
@@ -0,0 +1,31 @@
1
+ /**
2
+ * core/verse/github-types.ts — the GitHub wire contract for Ashlr Verse
3
+ * (docs/VERSE-WORKSPACES.md §2).
4
+ *
5
+ * `src/core/integrations/github.ts` already does the hard part; none of it was
6
+ * reachable from Verse. These are the shapes that carry it across the wire,
7
+ * plus the one shape the hub never had: a DISCLOSURE of what an approved
8
+ * proposal of kind `pr` is about to do, computed BEFORE the operator clicks.
9
+ *
10
+ * TWO RULES ENCODED HERE RATHER THAN LEFT TO THE UI
11
+ *
12
+ * 1. NO REMOTE URL EVER APPEARS IN THESE SHAPES. A git remote URL may carry
13
+ * credentials in its userinfo (`https://x-access-token:<token>@github.com/…`
14
+ * is a supported GitHub transport, and core/git.ts's canonicaliser accepts
15
+ * it). Repository identity therefore travels ONLY as `nameWithOwner`, which
16
+ * is derived from the URL and cannot contain one. `pushUrl`/`fetchUrls` from
17
+ * `resolveGitHubOriginAuthorityDetails()` are deliberately dropped on the
18
+ * floor; see test/verse-github-repo.test.ts for the guard that keeps it so.
19
+ *
20
+ * 2. THE PR PLAN IS A PREDICTION, AND SAYS SO. Before apply, `branch` and
21
+ * `base` are what `core/inbox/apply.ts` WOULD use; after apply they are what
22
+ * it DID use, read back off `proposal.remoteHandoff`. `baseSource` and
23
+ * `branchSource` name which of the two an operator is looking at, because a
24
+ * disclosure that cannot distinguish a prediction from a record is not a
25
+ * disclosure.
26
+ *
27
+ * Nothing here is a new source of truth — every field is a projection of state
28
+ * the hub already computes.
29
+ */
30
+ export {};
31
+ //# sourceMappingURL=github-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-types.js","sourceRoot":"","sources":["../../../src/core/verse/github-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * core/verse/mcp-cli-health.ts — per-account CLI health, and version drift.
3
+ *
4
+ * docs/VERSE-WORKSPACES.md §3: "Per-account CLI health is nearly free: the
5
+ * probes already report auth state, plan and CLI version per account. A
6
+ * version drift is worth surfacing loudly, because the Claude usage probe is
7
+ * pinned to an exact CLI version and fails closed when it moves."
8
+ *
9
+ * ── One correction to that premise, found in the source ─────────────────────
10
+ *
11
+ * Auth state and plan ARE already reported per account, on
12
+ * `VerseAccountRecord` (`state` / `authentication` / `planType`). The CLI
13
+ * VERSION is not. Nothing caches it:
14
+ *
15
+ * - `probeClaudeAccountUsage` runs `--version`, COMPARES it to an inline
16
+ * literal, and throws the string away — it keeps only the verdict, as the
17
+ * verbatim reason `usage-version-unsupported`
18
+ * (src/core/resources/claude-account-usage.ts:99).
19
+ * - `checkResourceLauncherCompatibility` DOES return a recognised numeric
20
+ * `version` (src/core/resources/launcher-compatibility.ts:163-165), but it
21
+ * is only ever reached from the `ashlr resources launcher check` CLI
22
+ * command. No route, collector or snapshot calls it.
23
+ *
24
+ * So this module splits the two honestly:
25
+ *
26
+ * 1. {@link buildVerseCliHealth} is PURE and free. It derives drift from
27
+ * what the collector already has: the verbatim probe reason plus the
28
+ * pinned-version constant. When the probe is failing closed on the pin,
29
+ * that is stated as a FACT, loudly, with the pin quoted — not as a
30
+ * generic "unknown".
31
+ * 2. {@link probeVerseAccountCliVersions} gets the actual installed version
32
+ * number, and it SPAWNS the provider CLI to do it. It is therefore not
33
+ * wired to a GET. A read route that silently launches four vendor
34
+ * binaries is a side effect wearing a read's clothes.
35
+ *
36
+ * ── What may leave this module ──────────────────────────────────────────────
37
+ *
38
+ * Never a launcher argv, never a private absolute path. The probe result
39
+ * shape carries id / provider / version / status / reason and nothing else;
40
+ * `ResourceLauncherCompatibilityResult` itself never echoes the command.
41
+ */
42
+ import { checkResourceLauncherCompatibility, type ResourceLauncherCompatibilityResult } from '../resources/launcher-compatibility.js';
43
+ import { type VerseAccountProvider, type VerseAccountRecord } from './accounts.js';
44
+ export type VerseCliVersionState =
45
+ /** A probe ran and the installed version is the pinned one. */
46
+ 'matches-pin'
47
+ /** The usage probe is failing closed because the installed CLI is not the pin. */
48
+ | 'drift'
49
+ /** A probe ran and reported a version, but nothing pins this provider. */
50
+ | 'reported'
51
+ /** No version has been read. This is the default, and it is not a fault. */
52
+ | 'unverified';
53
+ export interface VerseAccountCliHealth {
54
+ accountId: string;
55
+ label: string;
56
+ provider: VerseAccountProvider;
57
+ /** Straight off the collector's record — already surfaced, just not here. */
58
+ authentication: VerseAccountRecord['authentication'];
59
+ state: VerseAccountRecord['state'];
60
+ health: VerseAccountRecord['health'];
61
+ planType: string | null;
62
+ observedAt: string | null;
63
+ /** Verbatim probe reason. Never rewritten into prose. */
64
+ reason: string;
65
+ /** The installed CLI version, when something actually read it. Never guessed. */
66
+ version: string | null;
67
+ /** The version this hub's usage probe requires. Null when nothing is pinned. */
68
+ pinnedVersion: string | null;
69
+ versionState: VerseCliVersionState;
70
+ /**
71
+ * TRUE when the usage probe is refusing to read this account's quota
72
+ * BECAUSE of the version pin. This is the loud bit: it is a one-line
73
+ * constant bump, not an outage, and a silent "unknown" would hide it.
74
+ */
75
+ usageBlockedByPin: boolean;
76
+ notes: string[];
77
+ }
78
+ export interface VerseCliHealthSnapshot {
79
+ sampledAt: string;
80
+ accounts: VerseAccountCliHealth[];
81
+ /** TRUE when any account is blocked by the pin. Drives the loud banner. */
82
+ driftDetected: boolean;
83
+ notes: string[];
84
+ }
85
+ export declare function verseCliPinNote(pinned: string): string;
86
+ export declare const VERSE_CLI_VERSION_UNREAD_NOTE: string;
87
+ /**
88
+ * The pinned version is stored TWICE, and the two copies are never compared:
89
+ * the enforcing check is an inline literal in the usage probe, and
90
+ * `VERSE_CLAUDE_USAGE_PINNED_VERSION` is a mirror kept for display. They can
91
+ * drift silently. `test/verse-mcp-cli-health.test.ts` compares them.
92
+ */
93
+ export declare const VERSE_CLI_PIN_SOURCE = "src/core/resources/claude-account-usage.ts";
94
+ /** One measured version reading. The only shape the probe is allowed to emit. */
95
+ export interface VerseCliVersionReading {
96
+ /** Recognised numeric version, or null when the output was not recognised. */
97
+ version: string | null;
98
+ status: ResourceLauncherCompatibilityResult['status'];
99
+ reason: ResourceLauncherCompatibilityResult['reason'];
100
+ }
101
+ /**
102
+ * Per-account CLI health from what the collector already knows.
103
+ *
104
+ * PURE. No filesystem, no subprocess. `readings` is optional and only present
105
+ * after an operator has explicitly run the version probe.
106
+ */
107
+ export declare function buildVerseCliHealth(options: {
108
+ accounts: readonly VerseAccountRecord[];
109
+ readings?: ReadonlyMap<string, VerseCliVersionReading>;
110
+ now?: () => Date;
111
+ }): VerseCliHealthSnapshot;
112
+ /**
113
+ * Per-account launcher argv + private cwd. NEVER serialized, never logged.
114
+ *
115
+ * Exported only so the injection seam below can be typed by a caller (and so
116
+ * declaration emit does not have to name a private type). Producing one of
117
+ * these is reading an account's identity; publishing one is a leak.
118
+ */
119
+ export interface VerseCliAccountLaunch {
120
+ id: string;
121
+ provider: VerseAccountProvider;
122
+ command: string[];
123
+ cwd: string;
124
+ }
125
+ /** Injection seam so tests never spawn a provider CLI. */
126
+ export type VerseCliCompatibilityCheck = typeof checkResourceLauncherCompatibility;
127
+ export interface VerseCliProbeOptions {
128
+ accountsRoot: string;
129
+ /** Per-account deadline. The underlying check makes up to four invocations. */
130
+ timeoutMs?: number;
131
+ signal?: AbortSignal;
132
+ /** Injected compatibility check (tests). Defaults to the real one. */
133
+ check?: VerseCliCompatibilityCheck;
134
+ /** Injected launch reader (tests). Defaults to reading connections.json. */
135
+ readLaunches?: (accountsRoot: string) => VerseCliAccountLaunch[];
136
+ }
137
+ export declare const VERSE_CLI_PROBE_DEFAULT_TIMEOUT_MS = 10000;
138
+ /**
139
+ * Run the real `--version` / `--help` compatibility check per account.
140
+ *
141
+ * SPAWNS the provider CLI (help/version arguments only — no login, no prompt,
142
+ * no quota read; see launcher-compatibility.ts's own contract). Sequential on
143
+ * purpose: four concurrent native CLIs is exactly the burst that starves other
144
+ * probes on this machine.
145
+ *
146
+ * Returns ONLY the reading. The launcher argv and the private cwd that were
147
+ * needed to produce it are dropped here and never reach a caller.
148
+ */
149
+ export declare function probeVerseAccountCliVersions(options: VerseCliProbeOptions): Promise<Map<string, VerseCliVersionReading>>;
@@ -0,0 +1,227 @@
1
+ /**
2
+ * core/verse/mcp-cli-health.ts — per-account CLI health, and version drift.
3
+ *
4
+ * docs/VERSE-WORKSPACES.md §3: "Per-account CLI health is nearly free: the
5
+ * probes already report auth state, plan and CLI version per account. A
6
+ * version drift is worth surfacing loudly, because the Claude usage probe is
7
+ * pinned to an exact CLI version and fails closed when it moves."
8
+ *
9
+ * ── One correction to that premise, found in the source ─────────────────────
10
+ *
11
+ * Auth state and plan ARE already reported per account, on
12
+ * `VerseAccountRecord` (`state` / `authentication` / `planType`). The CLI
13
+ * VERSION is not. Nothing caches it:
14
+ *
15
+ * - `probeClaudeAccountUsage` runs `--version`, COMPARES it to an inline
16
+ * literal, and throws the string away — it keeps only the verdict, as the
17
+ * verbatim reason `usage-version-unsupported`
18
+ * (src/core/resources/claude-account-usage.ts:99).
19
+ * - `checkResourceLauncherCompatibility` DOES return a recognised numeric
20
+ * `version` (src/core/resources/launcher-compatibility.ts:163-165), but it
21
+ * is only ever reached from the `ashlr resources launcher check` CLI
22
+ * command. No route, collector or snapshot calls it.
23
+ *
24
+ * So this module splits the two honestly:
25
+ *
26
+ * 1. {@link buildVerseCliHealth} is PURE and free. It derives drift from
27
+ * what the collector already has: the verbatim probe reason plus the
28
+ * pinned-version constant. When the probe is failing closed on the pin,
29
+ * that is stated as a FACT, loudly, with the pin quoted — not as a
30
+ * generic "unknown".
31
+ * 2. {@link probeVerseAccountCliVersions} gets the actual installed version
32
+ * number, and it SPAWNS the provider CLI to do it. It is therefore not
33
+ * wired to a GET. A read route that silently launches four vendor
34
+ * binaries is a side effect wearing a read's clothes.
35
+ *
36
+ * ── What may leave this module ──────────────────────────────────────────────
37
+ *
38
+ * Never a launcher argv, never a private absolute path. The probe result
39
+ * shape carries id / provider / version / status / reason and nothing else;
40
+ * `ResourceLauncherCompatibilityResult` itself never echoes the command.
41
+ */
42
+ import { readFileSync } from 'node:fs';
43
+ import { dirname, join } from 'node:path';
44
+ import { checkResourceLauncherCompatibility, } from '../resources/launcher-compatibility.js';
45
+ import { VERSE_CLAUDE_USAGE_PINNED_VERSION, VERSE_CLAUDE_VERSION_REASON, } from './accounts.js';
46
+ // ---------------------------------------------------------------------------
47
+ // Notes
48
+ // ---------------------------------------------------------------------------
49
+ export function verseCliPinNote(pinned) {
50
+ return (`The Claude usage probe only runs against Claude Code ${pinned} and fails closed on any ` +
51
+ 'other build, so this account has no quota reading until the installed CLI matches the ' +
52
+ 'pin or the pin is moved. This is a version pin, not an outage.');
53
+ }
54
+ export const VERSE_CLI_VERSION_UNREAD_NOTE = 'No CLI version has been read for this account. Nothing in the collector records one; ' +
55
+ 'the version check is an explicit action because it launches the provider CLI.';
56
+ /**
57
+ * The pinned version is stored TWICE, and the two copies are never compared:
58
+ * the enforcing check is an inline literal in the usage probe, and
59
+ * `VERSE_CLAUDE_USAGE_PINNED_VERSION` is a mirror kept for display. They can
60
+ * drift silently. `test/verse-mcp-cli-health.test.ts` compares them.
61
+ */
62
+ export const VERSE_CLI_PIN_SOURCE = 'src/core/resources/claude-account-usage.ts';
63
+ // ---------------------------------------------------------------------------
64
+ // Pure derivation — free, and the default
65
+ // ---------------------------------------------------------------------------
66
+ /** Providers whose usage probe is pinned to an exact CLI build. */
67
+ const PINNED_VERSION = {
68
+ claude: VERSE_CLAUDE_USAGE_PINNED_VERSION,
69
+ };
70
+ function deriveOne(record, reading) {
71
+ const pinnedVersion = PINNED_VERSION[record.provider] ?? null;
72
+ // The collector's verbatim reason is the free drift signal: the usage probe
73
+ // emits exactly this code when `--version` did not match the pin.
74
+ const usageBlockedByPin = pinnedVersion !== null && record.reason === VERSE_CLAUDE_VERSION_REASON;
75
+ const version = reading?.version ?? null;
76
+ const versionState = usageBlockedByPin ? 'drift'
77
+ : version === null ? 'unverified'
78
+ : pinnedVersion === null ? 'reported'
79
+ : version === pinnedVersion ? 'matches-pin'
80
+ : 'drift';
81
+ const notes = [];
82
+ if (usageBlockedByPin || (pinnedVersion !== null && version !== null && version !== pinnedVersion)) {
83
+ notes.push(verseCliPinNote(pinnedVersion));
84
+ }
85
+ if (version === null)
86
+ notes.push(VERSE_CLI_VERSION_UNREAD_NOTE);
87
+ return {
88
+ accountId: record.id,
89
+ label: record.label,
90
+ provider: record.provider,
91
+ authentication: record.authentication,
92
+ state: record.state,
93
+ health: record.health,
94
+ planType: record.planType,
95
+ observedAt: record.observedAt,
96
+ reason: record.reason,
97
+ version,
98
+ pinnedVersion,
99
+ versionState,
100
+ usageBlockedByPin,
101
+ notes,
102
+ };
103
+ }
104
+ /**
105
+ * Per-account CLI health from what the collector already knows.
106
+ *
107
+ * PURE. No filesystem, no subprocess. `readings` is optional and only present
108
+ * after an operator has explicitly run the version probe.
109
+ */
110
+ export function buildVerseCliHealth(options) {
111
+ const readings = options.readings;
112
+ const accounts = options.accounts.map((record) => deriveOne(record, readings?.get(record.id)));
113
+ const drifted = accounts.filter((account) => account.versionState === 'drift');
114
+ const notes = [];
115
+ if (drifted.length > 0) {
116
+ notes.push(`${drifted.length} account${drifted.length === 1 ? '' : 's'} ` +
117
+ `${drifted.length === 1 ? 'is' : 'are'} running a CLI the usage probe does not accept. ` +
118
+ `The pin lives in ${VERSE_CLI_PIN_SOURCE}.`);
119
+ }
120
+ return {
121
+ sampledAt: (options.now ?? (() => new Date()))().toISOString(),
122
+ accounts,
123
+ driftDetected: drifted.length > 0,
124
+ notes,
125
+ };
126
+ }
127
+ /**
128
+ * Read the launcher argv and private state directory for each account.
129
+ *
130
+ * Both are the account's identity and stay inside this process. The returned
131
+ * value is consumed by {@link probeVerseAccountCliVersions} and never leaves.
132
+ */
133
+ function readAccountLaunches(accountsRoot) {
134
+ let parsed;
135
+ try {
136
+ const raw = readFileSync(join(accountsRoot, 'connections.json'), 'utf8');
137
+ if (raw.length > 1024 * 1024)
138
+ return [];
139
+ parsed = JSON.parse(raw);
140
+ }
141
+ catch {
142
+ return [];
143
+ }
144
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
145
+ return [];
146
+ const accounts = parsed['accounts'];
147
+ if (!Array.isArray(accounts))
148
+ return [];
149
+ const out = [];
150
+ for (const entry of accounts) {
151
+ if (entry === null || typeof entry !== 'object' || Array.isArray(entry))
152
+ continue;
153
+ const row = entry;
154
+ const id = row['id'];
155
+ const provider = row['provider'];
156
+ const command = row['command'];
157
+ if (typeof id !== 'string' || id.length === 0)
158
+ continue;
159
+ if (provider !== 'codex' && provider !== 'claude' && provider !== 'grok')
160
+ continue;
161
+ if (!Array.isArray(command) || !command.every((part) => typeof part === 'string'))
162
+ continue;
163
+ const launcher = command.find((part) => part.endsWith('launcher.mjs'));
164
+ if (launcher === undefined)
165
+ continue;
166
+ let cwd = null;
167
+ try {
168
+ const manifest = JSON.parse(readFileSync(join(dirname(launcher), 'profile.json'), 'utf8'));
169
+ if (manifest !== null && typeof manifest === 'object' && !Array.isArray(manifest)) {
170
+ const value = manifest['nativeStatePath'];
171
+ if (typeof value === 'string' && value.length > 0)
172
+ cwd = value;
173
+ }
174
+ }
175
+ catch {
176
+ cwd = null;
177
+ }
178
+ if (cwd === null)
179
+ continue;
180
+ out.push({ id, provider, command: command, cwd });
181
+ if (out.length >= 8)
182
+ break;
183
+ }
184
+ return out;
185
+ }
186
+ export const VERSE_CLI_PROBE_DEFAULT_TIMEOUT_MS = 10_000;
187
+ /**
188
+ * Run the real `--version` / `--help` compatibility check per account.
189
+ *
190
+ * SPAWNS the provider CLI (help/version arguments only — no login, no prompt,
191
+ * no quota read; see launcher-compatibility.ts's own contract). Sequential on
192
+ * purpose: four concurrent native CLIs is exactly the burst that starves other
193
+ * probes on this machine.
194
+ *
195
+ * Returns ONLY the reading. The launcher argv and the private cwd that were
196
+ * needed to produce it are dropped here and never reach a caller.
197
+ */
198
+ export async function probeVerseAccountCliVersions(options) {
199
+ const check = options.check ?? checkResourceLauncherCompatibility;
200
+ const read = options.readLaunches ?? readAccountLaunches;
201
+ const timeoutMs = options.timeoutMs ?? VERSE_CLI_PROBE_DEFAULT_TIMEOUT_MS;
202
+ const out = new Map();
203
+ for (const launch of read(options.accountsRoot)) {
204
+ if (options.signal?.aborted)
205
+ break;
206
+ try {
207
+ const result = await check({
208
+ provider: launch.provider,
209
+ command: launch.command,
210
+ cwd: launch.cwd,
211
+ timeoutMs,
212
+ ...(options.signal ? { signal: options.signal } : {}),
213
+ });
214
+ out.set(launch.id, {
215
+ version: result.version,
216
+ status: result.status,
217
+ reason: result.reason,
218
+ });
219
+ }
220
+ catch {
221
+ // A refused configuration is a reading of "unavailable", not a crash.
222
+ out.set(launch.id, { version: null, status: 'unavailable', reason: 'launcher-process-failed' });
223
+ }
224
+ }
225
+ return out;
226
+ }
227
+ //# sourceMappingURL=mcp-cli-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-cli-health.js","sourceRoot":"","sources":["../../../src/core/verse/mcp-cli-health.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * core/verse/mcp-control-api.ts — the HTTP surface for MCP and per-account
3
+ * CLI management.
4
+ *
5
+ * Deliberately a SEPARATE handler from `control-api.ts` rather than new arms
6
+ * on its `if`-chain: that file is owned by another work stream, and this one
7
+ * carries a mutation that installs arbitrary code. Keeping them apart means
8
+ * the review surface for "can the hub run new code" is one file.
9
+ *
10
+ * ── Route shape ─────────────────────────────────────────────────────────────
11
+ *
12
+ * GET /api/verse/mcp which servers each seat would load
13
+ * GET /api/verse/mcp/cli-health per-account auth, plan and version drift
14
+ * POST /api/verse/mcp/cli-probe read the installed CLI versions (SPAWNS)
15
+ * POST /api/verse/mcp/proposal validate + disclose a write (writes nothing)
16
+ * POST /api/verse/mcp/apply perform the write the digest confirms
17
+ *
18
+ * The two GETs are free: no subprocess, no provider call, no write. Everything
19
+ * that costs something or changes something is a POST behind
20
+ * `passesMutationGate`, including `cli-probe` — it launches up to four vendor
21
+ * binaries, and a read route that does that is a side effect in disguise.
22
+ *
23
+ * `proposal` is a POST for the same reason, even though it writes nothing: it
24
+ * carries an operator-supplied command in its body, and it is the step that
25
+ * must be authenticated for the resulting digest to mean anything.
26
+ *
27
+ * ── Ordering requirement ────────────────────────────────────────────────────
28
+ *
29
+ * These paths live under `/api/verse/*`, which `isVerseApiPath()` matches
30
+ * wholesale and 404s when it does not recognise. So the dispatch call in
31
+ * `src/core/web/api.ts` MUST come before both `isVerseControlPath` and
32
+ * `isVerseApiPath`. See the comment at that call site.
33
+ */
34
+ import type { IncomingMessage, ServerResponse } from 'node:http';
35
+ import type { AshlrConfig } from '../types.js';
36
+ import { type VerseMcpSeatInput } from './mcp-seat-view.js';
37
+ /** True for exactly the five MCP-management routes. */
38
+ export declare function isVerseMcpPath(path: string): boolean;
39
+ export interface VerseMcpApiContext {
40
+ cfg: AshlrConfig;
41
+ token: string;
42
+ allowDispatch: boolean;
43
+ readSession?: {
44
+ id: string;
45
+ expiresAt: number;
46
+ };
47
+ /** Test seam: override the accounts root without touching config. */
48
+ accountsRoot?: string;
49
+ }
50
+ /**
51
+ * Seat inputs for the MCP view, WITHOUT running `discoverSeats()`.
52
+ *
53
+ * `discoverSeats` probes Ollama over HTTP and builds telemetry — none of which
54
+ * this view needs, and all of which would make a cheap read expensive. What
55
+ * the MCP question turns on is (engine, account), and that is exactly what
56
+ * `readVerseAccountIdentities` publishes. Seat ids for native seats ARE the
57
+ * account ids (see VerseSeat.id's own documentation).
58
+ *
59
+ * Local seats are collapsed into ONE row on purpose: every Ollama tag is
60
+ * launched through the same `buildClaudeLaunch` argv, so they are all isolated
61
+ * identically and listing twelve identical rows would be noise, not detail.
62
+ */
63
+ export declare function verseMcpSeatInputs(accountsRoot: string): VerseMcpSeatInput[];
64
+ /**
65
+ * Handle one MCP-management request. Returns true when a response was
66
+ * written (including errors); false when `path` is not one of these routes.
67
+ */
68
+ export declare function handleVerseMcpApi(ctx: VerseMcpApiContext, req: IncomingMessage, res: ServerResponse, path: string, method: string): Promise<boolean>;