@bevel-software/platform-core-backend 0.20.0 → 0.22.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 (666) hide show
  1. package/dist/core/catalog-cache-invalidation.d.ts +7 -2
  2. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
  3. package/dist/core/catalog-cache-invalidation.js +8 -9
  4. package/dist/core/catalog-cache-invalidation.js.map +1 -1
  5. package/dist/core/core-ports.d.ts +19 -0
  6. package/dist/core/core-ports.d.ts.map +1 -1
  7. package/dist/core/core-ports.js.map +1 -1
  8. package/dist/core/create-core-server.d.ts +2 -0
  9. package/dist/core/create-core-server.d.ts.map +1 -1
  10. package/dist/core/create-core-server.js +55 -107
  11. package/dist/core/create-core-server.js.map +1 -1
  12. package/dist/core/create-core-services.d.ts +41 -4
  13. package/dist/core/create-core-services.d.ts.map +1 -1
  14. package/dist/core/create-core-services.js +183 -82
  15. package/dist/core/create-core-services.js.map +1 -1
  16. package/dist/core/lifecycle.d.ts +74 -28
  17. package/dist/core/lifecycle.d.ts.map +1 -1
  18. package/dist/core/lifecycle.js +157 -20
  19. package/dist/core/lifecycle.js.map +1 -1
  20. package/dist/core/public-config.d.ts +5 -1
  21. package/dist/core/public-config.d.ts.map +1 -1
  22. package/dist/core/public-config.js +7 -5
  23. package/dist/core/public-config.js.map +1 -1
  24. package/dist/core-config.d.ts +95 -1
  25. package/dist/core-config.d.ts.map +1 -1
  26. package/dist/core-config.js +24 -8
  27. package/dist/core-config.js.map +1 -1
  28. package/dist/index.d.ts +5 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +6 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/modules/access/access-control.service.d.ts +18 -1
  33. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  34. package/dist/modules/access/access-control.service.js +19 -8
  35. package/dist/modules/access/access-control.service.js.map +1 -1
  36. package/dist/modules/access/access-requests.contract.d.ts +75 -0
  37. package/dist/modules/access/access-requests.contract.d.ts.map +1 -0
  38. package/dist/modules/access/access-requests.contract.js +20 -0
  39. package/dist/modules/access/access-requests.contract.js.map +1 -0
  40. package/dist/modules/access/access-requests.routes.d.ts +35 -0
  41. package/dist/modules/access/access-requests.routes.d.ts.map +1 -0
  42. package/dist/modules/access/access-requests.routes.js +237 -0
  43. package/dist/modules/access/access-requests.routes.js.map +1 -0
  44. package/dist/modules/access/access-requests.service.d.ts +123 -0
  45. package/dist/modules/access/access-requests.service.d.ts.map +1 -0
  46. package/dist/modules/access/access-requests.service.js +337 -0
  47. package/dist/modules/access/access-requests.service.js.map +1 -0
  48. package/dist/modules/access/access.routes.d.ts +2 -1
  49. package/dist/modules/access/access.routes.d.ts.map +1 -1
  50. package/dist/modules/access/access.routes.js +10 -10
  51. package/dist/modules/access/access.routes.js.map +1 -1
  52. package/dist/modules/access/change-read-gate.d.ts +6 -3
  53. package/dist/modules/access/change-read-gate.d.ts.map +1 -1
  54. package/dist/modules/access/change-read-gate.js +9 -6
  55. package/dist/modules/access/change-read-gate.js.map +1 -1
  56. package/dist/modules/access/creator-access.d.ts +4 -2
  57. package/dist/modules/access/creator-access.d.ts.map +1 -1
  58. package/dist/modules/access/creator-access.js +7 -4
  59. package/dist/modules/access/creator-access.js.map +1 -1
  60. package/dist/modules/access-model/access-grammar.d.ts +12 -0
  61. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  62. package/dist/modules/access-model/access-grammar.js +24 -0
  63. package/dist/modules/access-model/access-grammar.js.map +1 -1
  64. package/dist/modules/agent-instructions/read-preamble.d.ts +2 -1
  65. package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -1
  66. package/dist/modules/agent-instructions/read-preamble.js +6 -9
  67. package/dist/modules/agent-instructions/read-preamble.js.map +1 -1
  68. package/dist/modules/auth/account-admission.d.ts +40 -0
  69. package/dist/modules/auth/account-admission.d.ts.map +1 -0
  70. package/dist/modules/auth/account-admission.js +17 -0
  71. package/dist/modules/auth/account-admission.js.map +1 -0
  72. package/dist/modules/auth/account.routes.d.ts.map +1 -1
  73. package/dist/modules/auth/account.routes.js +4 -1
  74. package/dist/modules/auth/account.routes.js.map +1 -1
  75. package/dist/modules/auth/auth.service.d.ts +45 -2
  76. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  77. package/dist/modules/auth/auth.service.js +44 -3
  78. package/dist/modules/auth/auth.service.js.map +1 -1
  79. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
  80. package/dist/modules/auth/oidc-auth-provider.js +9 -0
  81. package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
  82. package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
  83. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
  84. package/dist/modules/connection-probe/connection-probe.service.js +10 -2
  85. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
  86. package/dist/modules/database/advisory-lock.d.ts +17 -0
  87. package/dist/modules/database/advisory-lock.d.ts.map +1 -1
  88. package/dist/modules/database/advisory-lock.js +24 -3
  89. package/dist/modules/database/advisory-lock.js.map +1 -1
  90. package/dist/modules/database/connection.d.ts +67 -2
  91. package/dist/modules/database/connection.d.ts.map +1 -1
  92. package/dist/modules/database/connection.js +118 -12
  93. package/dist/modules/database/connection.js.map +1 -1
  94. package/dist/modules/database/migrate.d.ts +1 -1
  95. package/dist/modules/database/migrate.d.ts.map +1 -1
  96. package/dist/modules/database/migrate.js +27 -3
  97. package/dist/modules/database/migrate.js.map +1 -1
  98. package/dist/modules/github-app/github-app.client.d.ts +101 -0
  99. package/dist/modules/github-app/github-app.client.d.ts.map +1 -0
  100. package/dist/modules/github-app/github-app.client.js +222 -0
  101. package/dist/modules/github-app/github-app.client.js.map +1 -0
  102. package/dist/modules/github-app/github-app.connection.d.ts +102 -0
  103. package/dist/modules/github-app/github-app.connection.d.ts.map +1 -0
  104. package/dist/modules/github-app/github-app.connection.js +185 -0
  105. package/dist/modules/github-app/github-app.connection.js.map +1 -0
  106. package/dist/modules/github-app/github-app.routes.d.ts +59 -0
  107. package/dist/modules/github-app/github-app.routes.d.ts.map +1 -0
  108. package/dist/modules/github-app/github-app.routes.js +323 -0
  109. package/dist/modules/github-app/github-app.routes.js.map +1 -0
  110. package/dist/modules/github-app/index.d.ts +4 -0
  111. package/dist/modules/github-app/index.d.ts.map +1 -0
  112. package/dist/modules/github-app/index.js +4 -0
  113. package/dist/modules/github-app/index.js.map +1 -0
  114. package/dist/modules/kb-fs/branch-name.d.ts +0 -1
  115. package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
  116. package/dist/modules/kb-fs/branch-name.js +3 -3
  117. package/dist/modules/kb-fs/branch-name.js.map +1 -1
  118. package/dist/modules/kb-fs/clone-config.d.ts +9 -8
  119. package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
  120. package/dist/modules/kb-fs/clone-config.js +14 -12
  121. package/dist/modules/kb-fs/clone-config.js.map +1 -1
  122. package/dist/modules/kb-fs/remote-url.d.ts +22 -0
  123. package/dist/modules/kb-fs/remote-url.d.ts.map +1 -0
  124. package/dist/modules/kb-fs/remote-url.js +35 -0
  125. package/dist/modules/kb-fs/remote-url.js.map +1 -0
  126. package/dist/modules/mcp/mcp.service.d.ts +7 -0
  127. package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
  128. package/dist/modules/mcp/mcp.service.js +4 -4
  129. package/dist/modules/mcp/mcp.service.js.map +1 -1
  130. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
  131. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
  132. package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
  133. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
  134. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
  135. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
  136. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
  137. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
  138. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
  139. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
  140. package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
  141. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
  142. package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
  143. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
  144. package/dist/modules/plugins/discovery/native.source.js +4 -2
  145. package/dist/modules/plugins/discovery/native.source.js.map +1 -1
  146. package/dist/modules/plugins/index.d.ts +1 -1
  147. package/dist/modules/plugins/index.d.ts.map +1 -1
  148. package/dist/modules/plugins/index.js +1 -1
  149. package/dist/modules/plugins/index.js.map +1 -1
  150. package/dist/modules/plugins/join-proposals.d.ts +17 -5
  151. package/dist/modules/plugins/join-proposals.d.ts.map +1 -1
  152. package/dist/modules/plugins/join-proposals.js +76 -22
  153. package/dist/modules/plugins/join-proposals.js.map +1 -1
  154. package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
  155. package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
  156. package/dist/modules/plugins/join-request-jobs.service.js +6 -6
  157. package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
  158. package/dist/modules/plugins/join-requests.service.d.ts +113 -18
  159. package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
  160. package/dist/modules/plugins/join-requests.service.js +178 -33
  161. package/dist/modules/plugins/join-requests.service.js.map +1 -1
  162. package/dist/modules/plugins/plugin-links.d.ts +65 -10
  163. package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
  164. package/dist/modules/plugins/plugin-links.js +136 -31
  165. package/dist/modules/plugins/plugin-links.js.map +1 -1
  166. package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
  167. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
  168. package/dist/modules/plugins/plugin-links.service.js +151 -21
  169. package/dist/modules/plugins/plugin-links.service.js.map +1 -1
  170. package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
  171. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  172. package/dist/modules/plugins/plugin-provision.service.js +33 -26
  173. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  174. package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
  175. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
  176. package/dist/modules/plugins/plugin-rename.service.js +10 -12
  177. package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
  178. package/dist/modules/plugins/plugins.routes.d.ts +3 -0
  179. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  180. package/dist/modules/plugins/plugins.routes.js +48 -17
  181. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  182. package/dist/modules/plugins/plugins.service.d.ts +4 -4
  183. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  184. package/dist/modules/plugins/plugins.service.js +7 -10
  185. package/dist/modules/plugins/plugins.service.js.map +1 -1
  186. package/dist/modules/plugins/teams.routes.d.ts +4 -1
  187. package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
  188. package/dist/modules/plugins/teams.routes.js +11 -9
  189. package/dist/modules/plugins/teams.routes.js.map +1 -1
  190. package/dist/modules/secrets-vault/index.d.ts +1 -1
  191. package/dist/modules/secrets-vault/index.d.ts.map +1 -1
  192. package/dist/modules/secrets-vault/index.js +1 -1
  193. package/dist/modules/secrets-vault/index.js.map +1 -1
  194. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +29 -11
  195. package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
  196. package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
  197. package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
  198. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
  199. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  200. package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
  201. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  202. package/dist/modules/settings/deployment-settings.service.d.ts +34 -9
  203. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  204. package/dist/modules/settings/deployment-settings.service.js +113 -26
  205. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  206. package/dist/modules/settings/managed-repository.d.ts +43 -0
  207. package/dist/modules/settings/managed-repository.d.ts.map +1 -0
  208. package/dist/modules/settings/managed-repository.js +60 -0
  209. package/dist/modules/settings/managed-repository.js.map +1 -0
  210. package/dist/modules/settings/repository-source.d.ts +128 -0
  211. package/dist/modules/settings/repository-source.d.ts.map +1 -0
  212. package/dist/modules/settings/repository-source.js +150 -0
  213. package/dist/modules/settings/repository-source.js.map +1 -0
  214. package/dist/modules/settings/setup.routes.d.ts +34 -4
  215. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  216. package/dist/modules/settings/setup.routes.js +198 -18
  217. package/dist/modules/settings/setup.routes.js.map +1 -1
  218. package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
  219. package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
  220. package/dist/modules/skills/allowed-tools-check.js +8 -8
  221. package/dist/modules/skills/allowed-tools-check.js.map +1 -1
  222. package/dist/modules/skills/pending-skills.service.d.ts +3 -1
  223. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  224. package/dist/modules/skills/pending-skills.service.js +9 -6
  225. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  226. package/dist/modules/skills/skill-access-requests.routes.d.ts +8 -9
  227. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
  228. package/dist/modules/skills/skill-access-requests.routes.js +26 -65
  229. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
  230. package/dist/modules/skills/skills.routes.d.ts +4 -1
  231. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  232. package/dist/modules/skills/skills.routes.js +4 -4
  233. package/dist/modules/skills/skills.routes.js.map +1 -1
  234. package/dist/modules/skills/skills.service.d.ts +4 -2
  235. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  236. package/dist/modules/skills/skills.service.js +13 -11
  237. package/dist/modules/skills/skills.service.js.map +1 -1
  238. package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
  239. package/dist/modules/tool-helpers/tool-context.js +15 -0
  240. package/dist/modules/tool-helpers/tool-context.js.map +1 -1
  241. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
  242. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  243. package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
  244. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  245. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
  246. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  247. package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
  248. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  249. package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
  250. package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
  251. package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
  252. package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
  253. package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
  254. package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
  255. package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
  256. package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
  257. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
  258. package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
  259. package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
  260. package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
  261. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
  262. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  263. package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
  264. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  265. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
  266. package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
  267. package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
  268. package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
  269. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
  270. package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
  271. package/dist/modules/workflow/agent-tools/workflow.tools.js +47 -11
  272. package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
  273. package/dist/modules/workflow/git/git.service.d.ts +14 -6
  274. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  275. package/dist/modules/workflow/git/git.service.js +40 -18
  276. package/dist/modules/workflow/git/git.service.js.map +1 -1
  277. package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
  278. package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
  279. package/dist/modules/workflow/git/node-git-runner.js +40 -5
  280. package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
  281. package/dist/modules/workflow/git/pull-request.service.d.ts +45 -0
  282. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  283. package/dist/modules/workflow/git/pull-request.service.js +99 -0
  284. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  285. package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
  286. package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
  287. package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
  288. package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
  289. package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
  290. package/dist/modules/workflow/session-ontology.service.js +6 -2
  291. package/dist/modules/workflow/session-ontology.service.js.map +1 -1
  292. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  293. package/dist/modules/workflow/workflow.routes.js +3 -1
  294. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  295. package/dist/modules/workflow/workflow.service.d.ts +42 -10
  296. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  297. package/dist/modules/workflow/workflow.service.js +181 -71
  298. package/dist/modules/workflow/workflow.service.js.map +1 -1
  299. package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
  300. package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
  301. package/dist/modules/workspace/session-ontology.gate.js +3 -3
  302. package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
  303. package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
  304. package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
  305. package/dist/modules/workspace/startup/kb-git.js +15 -13
  306. package/dist/modules/workspace/startup/kb-git.js.map +1 -1
  307. package/dist/modules/workspace/startup/kb-startup-runner.d.ts +70 -7
  308. package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
  309. package/dist/modules/workspace/startup/kb-startup-runner.js +183 -38
  310. package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
  311. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
  312. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
  313. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
  314. package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
  315. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
  316. package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
  317. package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
  318. package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
  319. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
  320. package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
  321. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
  322. package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
  323. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
  324. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
  325. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
  326. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
  327. package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
  328. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  329. package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
  330. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  331. package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
  332. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  333. package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
  334. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  335. package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
  336. package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
  337. package/dist/modules/workspace/startup/steps/template-source.js +10 -6
  338. package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
  339. package/dist/modules/workspace/workspace.routes.d.ts +2 -1
  340. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  341. package/dist/modules/workspace/workspace.routes.js +8 -7
  342. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  343. package/dist/modules/workspace/workspace.service.d.ts +18 -12
  344. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  345. package/dist/modules/workspace/workspace.service.js +49 -29
  346. package/dist/modules/workspace/workspace.service.js.map +1 -1
  347. package/dist/modules/workspace/workspace.tools.d.ts +2 -1
  348. package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
  349. package/dist/modules/workspace/workspace.tools.js +48 -20
  350. package/dist/modules/workspace/workspace.tools.js.map +1 -1
  351. package/dist/modules/workspace/write-denial.d.ts +3 -2
  352. package/dist/modules/workspace/write-denial.d.ts.map +1 -1
  353. package/dist/modules/workspace/write-denial.js +6 -5
  354. package/dist/modules/workspace/write-denial.js.map +1 -1
  355. package/dist/shared/domain-errors.d.ts +39 -0
  356. package/dist/shared/domain-errors.d.ts.map +1 -1
  357. package/dist/shared/domain-errors.js +55 -0
  358. package/dist/shared/domain-errors.js.map +1 -1
  359. package/dist/shared/git.contract.d.ts +62 -7
  360. package/dist/shared/git.contract.d.ts.map +1 -1
  361. package/dist/shared/git.contract.js +30 -8
  362. package/dist/shared/git.contract.js.map +1 -1
  363. package/dist/shared/kb-context.d.ts +76 -0
  364. package/dist/shared/kb-context.d.ts.map +1 -0
  365. package/dist/shared/kb-context.js +108 -0
  366. package/dist/shared/kb-context.js.map +1 -0
  367. package/dist/shared/kb-layout.d.ts +3 -2
  368. package/dist/shared/kb-layout.d.ts.map +1 -1
  369. package/dist/shared/kb-layout.js +4 -3
  370. package/dist/shared/kb-layout.js.map +1 -1
  371. package/dist/shared/kb-layout.test.js +35 -27
  372. package/dist/shared/kb-layout.test.js.map +1 -1
  373. package/dist/shared/pending-proposals.d.ts +4 -1
  374. package/dist/shared/pending-proposals.d.ts.map +1 -1
  375. package/dist/shared/pending-proposals.js +3 -4
  376. package/dist/shared/pending-proposals.js.map +1 -1
  377. package/dist/shared/redact-secret.d.ts +5 -5
  378. package/dist/shared/redact-secret.js +5 -5
  379. package/dist/tenancy/index.d.ts +13 -0
  380. package/dist/tenancy/index.d.ts.map +1 -0
  381. package/dist/tenancy/index.js +13 -0
  382. package/dist/tenancy/index.js.map +1 -0
  383. package/dist/tenancy/static-tenant-source.d.ts +98 -0
  384. package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
  385. package/dist/tenancy/static-tenant-source.js +220 -0
  386. package/dist/tenancy/static-tenant-source.js.map +1 -0
  387. package/dist/tenancy/tenant-host.d.ts +80 -0
  388. package/dist/tenancy/tenant-host.d.ts.map +1 -0
  389. package/dist/tenancy/tenant-host.js +217 -0
  390. package/dist/tenancy/tenant-host.js.map +1 -0
  391. package/dist/tenancy/tenant-runtime.d.ts +91 -0
  392. package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
  393. package/dist/tenancy/tenant-runtime.js +174 -0
  394. package/dist/tenancy/tenant-runtime.js.map +1 -0
  395. package/dist/tenancy/tenant-secrets.d.ts +24 -0
  396. package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
  397. package/dist/tenancy/tenant-secrets.js +30 -0
  398. package/dist/tenancy/tenant-secrets.js.map +1 -0
  399. package/dist/tenancy/tenant-source.contract.d.ts +45 -0
  400. package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
  401. package/dist/tenancy/tenant-source.contract.js +21 -0
  402. package/dist/tenancy/tenant-source.contract.js.map +1 -0
  403. package/migrations/0000_core_init.sql +14 -14
  404. package/migrations/0003_deployment_settings.sql +1 -1
  405. package/migrations/0005_github_facade.sql +1 -1
  406. package/migrations/0012_agent_audit.sql +6 -6
  407. package/package.json +3 -3
  408. package/src/__tests__/branch-model.test.ts +7 -6
  409. package/src/__tests__/kb-context.ts +30 -0
  410. package/src/__tests__/kb-layout-config.test.ts +35 -30
  411. package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
  412. package/src/core/__tests__/lifecycle.test.ts +78 -1
  413. package/src/core/__tests__/public-config.test.ts +5 -1
  414. package/src/core/catalog-cache-invalidation.ts +16 -11
  415. package/src/core/core-ports.ts +19 -0
  416. package/src/core/create-core-server.ts +64 -107
  417. package/src/core/create-core-services.ts +238 -96
  418. package/src/core/lifecycle.ts +214 -29
  419. package/src/core/public-config.ts +8 -5
  420. package/src/core-config.ts +107 -8
  421. package/src/index.ts +25 -2
  422. package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
  423. package/src/modules/access/__tests__/access-requests.recut.test.ts +134 -0
  424. package/src/modules/access/__tests__/access-requests.routes.test.ts +610 -0
  425. package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
  426. package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
  427. package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
  428. package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
  429. package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
  430. package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
  431. package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
  432. package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
  433. package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
  434. package/src/modules/access/__tests__/creator-access.test.ts +3 -2
  435. package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
  436. package/src/modules/access/access-control.service.ts +20 -6
  437. package/src/modules/access/access-requests.contract.ts +93 -0
  438. package/src/modules/access/access-requests.routes.ts +286 -0
  439. package/src/modules/access/access-requests.service.ts +420 -0
  440. package/src/modules/access/access.routes.ts +10 -13
  441. package/src/modules/access/change-read-gate.ts +10 -4
  442. package/src/modules/access/creator-access.ts +7 -2
  443. package/src/modules/access-model/access-grammar.ts +22 -0
  444. package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
  445. package/src/modules/agent-instructions/read-preamble.ts +10 -12
  446. package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
  447. package/src/modules/auth/__tests__/auth.service.test.ts +98 -0
  448. package/src/modules/auth/account-admission.ts +44 -0
  449. package/src/modules/auth/account.routes.ts +4 -1
  450. package/src/modules/auth/auth.service.ts +61 -2
  451. package/src/modules/auth/oidc-auth-provider.ts +9 -0
  452. package/src/modules/connection-probe/connection-probe.service.ts +7 -1
  453. package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
  454. package/src/modules/database/__tests__/connection.test.ts +98 -0
  455. package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
  456. package/src/modules/database/advisory-lock.ts +28 -4
  457. package/src/modules/database/connection.ts +143 -13
  458. package/src/modules/database/migrate.ts +48 -14
  459. package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
  460. package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
  461. package/src/modules/github-app/__tests__/github-app.test.ts +848 -0
  462. package/src/modules/github-app/github-app.client.ts +268 -0
  463. package/src/modules/github-app/github-app.connection.ts +204 -0
  464. package/src/modules/github-app/github-app.routes.ts +359 -0
  465. package/src/modules/github-app/index.ts +19 -0
  466. package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
  467. package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
  468. package/src/modules/kb-fs/__tests__/remote-url.test.ts +38 -0
  469. package/src/modules/kb-fs/branch-name.ts +3 -3
  470. package/src/modules/kb-fs/clone-config.ts +15 -12
  471. package/src/modules/kb-fs/remote-url.ts +35 -0
  472. package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
  473. package/src/modules/mcp/mcp.service.ts +11 -4
  474. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
  475. package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
  476. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
  477. package/src/modules/plugins/__tests__/join-proposals.test.ts +100 -19
  478. package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
  479. package/src/modules/plugins/__tests__/join-requests.service.test.ts +28 -11
  480. package/src/modules/plugins/__tests__/join-requests.settlement.test.ts +371 -0
  481. package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
  482. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
  483. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
  484. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
  485. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
  486. package/src/modules/plugins/__tests__/plugins.routes.test.ts +12 -3
  487. package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
  488. package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
  489. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
  490. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
  491. package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
  492. package/src/modules/plugins/discovery/native.source.ts +4 -1
  493. package/src/modules/plugins/index.ts +1 -1
  494. package/src/modules/plugins/join-proposals.ts +87 -19
  495. package/src/modules/plugins/join-request-jobs.service.ts +8 -7
  496. package/src/modules/plugins/join-requests.service.ts +203 -43
  497. package/src/modules/plugins/plugin-links.service.ts +192 -19
  498. package/src/modules/plugins/plugin-links.ts +147 -27
  499. package/src/modules/plugins/plugin-provision.service.ts +54 -27
  500. package/src/modules/plugins/plugin-rename.service.ts +10 -10
  501. package/src/modules/plugins/plugins.routes.ts +48 -17
  502. package/src/modules/plugins/plugins.service.ts +7 -11
  503. package/src/modules/plugins/teams.routes.ts +16 -8
  504. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
  505. package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
  506. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
  507. package/src/modules/secrets-vault/index.ts +2 -0
  508. package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
  509. package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
  510. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +24 -11
  511. package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
  512. package/src/modules/settings/__tests__/repository-source.test.ts +191 -0
  513. package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
  514. package/src/modules/settings/__tests__/setup.routes.git-mode.test.ts +353 -0
  515. package/src/modules/settings/__tests__/setup.routes.github-app.test.ts +282 -0
  516. package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
  517. package/src/modules/settings/__tests__/setup.routes.managed-phase.test.ts +233 -0
  518. package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
  519. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
  520. package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
  521. package/src/modules/settings/deployment-settings.service.ts +130 -23
  522. package/src/modules/settings/managed-repository.ts +68 -0
  523. package/src/modules/settings/repository-source.ts +197 -0
  524. package/src/modules/settings/setup.routes.ts +233 -19
  525. package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
  526. package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
  527. package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
  528. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +7 -2
  529. package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
  530. package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
  531. package/src/modules/skills/allowed-tools-check.ts +7 -6
  532. package/src/modules/skills/pending-skills.service.ts +9 -8
  533. package/src/modules/skills/skill-access-requests.routes.ts +29 -79
  534. package/src/modules/skills/skills.routes.ts +4 -3
  535. package/src/modules/skills/skills.service.ts +13 -9
  536. package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
  537. package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
  538. package/src/modules/tool-helpers/tool-context.ts +15 -0
  539. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
  540. package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
  541. package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
  542. package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
  543. package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
  544. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
  545. package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
  546. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
  547. package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
  548. package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
  549. package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
  550. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
  551. package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
  552. package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
  553. package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
  554. package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
  555. package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
  556. package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
  557. package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
  558. package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
  559. package/src/modules/workflow/__tests__/apply-failure.test.ts +11 -2
  560. package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
  561. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
  562. package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
  563. package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
  564. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
  565. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
  566. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
  567. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +187 -35
  568. package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
  569. package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
  570. package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
  571. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
  572. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
  573. package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +148 -41
  574. package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +143 -2
  575. package/src/modules/workflow/agent-tools/workflow.tools.ts +60 -12
  576. package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
  577. package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
  578. package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
  579. package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
  580. package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
  581. package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
  582. package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
  583. package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
  584. package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
  585. package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
  586. package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
  587. package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
  588. package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
  589. package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
  590. package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
  591. package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
  592. package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
  593. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
  594. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +130 -8
  595. package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
  596. package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
  597. package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
  598. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
  599. package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
  600. package/src/modules/workflow/git/__tests__/pull-request.service.getPrDetail.test.ts +145 -2
  601. package/src/modules/workflow/git/__tests__/pull-request.service.viewer-can-delete.test.ts +166 -0
  602. package/src/modules/workflow/git/git.service.ts +49 -17
  603. package/src/modules/workflow/git/node-git-runner.ts +43 -4
  604. package/src/modules/workflow/git/pull-request.service.ts +119 -0
  605. package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
  606. package/src/modules/workflow/session-ontology.service.ts +7 -2
  607. package/src/modules/workflow/workflow.routes.ts +3 -1
  608. package/src/modules/workflow/workflow.service.ts +201 -76
  609. package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
  610. package/src/modules/workspace/__tests__/git-internals.security.test.ts +10 -9
  611. package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
  612. package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
  613. package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
  614. package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
  615. package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
  616. package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
  617. package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
  618. package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
  619. package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
  620. package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
  621. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
  622. package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
  623. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
  624. package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
  625. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
  626. package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
  627. package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +45 -1
  628. package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
  629. package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +237 -10
  630. package/src/modules/workspace/__tests__/workspace.tools.test.ts +44 -11
  631. package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
  632. package/src/modules/workspace/session-ontology.gate.ts +6 -4
  633. package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
  634. package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +240 -21
  635. package/src/modules/workspace/startup/kb-git.ts +15 -22
  636. package/src/modules/workspace/startup/kb-startup-runner.ts +198 -45
  637. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
  638. package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
  639. package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
  640. package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
  641. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
  642. package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
  643. package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
  644. package/src/modules/workspace/startup/steps/template-source.ts +8 -5
  645. package/src/modules/workspace/workspace.routes.ts +8 -7
  646. package/src/modules/workspace/workspace.service.ts +47 -28
  647. package/src/modules/workspace/workspace.tools.ts +56 -20
  648. package/src/modules/workspace/write-denial.ts +7 -5
  649. package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
  650. package/src/shared/domain-errors.ts +58 -0
  651. package/src/shared/git.contract.ts +80 -8
  652. package/src/shared/kb-context.ts +130 -0
  653. package/src/shared/kb-layout.test.ts +37 -27
  654. package/src/shared/kb-layout.ts +4 -3
  655. package/src/shared/pending-proposals.ts +7 -4
  656. package/src/shared/redact-secret.ts +5 -5
  657. package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
  658. package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
  659. package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
  660. package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
  661. package/src/tenancy/index.ts +35 -0
  662. package/src/tenancy/static-tenant-source.ts +296 -0
  663. package/src/tenancy/tenant-host.ts +261 -0
  664. package/src/tenancy/tenant-runtime.ts +208 -0
  665. package/src/tenancy/tenant-secrets.ts +41 -0
  666. package/src/tenancy/tenant-source.contract.ts +59 -0
