@bevel-software/platform-core-backend 0.4.1 → 0.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 (304) hide show
  1. package/dist/core/core-ports.d.ts +15 -0
  2. package/dist/core/core-ports.d.ts.map +1 -1
  3. package/dist/core/core-ports.js.map +1 -1
  4. package/dist/core/create-core-server.d.ts.map +1 -1
  5. package/dist/core/create-core-server.js +40 -6
  6. package/dist/core/create-core-server.js.map +1 -1
  7. package/dist/core/create-core-services.d.ts +17 -1
  8. package/dist/core/create-core-services.d.ts.map +1 -1
  9. package/dist/core/create-core-services.js +153 -29
  10. package/dist/core/create-core-services.js.map +1 -1
  11. package/dist/core-config.d.ts +70 -21
  12. package/dist/core-config.d.ts.map +1 -1
  13. package/dist/core-config.js +134 -58
  14. package/dist/core-config.js.map +1 -1
  15. package/dist/modules/access/access-control.interface.d.ts +6 -0
  16. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  17. package/dist/modules/access/access-control.service.d.ts +74 -5
  18. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  19. package/dist/modules/access/access-control.service.js +116 -15
  20. package/dist/modules/access/access-control.service.js.map +1 -1
  21. package/dist/modules/access/access-declarations.d.ts +93 -0
  22. package/dist/modules/access/access-declarations.d.ts.map +1 -0
  23. package/dist/modules/access/access-declarations.js +168 -0
  24. package/dist/modules/access/access-declarations.js.map +1 -0
  25. package/dist/modules/access/access-mutation.service.js +4 -4
  26. package/dist/modules/access/access-mutation.service.js.map +1 -1
  27. package/dist/modules/access/access-splice.d.ts +16 -1
  28. package/dist/modules/access/access-splice.d.ts.map +1 -1
  29. package/dist/modules/access/access-splice.js +40 -21
  30. package/dist/modules/access/access-splice.js.map +1 -1
  31. package/dist/modules/access/access.routes.d.ts.map +1 -1
  32. package/dist/modules/access/access.routes.js +51 -0
  33. package/dist/modules/access/access.routes.js.map +1 -1
  34. package/dist/modules/access/creator-access.d.ts +8 -0
  35. package/dist/modules/access/creator-access.d.ts.map +1 -1
  36. package/dist/modules/access/creator-access.js +21 -2
  37. package/dist/modules/access/creator-access.js.map +1 -1
  38. package/dist/modules/admin/admin-access.service.d.ts +1 -1
  39. package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
  40. package/dist/modules/admin/admin-access.service.js +3 -2
  41. package/dist/modules/admin/admin-access.service.js.map +1 -1
  42. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  43. package/dist/modules/auth/auth.routes.js +50 -1
  44. package/dist/modules/auth/auth.routes.js.map +1 -1
  45. package/dist/modules/auth/auth.service.d.ts +39 -10
  46. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  47. package/dist/modules/auth/auth.service.js +38 -16
  48. package/dist/modules/auth/auth.service.js.map +1 -1
  49. package/dist/modules/database/core-schema.d.ts +124 -0
  50. package/dist/modules/database/core-schema.d.ts.map +1 -1
  51. package/dist/modules/database/core-schema.js +45 -0
  52. package/dist/modules/database/core-schema.js.map +1 -1
  53. package/dist/modules/groups/group-provision.service.d.ts +130 -0
  54. package/dist/modules/groups/group-provision.service.d.ts.map +1 -0
  55. package/dist/modules/groups/group-provision.service.js +288 -0
  56. package/dist/modules/groups/group-provision.service.js.map +1 -0
  57. package/dist/modules/groups/groups.contract.d.ts +106 -0
  58. package/dist/modules/groups/groups.contract.d.ts.map +1 -0
  59. package/dist/modules/groups/groups.contract.js +36 -0
  60. package/dist/modules/groups/groups.contract.js.map +1 -0
  61. package/dist/modules/groups/groups.routes.d.ts +42 -0
  62. package/dist/modules/groups/groups.routes.d.ts.map +1 -0
  63. package/dist/modules/groups/groups.routes.js +379 -0
  64. package/dist/modules/groups/groups.routes.js.map +1 -0
  65. package/dist/modules/groups/groups.service.d.ts +60 -0
  66. package/dist/modules/groups/groups.service.d.ts.map +1 -0
  67. package/dist/modules/groups/groups.service.js +172 -0
  68. package/dist/modules/groups/groups.service.js.map +1 -0
  69. package/dist/modules/groups/index.d.ts +7 -0
  70. package/dist/modules/groups/index.d.ts.map +1 -0
  71. package/dist/modules/groups/index.js +6 -0
  72. package/dist/modules/groups/index.js.map +1 -0
  73. package/dist/modules/groups/join-proposals.d.ts +53 -0
  74. package/dist/modules/groups/join-proposals.d.ts.map +1 -0
  75. package/dist/modules/groups/join-proposals.js +67 -0
  76. package/dist/modules/groups/join-proposals.js.map +1 -0
  77. package/dist/modules/groups/join-requests.service.d.ts +81 -0
  78. package/dist/modules/groups/join-requests.service.d.ts.map +1 -0
  79. package/dist/modules/groups/join-requests.service.js +135 -0
  80. package/dist/modules/groups/join-requests.service.js.map +1 -0
  81. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +20 -0
  82. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  83. package/dist/modules/secrets-vault/secrets-vault.routes.js +59 -11
  84. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  85. package/dist/modules/settings/deployment-settings.service.d.ts +129 -0
  86. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -0
  87. package/dist/modules/settings/deployment-settings.service.js +368 -0
  88. package/dist/modules/settings/deployment-settings.service.js.map +1 -0
  89. package/dist/modules/settings/setup.routes.d.ts +49 -0
  90. package/dist/modules/settings/setup.routes.d.ts.map +1 -0
  91. package/dist/modules/settings/setup.routes.js +292 -0
  92. package/dist/modules/settings/setup.routes.js.map +1 -0
  93. package/dist/modules/skills/index.d.ts +2 -1
  94. package/dist/modules/skills/index.d.ts.map +1 -1
  95. package/dist/modules/skills/index.js +1 -0
  96. package/dist/modules/skills/index.js.map +1 -1
  97. package/dist/modules/skills/pending-skills.service.d.ts +40 -0
  98. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -0
  99. package/dist/modules/skills/pending-skills.service.js +147 -0
  100. package/dist/modules/skills/pending-skills.service.js.map +1 -0
  101. package/dist/modules/skills/skills.contract.d.ts +32 -3
  102. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  103. package/dist/modules/skills/skills.contract.js +1 -1
  104. package/dist/modules/skills/skills.routes.d.ts +3 -2
  105. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  106. package/dist/modules/skills/skills.routes.js +18 -1
  107. package/dist/modules/skills/skills.routes.js.map +1 -1
  108. package/dist/modules/skills/skills.service.d.ts +22 -0
  109. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  110. package/dist/modules/skills/skills.service.js +24 -12
  111. package/dist/modules/skills/skills.service.js.map +1 -1
  112. package/dist/modules/tool-manuals/index.d.ts +1 -1
  113. package/dist/modules/tool-manuals/index.d.ts.map +1 -1
  114. package/dist/modules/tool-manuals/index.js.map +1 -1
  115. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +47 -2
  116. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  117. package/dist/modules/tool-manuals/tool-manuals.contract.js +1 -1
  118. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +4 -0
  119. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  120. package/dist/modules/tool-manuals/tool-manuals.routes.js +23 -0
  121. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  122. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +3 -1
  123. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  124. package/dist/modules/tool-manuals/tool-manuals.service.js +84 -17
  125. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  126. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  127. package/dist/modules/tool-manuals/tool-manuals.tools.js +6 -2
  128. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  129. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  130. package/dist/modules/workflow/agent-tools/workflow.tools.js +4 -2
  131. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  132. package/dist/modules/workflow/event-bus.d.ts +11 -0
  133. package/dist/modules/workflow/event-bus.d.ts.map +1 -1
  134. package/dist/modules/workflow/event-bus.js +22 -0
  135. package/dist/modules/workflow/event-bus.js.map +1 -1
  136. package/dist/modules/workflow/file-lock.service.d.ts +9 -0
  137. package/dist/modules/workflow/file-lock.service.d.ts.map +1 -1
  138. package/dist/modules/workflow/file-lock.service.js +16 -0
  139. package/dist/modules/workflow/file-lock.service.js.map +1 -1
  140. package/dist/modules/workflow/git/branch-name.d.ts +1 -1
  141. package/dist/modules/workflow/git/branch-name.d.ts.map +1 -1
  142. package/dist/modules/workflow/git/branch-name.js +1 -1
  143. package/dist/modules/workflow/git/branch-name.js.map +1 -1
  144. package/dist/modules/workflow/git/git.service.d.ts +31 -2
  145. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  146. package/dist/modules/workflow/git/git.service.js +144 -52
  147. package/dist/modules/workflow/git/git.service.js.map +1 -1
  148. package/dist/modules/workflow/git/pull-request.service.d.ts +12 -6
  149. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  150. package/dist/modules/workflow/git/pull-request.service.js +19 -9
  151. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  152. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  153. package/dist/modules/workflow/review-workflow/review-workflow.service.js +12 -3
  154. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  155. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  156. package/dist/modules/workflow/workflow.routes.js +95 -14
  157. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  158. package/dist/modules/workflow/workflow.service.d.ts +92 -3
  159. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  160. package/dist/modules/workflow/workflow.service.js +381 -108
  161. package/dist/modules/workflow/workflow.service.js.map +1 -1
  162. package/dist/modules/workspace/bevel-ignore.d.ts +5 -0
  163. package/dist/modules/workspace/bevel-ignore.d.ts.map +1 -1
  164. package/dist/modules/workspace/bevel-ignore.js +5 -1
  165. package/dist/modules/workspace/bevel-ignore.js.map +1 -1
  166. package/dist/modules/workspace/kb-seed.interface.d.ts +1 -1
  167. package/dist/modules/workspace/kb-seed.service.d.ts +60 -5
  168. package/dist/modules/workspace/kb-seed.service.d.ts.map +1 -1
  169. package/dist/modules/workspace/kb-seed.service.js +228 -51
  170. package/dist/modules/workspace/kb-seed.service.js.map +1 -1
  171. package/dist/modules/workspace/workspace.routes.d.ts +2 -1
  172. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  173. package/dist/modules/workspace/workspace.routes.js +51 -18
  174. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  175. package/dist/modules/workspace/workspace.service.d.ts +16 -4
  176. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  177. package/dist/modules/workspace/workspace.service.js +36 -9
  178. package/dist/modules/workspace/workspace.service.js.map +1 -1
  179. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  180. package/dist/modules/workspace/workspace.tools.js +8 -3
  181. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  182. package/dist/shared/kb-layout.test.js +2 -2
  183. package/dist/test-setup.d.ts +2 -0
  184. package/dist/test-setup.d.ts.map +1 -0
  185. package/dist/test-setup.js +18 -0
  186. package/dist/test-setup.js.map +1 -0
  187. package/kb-template/.bevelignore +6 -2
  188. package/kb-template/AGENTS.md +272 -0
  189. package/kb-template/KnowledgeBase/How to get started.md +65 -0
  190. package/kb-template/access.md +36 -36
  191. package/migrations/0002_user_onboarding_done.sql +1 -0
  192. package/migrations/0003_deployment_settings.sql +9 -0
  193. package/migrations/meta/0001_snapshot.json +1486 -0
  194. package/migrations/meta/0002_snapshot.json +1493 -0
  195. package/migrations/meta/0003_snapshot.json +1552 -0
  196. package/migrations/meta/_journal.json +15 -1
  197. package/package.json +3 -3
  198. package/src/__tests__/branch-model.test.ts +79 -0
  199. package/src/__tests__/core-config.admin.test.ts +114 -0
  200. package/src/__tests__/database-url.test.ts +70 -0
  201. package/src/core/core-ports.ts +15 -0
  202. package/src/core/create-core-server.ts +55 -6
  203. package/src/core/create-core-services.ts +201 -36
  204. package/src/core-config.ts +139 -59
  205. package/src/modules/access/__tests__/access-declarations.test.ts +195 -0
  206. package/src/modules/access/__tests__/access-md-format.test.ts +191 -0
  207. package/src/modules/access/__tests__/access-splice.test.ts +21 -0
  208. package/src/modules/access/__tests__/access.routes.overrides.test.ts +214 -0
  209. package/src/modules/access/access-control.interface.ts +11 -0
  210. package/src/modules/access/access-control.service.ts +113 -15
  211. package/src/modules/access/access-declarations.ts +251 -0
  212. package/src/modules/access/access-mutation.service.ts +4 -4
  213. package/src/modules/access/access-splice.ts +64 -21
  214. package/src/modules/access/access.routes.ts +61 -0
  215. package/src/modules/access/creator-access.ts +22 -2
  216. package/src/modules/admin/admin-access.service.ts +4 -3
  217. package/src/modules/auth/__tests__/auth.service.test.ts +57 -0
  218. package/src/modules/auth/__tests__/onboarding-done.route.test.ts +147 -0
  219. package/src/modules/auth/auth.routes.ts +50 -1
  220. package/src/modules/auth/auth.service.ts +55 -17
  221. package/src/modules/database/core-schema.ts +46 -0
  222. package/src/modules/groups/__tests__/group-index.service.test.ts +225 -0
  223. package/src/modules/groups/__tests__/group-provision.service.test.ts +247 -0
  224. package/src/modules/groups/__tests__/groups.routes.test.ts +493 -0
  225. package/src/modules/groups/__tests__/join-proposals.test.ts +101 -0
  226. package/src/modules/groups/__tests__/join-requests.service.test.ts +175 -0
  227. package/src/modules/groups/group-provision.service.ts +328 -0
  228. package/src/modules/groups/groups.contract.ts +107 -0
  229. package/src/modules/groups/groups.routes.ts +433 -0
  230. package/src/modules/groups/groups.service.ts +193 -0
  231. package/src/modules/groups/index.ts +17 -0
  232. package/src/modules/groups/join-proposals.ts +114 -0
  233. package/src/modules/groups/join-requests.service.ts +185 -0
  234. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +283 -0
  235. package/src/modules/secrets-vault/secrets-vault.routes.ts +68 -12
  236. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +256 -0
  237. package/src/modules/settings/__tests__/setup.routes.test.ts +201 -0
  238. package/src/modules/settings/deployment-settings.service.ts +419 -0
  239. package/src/modules/settings/setup.routes.ts +317 -0
  240. package/src/modules/skills/__tests__/pending-skills.service.test.ts +240 -0
  241. package/src/modules/skills/__tests__/skills.service.test.ts +13 -13
  242. package/src/modules/skills/index.ts +3 -0
  243. package/src/modules/skills/pending-skills.service.ts +168 -0
  244. package/src/modules/skills/skills.contract.ts +34 -3
  245. package/src/modules/skills/skills.routes.ts +23 -2
  246. package/src/modules/skills/skills.service.ts +24 -16
  247. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +262 -0
  248. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -2
  249. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +61 -23
  250. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +156 -156
  251. package/src/modules/tool-manuals/index.ts +2 -0
  252. package/src/modules/tool-manuals/tool-manuals.contract.ts +50 -2
  253. package/src/modules/tool-manuals/tool-manuals.routes.ts +21 -0
  254. package/src/modules/tool-manuals/tool-manuals.service.ts +86 -17
  255. package/src/modules/tool-manuals/tool-manuals.tools.ts +206 -202
  256. package/src/modules/workflow/__tests__/event-bus.test.ts +35 -0
  257. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +251 -5
  258. package/src/modules/workflow/agent-tools/workflow.tools.ts +4 -2
  259. package/src/modules/workflow/event-bus.ts +25 -0
  260. package/src/modules/workflow/file-lock.service.ts +17 -0
  261. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +9 -49
  262. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +101 -0
  263. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +219 -211
  264. package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +132 -0
  265. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +4 -2
  266. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +275 -272
  267. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +5 -0
  268. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-cancel.test.ts +44 -0
  269. package/src/modules/workflow/git/branch-name.ts +5 -1
  270. package/src/modules/workflow/git/git.service.ts +159 -62
  271. package/src/modules/workflow/git/pull-request.service.ts +22 -11
  272. package/src/modules/workflow/review-workflow/__tests__/cancel-pr.test.ts +4 -1
  273. package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
  274. package/src/modules/workflow/workflow.routes.ts +91 -13
  275. package/src/modules/workflow/workflow.service.ts +353 -13
  276. package/src/modules/workspace/__tests__/kb-seed.service.test.ts +255 -24
  277. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +236 -233
  278. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +179 -176
  279. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +320 -303
  280. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +337 -276
  281. package/src/modules/workspace/__tests__/workspace.tools.test.ts +14 -6
  282. package/src/modules/workspace/bevel-ignore.ts +66 -62
  283. package/src/modules/workspace/kb-seed.interface.ts +1 -1
  284. package/src/modules/workspace/kb-seed.service.ts +247 -53
  285. package/src/modules/workspace/workspace.routes.ts +54 -18
  286. package/src/modules/workspace/workspace.service.ts +45 -7
  287. package/src/modules/workspace/workspace.tools.ts +922 -917
  288. package/src/shared/__tests__/frontmatter-id.test.ts +53 -0
  289. package/src/shared/__tests__/join-request.test.ts +96 -0
  290. package/src/shared/__tests__/kb-layout.group.test.ts +45 -0
  291. package/src/shared/kb-layout.test.ts +2 -2
  292. package/src/test-setup.ts +18 -0
  293. package/kb-template/Agents/README.md +0 -38
  294. package/kb-template/CLAUDE.md +0 -419
  295. package/kb-template/Data/.gitkeep +0 -0
  296. package/kb-template/Data/README.md +0 -39
  297. package/kb-template/KnowledgeBase/README.md +0 -37
  298. package/kb-template/Pipelines/.gitkeep +0 -0
  299. package/kb-template/Pipelines/README.md +0 -38
  300. package/kb-template/Skills/.gitkeep +0 -0
  301. package/kb-template/Skills/README.md +0 -32
  302. package/kb-template/Tools/.gitkeep +0 -0
  303. package/kb-template/Tools/README.md +0 -42
  304. /package/kb-template/{Agents → Groups}/.gitkeep +0 -0
