@bevel-software/platform-core-backend 0.4.1 → 0.5.1

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 (305) hide show
  1. package/THIRD-PARTY-NOTICES.md +6118 -0
  2. package/dist/core/core-ports.d.ts +15 -0
  3. package/dist/core/core-ports.d.ts.map +1 -1
  4. package/dist/core/core-ports.js.map +1 -1
  5. package/dist/core/create-core-server.d.ts.map +1 -1
  6. package/dist/core/create-core-server.js +40 -6
  7. package/dist/core/create-core-server.js.map +1 -1
  8. package/dist/core/create-core-services.d.ts +17 -1
  9. package/dist/core/create-core-services.d.ts.map +1 -1
  10. package/dist/core/create-core-services.js +153 -29
  11. package/dist/core/create-core-services.js.map +1 -1
  12. package/dist/core-config.d.ts +70 -21
  13. package/dist/core-config.d.ts.map +1 -1
  14. package/dist/core-config.js +134 -58
  15. package/dist/core-config.js.map +1 -1
  16. package/dist/modules/access/access-control.interface.d.ts +6 -0
  17. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  18. package/dist/modules/access/access-control.service.d.ts +74 -5
  19. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  20. package/dist/modules/access/access-control.service.js +116 -15
  21. package/dist/modules/access/access-control.service.js.map +1 -1
  22. package/dist/modules/access/access-declarations.d.ts +93 -0
  23. package/dist/modules/access/access-declarations.d.ts.map +1 -0
  24. package/dist/modules/access/access-declarations.js +168 -0
  25. package/dist/modules/access/access-declarations.js.map +1 -0
  26. package/dist/modules/access/access-mutation.service.js +4 -4
  27. package/dist/modules/access/access-mutation.service.js.map +1 -1
  28. package/dist/modules/access/access-splice.d.ts +16 -1
  29. package/dist/modules/access/access-splice.d.ts.map +1 -1
  30. package/dist/modules/access/access-splice.js +40 -21
  31. package/dist/modules/access/access-splice.js.map +1 -1
  32. package/dist/modules/access/access.routes.d.ts.map +1 -1
  33. package/dist/modules/access/access.routes.js +51 -0
  34. package/dist/modules/access/access.routes.js.map +1 -1
  35. package/dist/modules/access/creator-access.d.ts +8 -0
  36. package/dist/modules/access/creator-access.d.ts.map +1 -1
  37. package/dist/modules/access/creator-access.js +21 -2
  38. package/dist/modules/access/creator-access.js.map +1 -1
  39. package/dist/modules/admin/admin-access.service.d.ts +1 -1
  40. package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
  41. package/dist/modules/admin/admin-access.service.js +3 -2
  42. package/dist/modules/admin/admin-access.service.js.map +1 -1
  43. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  44. package/dist/modules/auth/auth.routes.js +50 -1
  45. package/dist/modules/auth/auth.routes.js.map +1 -1
  46. package/dist/modules/auth/auth.service.d.ts +39 -10
  47. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  48. package/dist/modules/auth/auth.service.js +38 -16
  49. package/dist/modules/auth/auth.service.js.map +1 -1
  50. package/dist/modules/database/core-schema.d.ts +124 -0
  51. package/dist/modules/database/core-schema.d.ts.map +1 -1
  52. package/dist/modules/database/core-schema.js +45 -0
  53. package/dist/modules/database/core-schema.js.map +1 -1
  54. package/dist/modules/groups/group-provision.service.d.ts +130 -0
  55. package/dist/modules/groups/group-provision.service.d.ts.map +1 -0
  56. package/dist/modules/groups/group-provision.service.js +288 -0
  57. package/dist/modules/groups/group-provision.service.js.map +1 -0
  58. package/dist/modules/groups/groups.contract.d.ts +106 -0
  59. package/dist/modules/groups/groups.contract.d.ts.map +1 -0
  60. package/dist/modules/groups/groups.contract.js +36 -0
  61. package/dist/modules/groups/groups.contract.js.map +1 -0
  62. package/dist/modules/groups/groups.routes.d.ts +42 -0
  63. package/dist/modules/groups/groups.routes.d.ts.map +1 -0
  64. package/dist/modules/groups/groups.routes.js +379 -0
  65. package/dist/modules/groups/groups.routes.js.map +1 -0
  66. package/dist/modules/groups/groups.service.d.ts +60 -0
  67. package/dist/modules/groups/groups.service.d.ts.map +1 -0
  68. package/dist/modules/groups/groups.service.js +172 -0
  69. package/dist/modules/groups/groups.service.js.map +1 -0
  70. package/dist/modules/groups/index.d.ts +7 -0
  71. package/dist/modules/groups/index.d.ts.map +1 -0
  72. package/dist/modules/groups/index.js +6 -0
  73. package/dist/modules/groups/index.js.map +1 -0
  74. package/dist/modules/groups/join-proposals.d.ts +53 -0
  75. package/dist/modules/groups/join-proposals.d.ts.map +1 -0
  76. package/dist/modules/groups/join-proposals.js +67 -0
  77. package/dist/modules/groups/join-proposals.js.map +1 -0
  78. package/dist/modules/groups/join-requests.service.d.ts +81 -0
  79. package/dist/modules/groups/join-requests.service.d.ts.map +1 -0
  80. package/dist/modules/groups/join-requests.service.js +135 -0
  81. package/dist/modules/groups/join-requests.service.js.map +1 -0
  82. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +20 -0
  83. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  84. package/dist/modules/secrets-vault/secrets-vault.routes.js +59 -11
  85. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  86. package/dist/modules/settings/deployment-settings.service.d.ts +129 -0
  87. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -0
  88. package/dist/modules/settings/deployment-settings.service.js +368 -0
  89. package/dist/modules/settings/deployment-settings.service.js.map +1 -0
  90. package/dist/modules/settings/setup.routes.d.ts +49 -0
  91. package/dist/modules/settings/setup.routes.d.ts.map +1 -0
  92. package/dist/modules/settings/setup.routes.js +292 -0
  93. package/dist/modules/settings/setup.routes.js.map +1 -0
  94. package/dist/modules/skills/index.d.ts +2 -1
  95. package/dist/modules/skills/index.d.ts.map +1 -1
  96. package/dist/modules/skills/index.js +1 -0
  97. package/dist/modules/skills/index.js.map +1 -1
  98. package/dist/modules/skills/pending-skills.service.d.ts +40 -0
  99. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -0
  100. package/dist/modules/skills/pending-skills.service.js +147 -0
  101. package/dist/modules/skills/pending-skills.service.js.map +1 -0
  102. package/dist/modules/skills/skills.contract.d.ts +32 -3
  103. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  104. package/dist/modules/skills/skills.contract.js +1 -1
  105. package/dist/modules/skills/skills.routes.d.ts +3 -2
  106. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  107. package/dist/modules/skills/skills.routes.js +18 -1
  108. package/dist/modules/skills/skills.routes.js.map +1 -1
  109. package/dist/modules/skills/skills.service.d.ts +22 -0
  110. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  111. package/dist/modules/skills/skills.service.js +24 -12
  112. package/dist/modules/skills/skills.service.js.map +1 -1
  113. package/dist/modules/tool-manuals/index.d.ts +1 -1
  114. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  115. package/dist/modules/tool-manuals/index.js.map +1 -1
  116. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +47 -2
  117. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  118. package/dist/modules/tool-manuals/tool-manuals.contract.js +1 -1
  119. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +4 -0
  120. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  121. package/dist/modules/tool-manuals/tool-manuals.routes.js +23 -0
  122. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  123. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +3 -1
  124. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  125. package/dist/modules/tool-manuals/tool-manuals.service.js +84 -17
  126. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  127. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  128. package/dist/modules/tool-manuals/tool-manuals.tools.js +6 -2
  129. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  130. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  131. package/dist/modules/workflow/agent-tools/workflow.tools.js +4 -2
  132. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  133. package/dist/modules/workflow/event-bus.d.ts +11 -0
  134. package/dist/modules/workflow/event-bus.d.ts.map +1 -1
  135. package/dist/modules/workflow/event-bus.js +22 -0
  136. package/dist/modules/workflow/event-bus.js.map +1 -1
  137. package/dist/modules/workflow/file-lock.service.d.ts +9 -0
  138. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  139. package/dist/modules/workflow/file-lock.service.js +16 -0
  140. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  141. package/dist/modules/workflow/git/branch-name.d.ts +1 -1
  142. package/dist/modules/workflow/git/branch-name.d.ts.map +1 -1
  143. package/dist/modules/workflow/git/branch-name.js +1 -1
  144. package/dist/modules/workflow/git/branch-name.js.map +1 -1
  145. package/dist/modules/workflow/git/git.service.d.ts +31 -2
  146. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  147. package/dist/modules/workflow/git/git.service.js +144 -52
  148. package/dist/modules/workflow/git/git.service.js.map +1 -1
  149. package/dist/modules/workflow/git/pull-request.service.d.ts +12 -6
  150. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  151. package/dist/modules/workflow/git/pull-request.service.js +19 -9
  152. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  153. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  154. package/dist/modules/workflow/review-workflow/review-workflow.service.js +12 -3
  155. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  156. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  157. package/dist/modules/workflow/workflow.routes.js +95 -14
  158. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  159. package/dist/modules/workflow/workflow.service.d.ts +92 -3
  160. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  161. package/dist/modules/workflow/workflow.service.js +381 -108
  162. package/dist/modules/workflow/workflow.service.js.map +1 -1
  163. package/dist/modules/workspace/bevel-ignore.d.ts +5 -0
  164. package/dist/modules/workspace/bevel-ignore.d.ts.map +1 -1
  165. package/dist/modules/workspace/bevel-ignore.js +5 -1
  166. package/dist/modules/workspace/bevel-ignore.js.map +1 -1
  167. package/dist/modules/workspace/kb-seed.interface.d.ts +1 -1
  168. package/dist/modules/workspace/kb-seed.service.d.ts +60 -5
  169. package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -1
  170. package/dist/modules/workspace/kb-seed.service.js +228 -51
  171. package/dist/modules/workspace/kb-seed.service.js.map +1 -1
  172. package/dist/modules/workspace/workspace.routes.d.ts +2 -1
  173. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  174. package/dist/modules/workspace/workspace.routes.js +51 -18
  175. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  176. package/dist/modules/workspace/workspace.service.d.ts +16 -4
  177. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  178. package/dist/modules/workspace/workspace.service.js +36 -9
  179. package/dist/modules/workspace/workspace.service.js.map +1 -1
  180. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  181. package/dist/modules/workspace/workspace.tools.js +8 -3
  182. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  183. package/dist/shared/kb-layout.test.js +2 -2
  184. package/dist/test-setup.d.ts +2 -0
  185. package/dist/test-setup.d.ts.map +1 -0
  186. package/dist/test-setup.js +18 -0
  187. package/dist/test-setup.js.map +1 -0
  188. package/kb-template/.bevelignore +6 -2
  189. package/kb-template/AGENTS.md +272 -0
  190. package/kb-template/KnowledgeBase/How to get started.md +65 -0
  191. package/kb-template/access.md +36 -36
  192. package/migrations/0002_user_onboarding_done.sql +1 -0
  193. package/migrations/0003_deployment_settings.sql +9 -0
  194. package/migrations/meta/0001_snapshot.json +1486 -0
  195. package/migrations/meta/0002_snapshot.json +1493 -0
  196. package/migrations/meta/0003_snapshot.json +1552 -0
  197. package/migrations/meta/_journal.json +15 -1
  198. package/package.json +5 -4
  199. package/src/__tests__/branch-model.test.ts +79 -0
  200. package/src/__tests__/core-config.admin.test.ts +114 -0
  201. package/src/__tests__/database-url.test.ts +70 -0
  202. package/src/core/core-ports.ts +15 -0
  203. package/src/core/create-core-server.ts +55 -6
  204. package/src/core/create-core-services.ts +201 -36
  205. package/src/core-config.ts +139 -59
  206. package/src/modules/access/__tests__/access-declarations.test.ts +195 -0
  207. package/src/modules/access/__tests__/access-md-format.test.ts +191 -0
  208. package/src/modules/access/__tests__/access-splice.test.ts +21 -0
  209. package/src/modules/access/__tests__/access.routes.overrides.test.ts +214 -0
  210. package/src/modules/access/access-control.interface.ts +11 -0
  211. package/src/modules/access/access-control.service.ts +113 -15
  212. package/src/modules/access/access-declarations.ts +251 -0
  213. package/src/modules/access/access-mutation.service.ts +4 -4
  214. package/src/modules/access/access-splice.ts +64 -21
  215. package/src/modules/access/access.routes.ts +61 -0
  216. package/src/modules/access/creator-access.ts +22 -2
  217. package/src/modules/admin/admin-access.service.ts +4 -3
  218. package/src/modules/auth/__tests__/auth.service.test.ts +57 -0
  219. package/src/modules/auth/__tests__/onboarding-done.route.test.ts +147 -0
  220. package/src/modules/auth/auth.routes.ts +50 -1
  221. package/src/modules/auth/auth.service.ts +55 -17
  222. package/src/modules/database/core-schema.ts +46 -0
  223. package/src/modules/groups/__tests__/group-index.service.test.ts +225 -0
  224. package/src/modules/groups/__tests__/group-provision.service.test.ts +247 -0
  225. package/src/modules/groups/__tests__/groups.routes.test.ts +493 -0
  226. package/src/modules/groups/__tests__/join-proposals.test.ts +101 -0
  227. package/src/modules/groups/__tests__/join-requests.service.test.ts +175 -0
  228. package/src/modules/groups/group-provision.service.ts +328 -0
  229. package/src/modules/groups/groups.contract.ts +107 -0
  230. package/src/modules/groups/groups.routes.ts +433 -0
  231. package/src/modules/groups/groups.service.ts +193 -0
  232. package/src/modules/groups/index.ts +17 -0
  233. package/src/modules/groups/join-proposals.ts +114 -0
  234. package/src/modules/groups/join-requests.service.ts +185 -0
  235. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +283 -0
  236. package/src/modules/secrets-vault/secrets-vault.routes.ts +68 -12
  237. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +256 -0
  238. package/src/modules/settings/__tests__/setup.routes.test.ts +201 -0
  239. package/src/modules/settings/deployment-settings.service.ts +419 -0
  240. package/src/modules/settings/setup.routes.ts +317 -0
  241. package/src/modules/skills/__tests__/pending-skills.service.test.ts +240 -0
  242. package/src/modules/skills/__tests__/skills.service.test.ts +13 -13
  243. package/src/modules/skills/index.ts +3 -0
  244. package/src/modules/skills/pending-skills.service.ts +168 -0
  245. package/src/modules/skills/skills.contract.ts +34 -3
  246. package/src/modules/skills/skills.routes.ts +23 -2
  247. package/src/modules/skills/skills.service.ts +24 -16
  248. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +262 -0
  249. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -2
  250. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +61 -23
  251. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +156 -156
  252. package/src/modules/tool-manuals/index.ts +2 -0
  253. package/src/modules/tool-manuals/tool-manuals.contract.ts +50 -2
  254. package/src/modules/tool-manuals/tool-manuals.routes.ts +21 -0
  255. package/src/modules/tool-manuals/tool-manuals.service.ts +86 -17
  256. package/src/modules/tool-manuals/tool-manuals.tools.ts +206 -202
  257. package/src/modules/workflow/__tests__/event-bus.test.ts +35 -0
  258. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +251 -5
  259. package/src/modules/workflow/agent-tools/workflow.tools.ts +4 -2
  260. package/src/modules/workflow/event-bus.ts +25 -0
  261. package/src/modules/workflow/file-lock.service.ts +17 -0
  262. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +9 -49
  263. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +101 -0
  264. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +219 -211
  265. package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +132 -0
  266. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +4 -2
  267. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +275 -272
  268. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +5 -0
  269. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +44 -0
  270. package/src/modules/workflow/git/branch-name.ts +5 -1
  271. package/src/modules/workflow/git/git.service.ts +159 -62
  272. package/src/modules/workflow/git/pull-request.service.ts +22 -11
  273. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +4 -1
  274. package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
  275. package/src/modules/workflow/workflow.routes.ts +91 -13
  276. package/src/modules/workflow/workflow.service.ts +353 -13
  277. package/src/modules/workspace/__tests__/kb-seed.service.test.ts +255 -24
  278. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +236 -233
  279. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +179 -176
  280. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +320 -303
  281. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +337 -276
  282. package/src/modules/workspace/__tests__/workspace.tools.test.ts +14 -6
  283. package/src/modules/workspace/bevel-ignore.ts +66 -62
  284. package/src/modules/workspace/kb-seed.interface.ts +1 -1
  285. package/src/modules/workspace/kb-seed.service.ts +247 -53
  286. package/src/modules/workspace/workspace.routes.ts +54 -18
  287. package/src/modules/workspace/workspace.service.ts +45 -7
  288. package/src/modules/workspace/workspace.tools.ts +922 -917
  289. package/src/shared/__tests__/frontmatter-id.test.ts +53 -0
  290. package/src/shared/__tests__/join-request.test.ts +96 -0
  291. package/src/shared/__tests__/kb-layout.group.test.ts +45 -0
  292. package/src/shared/kb-layout.test.ts +2 -2
  293. package/src/test-setup.ts +18 -0
  294. package/kb-template/Agents/README.md +0 -38
  295. package/kb-template/CLAUDE.md +0 -419
  296. package/kb-template/Data/.gitkeep +0 -0
  297. package/kb-template/Data/README.md +0 -39
  298. package/kb-template/KnowledgeBase/README.md +0 -37
  299. package/kb-template/Pipelines/.gitkeep +0 -0
  300. package/kb-template/Pipelines/README.md +0 -38
  301. package/kb-template/Skills/.gitkeep +0 -0
  302. package/kb-template/Skills/README.md +0 -32
  303. package/kb-template/Tools/.gitkeep +0 -0
  304. package/kb-template/Tools/README.md +0 -42
  305. /package/kb-template/{Agents → Groups}/.gitkeep +0 -0