@@ -16,6 +16,7 @@ import { usersDbDouble } from './users-db-double.js';
16
16
  import { AccessControlService } from '../access-control.service.js';
17
17
  import { createAccessRoutes } from '../access.routes.js';
18
18
  import { branchForWorkspaceId, workspaceIdForBranch } from '../../../shared/workspace-id.js';
19
+ import { testKbContext } from '../../../__tests__/kb-context.js';
19
20
 
20
21
  /**
21
22
  * Manage access on a file that exists ONLY on a change request's branch.
@@ -136,7 +137,7 @@ describe('access routes on a proposal branch (real git)', () => {
136
137
  workflowService,
137
138
  { emit: () => {} } as unknown as WorkflowEventBus,
138
139
  usersDbDouble(),
139
- KB,
140
+ testKbContext({ kbDirName: KB }),
140
141
  ),
141
142
  );
142
143
  server = await new Promise<Server>((resolve) => {
@@ -11,6 +11,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
11
11
  import type { Database } from '../../database/connection.js';
12
12
  import { createAccessRoutes } from '../access.routes.js';
13
13
  import { WorkflowDomainError } from '../../../shared/domain-errors.js';
14
+ import { testKbContext } from '../../../__tests__/kb-context.js';
14
15
 
15
16
  /**
16
17
  * HTTP contract for `GET /access/prospective` — what the move confirmation
@@ -67,7 +68,7 @@ async function makeHarness(opts: { canRead?: boolean } = {}): Promise<Harness> {
67
68
  {} as unknown as WorkflowService,
68
69
  { emit: vi.fn() } as unknown as WorkflowEventBus,
69
70
  {} as unknown as Database,
70
- KB,
71
+ testKbContext({ kbDirName: KB }),
71
72
  ),
72
73
  );
73
74
 
@@ -10,6 +10,7 @@ import type { WorkflowService } from '../../workflow/workflow.service.js';
10
10
  import type { WorkflowEventBus } from '../../workflow/event-bus.js';
11
11
  import { createAccessRoutes } from '../access.routes.js';
12
12
  import { usersDbDouble } from './users-db-double.js';
13
+ import { testKbContext } from '../../../__tests__/kb-context.js';
13
14
 
14
15
  /**
15
16
  * HTTP-level contract tests for the revoke route's CLASSIFICATION + MODE logic —
@@ -133,7 +134,7 @@ async function makeHarness(opts: {
133
134
  (req as unknown as { userId: string }).userId = USER.id;
134
135
  next();
135
136
  });
136
- app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, KB));
137
+ app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB })));
137
138
 
138
139
  const server = await new Promise<Server>((resolve) => {
139
140
  const s = app.listen(0, () => resolve(s));
@@ -11,6 +11,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
11
11
  import { createAccessRoutes } from '../access.routes.js';
12
12
  import type { Database } from '../../database/connection.js';
13
13
  import { usersDbDouble } from './users-db-double.js';
14
+ import { testKbContext } from '../../../__tests__/kb-context.js';
14
15
 
15
16
  /**
16
17
  * A grant to an email with NO ACCOUNT says so, and stays allowed.
@@ -107,7 +108,7 @@ async function makeHarness(opts: {
107
108
  workflowService,
108
109
  { emit: vi.fn() } as unknown as WorkflowEventBus,
109
110
  opts.db ?? usersDbDouble(opts.accounts ?? []),
110
- KB,
111
+ testKbContext({ kbDirName: KB }),
111
112
  ),
112
113
  );
113
114
 
@@ -8,6 +8,8 @@ import { AccessControlService } from '../access-control.service.js';
8
8
  import { ChangeReadGate, isNewTopLevelFolderPath } from '../change-read-gate.js';
9
9
  import { AccessDeniedError } from '../../access-model/access-errors.js';
10
10
  import { SYNCED_GROUPS_YAML } from '../../access-model/group-files.js';
11
+ import { DEFAULT_KB_LAYOUT } from '@bevel-software/platform-shared';
12
+ import { testKbContext } from '../../../__tests__/kb-context.js';
11
13
 
12
14
  const KB = 'knowledge-base';
13
15
  const WS = 'ws-gate';
@@ -56,7 +58,7 @@ describe('ChangeReadGate', () => {
56
58
  await write(repo, 'roles.yaml', ROLES_YAML);
57
59
  const ws = stubWorkspaceService(workspaceDir);
58
60
  access = new AccessControlService(ws, KB, new NodeFs());
59
- gate = new ChangeReadGate(ws, access, KB, new NodeFs());
61
+ gate = new ChangeReadGate(ws, access, testKbContext({ kbDirName: KB }), new NodeFs());
60
62
  });
61
63
 
62
64
  afterEach(async () => {
@@ -255,18 +257,18 @@ describe('isNewTopLevelFolderPath', () => {
255
257
  const exists = async (p: string) => onDisk.has(p);
256
258
 
257
259
  it('is true for a path inside a folder directly under a creatable root that is not on disk', async () => {
258
- expect(await isNewTopLevelFolderPath('KnowledgeBase/New/a.md', 'file', exists)).toBe(true);
259
- expect(await isNewTopLevelFolderPath('Skills/fresh/SKILL.md', 'file', exists)).toBe(true);
260
- expect(await isNewTopLevelFolderPath('Plugins/team/deep/er/x', 'file', exists)).toBe(true);
261
- expect(await isNewTopLevelFolderPath('KnowledgeBase/New', 'dir', exists)).toBe(true);
260
+ expect(await isNewTopLevelFolderPath('KnowledgeBase/New/a.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(true);
261
+ expect(await isNewTopLevelFolderPath('Skills/fresh/SKILL.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(true);
262
+ expect(await isNewTopLevelFolderPath('Plugins/team/deep/er/x', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(true);
263
+ expect(await isNewTopLevelFolderPath('KnowledgeBase/New', 'dir', exists, DEFAULT_KB_LAYOUT)).toBe(true);
262
264
  });
263
265
 
264
266
  it('is false when the top-level folder exists, for a loose file at the root, and outside the three roots', async () => {
265
- expect(await isNewTopLevelFolderPath('KnowledgeBase/Existing/a.md', 'file', exists)).toBe(false);
266
- expect(await isNewTopLevelFolderPath('Skills/known', 'dir', exists)).toBe(false);
267
- expect(await isNewTopLevelFolderPath('KnowledgeBase/loose.md', 'file', exists)).toBe(false);
268
- expect(await isNewTopLevelFolderPath('KnowledgeBase', 'dir', exists)).toBe(false);
269
- expect(await isNewTopLevelFolderPath('Data/New/a.md', 'file', exists)).toBe(false);
270
- expect(await isNewTopLevelFolderPath('roles.yaml', 'file', exists)).toBe(false);
267
+ expect(await isNewTopLevelFolderPath('KnowledgeBase/Existing/a.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
268
+ expect(await isNewTopLevelFolderPath('Skills/known', 'dir', exists, DEFAULT_KB_LAYOUT)).toBe(false);
269
+ expect(await isNewTopLevelFolderPath('KnowledgeBase/loose.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
270
+ expect(await isNewTopLevelFolderPath('KnowledgeBase', 'dir', exists, DEFAULT_KB_LAYOUT)).toBe(false);
271
+ expect(await isNewTopLevelFolderPath('Data/New/a.md', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
272
+ expect(await isNewTopLevelFolderPath('roles.yaml', 'file', exists, DEFAULT_KB_LAYOUT)).toBe(false);
271
273
  });
272
274
  });
@@ -7,6 +7,7 @@ import os from 'node:os';
7
7
  import type { WorkspaceService } from '../../workspace/workspace.service.js';
8
8
  import { AccessControlService } from '../access-control.service.js';
9
9
  import { CreatorAccessService } from '../creator-access.js';
10
+ import { testKbContext } from '../../../__tests__/kb-context.js';
10
11
 
11
12
  const KB = 'knowledge-base';
12
13
  const WS = 'ws-creator';
@@ -61,7 +62,7 @@ describe('CreatorAccessService.planForCreate', () => {
61
62
  await fs.mkdir(path.join(repo, 'Plugins'), { recursive: true });
62
63
  await write(repo, 'roles.yaml', ROLES_YAML);
63
64
  const ws = stubWorkspaceService(workspaceDir);
64
- svc = new CreatorAccessService(ws, new AccessControlService(ws, KB, new NodeFs()), KB, new NodeFs());
65
+ svc = new CreatorAccessService(ws, new AccessControlService(ws, KB, new NodeFs()), testKbContext({ kbDirName: KB }), new NodeFs());
65
66
  });
66
67
 
67
68
  afterEach(async () => {
@@ -175,7 +176,7 @@ describe('CreatorAccessService.planForCreate', () => {
175
176
  },
176
177
  });
177
178
  const ws = stubWorkspaceService(path.join(root, WS));
178
- const withFailingDisk = new CreatorAccessService(ws, new AccessControlService(ws, KB, real), KB, failing);
179
+ const withFailingDisk = new CreatorAccessService(ws, new AccessControlService(ws, KB, real), testKbContext({ kbDirName: KB }), failing);
179
180
  expect(await withFailingDisk.planForCreate(WS, ALICE, `${KB}/KnowledgeBase/Projects`, 'dir')).toBeNull();
180
181
  expect(await withFailingDisk.planForCreate(WS, ALICE, `${KB}/KnowledgeBase/Projects/a.md`, 'file')).toBeNull();
181
182
  // The same creation on a disk that answers is planned, so it was the
@@ -15,6 +15,7 @@ import type { WorkflowEventBus } from '../../workflow/event-bus.js';
15
15
  import type { FileTreeEntry } from '@bevel-software/platform-shared';
16
16
  import { createAccessRoutes } from '../access.routes.js';
17
17
  import { usersDbDouble } from './users-db-double.js';
18
+ import { testKbContext } from '../../../__tests__/kb-context.js';
18
19
 
19
20
  /**
20
21
  * HTTP-level contract tests for the /api/access/roles routes: the admin gate
@@ -137,7 +138,7 @@ async function makeHarness(opts: { isAdmin?: boolean } = {}): Promise<{ server:
137
138
  (req as unknown as { userId: string }).userId = ADMIN.id;
138
139
  next();
139
140
  });
140
- app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, KB));
141
+ app.use('/api', createAccessRoutes(accessControl, workspaceService, authService, workflowService, eventBus, db, testKbContext({ kbDirName: KB })));
141
142
 
142
143
  const server = await new Promise<Server>((resolve) => {
143
144
  const s = app.listen(0, () => resolve(s));
@@ -22,10 +22,14 @@ import type {
22
22
  ProspectiveHolders,
23
23
  ResolvedPrincipal,
24
24
  } from './access-control.interface.js';
25
- import { PLUGINS_DIR, PLUGIN_MANIFEST_FILE, isPersonalPluginDir,
25
+ import {
26
+ DEFAULT_KB_LAYOUT,
27
+ PLUGIN_MANIFEST_FILE,
28
+ isPersonalPluginDir,
26
29
  pluginIdentityOf,
27
30
  platformRestoreDestination,
28
31
  } from '@bevel-software/platform-shared';
32
+ import type { KbContext } from '../../shared/kb-context.js';
29
33
  import { AccessConfigError, AccessUnreadableError } from '../access-model/access-errors.js';
30
34
  import { WorkflowDomainError } from '../../shared/domain-errors.js';
31
35
  import { synthesizePluginPrincipals } from '../access-model/plugin-principals.js';
@@ -291,13 +295,14 @@ interface AccessScope {
291
295
  */