@@ -1,6 +1,12 @@
1
1
  import type { AuthProviderPlugin } from '../modules/auth/auth.routes.js';
2
2
  import type { AuthUser } from '@bevel-software/platform-shared';
3
- import { DEFAULT_BRANCH, PROTECTED_BRANCHES, SKILLS_DIR, TOOLS_DIR } from '@bevel-software/platform-shared';
3
+ import {
4
+ DEFAULT_BRANCH,
5
+ configureBranchModel,
6
+ validateBranchModel,
7
+ PROTECTED_BRANCHES,
8
+ GROUPS_DIR,
9
+ } from '@bevel-software/platform-shared';
4
10
  import { CoreConfig } from '../core-config.js';
5
11
  import { getDb, type Database } from '../modules/database/connection.js';
6
12
  import { runCoreMigrations } from '../modules/database/migrate.js';
@@ -8,6 +14,15 @@ import { coreMigrationsDir } from '../assets.js';
8
14
  import { WorkspaceService } from '../modules/workspace/workspace.service.js';
9
15
  import { RoutineWritePolicyService } from '../modules/workspace/routine-write-policy.js';
10
16
  import { KbSeedService } from '../modules/workspace/kb-seed.service.js';
17
+ import { DeploymentSettingsService } from '../modules/settings/deployment-settings.service.js';
18
+
19
+ /** `a.com, b.com` → `['a.com','b.com']`, tolerating a leading `@` or `.`. */
20
+ function parseDomainList(raw: string): string[] {
21
+ return raw
22
+ .split(/[\s,]+/)
23
+ .map((d) => d.trim().toLowerCase().replace(/^[@.]+/, ''))
24
+ .filter((d) => d.length > 0);
25
+ }
11
26
  import { SpillStore } from '../modules/workspace/spill-store.js';