@@ -15,6 +15,20 @@
15
15
  "when": 1785534603355,
16
16
  "tag": "0001_user_password_hash",
17
17
  "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "7",
22
+ "when": 1785934874039,
23
+ "tag": "0002_user_onboarding_done",
24
+ "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "7",
29
+ "when": 1785943322636,
30
+ "tag": "0003_deployment_settings",
31
+ "breakpoints": true
18
32
  }
19
33
  ]
20
- }
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bevel-software/platform-core-backend",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Open-source core backend of the Bevel platform: git-backed knowledge workspace, workflow (branches/change requests/locks/SSE), skills, tools, secrets vault, access control and the remote MCP surface.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,8 @@
22
22
  "dist",
23
23
  "src",
24
24
  "migrations",
25
- "kb-template"
25
+ "kb-template",
26
+ "THIRD-PARTY-NOTICES.md"
26
27
  ],
27
28
  "engines": {
28
29
  "node": ">=22 <23"
@@ -45,7 +46,7 @@
45
46
  "pg": "^8.20.0",
46
47
  "yaml": "^2.9.0",
47
48
  "zod": "^3.24.0",
48
- "@bevel-software/platform-shared": "0.4.1"
49
+ "@bevel-software/platform-shared": "0.5.1"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@types/adm-zip": "^0.5.8",
@@ -64,7 +65,7 @@
64
65
  "license": "Apache-2.0",
65
66
  "repository": {
66
67
  "type": "git",
67
- "url": "git+https://github.com/Bevel-Software/skill-and-tool-management.git",
68
+ "url": "git+https://github.com/Bevel-Software/Hexis.git",
68
69
  "directory": "packages/core-backend"
69
70
  },
70
71
  "publishConfig": {
@@ -0,0 +1,79 @@
1
+ import { describe, it, expect, afterEach, beforeEach } from 'vitest';
2
+ import {
3
+ configureBranchModel,
4
+ isBranchModelConfigured,
5
+ isProtectedBranch,
6
+ protectedBranchDisplayName,
7
+ DEFAULT_BRANCH,
8
+ PROTECTED_BRANCHES,
9
+ } from '@bevel-software/platform-shared';
10
+
11
+ const PAIR = {
12
+ defaultBranch: 'target-company-state',
13
+ protectedBranches: ['current-company-state', 'target-company-state'],
14
+ };
15
+
16
+ beforeEach(() => configureBranchModel(PAIR));
17
+ // The model is module-global by design (that is what lets every call site keep
18
+ // importing a binding). Restore it so a suite that runs after this one in the
19
+ // same worker is not left with whatever the last case configured.
20
+ afterEach(() => configureBranchModel(PAIR));
21
+
22
+ describe('configureBranchModel', () => {
23
+ it('applies the model to every export', () => {
24
+ expect(isBranchModelConfigured()).toBe(true);
25
+ expect(DEFAULT_BRANCH).toBe('target-company-state');
26
+ expect([...PROTECTED_BRANCHES].sort()).toEqual([
27
+ 'current-company-state',
28
+ 'target-company-state',
29
+ ]);
30
+ expect(isProtectedBranch('current-company-state')).toBe(true);
31
+ expect(isProtectedBranch('someone/draft')).toBe(false);
32
+ });
33
+
34
+ it('accepts the comma/space list the environment supplies', () => {
35
+ configureBranchModel({ defaultBranch: 'main', protectedBranches: 'main, release' });
36
+ expect([...PROTECTED_BRANCHES].sort()).toEqual(['main', 'release']);
37
+ });
38
+
39
+ it('derives display names from the slug', () => {
40
+ expect(protectedBranchDisplayName('target-company-state')).toBe('Target company state');
41
+ // Unknown names return null so the caller decides between the raw slug and
42
+ // hiding the affordance — it must not invent a name for a feature branch.
43
+ expect(protectedBranchDisplayName('someone/draft')).toBeNull();
44
+ });
45
+
46
+ /**
47
+ * The default branch is where users land and the default propose target. If
48
+ * it is not itself protected, `isProtectedBranch(DEFAULT_BRANCH)` is false and
49
+ * every protected-branch guard silently stops applying to the one branch that
50
+ * needs them most. Refused rather than shipped.
51
+ */
52
+ it('refuses a default branch that is not protected', () => {
53
+ expect(() =>
54
+ configureBranchModel({ defaultBranch: 'main', protectedBranches: ['release'] }),
55
+ ).toThrow(/must be one of the protected branches/);
56
+ });
57
+
58
+ it('refuses an empty model rather than defaulting to something plausible', () => {
59
+ expect(() => configureBranchModel({ defaultBranch: '', protectedBranches: ['main'] })).toThrow(
60
+ /default branch is required/,
61
+ );
62
+ expect(() => configureBranchModel({ defaultBranch: 'main', protectedBranches: [] })).toThrow(
63
+ /At least one protected branch/,
64
+ );
65
+ });
66
+
67
+ /**
68
+ * A rejected model must not half-apply. The previous one stays in force, so a
69
+ * bad `/api/config` payload cannot leave the app pointing at a branch that is
70
+ * neither the old value nor the new one.
71
+ */
72
+ it('leaves the previous model intact when it refuses a new one', () => {
73
+ expect(() =>
74
+ configureBranchModel({ defaultBranch: 'nope', protectedBranches: ['other'] }),
75
+ ).toThrow();
76
+ expect(DEFAULT_BRANCH).toBe('target-company-state');
77
+ expect(isProtectedBranch('target-company-state')).toBe(true);
78
+ });
79
+ });
@@ -0,0 +1,114 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { CoreConfig } from '../core-config.js';
3
+
4
+ /**
5
+ * The boot contract around the deployment owner.
6
+ *
7
+ * `ADMIN_EMAIL` is required outright — it is the always-admin, the initial
8
+ * Admin written into a freshly seeded `roles.yaml`, and half the bootstrap
9
+ * credential. A deployment without one cannot be administered at all, so
10
+ * refusing to boot beats booting into something nobody can get into.
11
+ *
12
+ * `ADMIN_PASSWORD` is required only while password login is ENABLED. An
13
+ * SSO-only deployment would otherwise have to hold a shared password the
14
+ * server is configured to reject — an unused credential someone still has to
15
+ * store and rotate.
16
+ */
17
+ const REQUIRED = {
18
+ KB_REPO_URL: 'https://example.com/org/kb.git',
19
+ ADMIN_EMAIL: 'root@example.com',
20
+ ADMIN_PASSWORD: 'sup3r-secret',
21
+ // The other two boot requirements, so a case about the admin pair fails for
22
+ // the reason it is testing rather than for a missing neighbour.
23
+ JWT_SECRET: 'test-jwt-secret',
24
+ SECRETS_ENC_KEY: 'kToAi8FXWDpDn3A6yQ/60O39bv05N7XzVOIu/0CJrFc=',
25
+ };
26
+
27
+ let saved: NodeJS.ProcessEnv;
28
+
29
+ beforeEach(() => {
30
+ saved = { ...process.env };
31
+ // A clean slate for the keys under test; anything else the constructor reads
32
+ // has a default or is supplied by REQUIRED.
33
+ for (const key of [
34
+ 'ADMIN_EMAIL',
35
+ 'ADMIN_PASSWORD',
36
+ 'LOGIN_PASSWORD',
37
+ 'SEED_ADMIN_EMAILS',
38
+ 'JWT_SECRET',
39
+ 'SECRETS_ENC_KEY',
40
+ 'CONNECTOR_CONFIG_ENC_KEY',
41
+ 'SHAREPOINT_TOKEN_ENC_KEY',
42
+ ]) {
43
+ delete process.env[key];
44
+ }
45
+ Object.assign(process.env, REQUIRED);
46
+ });
47
+
48
+ afterEach(() => {
49
+ process.env = saved;
50
+ });
51
+
52
+ describe('CoreConfig — the deployment owner', () => {
53
+ it('refuses to boot without ADMIN_EMAIL', () => {
54
+ delete process.env.ADMIN_EMAIL;
55
+ expect(() => new CoreConfig()).toThrow(/ADMIN_EMAIL is required/);
56
+ });
57
+
58
+ it('refuses a malformed ADMIN_EMAIL rather than seeding a broken roles.yaml', () => {
59
+ process.env.ADMIN_EMAIL = 'not-an-email';
60
+ expect(() => new CoreConfig()).toThrow(/not a valid email/);
61
+ });
62
+
63
+ it('requires ADMIN_PASSWORD while password login is enabled', () => {
64
+ delete process.env.ADMIN_PASSWORD;
65
+ expect(() => new CoreConfig()).toThrow(/ADMIN_PASSWORD is required/);
66
+ });
67
+
68
+ it('does NOT require ADMIN_PASSWORD on an SSO-only deployment', () => {
69
+ delete process.env.ADMIN_PASSWORD;
70
+ process.env.LOGIN_PASSWORD = 'false';
71
+ const config = new CoreConfig();
72
+ expect(config.loginPasswordEnabled).toBe(false);
73
+ expect(config.adminPassword).toBe('');
74
+ // The identity is still there — "there is always an identifiable admin"
75
+ // holds whether or not a password exists.
76
+ expect(config.adminEmail).toBe('root@example.com');
77
+ });
78
+
79
+ it('normalizes the owner address, since it is matched against sign-in emails', () => {
80
+ process.env.ADMIN_EMAIL = ' Root@Example.COM ';
81
+ expect(new CoreConfig().adminEmail).toBe('root@example.com');
82
+ });
83
+
84
+ /**
85
+ * Both sign what the deployment hands out — sessions, and the git credential
86
+ * the setup screen stores. Unset, the failure used to surface at the first
87
+ * login or the last step of first-run setup; a boot error naming the variable
88
+ * is the same information, much earlier.
89
+ */
90
+ it('refuses to boot without JWT_SECRET', () => {
91
+ delete process.env.JWT_SECRET;
92
+ expect(() => new CoreConfig()).toThrow(/JWT_SECRET is required/);
93
+ });
94
+
95
+ it('refuses to boot without SECRETS_ENC_KEY', () => {
96
+ delete process.env.SECRETS_ENC_KEY;
97
+ expect(() => new CoreConfig()).toThrow(/SECRETS_ENC_KEY is required/);
98
+ });
99
+
100
+ it('still accepts the legacy encryption-key names', () => {
101
+ delete process.env.SECRETS_ENC_KEY;
102
+ process.env.CONNECTOR_CONFIG_ENC_KEY = 'kToAi8FXWDpDn3A6yQ/60O39bv05N7XzVOIu/0CJrFc=';
103
+ // Not just "does not throw": the legacy name has to actually supply the
104
+ // key, or an existing deployment boots and then cannot decrypt anything.
105
+ expect(new CoreConfig().secretsEncKey).toBe('kToAi8FXWDpDn3A6yQ/60O39bv05N7XzVOIu/0CJrFc=');
106
+ });
107
+
108
+ it('ignores a leftover SEED_ADMIN_EMAILS instead of failing on it', () => {
109
+ // The variable is gone, not renamed. An operator upgrading with a stale
110
+ // value in their env should boot, not hit an unknown-key error.
111
+ process.env.SEED_ADMIN_EMAILS = 'someone-else@example.com';
112
+ expect(() => new CoreConfig()).not.toThrow();
113
+ });
114
+ });
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { resolveDatabaseUrl } from '../core-config.js';
3
+
4
+ /**
5
+ * The connection string is composed HERE rather than in docker-compose, where
6
+ * it needed a nested interpolated default that deployment UIs mis-parse. Two
7
+ * things follow that are worth pinning.
8
+ */
9
+ describe('resolveDatabaseUrl', () => {
10
+ it('uses an explicit DATABASE_URL over the parts', () => {
11
+ expect(
12
+ resolveDatabaseUrl({
13
+ DATABASE_URL: 'postgresql://me:pw@managed.example.com:5432/prod',
14
+ POSTGRES_USER: 'ignored',
15
+ POSTGRES_HOST: 'db',
16
+ } as NodeJS.ProcessEnv),
17
+ ).toBe('postgresql://me:pw@managed.example.com:5432/prod');
18
+ });
19
+
20
+ it('builds from the parts when none is given', () => {
21
+ expect(
22
+ resolveDatabaseUrl({
23
+ POSTGRES_USER: 'alice',
24
+ POSTGRES_PASSWORD: 's3cret',
25
+ POSTGRES_DB: 'kb',
26
+ POSTGRES_HOST: 'db',
27
+ } as NodeJS.ProcessEnv),
28
+ ).toBe('postgresql://alice:s3cret@db:5432/kb');
29
+ });
30
+
31
+ /** `localhost` suits `pnpm dev`; compose passes the service name instead. */
32
+ it('defaults to a local Postgres', () => {
33
+ expect(resolveDatabaseUrl({} as NodeJS.ProcessEnv)).toBe(
34
+ 'postgresql://bevel:bevel@localhost:5432/bevel',
35
+ );
36
+ });
37
+
38
+ /**
39
+ * The reason code beats shell interpolation for this. A password with `@`,
40
+ * `/` or `:` in it produced an unparseable URL — and the resulting failure
41
+ * named the host or the database, never the password that broke it.
42
+ */
43
+ it('encodes credentials that would otherwise break the URL', () => {
44
+ const url = resolveDatabaseUrl({
45
+ POSTGRES_USER: 'user@corp',
46
+ POSTGRES_PASSWORD: 'p@ss:w/rd',
47
+ POSTGRES_DB: 'kb',
48
+ POSTGRES_HOST: 'db',
49
+ } as NodeJS.ProcessEnv);
50
+ // Parseable, and every part survives the round trip intact.
51
+ const parsed = new URL(url);
52
+ expect(parsed.hostname).toBe('db');
53
+ expect(decodeURIComponent(parsed.username)).toBe('user@corp');
54
+ expect(decodeURIComponent(parsed.password)).toBe('p@ss:w/rd');
55
+ expect(decodeURIComponent(parsed.pathname.slice(1))).toBe('kb');
56
+ });
57
+
58
+ it('honours a non-default port', () => {
59
+ expect(
60
+ resolveDatabaseUrl({ POSTGRES_HOST: 'db', POSTGRES_PORT: '6432' } as NodeJS.ProcessEnv),
61
+ ).toBe('postgresql://bevel:bevel@db:6432/bevel');
62
+ });
63
+
64
+ /** Blank is not a value — an empty variable must not win over the parts. */
65
+ it('treats a blank DATABASE_URL as absent', () => {
66
+ expect(
67
+ resolveDatabaseUrl({ DATABASE_URL: ' ', POSTGRES_HOST: 'db' } as NodeJS.ProcessEnv),
68
+ ).toBe('postgresql://bevel:bevel@db:5432/bevel');
69
+ });
70
+ });
@@ -76,6 +76,21 @@ export interface CorePorts {
76
76
  * construction (participants are only iterated at erasure time).
77
77
  */
78
78
  erasureParticipants?: IErasureParticipant[];
79
+ /**
80
+ * Root folders THIS distribution reserves in the knowledge base, on top of
81
+ * core's `KnowledgeBase/` + `Groups/`. Core default: `[]`.
82
+ *
83
+ * A name here does two things: the seeder guarantees the folder exists on
84
+ * every branch it tops up, and — because the names are already reserved in
85
+ * `kb-layout.ts` — the file tree keeps rendering it as its own root instead
86
+ * of folding it into Knowledge as stray content.
87
+ *
88
+ * The folder is created as an empty `<dir>/.gitkeep`, written rather than
89
+ * copied, so reserving a root does not oblige a distribution to fork the
90
+ * packaged template. Ship a `KB_TEMPLATE_DIR` of your own when those folders
91
+ * should arrive carrying READMEs.
92
+ */
93
+ kbExtraRootDirs?: readonly string[];
79
94
  }
80
95
 
81
96
  /**
@@ -21,6 +21,7 @@ import { registerWorkflowTools } from '../modules/workflow/agent-tools/workflow.
21
21
  import { registerWorkspaceTools } from '../modules/workspace/workspace.tools.js';
22
22
  import { RECOVERY_BOT_EMAIL } from '../modules/workflow/recovery-bot.js';
23
23
  import { registerSkillsTools, createSkillsRoutes } from '../modules/skills/index.js';
24
+ import { createGroupsRoutes } from '../modules/groups/index.js';
24
25
  import type { SessionOntologyGate } from '../modules/workspace/session-ontology.gate.js';
25
26
  import {
26
27
  createSecretsVaultRoutes,
@@ -28,6 +29,8 @@ import {
28
29
  } from '../modules/secrets-vault/index.js';
29
30
  import { createAdminAccessRoutes } from '../modules/admin/admin-access.routes.js';
30
31
  import { createAccountRoutes } from '../modules/auth/account.routes.js';
32
+ import { createSetupRoutes } from '../modules/settings/setup.routes.js';
33
+ import { DEFAULT_BRANCH, PROTECTED_BRANCHES } from '@bevel-software/platform-shared';
31
34
  import { GIT_SHA } from '../version.js';
32
35
  import type { CoreServices } from './create-core-services.js';
33
36
 
@@ -147,6 +150,29 @@ export async function createCoreServer(
147
150
  res.json({ status: 'ok', sha: GIT_SHA, timestamp: Date.now() });
148
151
  });
149
152
 
153
+ /**
154
+ * The handful of facts the browser needs BEFORE it can render anything, and
155
+ * therefore before it can authenticate: the branch model.
156
+ *
157
+ * Unauthenticated on purpose. The login screen, the router and every module
158
+ * that reads `DEFAULT_BRANCH` are loaded before a session exists, so a gated
159
+ * endpoint could not answer in time. What it discloses is two branch names —
160
+ * which every change request and every URL already shows to anyone who does
161
+ * get in — and nothing about the repository they live in.
162
+ *
163
+ * This replaces baking the values into the frontend bundle at build time.
164
+ * One artifact now serves any deployment, and renaming a branch no longer
165
+ * means a rebuild.
166
+ */
167
+ app.get('/api/config', (_req, res) => {
168
+ res.json({
169
+ branchModel: {
170
+ defaultBranch: DEFAULT_BRANCH,
171
+ protectedBranches: [...PROTECTED_BRANCHES],
172
+ },
173
+ });
174
+ });
175
+
150
176
  // Overlay boot-time side effects (startup reconciles, periodic sweeps).
151
177
  await ext.onBoot?.(core);
152
178
 
@@ -225,12 +251,12 @@ export async function createCoreServer(
225
251
  const sessionOntologyGate = {
226
252
  service: core.sessionOntologyService,
227
253
  enabled: core.config.ontologySessionBlock,
228
- kbDirName: core.config.kbDirName,
254
+ kbDirName: core.kbDirName,
229
255
  recoveryBotEmail: RECOVERY_BOT_EMAIL,
230
256
  hooks: core.workflowService.hooks,
231
257
  };
232
258
  registerWorkflowTools(core.toolRegistry, toolsRouter, ta, th);
233
- registerWorkspaceTools(core.toolRegistry, toolsRouter, ta, th, core.spillStore, core.accessControl, core.config.kbDirName, sessionOntologyGate, core.routineWritePolicy, core.sessionSink);
259
+ registerWorkspaceTools(core.toolRegistry, toolsRouter, ta, th, core.spillStore, core.accessControl, core.kbDirName, sessionOntologyGate, core.routineWritePolicy, core.sessionSink);
234
260
  registerSkillsTools(core.toolRegistry, toolsRouter, ta, th, core.skillService);
235
261
  registerToolManualsTools(core.toolRegistry, toolsRouter, ta, th, core.toolManualService, {
236
262
  accessControl: core.accessControl,
@@ -274,8 +300,9 @@ export async function createCoreServer(
274
300
  core.workflowService,
275
301
  core.eventBus,
276
302
  core.accessControl,
277
- core.config.kbDirName,
303
+ core.kbDirName,
278
304
  core.creatorAccess,
305
+ core.adminAccess,
279
306
  ));
280
307
  // Workflow is the only branches / changes / change-request surface. The
281
308
  // former /git/*, /pr/*, /pr/:n/* routes are gone — every consumer goes
@@ -297,7 +324,7 @@ export async function createCoreServer(
297
324
  core.authService,
298
325
  core.workflowService,
299
326
  core.accessControl,
300
- core.config.kbDirName,
327
+ core.kbDirName,
301
328
  ));
302
329
  app.use('/api', core.authMiddleware, createAccessRoutes(
303
330
  core.accessControl,
@@ -306,9 +333,27 @@ export async function createCoreServer(
306
333
  core.workflowService,
307
334
  core.eventBus,
308
335
  core.db,
309
- core.config.kbDirName,
336
+ core.kbDirName,
337
+ ));
338
+ app.use(
339
+ '/api',
340
+ core.authMiddleware,
341
+ createSkillsRoutes(core.skillService, core.pendingSkillsService),
342
+ );
343
+ // Group enumeration + join requests. Browser-only (JWT), and fail-closed
344
+ // like every other read surface: groups the caller cannot access (member,
345
+ // manager, or discoverable via the access.md file's own read grant) are
346
+ // absent from the list. A join request is a plain change request.
347
+ app.use('/api', core.authMiddleware, createGroupsRoutes(
348
+ core.groupIndexService,
349
+ core.accessControl,
350
+ core.workflowService,
351
+ core.workspaceService,
352
+ core.joinRequestsService,
353
+ core.groupProvisionService,
354
+ core.kbDirName,
355
+ async (req) => (req.userId ? ((await core.authService.getUserById(req.userId)) ?? null) : null),
310
356
  ));
311
- app.use('/api', core.authMiddleware, createSkillsRoutes(core.skillService));
312
357
  // Admin-status resolver (CORE — see the note in admin-access.routes.ts;
313
358
  // the full admin router is an enterprise `ext.authed` extension).
314
359
  app.use('/api', core.authMiddleware, createAdminAccessRoutes(core.adminAccess));
@@ -319,6 +364,10 @@ export async function createCoreServer(
319
364
  core.adminAccess,
320
365
  core.accountErasureService,
321
366
  ));
367
+ // First-run setup. Mounted with the other authed routes but touching NO
368
+ // workspace — it has to work on a deployment that has no knowledge base yet,
369
+ // which is the whole reason it exists.
370
+ app.use('/api', core.authMiddleware, createSetupRoutes(core.settings, core.adminAccess));
322
371
  app.use('/api', core.authMiddleware, createToolManualsBrowserRoutes(core.toolManualService));
323
372
  app.use('/api', core.authMiddleware, createSecretsVaultRoutes(secretsVaultRoutesDeps));
324
373
  // The authed tail of the MCP OAuth flow: /connect calls these to describe