292
296
  function accessFileListener(
293
297
  repoDir: string,
298
+ pluginsDir: string,
294
299
  out: Map<string, AccessFile>,
295
300
  pluginDirs: Map<string, string>,
296
301
  ): WalkListener {
297
302
  return {
298
303
  async onFile(dir, name) {
299
304
  const rel = dir ? `${dir}/${name}` : name;
300
- if (name === PLUGIN_MANIFEST_FILE && dir.startsWith(`${PLUGINS_DIR}/`)) {
305
+ if (name === PLUGIN_MANIFEST_FILE && dir.startsWith(`${pluginsDir}/`)) {
301
306
  const text = await fs.readFile(path.join(repoDir, rel), 'utf-8').catch(() => null);
302
307
  pluginDirs.set(dir, pluginIdentityOf(parseManifestText(text), path.posix.basename(dir)));
303
308
  return;
@@ -1301,6 +1306,14 @@ export class AccessControlService implements IAccessControl {
1301
1306
  * many fixtures construct this service directly.
1302
1307
  */
1303
1308
  private readonly gitRunner: IGitRunner = new NodeGitRunner(),
1309
+ /**
1310
+ * The knowledge base's layout — which folder is the plugins root, what the
1311
+ * platform files are called. Read per use, never captured: the roots are
1312
+ * renameable from the setup screen. Defaulted for the same reason as
1313
+ * `deploymentOwners`: the fixtures that construct this service directly
1314
+ * run on the default layout.
1315
+ */
1316
+ private readonly kb: Pick<KbContext, 'layout'> = { layout: DEFAULT_KB_LAYOUT },
1304
1317
  ) {
1305
1318
  this.deploymentOwners = new Set(
1306
1319
  deploymentOwners.filter(Boolean).map((e) => canonicalEmail(e)),
@@ -1806,7 +1819,7 @@ export class AccessControlService implements IAccessControl {
1806
1819
  principal.kind === 'plugin' &&
1807
1820
  principal.pluginName &&
1808
1821
  principal.pluginDir &&
1809
- !isPersonalPluginDir(principal.pluginDir)
1822
+ !isPersonalPluginDir(principal.pluginDir, this.kb.layout)
1810
1823
  ) {
1811
1824
  plugins.set(principal.pluginName, principal.pluginDir);
1812
1825
  }
@@ -1982,7 +1995,7 @@ export class AccessControlService implements IAccessControl {
1982
1995
  userEmail: string,
1983
1996
  destinationRelativePath: string,
1984
1997
  ): Promise<boolean> {
1985
- const target = platformRestoreDestination(destinationRelativePath);
1998
+ const target = platformRestoreDestination(destinationRelativePath, this.kb.layout);
1986
1999
  if (target === null) return false;
1987
2000
  const email = canonicalEmail(userEmail);
1988
2001
 
@@ -2143,7 +2156,7 @@ export class AccessControlService implements IAccessControl {
2143
2156
  // / `roles.yaml` because `hasPermissionResolved` admin-rescues those
2144
2157
  // paths, so the bad config remains fixable from inside the app.
2145
2158
  const pluginDirs = new Map<string, string>();
2146
- await this.disk.walkKb(repoDir, [accessFileListener(repoDir, accessFiles, pluginDirs)]);
2159
+ await this.disk.walkKb(repoDir, [accessFileListener(repoDir, this.kb.layout.pluginsDir, accessFiles, pluginDirs)]);
2147
2160
 
2148
2161
  // Plugin principals (`plugin/<Name>/<verb>`), derived from each plugin
2149
2162
  // folder's own access.md — a plugin being a folder with a manifest, at
@@ -2320,11 +2333,12 @@ export class AccessControlService implements IAccessControl {
2320
2333
  const { stdout } = await this.gitRunner.run(repoDir, ['ls-tree', '-r', '--name-only', ref]);
2321
2334
  const out: string[] = [];
2322
2335
  const pluginDirs: string[] = [];
2336
+ const { pluginsDir } = this.kb.layout;
2323
2337
  for (const line of stdout.split('\n')) {
2324
2338
  const p = line.trim();
2325
2339
  if (!p) continue;
2326
2340
  if (p === 'access.md' || p.endsWith('/access.md')) out.push(p);
2327
- else if (p.startsWith(`${PLUGINS_DIR}/`) && p.endsWith(`/${PLUGIN_MANIFEST_FILE}`)) {
2341
+ else if (p.startsWith(`${pluginsDir}/`) && p.endsWith(`/${PLUGIN_MANIFEST_FILE}`)) {
2328
2342
  pluginDirs.push(p.slice(0, -(PLUGIN_MANIFEST_FILE.length + 1)));
2329
2343
  }
2330
2344
  }
@@ -0,0 +1,93 @@
1
+ import type { AuthUser, ChangeRequest } from '@bevel-software/platform-shared';
2
+ import type { Verb } from '../access-model/access-grammar.js';
3
+ import { accessMdPathForFolder, type TargetKind } from './access-mutation.service.js';
4
+
5
+ /**
6
+ * The ITEM a request is about — a folder, or a file that carries its own
7
+ * rules. Everything downstream (the branch name, the file spliced, the
8
+ * permission checked) is derived from this pair, so a request on a file can
9
+ * never reach beyond that file.
10
+ */
11
+ export interface AccessRequestTarget {
12
+ /** Repo-relative path of the item itself — NOT of its rules file. */
13
+ path: string;
14
+ kind: TargetKind;
15
+ }
16
+
17
+ /** A folder target, spelled once. */
18
+ export function folderTarget(repoRelFolder: string): AccessRequestTarget {
19
+ return { path: repoRelFolder, kind: 'folder' };
20
+ }
21
+
22
+ /**
23
+ * The file whose rules a request on this item edits: a folder's `access.md`,
24
+ * or the file itself (its own frontmatter). The one place the two cases part,
25
+ * so a file request writes nothing a sibling can see.
26
+ */
27
+ export function rulesFileFor(target: AccessRequestTarget): string {
28
+ return target.kind === 'folder' ? accessMdPathForFolder(target.path) : target.path;
29
+ }
30
+
31
+ /** The levels the Manage access dialog can ask for. `owner` covers `write`. */
32
+ export type RequestLevel = Extract<Verb, 'write' | 'owner'>;
33
+ export const REQUEST_LEVELS: readonly RequestLevel[] = ['write', 'owner'];
34
+ export function isRequestLevel(value: unknown): value is RequestLevel {
35
+ return typeof value === 'string' && (REQUEST_LEVELS as readonly string[]).includes(value);
36
+ }
37
+
38
+ /** The optional note a requester may attach, in characters. */
39
+ export const REQUEST_NOTE_MAX = 500;
40
+
41
+ /**
42
+ * One grant a request's branch still proposes. Structurally the plugin
43
+ * module's `JoinProposal` — stated here so the access module can consume the
44
+ * request lifecycle without importing the module that implements it (the
45
+ * dependency runs library → access, and this keeps it that way).
46
+ */
47
+ export interface AccessProposal {
48
+ verb: Verb;
49
+ id: string;
50
+ principal:
51
+ | { kind: 'user'; email: string; displayName: string }
52
+ | { kind: 'role'; role: string };
53
+ label: string;
54
+ }
55
+
56
+ /** One open request, as the editors' surfaces render it. */
57
+ export interface AccessRequestSummary {
58
+ number: number;
59
+ branch: string;
60
+ requesterName: string;
61
+ createdAt: string;
62
+ proposals: AccessProposal[];
63
+ /** The requester's note, as plain text, when they gave one. */
64
+ note?: string;
65
+ }
66
+
67
+ /**
68
+ * The request lifecycle, as the access module needs it: what a branch still
69
+ * proposes, the open requests for an item, and the settle-if-nothing-is-left
70
+ * check. Implemented by `JoinRequestsService`.
71
+ *
72
+ * `key` is what `joinBranchFor` was given when the branch was named — a
73
+ * plugin's name for a Subscribe request, and the ITEM'S OWN PATH for every
74
+ * request that comes from the Manage access dialog. Keying a skill folder's
75
+ * request on its path is what makes the skill page's "Request write access"
76
+ * and a Can edit request on that same folder one request rather than two.
77
+ */
78
+ export interface IAccessRequestLifecycle {
79
+ /** Null ⇒ the branch could not be read; that is not "proposes nothing". */
80
+ proposalsOn(branch: string, target: AccessRequestTarget): Promise<AccessProposal[] | null>;
81
+ list(
82
+ key: string,
83
+ target: AccessRequestTarget,
84
+ crs: ChangeRequest[],
85
+ actor: AuthUser,
86
+ ): Promise<AccessRequestSummary[]>;
87
+ reconcile(
88
+ key: string,
89
+ target: AccessRequestTarget,
90
+ cr: ChangeRequest,
91
+ actor: AuthUser,
92
+ ): Promise<boolean>;
93
+ }
@@ -0,0 +1,286 @@
1
+ import express from 'express';
2
+ import type { AuthUser } from '@bevel-software/platform-shared';
3
+ import type { IWorkflowService } from '@bevel-software/platform-shared';
4
+ import { logger } from '../../shared/logging.js';
5
+ import type { KbContext } from '../../shared/kb-context.js';
6
+ import { branchForWorkspaceId } from '../../shared/workspace-id.js';
7
+ import type { AuthService } from '../auth/auth.service.js';
8
+ import type { WorkspaceService } from '../workspace/workspace.service.js';
9
+ import type { IAccessControl } from './access-control.interface.js';
10
+ import {
11
+ AccessMutationError,
12
+ AccessMutationService,
13
+ fileCarriesAccessRules,
14
+ governingFolderOf,
15
+ type TargetKind,
16
+ } from './access-mutation.service.js';
17
+ import { toHttpError as sharedToHttpError } from './admin-route-helpers.js';
18
+ import {
19
+ isRequestLevel,
20
+ rulesFileFor,
21
+ REQUEST_NOTE_MAX,
22
+ type AccessRequestTarget,
23
+ type IAccessRequestLifecycle,
24
+ } from './access-requests.contract.js';
25
+ import {
26
+ AccessRequestsService,
27
+ extractRequestNote,
28
+ itemNameOf,
29
+ } from './access-requests.service.js';
30
+ import '../auth/auth.middleware.js'; // Express Request.userId augmentation
31
+
32
+ const log = logger('access.request');
33
+
34
+ /**
35
+ * Asking for Can edit or Owner on one item, and the editors' answer.
36
+ *
37
+ * POST /api/workspace/:id/access/request → { ok, number, state, level }
38
+ * GET /api/workspace/:id/access/request?path=&kind= → { state, level?, number? }
39
+ * GET /api/workspace/:id/access/requests?path=&kind= → { requests } (editors)
40
+ * POST /api/workspace/:id/access/requests/:n/reconcile → { closed }
41
+ *
42
+ * LIVE WORKSPACE ONLY. A draft branch's rules may differ from the ones the
43
+ * person is asking about, and a request opened against a draft would propose a
44
+ * grant into a branch nobody merges — so every route here refuses any
45
+ * workspace but the default one, rather than quietly retargeting.
46
+ *
47
+ * Fail-closed like the rest of the access family: an item the caller cannot
48
+ * read answers as unknown, and the editors' listing answers `[]` to everyone
49
+ * else rather than 403 — "am I an editor here" stays a question only the
50
+ * server answers.
51
+ */
52
+ export function createAccessRequestRoutes(deps: {
53
+ accessControl: IAccessControl;
54
+ workspaceService: WorkspaceService;
55
+ authService: AuthService;
56
+ workflow: IWorkflowService;
57
+ lifecycle: IAccessRequestLifecycle;
58
+ kb: KbContext;
59
+ }): express.Router {
60
+ const { accessControl, workspaceService, authService, workflow, lifecycle, kb } = deps;
61
+ const { kbDirName } = kb;
62
+ const router = express.Router({ mergeParams: true });
63
+ const mutation = new AccessMutationService(workspaceService, accessControl, kbDirName);
64
+ const requests = new AccessRequestsService({ workflow, workspaceService, lifecycle, kb });
65
+
66
+ async function requireUser(
67
+ req: express.Request,
68
+ res: express.Response,
69
+ ): Promise<AuthUser | null> {
70
+ if (!req.userId) {
71
+ res.status(401).json({ error: 'Unauthenticated' });
72
+ return null;
73
+ }
74
+ const user = await authService.getUserById(req.userId);
75
+ if (!user) {
76
+ res.status(401).json({ error: 'User not found' });
77
+ return null;
78
+ }
79
+ return user;
80
+ }
81
+
82
+ /** Workspace-relative (what the dialog holds) → repo-relative (what the model speaks). */
83
+ function toRepoRelative(p: string): string {
84
+ const prefix = `${kbDirName}/`;
85
+ if (p === kbDirName) return '';
86
+ return p.startsWith(prefix) ? p.slice(prefix.length) : p;
87
+ }
88
+
89
+ /**
90
+ * Refuse a target that could escape the KB repo, or that is spelled in more
91
+ * than one way. Mirrors the grant route's guard — the branch name and the
92
+ * spliced path are both derived from this string, so two spellings of one
93
+ * item would be two different requests.
94
+ */
95
+ function assertSafeTarget(repoRelTarget: string, kind: TargetKind): void {
96
+ if (kind === 'file' && !repoRelTarget) {
97
+ throw new AccessMutationError('file path is required');
98
+ }
99
+ if (
100
+ repoRelTarget.startsWith('/') ||
101
+ repoRelTarget.includes('\\') ||
102
+ repoRelTarget.includes('\0') ||
103
+ repoRelTarget.split('/').some((s) => s === '..' || s === '.')
104
+ ) {
105
+ throw new AccessMutationError('path must stay inside the KB repo');
106
+ }
107
+ }
108
+
109
+ /**
110
+ * The (live workspace, readable item) pair every route here needs, or null
111
+ * after the refusal has been answered.
112
+ *
113
+ * The two refusals are deliberately different shapes: a non-default
114
+ * workspace is the CALLER asking the wrong question (400), while an item the
115
+ * caller cannot read answers exactly as one that does not exist (404) — a
116
+ * 403 would confirm the item is there.
117
+ */
118
+ async function resolveTarget(
119
+ req: express.Request,
120
+ res: express.Response,
121
+ rawPath: unknown,
122
+ rawKind: unknown,
123
+ ): Promise<{ user: AuthUser; target: AccessRequestTarget; wsId: string } | null> {
124
+ const user = await requireUser(req, res);
125
+ if (!user) return null;
126
+ const wsId = kb.defaultWorkspaceId();
127
+ if (branchForWorkspaceId(String(req.params.id)) !== kb.defaultBranch) {
128
+ res.status(400).json({
129
+ error: 'Access requests are only made on the live workspace.',
130
+ kind: 'not-live-workspace',
131
+ });
132
+ return null;
133
+ }
134
+ if (typeof rawPath !== 'string') {
135
+ res.status(400).json({ error: 'path is required', kind: 'access-error' });
136
+ return null;
137
+ }
138
+ const kind: TargetKind = rawKind === 'folder' ? 'folder' : 'file';
139
+ const target: AccessRequestTarget = { path: toRepoRelative(rawPath), kind };
140
+ try {
141
+ assertSafeTarget(target.path, kind);
142
+ } catch (err) {
143
+ const { status, body } = sharedToHttpError(err, 'access');
144
+ res.status(status).json(body);
145
+ return null;
146
+ }
147
+ if (!(await accessControl.canRead(wsId, user.email, target.path))) {
148
+ res.status(404).json({ error: 'Not found', kind: 'unknown-target' });
149
+ return null;
150
+ }
151
+ return { user, target, wsId };
152
+ }
153
+
154
+ /** Can the caller change this item's rules — the gate an Accept runs through. */
155
+ const isEditor = (wsId: string, email: string, target: AccessRequestTarget) =>
156
+ accessControl.canWrite(wsId, email, rulesFileFor(target));
157
+
158
+ /**
159
+ * A FILE that cannot hold rules of its own is governed by its folder, and
160
+ * there is nothing here to request. The dialog already points at the folder
161
+ * for these; the route says the same thing rather than splicing YAML into a
162
+ * PDF. Content counts as much as the name — bytes that are not text refuse
163
+ * too, exactly as the grant route refuses them.
164
+ */
165
+ async function assertCanCarryRules(wsId: string, target: AccessRequestTarget): Promise<void> {
166
+ if (target.kind !== 'file') return;
167
+ if (fileCarriesAccessRules(target.path) && (await mutation.targetHoldsText(wsId, target.path))) {
168
+ return;
169
+ }
170
+ throw new AccessMutationError(
171
+ `Access for this file is managed on its folder. Ask there instead.`,
172
+ 409,
173
+ { kind: 'folder-governs', folder: governingFolderOf(target.path) },
174
+ );
175
+ }
176
+
177
+ router.post('/workspace/:id/access/request', async (req, res) => {
178
+ const body = (req.body ?? {}) as Record<string, unknown>;
179
+ const ctx = await resolveTarget(req, res, body.path, body.kind);
180
+ if (!ctx) return;
181
+ const { user, target, wsId } = ctx;
182
+ try {
183
+ if (!isRequestLevel(body.level)) {
184
+ throw new AccessMutationError('level must be "write" or "owner"');
185
+ }
186
+ const note = typeof body.note === 'string' ? body.note : '';
187
+ if (typeof body.note !== 'undefined' && typeof body.note !== 'string') {
188
+ throw new AccessMutationError('note must be text');
189
+ }
190
+ if (note.length > REQUEST_NOTE_MAX) {
191
+ throw new AccessMutationError(`A note can be at most ${REQUEST_NOTE_MAX} characters.`);
192
+ }
193
+ await assertCanCarryRules(wsId, target);
194
+ if (await isEditor(wsId, user.email, target)) {
195
+ throw new AccessMutationError(
196
+ `You can already change who can use this ${target.kind}.`,
197
+ 409,
198
+ { kind: 'already-writable' },
199
+ );
200
+ }
201
+ const opened = await requests.open({
202
+ user,
203
+ target,
204
+ itemName: itemNameOf(target.path),
205
+ level: body.level,
206
+ note,
207
+ });
208
+ res.json({ ok: true, state: 'pending', number: opened.number, level: opened.level });
209
+ } catch (err) {
210
+ const { status, body: errBody } = sharedToHttpError(err, 'access');
211
+ if (status >= 500) log.error('failed to open an access request:', { err });
212
+ res.status(status).json(errBody);
213
+ }
214
+ });
215
+
216
+ router.get('/workspace/:id/access/request', async (req, res) => {
217
+ const ctx = await resolveTarget(req, res, req.query.path, req.query.kind);
218
+ if (!ctx) return;
219
+ try {
220
+ res.json(await requests.status(ctx.user, ctx.target));
221
+ } catch (err) {
222
+ log.error('failed to read an access request:', { err });
223
+ res.status(500).json({ error: 'Failed to read the request' });
224
+ }
225
+ });
226
+
227
+ router.get('/workspace/:id/access/requests', async (req, res) => {
228
+ const ctx = await resolveTarget(req, res, req.query.path, req.query.kind);
229
+ if (!ctx) return;
230
+ const { user, target, wsId } = ctx;
231
+ try {
232
+ if (!(await isEditor(wsId, user.email, target))) {
233
+ res.json({ requests: [] });
234
+ return;
235
+ }
236
+ const crs = await workflow.listChangeRequests();
237
+ const rows = await lifecycle.list(target.path, target, crs, user);
238
+ // The note lives in the request's description and nowhere else, so it
239
+ // is read back per request. Bounded by the open requests on ONE item —
240
+ // a handful at most — and a detail that cannot be fetched costs the
241
+ // note, never the line it belongs to.
242
+ const withNotes = await Promise.all(
243
+ rows.map(async (row) => {
244
+ const detail = await workflow
245
+ .getChangeRequestDetail(row.number, { patches: false })
246
+ .catch(() => null);
247
+ const note = extractRequestNote(detail?.body);
248
+ return note ? { ...row, note } : row;
249
+ }),
250
+ );
251
+ res.json({ requests: withNotes });
252
+ } catch (err) {
253
+ log.error('failed to list access requests:', { err });
254
+ res.status(500).json({ error: 'Failed to list access requests' });
255
+ }
256
+ });
257
+
258
+ router.post('/workspace/:id/access/requests/:number/reconcile', async (req, res) => {
259
+ const body = (req.body ?? {}) as Record<string, unknown>;
260
+ const ctx = await resolveTarget(req, res, body.path, body.kind);
261
+ if (!ctx) return;
262
+ const { user, target, wsId } = ctx;
263
+ try {
264
+ if (!(await isEditor(wsId, user.email, target))) {
265
+ res.status(404).json({ error: 'Not found' });
266
+ return;
267
+ }
268
+ const number = Number(req.params.number);
269
+ if (!Number.isSafeInteger(number) || number <= 0) {
270
+ res.status(400).json({ error: 'Invalid change request number' });
271
+ return;
272
+ }
273
+ const cr = await workflow.getChangeRequest(number);
274
+ if (!cr) {
275
+ res.status(404).json({ error: 'Not found' });
276
+ return;
277
+ }
278
+ res.json({ closed: await lifecycle.reconcile(target.path, target, cr, user) });
279
+ } catch (err) {
280
+ log.error('failed to reconcile an access request:', { err });
281
+ res.status(500).json({ error: 'Failed to reconcile the request' });
282
+ }
283
+ });
284
+
285
+ return router;
286
+ }