brains-mcp 1.61.2

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 (350) hide show
  1. package/README.md +95 -0
  2. package/dist/access/projectShares.d.ts +48 -0
  3. package/dist/access/projectShares.d.ts.map +1 -0
  4. package/dist/access/projectShares.js +203 -0
  5. package/dist/access/projectShares.js.map +1 -0
  6. package/dist/access/sharedInstructionsNote.d.ts +37 -0
  7. package/dist/access/sharedInstructionsNote.d.ts.map +1 -0
  8. package/dist/access/sharedInstructionsNote.js +76 -0
  9. package/dist/access/sharedInstructionsNote.js.map +1 -0
  10. package/dist/admin/seedUsers.d.ts +23 -0
  11. package/dist/admin/seedUsers.d.ts.map +1 -0
  12. package/dist/admin/seedUsers.js +168 -0
  13. package/dist/admin/seedUsers.js.map +1 -0
  14. package/dist/api/openapi.d.ts +2 -0
  15. package/dist/api/openapi.d.ts.map +1 -0
  16. package/dist/api/openapi.js +942 -0
  17. package/dist/api/openapi.js.map +1 -0
  18. package/dist/auth/clientStore.d.ts +43 -0
  19. package/dist/auth/clientStore.d.ts.map +1 -0
  20. package/dist/auth/clientStore.js +168 -0
  21. package/dist/auth/clientStore.js.map +1 -0
  22. package/dist/auth/deleteAccount.d.ts +34 -0
  23. package/dist/auth/deleteAccount.d.ts.map +1 -0
  24. package/dist/auth/deleteAccount.js +203 -0
  25. package/dist/auth/deleteAccount.js.map +1 -0
  26. package/dist/auth/oauth.d.ts +118 -0
  27. package/dist/auth/oauth.d.ts.map +1 -0
  28. package/dist/auth/oauth.js +491 -0
  29. package/dist/auth/oauth.js.map +1 -0
  30. package/dist/auth/patStore.d.ts +87 -0
  31. package/dist/auth/patStore.d.ts.map +1 -0
  32. package/dist/auth/patStore.js +308 -0
  33. package/dist/auth/patStore.js.map +1 -0
  34. package/dist/auth/supabase.d.ts +15 -0
  35. package/dist/auth/supabase.d.ts.map +1 -0
  36. package/dist/auth/supabase.js +66 -0
  37. package/dist/auth/supabase.js.map +1 -0
  38. package/dist/auth/tokenStore.d.ts +73 -0
  39. package/dist/auth/tokenStore.d.ts.map +1 -0
  40. package/dist/auth/tokenStore.js +261 -0
  41. package/dist/auth/tokenStore.js.map +1 -0
  42. package/dist/backup/drive.d.ts +24 -0
  43. package/dist/backup/drive.d.ts.map +1 -0
  44. package/dist/backup/drive.js +70 -0
  45. package/dist/backup/drive.js.map +1 -0
  46. package/dist/billing/tierStore.d.ts +59 -0
  47. package/dist/billing/tierStore.d.ts.map +1 -0
  48. package/dist/billing/tierStore.js +152 -0
  49. package/dist/billing/tierStore.js.map +1 -0
  50. package/dist/digest/activityDigest.d.ts +32 -0
  51. package/dist/digest/activityDigest.d.ts.map +1 -0
  52. package/dist/digest/activityDigest.js +299 -0
  53. package/dist/digest/activityDigest.js.map +1 -0
  54. package/dist/digest/digestCron.d.ts +40 -0
  55. package/dist/digest/digestCron.d.ts.map +1 -0
  56. package/dist/digest/digestCron.js +146 -0
  57. package/dist/digest/digestCron.js.map +1 -0
  58. package/dist/digest/digestPreferences.d.ts +25 -0
  59. package/dist/digest/digestPreferences.d.ts.map +1 -0
  60. package/dist/digest/digestPreferences.js +79 -0
  61. package/dist/digest/digestPreferences.js.map +1 -0
  62. package/dist/digest/emailSender.d.ts +20 -0
  63. package/dist/digest/emailSender.d.ts.map +1 -0
  64. package/dist/digest/emailSender.js +35 -0
  65. package/dist/digest/emailSender.js.map +1 -0
  66. package/dist/drive/client.d.ts +43 -0
  67. package/dist/drive/client.d.ts.map +1 -0
  68. package/dist/drive/client.js +282 -0
  69. package/dist/drive/client.js.map +1 -0
  70. package/dist/drive/errors.d.ts +27 -0
  71. package/dist/drive/errors.d.ts.map +1 -0
  72. package/dist/drive/errors.js +51 -0
  73. package/dist/drive/errors.js.map +1 -0
  74. package/dist/drive/fileCache.d.ts +22 -0
  75. package/dist/drive/fileCache.d.ts.map +1 -0
  76. package/dist/drive/fileCache.js +57 -0
  77. package/dist/drive/fileCache.js.map +1 -0
  78. package/dist/governance/accessControl.d.ts +87 -0
  79. package/dist/governance/accessControl.d.ts.map +1 -0
  80. package/dist/governance/accessControl.js +179 -0
  81. package/dist/governance/accessControl.js.map +1 -0
  82. package/dist/html/components.d.ts +8 -0
  83. package/dist/html/components.d.ts.map +1 -0
  84. package/dist/html/components.js +19 -0
  85. package/dist/html/components.js.map +1 -0
  86. package/dist/html/layout.d.ts +7 -0
  87. package/dist/html/layout.d.ts.map +1 -0
  88. package/dist/html/layout.js +24 -0
  89. package/dist/html/layout.js.map +1 -0
  90. package/dist/html/metricsUi.d.ts +8 -0
  91. package/dist/html/metricsUi.d.ts.map +1 -0
  92. package/dist/html/metricsUi.js +271 -0
  93. package/dist/html/metricsUi.js.map +1 -0
  94. package/dist/httpServer.d.ts +19 -0
  95. package/dist/httpServer.d.ts.map +1 -0
  96. package/dist/httpServer.js +3913 -0
  97. package/dist/httpServer.js.map +1 -0
  98. package/dist/index.d.ts +3 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +40 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/jobs/asyncJobRegistry.d.ts +30 -0
  103. package/dist/jobs/asyncJobRegistry.d.ts.map +1 -0
  104. package/dist/jobs/asyncJobRegistry.js +70 -0
  105. package/dist/jobs/asyncJobRegistry.js.map +1 -0
  106. package/dist/observability/metricsQuery.d.ts +41 -0
  107. package/dist/observability/metricsQuery.d.ts.map +1 -0
  108. package/dist/observability/metricsQuery.js +106 -0
  109. package/dist/observability/metricsQuery.js.map +1 -0
  110. package/dist/observability/relevanceSignal.d.ts +26 -0
  111. package/dist/observability/relevanceSignal.d.ts.map +1 -0
  112. package/dist/observability/relevanceSignal.js +95 -0
  113. package/dist/observability/relevanceSignal.js.map +1 -0
  114. package/dist/observability/requestEvents.d.ts +31 -0
  115. package/dist/observability/requestEvents.d.ts.map +1 -0
  116. package/dist/observability/requestEvents.js +54 -0
  117. package/dist/observability/requestEvents.js.map +1 -0
  118. package/dist/observability/slowQueryAlert.d.ts +25 -0
  119. package/dist/observability/slowQueryAlert.d.ts.map +1 -0
  120. package/dist/observability/slowQueryAlert.js +67 -0
  121. package/dist/observability/slowQueryAlert.js.map +1 -0
  122. package/dist/observability/zeroResultTriage.d.ts +22 -0
  123. package/dist/observability/zeroResultTriage.d.ts.map +1 -0
  124. package/dist/observability/zeroResultTriage.js +116 -0
  125. package/dist/observability/zeroResultTriage.js.map +1 -0
  126. package/dist/search/embeddings.d.ts +31 -0
  127. package/dist/search/embeddings.d.ts.map +1 -0
  128. package/dist/search/embeddings.js +91 -0
  129. package/dist/search/embeddings.js.map +1 -0
  130. package/dist/search/fileWatcher.d.ts +28 -0
  131. package/dist/search/fileWatcher.d.ts.map +1 -0
  132. package/dist/search/fileWatcher.js +103 -0
  133. package/dist/search/fileWatcher.js.map +1 -0
  134. package/dist/search/hotQueryCache.d.ts +31 -0
  135. package/dist/search/hotQueryCache.d.ts.map +1 -0
  136. package/dist/search/hotQueryCache.js +103 -0
  137. package/dist/search/hotQueryCache.js.map +1 -0
  138. package/dist/search/postgresIndex.d.ts +439 -0
  139. package/dist/search/postgresIndex.d.ts.map +1 -0
  140. package/dist/search/postgresIndex.js +1610 -0
  141. package/dist/search/postgresIndex.js.map +1 -0
  142. package/dist/search/sqliteIndex.d.ts +66 -0
  143. package/dist/search/sqliteIndex.d.ts.map +1 -0
  144. package/dist/search/sqliteIndex.js +342 -0
  145. package/dist/search/sqliteIndex.js.map +1 -0
  146. package/dist/server.d.ts +43 -0
  147. package/dist/server.d.ts.map +1 -0
  148. package/dist/server.js +532 -0
  149. package/dist/server.js.map +1 -0
  150. package/dist/session/bundle.d.ts +53 -0
  151. package/dist/session/bundle.d.ts.map +1 -0
  152. package/dist/session/bundle.js +97 -0
  153. package/dist/session/bundle.js.map +1 -0
  154. package/dist/storage/client.d.ts +157 -0
  155. package/dist/storage/client.d.ts.map +1 -0
  156. package/dist/storage/client.js +825 -0
  157. package/dist/storage/client.js.map +1 -0
  158. package/dist/tools/addWebhookPattern.d.ts +15 -0
  159. package/dist/tools/addWebhookPattern.d.ts.map +1 -0
  160. package/dist/tools/addWebhookPattern.js +15 -0
  161. package/dist/tools/addWebhookPattern.js.map +1 -0
  162. package/dist/tools/appendLog.d.ts +18 -0
  163. package/dist/tools/appendLog.d.ts.map +1 -0
  164. package/dist/tools/appendLog.js +18 -0
  165. package/dist/tools/appendLog.js.map +1 -0
  166. package/dist/tools/auditIndex.d.ts +15 -0
  167. package/dist/tools/auditIndex.d.ts.map +1 -0
  168. package/dist/tools/auditIndex.js +165 -0
  169. package/dist/tools/auditIndex.js.map +1 -0
  170. package/dist/tools/batchDeletePages.d.ts +18 -0
  171. package/dist/tools/batchDeletePages.d.ts.map +1 -0
  172. package/dist/tools/batchDeletePages.js +143 -0
  173. package/dist/tools/batchDeletePages.js.map +1 -0
  174. package/dist/tools/coerce.d.ts +3 -0
  175. package/dist/tools/coerce.d.ts.map +1 -0
  176. package/dist/tools/coerce.js +12 -0
  177. package/dist/tools/coerce.js.map +1 -0
  178. package/dist/tools/createPage.d.ts +24 -0
  179. package/dist/tools/createPage.d.ts.map +1 -0
  180. package/dist/tools/createPage.js +99 -0
  181. package/dist/tools/createPage.js.map +1 -0
  182. package/dist/tools/createPages.d.ts +51 -0
  183. package/dist/tools/createPages.d.ts.map +1 -0
  184. package/dist/tools/createPages.js +91 -0
  185. package/dist/tools/createPages.js.map +1 -0
  186. package/dist/tools/deletePage.d.ts +19 -0
  187. package/dist/tools/deletePage.d.ts.map +1 -0
  188. package/dist/tools/deletePage.js +127 -0
  189. package/dist/tools/deletePage.js.map +1 -0
  190. package/dist/tools/exportWiki.d.ts +48 -0
  191. package/dist/tools/exportWiki.d.ts.map +1 -0
  192. package/dist/tools/exportWiki.js +233 -0
  193. package/dist/tools/exportWiki.js.map +1 -0
  194. package/dist/tools/extractLinks.d.ts +14 -0
  195. package/dist/tools/extractLinks.d.ts.map +1 -0
  196. package/dist/tools/extractLinks.js +25 -0
  197. package/dist/tools/extractLinks.js.map +1 -0
  198. package/dist/tools/generateClaudeInstructions.d.ts +27 -0
  199. package/dist/tools/generateClaudeInstructions.d.ts.map +1 -0
  200. package/dist/tools/generateClaudeInstructions.js +263 -0
  201. package/dist/tools/generateClaudeInstructions.js.map +1 -0
  202. package/dist/tools/generateGptInstructions.d.ts +27 -0
  203. package/dist/tools/generateGptInstructions.d.ts.map +1 -0
  204. package/dist/tools/generateGptInstructions.js +208 -0
  205. package/dist/tools/generateGptInstructions.js.map +1 -0
  206. package/dist/tools/getBacklinks.d.ts +12 -0
  207. package/dist/tools/getBacklinks.d.ts.map +1 -0
  208. package/dist/tools/getBacklinks.js +13 -0
  209. package/dist/tools/getBacklinks.js.map +1 -0
  210. package/dist/tools/getRelated.d.ts +12 -0
  211. package/dist/tools/getRelated.d.ts.map +1 -0
  212. package/dist/tools/getRelated.js +13 -0
  213. package/dist/tools/getRelated.js.map +1 -0
  214. package/dist/tools/healthCheck.d.ts +5 -0
  215. package/dist/tools/healthCheck.d.ts.map +1 -0
  216. package/dist/tools/healthCheck.js +167 -0
  217. package/dist/tools/healthCheck.js.map +1 -0
  218. package/dist/tools/importBrainSource.d.ts +36 -0
  219. package/dist/tools/importBrainSource.d.ts.map +1 -0
  220. package/dist/tools/importBrainSource.js +314 -0
  221. package/dist/tools/importBrainSource.js.map +1 -0
  222. package/dist/tools/importWikiZip.d.ts +60 -0
  223. package/dist/tools/importWikiZip.d.ts.map +1 -0
  224. package/dist/tools/importWikiZip.js +379 -0
  225. package/dist/tools/importWikiZip.js.map +1 -0
  226. package/dist/tools/initProject.d.ts +34 -0
  227. package/dist/tools/initProject.d.ts.map +1 -0
  228. package/dist/tools/initProject.js +443 -0
  229. package/dist/tools/initProject.js.map +1 -0
  230. package/dist/tools/instructionContext.d.ts +60 -0
  231. package/dist/tools/instructionContext.d.ts.map +1 -0
  232. package/dist/tools/instructionContext.js +210 -0
  233. package/dist/tools/instructionContext.js.map +1 -0
  234. package/dist/tools/listArchivedPages.d.ts +17 -0
  235. package/dist/tools/listArchivedPages.d.ts.map +1 -0
  236. package/dist/tools/listArchivedPages.js +51 -0
  237. package/dist/tools/listArchivedPages.js.map +1 -0
  238. package/dist/tools/listPages.d.ts +28 -0
  239. package/dist/tools/listPages.d.ts.map +1 -0
  240. package/dist/tools/listPages.js +166 -0
  241. package/dist/tools/listPages.js.map +1 -0
  242. package/dist/tools/listWebhooks.d.ts +7 -0
  243. package/dist/tools/listWebhooks.d.ts.map +1 -0
  244. package/dist/tools/listWebhooks.js +10 -0
  245. package/dist/tools/listWebhooks.js.map +1 -0
  246. package/dist/tools/movePage.d.ts +18 -0
  247. package/dist/tools/movePage.d.ts.map +1 -0
  248. package/dist/tools/movePage.js +182 -0
  249. package/dist/tools/movePage.js.map +1 -0
  250. package/dist/tools/pageHistory.d.ts +36 -0
  251. package/dist/tools/pageHistory.d.ts.map +1 -0
  252. package/dist/tools/pageHistory.js +60 -0
  253. package/dist/tools/pageHistory.js.map +1 -0
  254. package/dist/tools/patchPage.d.ts +141 -0
  255. package/dist/tools/patchPage.d.ts.map +1 -0
  256. package/dist/tools/patchPage.js +287 -0
  257. package/dist/tools/patchPage.js.map +1 -0
  258. package/dist/tools/pullSessionBundle.d.ts +34 -0
  259. package/dist/tools/pullSessionBundle.d.ts.map +1 -0
  260. package/dist/tools/pullSessionBundle.js +432 -0
  261. package/dist/tools/pullSessionBundle.js.map +1 -0
  262. package/dist/tools/purgeWiki.d.ts +23 -0
  263. package/dist/tools/purgeWiki.d.ts.map +1 -0
  264. package/dist/tools/purgeWiki.js +113 -0
  265. package/dist/tools/purgeWiki.js.map +1 -0
  266. package/dist/tools/pushSessionBundle.d.ts +139 -0
  267. package/dist/tools/pushSessionBundle.d.ts.map +1 -0
  268. package/dist/tools/pushSessionBundle.js +282 -0
  269. package/dist/tools/pushSessionBundle.js.map +1 -0
  270. package/dist/tools/readGovernanceAudit.d.ts +17 -0
  271. package/dist/tools/readGovernanceAudit.d.ts.map +1 -0
  272. package/dist/tools/readGovernanceAudit.js +21 -0
  273. package/dist/tools/readGovernanceAudit.js.map +1 -0
  274. package/dist/tools/readPage.d.ts +13 -0
  275. package/dist/tools/readPage.d.ts.map +1 -0
  276. package/dist/tools/readPage.js +46 -0
  277. package/dist/tools/readPage.js.map +1 -0
  278. package/dist/tools/removeWebhook.d.ts +14 -0
  279. package/dist/tools/removeWebhook.d.ts.map +1 -0
  280. package/dist/tools/removeWebhook.js +12 -0
  281. package/dist/tools/removeWebhook.js.map +1 -0
  282. package/dist/tools/resolveProject.d.ts +58 -0
  283. package/dist/tools/resolveProject.d.ts.map +1 -0
  284. package/dist/tools/resolveProject.js +175 -0
  285. package/dist/tools/resolveProject.js.map +1 -0
  286. package/dist/tools/restorePage.d.ts +47 -0
  287. package/dist/tools/restorePage.d.ts.map +1 -0
  288. package/dist/tools/restorePage.js +111 -0
  289. package/dist/tools/restorePage.js.map +1 -0
  290. package/dist/tools/searchPages.d.ts +28 -0
  291. package/dist/tools/searchPages.d.ts.map +1 -0
  292. package/dist/tools/searchPages.js +372 -0
  293. package/dist/tools/searchPages.js.map +1 -0
  294. package/dist/tools/setWebhook.d.ts +30 -0
  295. package/dist/tools/setWebhook.d.ts.map +1 -0
  296. package/dist/tools/setWebhook.js +30 -0
  297. package/dist/tools/setWebhook.js.map +1 -0
  298. package/dist/tools/shareProject.d.ts +25 -0
  299. package/dist/tools/shareProject.d.ts.map +1 -0
  300. package/dist/tools/shareProject.js +56 -0
  301. package/dist/tools/shareProject.js.map +1 -0
  302. package/dist/tools/suggestLinks.d.ts +29 -0
  303. package/dist/tools/suggestLinks.d.ts.map +1 -0
  304. package/dist/tools/suggestLinks.js +214 -0
  305. package/dist/tools/suggestLinks.js.map +1 -0
  306. package/dist/tools/updatePage.d.ts +27 -0
  307. package/dist/tools/updatePage.d.ts.map +1 -0
  308. package/dist/tools/updatePage.js +118 -0
  309. package/dist/tools/updatePage.js.map +1 -0
  310. package/dist/webhooks/dispatch.d.ts +49 -0
  311. package/dist/webhooks/dispatch.d.ts.map +1 -0
  312. package/dist/webhooks/dispatch.js +159 -0
  313. package/dist/webhooks/dispatch.js.map +1 -0
  314. package/dist/webhooks/webhookRules.d.ts +34 -0
  315. package/dist/webhooks/webhookRules.d.ts.map +1 -0
  316. package/dist/webhooks/webhookRules.js +129 -0
  317. package/dist/webhooks/webhookRules.js.map +1 -0
  318. package/dist/wiki/frontmatter.d.ts +45 -0
  319. package/dist/wiki/frontmatter.d.ts.map +1 -0
  320. package/dist/wiki/frontmatter.js +68 -0
  321. package/dist/wiki/frontmatter.js.map +1 -0
  322. package/dist/wiki/index.d.ts +22 -0
  323. package/dist/wiki/index.d.ts.map +1 -0
  324. package/dist/wiki/index.js +203 -0
  325. package/dist/wiki/index.js.map +1 -0
  326. package/dist/wiki/ingestPolicy.d.ts +18 -0
  327. package/dist/wiki/ingestPolicy.d.ts.map +1 -0
  328. package/dist/wiki/ingestPolicy.js +64 -0
  329. package/dist/wiki/ingestPolicy.js.map +1 -0
  330. package/dist/wiki/layoutLint.d.ts +51 -0
  331. package/dist/wiki/layoutLint.d.ts.map +1 -0
  332. package/dist/wiki/layoutLint.js +146 -0
  333. package/dist/wiki/layoutLint.js.map +1 -0
  334. package/dist/wiki/links.d.ts +6 -0
  335. package/dist/wiki/links.d.ts.map +1 -0
  336. package/dist/wiki/links.js +45 -0
  337. package/dist/wiki/links.js.map +1 -0
  338. package/dist/wiki/log.d.ts +58 -0
  339. package/dist/wiki/log.d.ts.map +1 -0
  340. package/dist/wiki/log.js +126 -0
  341. package/dist/wiki/log.js.map +1 -0
  342. package/dist/wiki/path.d.ts +8 -0
  343. package/dist/wiki/path.d.ts.map +1 -0
  344. package/dist/wiki/path.js +25 -0
  345. package/dist/wiki/path.js.map +1 -0
  346. package/dist/wiki/storageLimits.d.ts +163 -0
  347. package/dist/wiki/storageLimits.d.ts.map +1 -0
  348. package/dist/wiki/storageLimits.js +281 -0
  349. package/dist/wiki/storageLimits.js.map +1 -0
  350. package/package.json +81 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * accessControl.ts