12
27
  import { UuidSessionSink, type ISessionSink } from '../modules/workspace/session-sink.js';
13
28
  import { AuthService } from '../modules/auth/auth.service.js';
@@ -16,8 +31,9 @@ import { OidcAuthProvider } from '../modules/auth/oidc-auth-provider.js';
16
31
  import { createAuthMiddleware } from '../modules/auth/auth.middleware.js';
17
32
  import { AccessControlService } from '../modules/access/access-control.service.js';
18
33
  import { CreatorAccessService } from '../modules/access/creator-access.js';
19
- import { SkillService } from '../modules/skills/index.js';
34
+ import { PendingSkillsService, SkillService } from '../modules/skills/index.js';
20
35
  import { ToolManualService } from '../modules/tool-manuals/index.js';
36
+ import { GroupIndexService, GroupProvisionService, JoinRequestsService } from '../modules/groups/index.js';
21
37
  import {
22
38
  DbSecretsVaultService,
23
39
  McpOAuthDiscoveryService,
@@ -84,7 +100,11 @@ export interface CoreServices {
84
100
  sessionOntologyService: SessionOntologyService;
85
101
  routineWritePolicy: RoutineWritePolicyService;
86
102
  skillService: SkillService;
103
+ pendingSkillsService: PendingSkillsService;
87
104
  toolManualService: ToolManualService;
105
+ groupIndexService: GroupIndexService;
106
+ groupProvisionService: GroupProvisionService;
107
+ joinRequestsService: JoinRequestsService;
88
108
  authService: AuthService;
89
109
  authMiddleware: ReturnType<typeof createAuthMiddleware>;
90
110
  accountErasureService: AccountErasureService;
@@ -100,6 +120,16 @@ export interface CoreServices {
100
120
  pendingCommitsWorker: PendingCommitsWorker;
101
121
  recoveryBot: AuthUser;
102
122
  adminAccess: AdminAccessService;
123
+ /** Deployment settings, env-first — the KB remote resolves through these. */
124
+ settings: DeploymentSettingsService;
125
+ /**
126
+ * The knowledge-base directory name IN EFFECT — environment first, then the
127
+ * stored setting, then the default. Published here because `config.kbDirName`
128
+ * is only the environment's half of that answer: reading it directly gives
129
+ * the empty string on any deployment configured through the setup screen, and
130
+ * every path built from it would be wrong.
131
+ */
132
+ kbDirName: string;
103
133
  secretsVaultService: DbSecretsVaultService;
104
134
  externalApiKeyService: ExternalApiKeyService;
105
135
  internalTokenService: InternalTokenService;
@@ -139,33 +169,77 @@ export async function createCoreServices(
139
169
  // `migrations/` folder, tracked in `__drizzle_migrations_core`. An
140
170
  // enterprise overlay runs its own history AFTER this (see migrate.ts).
141
171
  await runCoreMigrations(db, coreMigrationsDir());
172
+
173
+ // Deployment settings come next, before ANY service is built: the KB remote
174
+ // is resolved through them, and a fresh install has none of it in the
175
+ // environment. `load()` is env-first, so an existing deployment sees exactly
176
+ // what it saw before — a stored row only answers where a variable is silent.
177
+ const settings = new DeploymentSettingsService(db, config.secretsEncKey);
178
+ await settings.load();
179
+
180
+ /**
181
+ * The branch model, before anything reads it. It used to be applied at import
182
+ * time from the environment, which is what forced the frontend to bake it
183
+ * into its bundle; it now comes through the settings store like the rest of
184
+ * the deployment's configuration (environment first, as always).
185
+ *
186
+ * UNCONFIGURED IS ALLOWED, and that is the point: a fresh deployment has no
187
+ * branch model, and refusing to boot would take away the setup screen where
188
+ * one gets entered. What reads it before then is the setup path itself, which
189
+ * does not need it — the bootstrap admin is recognised without a workspace.
190
+ * `isComplete` keeps the rest of the app behind the gate until it is set, and
191
+ * the setting is restart-to-apply because services take the value at
192
+ * construction.
193
+ */
194
+ const branchModel = {
195
+ defaultBranch: settings.resolve('defaultBranch'),
196
+ protectedBranches: settings.resolve('protectedBranches'),
197
+ };
198
+ if (!validateBranchModel(branchModel)) configureBranchModel(branchModel);
199
+ // A token supplied through the setup screen has to reach the credential
200
+ // helper, which reads `$GITHUB_TOKEN` at call time.
201
+ settings.syncGitTokenEnv();
202
+
203
+ // `kbDirName` is read ONCE and threaded into a dozen services as a plain
204
+ // string, which is why changing it needs a restart (the setting says so).
205
+ // The remote URL and username are read per-operation instead, so an admin
206
+ // finishing setup can clone immediately without bouncing the process.
207
+ const kbDirName = settings.resolve('kbDirName') || 'knowledge-base';
142
208
  const workspaceService = new WorkspaceService(
143
209
  config.workspacesRoot,
144
- config.kbRepoUrl,
145
- config.kbDirName,
146
- config.gitUsername,
210
+ () => settings.resolve('kbRepoUrl'),
211
+ kbDirName,
212
+ () => settings.resolve('gitUsername') || 'x-access-token',
147
213
  );
148
214
  // Seed (or top-up) the KB remote before the first clone so the rest of the app
149
215
  // can keep assuming the remote already carries the protected branches + base
150
216
  // scaffolding — a fresh or partially-populated remote no longer needs to have
151
217
  // been forked from the standalone template.
152
218
  const kbSeedService = new KbSeedService(
153
- config.kbRepoUrl,
219
+ () => settings.resolve('kbRepoUrl'),
154
220
  config.kbTemplateDir,
155
- [...PROTECTED_BRANCHES],
156
- DEFAULT_BRANCH,
157
- config.seedAdminEmails,
158
- config.gitUsername,
221
+ // Getters, not values: the branch model can arrive from the setup screen
222
+ // after this object exists, and seeding is the first thing that needs it.
223
+ () => [...PROTECTED_BRANCHES],
224
+ () => DEFAULT_BRANCH,
225
+ // The deployment owner is the initial Admin of a freshly seeded KB — the
226
+ // same answer `SEED_ADMIN_EMAILS` used to ask for a second time.
227
+ [config.adminEmail],
228
+ () => settings.resolve('gitUsername') || 'x-access-token',
229
+ // Root folders this distribution reserves on top of core's two. A plain
230
+ // value, not a getter: these are named in the composition root rather than
231
+ // collected on the setup screen, so there is nothing to re-read.
232
+ ports.kbExtraRootDirs ?? [],
159
233
  );
160
234
  workspaceService.setSeedService(kbSeedService);
161
235
  // Shared, workspace-independent store for oversized `call_tool_chain` results,
162
236
  // read back via `read_file`. Sibling of `workspacesRoot`, never committed.
163
237
  const spillStore = new SpillStore(config.spillRoot);
164
- const accessControl = new AccessControlService(workspaceService, config.kbDirName);
238
+ const accessControl = new AccessControlService(workspaceService, kbDirName);
165
239
  // Creator read-grant on creation: read is default-deny, so every surface
166
240
  // that creates KB files/folders (human routes, agent tools, upload apply)
167
241
  // consults this planner to keep creations visible to their creator.
168
- const creatorAccess = new CreatorAccessService(workspaceService, accessControl, config.kbDirName);
242
+ const creatorAccess = new CreatorAccessService(workspaceService, accessControl, kbDirName);
169
243
 
170
244
  // Ontology-session boundary: records each agent run's touched ontologies and
171
245
  // blocks writes once a run has crossed ontologies. Postgres-backed so the
@@ -177,15 +251,33 @@ export async function createCoreServices(
177
251
  // one run, unlike the Postgres-backed ontology touched-set above.
178
252
  const routineWritePolicy = new RoutineWritePolicyService();
179
253
  // Skills: discovered from the default-branch workspace only (global catalog).
180
- const skillService = new SkillService(workspaceService, accessControl, config.kbDirName);
181
- // Tool manuals: user-authored `*.tool` files under `Tools/` in the default
254
+ const skillService = new SkillService(workspaceService, accessControl, kbDirName);
255
+ // Tool manuals: user-authored `*.tool` files under `Groups/` in the default
182
256
  // branch — access-controlled like Skills, served to external agents via
183
257
  // `GET /api/agent/all-tools` and registered on the MCP proxy's UTCP client.
184
- const toolManualService = new ToolManualService(workspaceService, accessControl, config.kbDirName);
258
+ const toolManualService = new ToolManualService(workspaceService, accessControl, kbDirName);
259
+ // Groups: the folders under `Groups/` that carry a
260
+ // team's skills AND the tools they need. Enumerated for EVERY authenticated
261
+ // caller — a group they cannot read still exists for them, as a locked one —
262
+ // with the counts read off the two catalogs above rather than a second scan.
263
+ const groupIndexService = new GroupIndexService(
264
+ workspaceService,
265
+ accessControl,
266
+ skillService,
267
+ toolManualService,
268
+ kbDirName,
269
+ );
185
270
  // Auth service — resolves identities for login, PR author attribution, and
186
271
  // access lookups. (Change requests now store the author email directly, so
187
272
  // PullRequestService no longer depends on it for attribution.)
188
- const authService = new AuthService(db, config);
273
+ // The allow-list is resolved, not read off the environment: it is settable
274
+ // from the setup screen alongside the SSO configuration it guards.
275
+ const authService = new AuthService(db, {
276
+ jwtSecret: config.jwtSecret,
277
+ adminEmail: config.adminEmail,
278
+ adminPassword: config.adminPassword,
279
+ allowedEmailDomains: parseDomainList(settings.resolve('allowedEmailDomains')),
280
+ });
189
281
  const authMiddleware = createAuthMiddleware(authService);
190
282
 
191
283
  // Shared mutex so git and diff operations on the same workspace serialize
@@ -201,7 +293,7 @@ export async function createCoreServices(
201
293
  const gitService = new GitService(
202
294
  workspaceService,
203
295
  workflowHooks,
204
- config.kbDirName,
296
+ kbDirName,
205
297
  workspaceMutex,
206
298
  accessControl,
207
299
  );
@@ -219,7 +311,7 @@ export async function createCoreServices(
219
311
  workspaceMutex,
220
312
  config.workspacesRoot,
221
313
  config.backupsRoot,
222
- config.kbDirName,
314
+ kbDirName,
223
315
  );
224
316
  // Late-bind the diff service into WorkspaceService so file writes/moves
225
317
  // trigger backup updates. (GitService used to take a diffService too — for
@@ -246,10 +338,15 @@ export async function createCoreServices(
246
338
  // worker drains the queue out of band so a git hiccup can't block the
247
339
  // user-visible save path. See `lock-decoupling-plan.md`.
248
340
  const pendingCommitsService = new PendingCommitsService(db);
249
- // Let the git status path tell an expected dirty tree (queued saves still
250
- // draining) from a genuinely orphaned one before it warns loudly.
251
- gitService.setPendingCommitsProbe((workspaceId) =>
252
- pendingCommitsService.hasAnyForWorkspace(workspaceId),
341
+ // Let the git status path tell an expected dirty tree from a genuinely
342
+ // orphaned one before it warns loudly. Two innocent explanations: queued
343
+ // saves still draining through the worker, and a lock currently HELD —
344
+ // deletes and saves mutate the disk while the lock is held but only queue
345
+ // their commit at release, so a status poll landing inside that window sees
346
+ // dirt with an empty queue (observed on multi-folder deletes).
347
+ gitService.setPendingCommitsProbe(async (workspaceId) =>
348
+ (await pendingCommitsService.hasAnyForWorkspace(workspaceId)) ||
349
+ (await fileLockService.hasAnyActive(workspaceId)),
253
350
  );
254
351
  // In-process event bus: every WorkflowService mutation method that
255
352
  // succeeds emits a typed event; the SSE route fans it to connected
@@ -270,7 +367,7 @@ export async function createCoreServices(
270
367
  accessControl,
271
368
  fileLockService,
272
369
  pendingCommitsService,
273
- config.kbDirName,
370
+ kbDirName,
274
371
  eventBus,
275
372
  fileChangeNotifier,
276
373
  // Exposed as `workflowService.hooks` — the SAME instance GitService and
@@ -279,6 +376,35 @@ export async function createCoreServices(
279
376
  workflowHooks,
280
377
  );
281
378
 
379
+ // Join requests: derived entirely from two copies of a group's `access.md`
380
+ // (the request's branch vs the default branch), so it holds no state — it
381
+ // only needs to read files at refs and to close a request whose proposals
382
+ // have all landed.
383
+ const joinRequestsService = new JoinRequestsService(workspaceService, workflowService);
384
+ // Group provisioning — the one privileged door that brings `Groups/<name>/`
385
+ // folders into existence (named groups and personal folders alike). Commits
386
+ // INLINE through the same pipeline the pending-commits worker uses, so the
387
+ // folder's rules are at HEAD before the endpoint answers.
388
+ const groupProvisionService = new GroupProvisionService(
389
+ workspaceService,
390
+ workflowService,
391
+ accessControl,
392
+ kbDirName,
393
+ eventBus,
394
+ );
395
+
396
+ // Pending skills: the other half of the catalog — skills that exist only on
397
+ // an open change request's branch. Built here rather than beside
398
+ // `skillService` because it needs the workflow service, which does not exist
399
+ // that early; it holds no state of its own, so where it is constructed is the
400
+ // only thing the ordering decides.
401
+ const pendingSkillsService = new PendingSkillsService(
402
+ workspaceService,
403
+ accessControl,
404
+ skillService,
405
+ workflowService,
406
+ );
407
+
282
408
  // Subscriber A — catalog freshness: a committed change drops the affected
283
409
  // caches immediately instead of waiting out their TTLs. The tool/skill
284
410
  // catalogs are global but read the DEFAULT branch only, so only
@@ -288,8 +414,33 @@ export async function createCoreServices(
288
414
  // caches are independent, so the split preserves behavior.)
289
415
  fileChangeNotifier.onFilesChanged(({ branch, paths }) => {
290
416
  if (branch !== DEFAULT_BRANCH) return;
291
- if (paths.some((p) => p.startsWith(`${config.kbDirName}/${TOOLS_DIR}/`))) toolManualService.invalidate();
292
- if (paths.some((p) => p.startsWith(`${config.kbDirName}/${SKILLS_DIR}/`))) skillService.invalidate();
417
+ // Skills, tools and the group index all live under `Groups/`, so one
418
+ // touch check drives all three caches. An access grant lands as a
419
+ // default-branch change to `Groups/<group>/access.md`, so this is also
420
+ // what makes a newly-granted group unlock within one round-trip instead
421
+ // of one TTL.
422
+ const touched = paths.some((p) => p.startsWith(`${kbDirName}/${GROUPS_DIR}/`));
423
+ if (touched) {
424
+ toolManualService.invalidate();
425
+ skillService.invalidate();
426
+ groupIndexService.invalidate();
427
+ }
428
+ });
429
+
430
+ // Subscriber B — WRITE-time freshness for the same three caches. The
431
+ // workspace routes emit `fs-tree-changed` the moment bytes hit a working
432
+ // tree; Subscriber A above fires only when the ASYNC commit lands. Between
433
+ // the two, "create a skill, reload the catalog" raced the commit pipeline
434
+ // and lost — the new skill's card stayed invisible until a refresh outlived
435
+ // the TTL. The catalogs scan the working tree anyway, so invalidating at
436
+ // write time makes the very next read see the file. No path filter: this
437
+ // event carries none, and a spurious drop only costs one re-scan.
438
+ eventBus.onEmit((event) => {
439
+ if (event.kind !== 'fs-tree-changed') return;
440
+ if (!('branch' in event) || event.branch !== DEFAULT_BRANCH) return;
441
+ toolManualService.invalidate();
442
+ skillService.invalidate();
443
+ groupIndexService.invalidate();
293
444
  });
294
445
 
295
446
  // Admin = `Admin` role in roles.yaml, resolved through the access model on the
@@ -297,10 +448,12 @@ export async function createCoreServices(
297
448
  const adminAccess = new AdminAccessService(
298
449
  accessControl,
299
450
  workspaceService,
300
- DEFAULT_BRANCH,
301
- // The env bootstrap admin administers accounts/roles even before the KB's
302
- // roles.yaml lists it (see AdminAccessService).
303
- config.adminEmail ? [config.adminEmail] : [],
451
+ () => DEFAULT_BRANCH,
452
+ // The deployment owner administers accounts/roles even before the KB's
453
+ // roles.yaml lists them, and whatever the sign-in method — this list is
454
+ // consulted before any roles.yaml lookup, so it holds for SSO too.
455
+ // Required by CoreConfig, hence no empty case.
456
+ [config.adminEmail],
304
457
  );
305
458
 
306
459
  // Secrets Vault: the per-user store of credentials (static API keys + OAuth
@@ -428,7 +581,7 @@ export async function createCoreServices(
428
581
  workspaceService,
429
582
  workflowService,
430
583
  events: eventBus,
431
- kbDirName: config.kbDirName,
584
+ kbDirName: kbDirName,
432
585
  creatorAccess,
433
586
  });
434
587
  const toolHandlerFactory = createToolHandlerFactory(resolveToolContext);
@@ -486,14 +639,20 @@ export async function createCoreServices(
486
639
  // the server is built, later). Core contributes the generic OIDC provider
487
640
  // when the env configures one.
488
641
  const authProviders = ports.authProviders ?? [];
489
- if (config.oidcIssuerUrl && config.oidcClientId && config.oidcClientSecret) {
642
+ // Resolved through settings, so an admin can configure SSO from the setup
643
+ // screen instead of the environment. Env still wins, so a deployment that
644
+ // sets these keeps behaving exactly as it did.
645
+ const oidcIssuerUrl = settings.resolve('oidcIssuerUrl');
646
+ const oidcClientId = settings.resolve('oidcClientId');
647
+ const oidcClientSecret = settings.resolve('oidcClientSecret');
648
+ if (oidcIssuerUrl && oidcClientId && oidcClientSecret) {
490
649
  authProviders.push(
491
650
  new OidcAuthProvider({
492
- issuerUrl: config.oidcIssuerUrl,
493
- clientId: config.oidcClientId,
494
- clientSecret: config.oidcClientSecret,
495
- scopes: config.oidcScopes,
496
- label: config.oidcProviderLabel,
651
+ issuerUrl: oidcIssuerUrl,
652
+ clientId: oidcClientId,
653
+ clientSecret: oidcClientSecret,
654
+ scopes: settings.resolve('oidcScopes') || 'openid profile email',
655
+ label: settings.resolve('oidcProviderLabel') || 'Single sign-on',
497
656
  publicBackendUrl: config.publicBackendUrl,
498
657
  publicFrontendUrl: config.publicFrontendUrl,
499
658
  cookieSecure: config.publicBackendUrl.startsWith('https'),
@@ -506,13 +665,19 @@ export async function createCoreServices(
506
665
  db,
507
666
  workspaceService,
508
667
  kbSeedService,
668
+ settings,
669
+ kbDirName,
509
670
  spillStore,
510
671
  accessControl,
511
672
  creatorAccess,
512
673
  sessionOntologyService,
513
674
  routineWritePolicy,
514
675
  skillService,
676
+ pendingSkillsService,
515
677
  toolManualService,
678
+ groupIndexService,
679
+ groupProvisionService,
680
+ joinRequestsService,
516
681
  authService,
517
682
  authMiddleware,
518
683
  accountErasureService,