3
+ *
4
+ * Runtime governance for Hive (team) brains.
5
+ *
6
+ * When BRAINS_HIVE_MODE=true, all read paths filter pages by:
7
+ * 1. access_level — page must have access_level ≤ requesting user's level
8
+ * 2. audience — when session_audience is set, page audience must match
9
+ *
10
+ * Solo (non-Hive) brains: isHiveModeEnabled() returns false, userLevel is
11
+ * effectively 5 (owner), and no filtering occurs.
12
+ *
13
+ * Access level hierarchy:
14
+ * 1 = contributor (default)
15
+ * 2 = team
16
+ * 3 = manager
17
+ * 4 = exec
18
+ * 5 = owner
19
+ */
20
+ export declare const ACCESS_LEVEL_CONTRIBUTOR = 1;
21
+ export declare const ACCESS_LEVEL_TEAM = 2;
22
+ export declare const ACCESS_LEVEL_MANAGER = 3;
23
+ export declare const ACCESS_LEVEL_EXEC = 4;
24
+ export declare const ACCESS_LEVEL_OWNER = 5;
25
+ /** Default access level assigned to pages with no explicit access_level field. */
26
+ export declare const DEFAULT_PAGE_ACCESS_LEVEL = 1;
27
+ /** Default audience assigned to pages with no explicit audience field. */
28
+ export declare const DEFAULT_PAGE_AUDIENCE: "internal";
29
+ /** Access context passed through all read paths. */
30
+ export interface AccessContext {
31
+ /** The requesting user's access level (1–5). 5 = full access (solo / owner). */
32
+ userAccessLevel: number;
33
+ /**
34
+ * Optional audience constraint declared by the caller for this session.
35
+ * "client-safe" → only return pages with audience client-safe or public.
36
+ * "public" → only return pages with audience public.
37
+ * Omitted / undefined → no audience filter applied.
38
+ */
39
+ sessionAudience?: string;
40
+ }
41
+ /** Default context used when governance is not applicable (solo brains). */
42
+ export declare const DEFAULT_ACCESS_CONTEXT: AccessContext;
43
+ /**
44
+ * Returns true only when BRAINS_HIVE_MODE=true is set.
45
+ * Solo (non-Hive) brains are a no-op for access control.
46
+ */
47
+ export declare function isHiveModeEnabled(): boolean;
48
+ /**
49
+ * Coerce an unknown frontmatter value to a valid access level integer (1–5).
50
+ * Returns DEFAULT_PAGE_ACCESS_LEVEL when the value is absent or invalid.
51
+ */
52
+ export declare function parseAccessLevel(raw: unknown): number;
53
+ /**
54
+ * Coerce an unknown frontmatter value to a valid audience string.
55
+ * Returns DEFAULT_PAGE_AUDIENCE when the value is absent or invalid.
56
+ */
57
+ export declare function parseAudience(raw: unknown): string;
58
+ /**
59
+ * Returns true when the page should be visible to the requesting user.
60
+ *
61
+ * Rules:
62
+ * - page.access_level ≤ user.access_level (numeric gate)
63
+ * - if sessionAudience = "public" → page.audience must be "public"
64
+ * - if sessionAudience = "client-safe" → page.audience must be "client-safe" or "public"
65
+ * - otherwise no audience gate applies
66
+ */
67
+ export declare function pagePassesFilter(pageAccessLevel: number, pageAudience: string | undefined, userAccessLevel: number, sessionAudience?: string): boolean;
68
+ /**
69
+ * Retrieves the access level for a given user from the `user_access_levels`
70
+ * Supabase table.
71
+ *
72
+ * - Returns ACCESS_LEVEL_OWNER (5) when Hive mode is disabled (solo brains).
73
+ * - Returns DEFAULT_PAGE_ACCESS_LEVEL (1) when the user has no entry.
74
+ * - Falls back to DEFAULT_PAGE_ACCESS_LEVEL on any network/parse error.
75
+ */
76
+ export declare function getUserAccessLevel(userId: string): Promise<number>;
77
+ /**
78
+ * Fire-and-forget: write a governance filter event to Supabase.
79
+ * Never throws — failures are silently ignored.
80
+ */
81
+ export declare function writeGovernanceAuditEntry(userId: string, pageName: string, pageAccessLevel: number, userAccessLevel: number, sessionAudience?: string, reason?: string): void;
82
+ /**
83
+ * Read governance audit log entries.
84
+ * Only exec+ users (access_level ≥ 4) may call this.
85
+ */
86
+ export declare function readGovernanceAuditLog(requestingUserId: string, limit?: number): Promise<object>;
87
+ //# sourceMappingURL=accessControl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessControl.d.ts","sourceRoot":"","sources":["../../src/governance/accessControl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,iBAAiB,IAAW,CAAC;AAC1C,eAAO,MAAM,oBAAoB,IAAQ,CAAC;AAC1C,eAAO,MAAM,iBAAiB,IAAW,CAAC;AAC1C,eAAO,MAAM,kBAAkB,IAAU,CAAC;AAE1C,kFAAkF;AAClF,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB,EAAG,UAAmB,CAAC;AAEzD,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,EAAE,aAAuD,CAAC;AAI7F;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAyBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAIrD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAGlD;AAID;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,eAAe,EAAE,MAAM,EACvB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAUT;AAID;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAexE;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,eAAe,CAAC,EAAE,MAAM,EACxB,MAAM,SAA0B,GAC/B,IAAI,CAmBN;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,gBAAgB,EAAE,MAAM,EACxB,KAAK,SAAM,GACV,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * accessControl.ts
3
+ *
4
+ * Runtime governance for Hive (team) brains.
5
+ *
6
+ * When BRAINS_HIVE_MODE=true, all read paths filter pages by:
7
+ * 1. access_level — page must have access_level ≤ requesting user's level
8
+ * 2. audience — when session_audience is set, page audience must match
9
+ *
10
+ * Solo (non-Hive) brains: isHiveModeEnabled() returns false, userLevel is
11
+ * effectively 5 (owner), and no filtering occurs.
12
+ *
13
+ * Access level hierarchy:
14
+ * 1 = contributor (default)
15
+ * 2 = team
16
+ * 3 = manager
17
+ * 4 = exec
18
+ * 5 = owner
19
+ */
20
+ // ─── Constants ────────────────────────────────────────────────────────────────
21
+ export const ACCESS_LEVEL_CONTRIBUTOR = 1;
22
+ export const ACCESS_LEVEL_TEAM = 2;
23
+ export const ACCESS_LEVEL_MANAGER = 3;
24
+ export const ACCESS_LEVEL_EXEC = 4;
25
+ export const ACCESS_LEVEL_OWNER = 5;
26
+ /** Default access level assigned to pages with no explicit access_level field. */
27
+ export const DEFAULT_PAGE_ACCESS_LEVEL = 1;
28
+ /** Default audience assigned to pages with no explicit audience field. */
29
+ export const DEFAULT_PAGE_AUDIENCE = "internal";
30
+ /** Default context used when governance is not applicable (solo brains). */
31
+ export const DEFAULT_ACCESS_CONTEXT = { userAccessLevel: ACCESS_LEVEL_OWNER };
32
+ // ─── Environment ──────────────────────────────────────────────────────────────
33
+ /**
34
+ * Returns true only when BRAINS_HIVE_MODE=true is set.
35
+ * Solo (non-Hive) brains are a no-op for access control.
36
+ */
37
+ export function isHiveModeEnabled() {
38
+ return (process.env.BRAINS_HIVE_MODE ?? "").toLowerCase() === "true";
39
+ }
40
+ function supabaseUrl() {
41
+ return (process.env.SUPABASE_URL ?? "").trim().replace(/\/$/, "");
42
+ }
43
+ function serviceRoleKey() {
44
+ return (process.env.SUPABASE_SERVICE_ROLE_KEY ?? "").trim();
45
+ }
46
+ function pgHeaders() {
47
+ const key = serviceRoleKey();
48
+ return {
49
+ "content-type": "application/json",
50
+ "apikey": key,
51
+ "authorization": `Bearer ${key}`,
52
+ };
53
+ }
54
+ function isPostgresConfigured() {
55
+ return supabaseUrl().length > 0 && serviceRoleKey().length > 0;
56
+ }
57
+ // ─── Access level parsing ─────────────────────────────────────────────────────
58
+ /**
59
+ * Coerce an unknown frontmatter value to a valid access level integer (1–5).
60
+ * Returns DEFAULT_PAGE_ACCESS_LEVEL when the value is absent or invalid.
61
+ */
62
+ export function parseAccessLevel(raw) {
63
+ const n = typeof raw === "number" ? raw : parseInt(String(raw ?? ""), 10);
64
+ if (Number.isInteger(n) && n >= 1 && n <= 5)
65
+ return n;
66
+ return DEFAULT_PAGE_ACCESS_LEVEL;
67
+ }
68
+ /**
69
+ * Coerce an unknown frontmatter value to a valid audience string.
70
+ * Returns DEFAULT_PAGE_AUDIENCE when the value is absent or invalid.
71
+ */
72
+ export function parseAudience(raw) {
73
+ if (raw === "client-safe" || raw === "public" || raw === "internal")
74
+ return raw;
75
+ return DEFAULT_PAGE_AUDIENCE;
76
+ }
77
+ // ─── Filtering ────────────────────────────────────────────────────────────────
78
+ /**
79
+ * Returns true when the page should be visible to the requesting user.
80
+ *
81
+ * Rules:
82
+ * - page.access_level ≤ user.access_level (numeric gate)
83
+ * - if sessionAudience = "public" → page.audience must be "public"
84
+ * - if sessionAudience = "client-safe" → page.audience must be "client-safe" or "public"
85
+ * - otherwise no audience gate applies
86
+ */
87
+ export function pagePassesFilter(pageAccessLevel, pageAudience, userAccessLevel, sessionAudience) {
88
+ if (pageAccessLevel > userAccessLevel)
89
+ return false;
90
+ if (sessionAudience) {
91
+ const aud = parseAudience(pageAudience);
92
+ if (sessionAudience === "public" && aud !== "public")
93
+ return false;
94
+ if (sessionAudience === "client-safe" && aud === "internal")
95
+ return false;
96
+ }
97
+ return true;
98
+ }
99
+ // ─── User access level lookup ─────────────────────────────────────────────────
100
+ /**
101
+ * Retrieves the access level for a given user from the `user_access_levels`
102
+ * Supabase table.
103
+ *
104
+ * - Returns ACCESS_LEVEL_OWNER (5) when Hive mode is disabled (solo brains).
105
+ * - Returns DEFAULT_PAGE_ACCESS_LEVEL (1) when the user has no entry.
106
+ * - Falls back to DEFAULT_PAGE_ACCESS_LEVEL on any network/parse error.
107
+ */
108
+ export async function getUserAccessLevel(userId) {
109
+ if (!isHiveModeEnabled())
110
+ return ACCESS_LEVEL_OWNER;
111
+ if (!userId || !isPostgresConfigured())
112
+ return DEFAULT_PAGE_ACCESS_LEVEL;
113
+ try {
114
+ const url = `${supabaseUrl()}/rest/v1/user_access_levels?user_id=eq.${encodeURIComponent(userId)}&select=access_level&limit=1`;
115
+ const res = await fetch(url, { headers: pgHeaders() });
116
+ if (!res.ok)
117
+ return DEFAULT_PAGE_ACCESS_LEVEL;
118
+ const rows = (await res.json());
119
+ if (rows.length === 0)
120
+ return DEFAULT_PAGE_ACCESS_LEVEL;
121
+ return parseAccessLevel(rows[0].access_level);
122
+ }
123
+ catch {
124
+ return DEFAULT_PAGE_ACCESS_LEVEL;
125
+ }
126
+ }
127
+ // ─── Audit log ────────────────────────────────────────────────────────────────
128
+ /**
129
+ * Fire-and-forget: write a governance filter event to Supabase.
130
+ * Never throws — failures are silently ignored.
131
+ */
132
+ export function writeGovernanceAuditEntry(userId, pageName, pageAccessLevel, userAccessLevel, sessionAudience, reason = "access_level_exceeded") {
133
+ if (!isPostgresConfigured())
134
+ return;
135
+ const row = {
136
+ user_id: userId,
137
+ page_name: pageName,
138
+ page_access_level: pageAccessLevel,
139
+ user_access_level: userAccessLevel,
140
+ session_audience: sessionAudience ?? null,
141
+ reason,
142
+ };
143
+ fetch(`${supabaseUrl()}/rest/v1/governance_audit_log`, {
144
+ method: "POST",
145
+ headers: { ...pgHeaders(), prefer: "return=minimal" },
146
+ body: JSON.stringify(row),
147
+ }).catch((err) => {
148
+ console.error("[governance] audit log write failed:", err instanceof Error ? err.message : String(err));
149
+ });
150
+ }
151
+ /**
152
+ * Read governance audit log entries.
153
+ * Only exec+ users (access_level ≥ 4) may call this.
154
+ */
155
+ export async function readGovernanceAuditLog(requestingUserId, limit = 100) {
156
+ if (!isHiveModeEnabled()) {
157
+ return { ok: false, error: "Hive mode is not enabled" };
158
+ }
159
+ const requestingLevel = await getUserAccessLevel(requestingUserId);
160
+ if (requestingLevel < ACCESS_LEVEL_EXEC) {
161
+ return { ok: false, error: "Insufficient access level — exec (4) or higher required" };
162
+ }
163
+ if (!isPostgresConfigured()) {
164
+ return { ok: false, error: "Supabase not configured" };
165
+ }
166
+ try {
167
+ const url = `${supabaseUrl()}/rest/v1/governance_audit_log?select=*&order=created_at.desc&limit=${limit}`;
168
+ const res = await fetch(url, { headers: pgHeaders() });
169
+ if (!res.ok) {
170
+ return { ok: false, error: `Failed to fetch audit log: ${res.status}` };
171
+ }
172
+ const entries = await res.json();
173
+ return { ok: true, entries, count: entries.length };
174
+ }
175
+ catch (err) {
176
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
177
+ }
178
+ }
179
+ //# sourceMappingURL=accessControl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessControl.js","sourceRoot":"","sources":["../../src/governance/accessControl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare function renderWordmark(subtitle?: string): string;
2
+ export declare function renderErrorBox(message?: string): string;
3
+ export declare function renderMetaRows(rows: Array<{
4
+ label: string;
5
+ value: string;
6
+ }>): string;
7
+ export declare function renderStatusBadge(variant: "ok" | "err", text: string): string;
8
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/html/components.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAMpF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,IAAI,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7E"}
@@ -0,0 +1,19 @@
1
+ export function renderWordmark(subtitle) {
2
+ const subtitleHtml = subtitle ? `<p class="brand-subtitle">${subtitle}</p>` : "";
3
+ return `<a class="brand-header" href="/" aria-label="Brains home"><img class="brand-logo" src="/assets/img/brains-logo.svg" alt="" /><span class="brand-wordmark">br<span class="brand-ai">ai</span>ns</span></a>${subtitleHtml}`;
4
+ }
5
+ export function renderErrorBox(message) {
6
+ if (!message)
7
+ return "";
8
+ return `<div class="alert alert-error">${message}</div>`;
9
+ }
10
+ export function renderMetaRows(rows) {
11
+ const rowHtml = rows
12
+ .map((row) => `<div><strong>${row.label}:</strong> ${row.value}</div>`)
13
+ .join("");
14
+ return `<section class="meta-box">${rowHtml}</section>`;
15
+ }
16
+ export function renderStatusBadge(variant, text) {
17
+ return `<div class="status-pill status-pill--${variant}">${text}</div>`;
18
+ }
19
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/html/components.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAC,QAAiB;IAC9C,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,6BAA6B,QAAQ,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,OAAO,4MAA4M,YAAY,EAAE,CAAC;AACpO,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,OAAO,kCAAkC,OAAO,QAAQ,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAA6C;IAC1E,MAAM,OAAO,GAAG,IAAI;SACjB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,GAAG,CAAC,KAAK,cAAc,GAAG,CAAC,KAAK,QAAQ,CAAC;SACtE,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO,6BAA6B,OAAO,YAAY,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAqB,EAAE,IAAY;IACnE,OAAO,wCAAwC,OAAO,KAAK,IAAI,QAAQ,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare function renderPageShell(params: {
2
+ title: string;
3
+ body: string;
4
+ cssPaths: string[];
5
+ bodyClass?: string;
6
+ }): string;
7
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/html/layout.ts"],"names":[],"mappings":"AAMA,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAkBT"}
@@ -0,0 +1,24 @@
1
+ function renderStylesheetLinks(cssPaths) {
2
+ return cssPaths
3
+ .map((href) => `<link rel="stylesheet" href="${href}" />`)
4
+ .join("\n ");
5
+ }
6
+ export function renderPageShell(params) {
7
+ const bodyClass = params.bodyClass ? ` class="${params.bodyClass}"` : "";
8
+ return `<!doctype html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="utf-8" />
12
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
13
+ <title>${params.title}</title>
14
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
15
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
16
+ <link href="https://fonts.googleapis.com/css2?family=Pacifico&family=Eczar:wght@400;500;600&family=Open+Sans:wght@400;500;600&family=Caveat:wght@600&display=swap" rel="stylesheet" />
17
+ ${renderStylesheetLinks(params.cssPaths)}
18
+ </head>
19
+ <body${bodyClass}>
20
+ ${params.body}
21
+ </body>
22
+ </html>`;
23
+ }
24
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/html/layout.ts"],"names":[],"mappings":"AAAA,SAAS,qBAAqB,CAAC,QAAkB;IAC/C,OAAO,QAAQ;SACZ,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gCAAgC,IAAI,MAAM,CAAC;SACzD,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAK/B;IACC,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzE,OAAO;;;;;WAKE,MAAM,CAAC,KAAK;;;;IAInB,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC;;OAEnC,SAAS;EACd,MAAM,CAAC,IAAI;;QAEL,CAAC;AACT,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * metricsUi.ts
3
+ *
4
+ * Self-contained HTML dashboard for /metrics-ui.
5
+ * Polls /api/v1/metrics every 30 s. Chart.js loaded via CDN — no npm install.
6
+ */
7
+ export declare function metricsUiHtml(): string;
8
+ //# sourceMappingURL=metricsUi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metricsUi.d.ts","sourceRoot":"","sources":["../../src/html/metricsUi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,aAAa,IAAI,MAAM,CAuQtC"}
@@ -0,0 +1,271 @@
1
+ /**
2
+ * metricsUi.ts
3
+ *
4
+ * Self-contained HTML dashboard for /metrics-ui.
5
+ * Polls /api/v1/metrics every 30 s. Chart.js loaded via CDN — no npm install.
6
+ */
7
+ export function metricsUiHtml() {
8
+ return `<!doctype html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="utf-8" />
12
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
13
+ <title>brains Metrics</title>
14
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
15
+ <style>
16
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
17
+ body { font-family: system-ui, sans-serif; background: #0f1117; color: #e2e8f0; min-height: 100vh; padding: 1.5rem; }
18
+ h1 { font-size: 1.5rem; font-weight: 700; margin-bottom: 0.25rem; }
19
+ .subtitle { color: #94a3b8; font-size: 0.875rem; margin-bottom: 1.5rem; }
20
+ .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1rem; }
21
+ .card { background: #1e2130; border: 1px solid #2d3148; border-radius: 0.75rem; padding: 1.25rem; }
22
+ .card h2 { font-size: 0.875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #94a3b8; margin-bottom: 1rem; }
23
+ .stat-row { display: flex; gap: 1.5rem; margin-bottom: 1rem; }
24
+ .stat { display: flex; flex-direction: column; }
25
+ .stat-value { font-size: 1.75rem; font-weight: 700; color: #f8fafc; line-height: 1; }
26
+ .stat-label { font-size: 0.75rem; color: #64748b; margin-top: 0.25rem; }
27
+ .chart-wrap { position: relative; height: 120px; }
28
+ .queries { list-style: none; }
29
+ .queries li { display: flex; justify-content: space-between; padding: 0.375rem 0; border-bottom: 1px solid #2d3148; font-size: 0.875rem; }
30
+ .queries li:last-child { border-bottom: none; }
31
+ .q-text { color: #cbd5e1; font-family: monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
32
+ .status-bar { display: flex; align-items: center; gap: 0.5rem; font-size: 0.75rem; color: #64748b; margin-top: 1.5rem; }
33
+ .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; flex-shrink: 0; }
34
+ .dot.error { background: #ef4444; }
35
+ /* ── Fallback rate SLI panel ─────────────────────── */
36
+ .sli-ok { color: #22c55e; }
37
+ .sli-warn { color: #ef4444; }
38
+ .sli-label { font-size: 0.75rem; color: #64748b; margin-top: 0.25rem; }
39
+ .sli-threshold { font-size: 0.75rem; color: #64748b; margin-top: 0.5rem; }
40
+ select { background: #1e2130; color: #e2e8f0; border: 1px solid #2d3148; border-radius: 0.375rem; padding: 0.25rem 0.5rem; font-size: 0.875rem; margin-left: 0.5rem; }
41
+ .header-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 0.5rem; }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <div class="header-row">
46
+ <div>
47
+ <h1>brains Metrics</h1>
48
+ <p class="subtitle">Live request telemetry from <code>request_events</code></p>
49
+ </div>
50
+ <div>
51
+ <label for="win-select" style="font-size:0.875rem;color:#94a3b8;">Window</label>
52
+ <select id="win-select">
53
+ <option value="1h" selected>Last 1h</option>
54
+ <option value="6h">Last 6h</option>
55
+ <option value="24h">Last 24h</option>
56
+ <option value="7d">Last 7d</option>
57
+ </select>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="grid">
62
+ <!-- Search latency -->
63
+ <div class="card">
64
+ <h2>Search Latency</h2>
65
+ <div class="stat-row">
66
+ <div class="stat"><span class="stat-value" id="s-p50">—</span><span class="stat-label">p50 ms</span></div>
67
+ <div class="stat"><span class="stat-value" id="s-p95">—</span><span class="stat-label">p95 ms</span></div>
68
+ <div class="stat"><span class="stat-value" id="s-p99">—</span><span class="stat-label">p99 ms</span></div>
69
+ </div>
70
+ <div class="chart-wrap"><canvas id="latency-chart"></canvas></div>
71
+ </div>
72
+
73
+ <!-- Backend breakdown -->
74
+ <div class="card">
75
+ <h2>Backend Breakdown</h2>
76
+ <div class="chart-wrap"><canvas id="backend-chart"></canvas></div>
77
+ </div>
78
+
79
+ <!-- Error & zero-result rates -->
80
+ <div class="card">
81
+ <h2>Rates</h2>
82
+ <div class="stat-row">
83
+ <div class="stat"><span class="stat-value" id="err-rate">—</span><span class="stat-label">Error rate</span></div>
84
+ <div class="stat"><span class="stat-value" id="zero-rate">—</span><span class="stat-label">Zero-result rate</span></div>
85
+ </div>
86
+ <div class="stat-row">
87
+ <div class="stat"><span class="stat-value" id="s-total">—</span><span class="stat-label">Search requests</span></div>
88
+ <div class="stat"><span class="stat-value" id="w-total">—</span><span class="stat-label">Write requests</span></div>
89
+ </div>
90
+ </div>
91
+
92
+ <!-- Fallback rate SLI -->
93
+ <div class="card">
94
+ <h2>Fallback Rate SLI</h2>
95
+ <div class="stat-row">
96
+ <div class="stat">
97
+ <span class="stat-value" id="fallback-rate">—</span>
98
+ <span class="sli-label">scan-fallback rate</span>
99
+ </div>
100
+ <div class="stat">
101
+ <span class="stat-value sli-ok" id="sli-status">—</span>
102
+ <span class="sli-label">SLI status</span>
103
+ </div>
104
+ </div>
105
+ <div class="sli-threshold">Target: &lt; 2% over window · Above 2% = index stale or corrupt</div>
106
+ </div>
107
+
108
+ <!-- Write latency -->
109
+ <div class="card">
110
+ <h2>Write Latency</h2>
111
+ <div class="stat-row">
112
+ <div class="stat"><span class="stat-value" id="w-p50">—</span><span class="stat-label">p50 ms</span></div>
113
+ <div class="stat"><span class="stat-value" id="w-p95">—</span><span class="stat-label">p95 ms</span></div>
114
+ </div>
115
+ <div class="stat-row">
116
+ <div class="stat"><span class="stat-value" id="overall-p95">—</span><span class="stat-label">Overall p95 ms</span></div>
117
+ <div class="stat"><span class="stat-value" id="overall-err">—</span><span class="stat-label">Overall error rate</span></div>
118
+ </div>
119
+ </div>
120
+
121
+ <!-- Top queries -->
122
+ <div class="card" style="grid-column: span 2;">
123
+ <h2>Top Queries by Volume</h2>
124
+ <ul class="queries" id="top-queries"><li><span class="q-text" style="color:#64748b;">Loading…</span></li></ul>
125
+ </div>
126
+ </div>
127
+
128
+ <div class="status-bar">
129
+ <span class="dot" id="status-dot"></span>
130
+ <span id="status-text">Initializing…</span>
131
+ </div>
132
+
133
+ <script>
134
+ let latencyChart = null;
135
+ let backendChart = null;
136
+ const latencyHistory = [];
137
+ const MAX_HISTORY = 30;
138
+
139
+ function pct(v) { return (v * 100).toFixed(1) + '%'; }
140
+ function ms(v) { return v === 0 ? '0' : String(v); }
141
+
142
+ function setStatus(ok, msg) {
143
+ document.getElementById('status-dot').className = 'dot' + (ok ? '' : ' error');
144
+ document.getElementById('status-text').textContent = msg;
145
+ }
146
+
147
+ function initLatencyChart() {
148
+ const ctx = document.getElementById('latency-chart').getContext('2d');
149
+ latencyChart = new Chart(ctx, {
150
+ type: 'line',
151
+ data: {
152
+ labels: [],
153
+ datasets: [
154
+ { label: 'p50', data: [], borderColor: '#22c55e', backgroundColor: 'transparent', tension: 0.3, pointRadius: 2 },
155
+ { label: 'p95', data: [], borderColor: '#f59e0b', backgroundColor: 'transparent', tension: 0.3, pointRadius: 2 },
156
+ { label: 'p99', data: [], borderColor: '#ef4444', backgroundColor: 'transparent', tension: 0.3, pointRadius: 2 },
157
+ ],
158
+ },
159
+ options: {
160
+ responsive: true, maintainAspectRatio: false,
161
+ plugins: { legend: { labels: { color: '#94a3b8', boxWidth: 12, font: { size: 10 } } } },
162
+ scales: {
163
+ x: { display: false },
164
+ y: { ticks: { color: '#64748b', font: { size: 10 } }, grid: { color: '#2d3148' }, beginAtZero: true },
165
+ },
166
+ },
167
+ });
168
+ }
169
+
170
+ function initBackendChart() {
171
+ const ctx = document.getElementById('backend-chart').getContext('2d');
172
+ backendChart = new Chart(ctx, {
173
+ type: 'doughnut',
174
+ data: { labels: [], datasets: [{ data: [], backgroundColor: ['#6366f1','#22c55e','#f59e0b','#ef4444','#a78bfa'], borderWidth: 0 }] },
175
+ options: {
176
+ responsive: true, maintainAspectRatio: false,
177
+ plugins: { legend: { position: 'right', labels: { color: '#94a3b8', font: { size: 10 }, boxWidth: 12 } } },
178
+ },
179
+ });
180
+ }
181
+
182
+ function updateCharts(metrics) {
183
+ const s = metrics.search;
184
+ const now = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
185
+ latencyHistory.push({ t: now, p50: s.p50, p95: s.p95, p99: s.p99 });
186
+ if (latencyHistory.length > MAX_HISTORY) latencyHistory.shift();
187
+
188
+ latencyChart.data.labels = latencyHistory.map(h => h.t);
189
+ latencyChart.data.datasets[0].data = latencyHistory.map(h => h.p50);
190
+ latencyChart.data.datasets[1].data = latencyHistory.map(h => h.p95);
191
+ latencyChart.data.datasets[2].data = latencyHistory.map(h => h.p99);
192
+ latencyChart.update('none');
193
+
194
+ const bd = s.backendBreakdown;
195
+ const labels = Object.keys(bd);
196
+ const values = labels.map(k => bd[k]);
197
+ backendChart.data.labels = labels.length ? labels : ['(no data)'];
198
+ backendChart.data.datasets[0].data = values.length ? values : [1];
199
+ backendChart.update('none');
200
+ }
201
+
202
+ function updateStats(metrics) {
203
+ const s = metrics.search;
204
+ const w = metrics.writes;
205
+ const o = metrics.overall;
206
+ document.getElementById('s-p50').textContent = ms(s.p50);
207
+ document.getElementById('s-p95').textContent = ms(s.p95);
208
+ document.getElementById('s-p99').textContent = ms(s.p99);
209
+ document.getElementById('err-rate').textContent = pct(s.errorRate);
210
+ document.getElementById('zero-rate').textContent = pct(s.zeroResultRate);
211
+ document.getElementById('s-total').textContent = String(s.totalRequests);
212
+ document.getElementById('w-total').textContent = String(w.totalRequests);
213
+ document.getElementById('w-p50').textContent = ms(w.p50);
214
+ document.getElementById('w-p95').textContent = ms(w.p95);
215
+ document.getElementById('overall-p95').textContent = ms(o.p95);
216
+ document.getElementById('overall-err').textContent = pct(o.errorRate);
217
+
218
+ // Fallback rate SLI (target < 2%)
219
+ const fbRate = s.fallbackRate ?? 0;
220
+ const fbEl = document.getElementById('fallback-rate');
221
+ const sliEl = document.getElementById('sli-status');
222
+ fbEl.textContent = pct(fbRate);
223
+ if (fbRate >= 0.02) {
224
+ fbEl.className = 'stat-value sli-warn';
225
+ sliEl.textContent = 'BREACH';
226
+ sliEl.className = 'stat-value sli-warn';
227
+ } else {
228
+ fbEl.className = 'stat-value sli-ok';
229
+ sliEl.textContent = 'OK';
230
+ sliEl.className = 'stat-value sli-ok';
231
+ }
232
+
233
+ const ul = document.getElementById('top-queries');
234
+ if (!s.topQueries.length) {
235
+ ul.innerHTML = '<li><span class="q-text" style="color:#64748b;">(no queries yet)</span></li>';
236
+ } else {
237
+ ul.innerHTML = s.topQueries.map(q => \`<li><span class="q-text">\${q}</span></li>\`).join('');
238
+ }
239
+ }
240
+
241
+ async function fetchMetrics() {
242
+ const win = document.getElementById('win-select').value;
243
+ try {
244
+ const headers = {};
245
+ const apiKey = window.__brainsApiKey;
246
+ if (apiKey) headers['x-api-key'] = apiKey;
247
+ const res = await fetch('/api/v1/metrics?window=' + win, { headers });
248
+ if (!res.ok) {
249
+ setStatus(false, 'Error ' + res.status + ' — ' + new Date().toLocaleTimeString());
250
+ return;
251
+ }
252
+ const data = await res.json();
253
+ updateStats(data);
254
+ updateCharts(data);
255
+ setStatus(true, 'Updated ' + new Date().toLocaleTimeString() + ' · window=' + win);
256
+ } catch (e) {
257
+ setStatus(false, 'Fetch error: ' + String(e));
258
+ }
259
+ }
260
+
261
+ initLatencyChart();
262
+ initBackendChart();
263
+ fetchMetrics();
264
+ setInterval(fetchMetrics, 30000);
265
+
266
+ document.getElementById('win-select').addEventListener('change', fetchMetrics);
267
+ </script>
268
+ </body>
269
+ </html>`;
270
+ }
271
+ //# sourceMappingURL=metricsUi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metricsUi.js","sourceRoot":"","sources":["../../src/html/metricsUi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAqQD,CAAC;AACT,CAAC"}