@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
@@ -0,0 +1,221 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import type { TenantConfig } from '../../core-config.js';
3
+ import { TenantRuntime, type TenantGraph } from '../tenant-runtime.js';
4
+ import type { TenantDescriptor } from '../tenant-source.contract.js';
5
+
6
+ const descriptor: TenantDescriptor = { slug: 'acme', hosts: ['acme.test'], config: {} as TenantConfig };
7
+
8
+ /** A graph builder whose completion the test controls. */
9
+ function harness(opts: { fail?: boolean | (() => boolean); now?: () => number; retry?: { initialMs: number; maxMs: number } } = {}) {
10
+ const events: string[] = [];
11
+ let release: (() => void) | null = null;
12
+ let activations = 0;
13
+ const runtime = new TenantRuntime(descriptor, {
14
+ activate: async () => {
15
+ activations += 1;
16
+ events.push(`activate:${activations}`);
17
+ await new Promise<void>((resolve) => {
18
+ release = resolve;
19
+ });
20
+ if (typeof opts.fail === 'function' ? opts.fail() : opts.fail) throw new Error('the remote said no');
21
+ return { core: { id: activations } } as unknown as TenantGraph;
22
+ },
23
+ stop: async (graph) => {
24
+ events.push(`stop:${(graph.core as unknown as { id: number }).id}`);
25
+ },
26
+ busy: async () => false,
27
+ now: opts.now,
28
+ retry: opts.retry,
29
+ });
30
+ return { runtime, events, finish: () => release?.(), activations: () => activations };
31
+ }
32
+
33
+ const settle = () => new Promise((resolve) => setImmediate(resolve));
34
+
35
+ describe('TenantRuntime', () => {
36
+ it('builds the graph once for a burst of first requests, and hands every one the same graph', async () => {
37
+ const h = harness();
38
+ expect(h.runtime.state).toBe('idle');
39
+ const first = h.runtime.handle();
40
+ const second = h.runtime.handle();
41
+ const third = h.runtime.handle();
42
+ await settle();
43
+ expect(h.runtime.state).toBe('activating');
44
+ h.finish();
45
+ const graphs = await Promise.all([first, second, third]);
46
+ expect(h.activations()).toBe(1);
47
+ expect(graphs[1]).toBe(graphs[0]);
48
+ expect(graphs[2]).toBe(graphs[0]);
49
+ expect(h.runtime.state).toBe('active');
50
+ // Once active, a request is answered from the running graph.
51
+ expect(await h.runtime.handle()).toBe(graphs[0]);
52
+ expect(h.activations()).toBe(1);
53
+ });
54
+
55
+ it('remembers a failed activation for a growing window, and tries again once it has passed', async () => {
56
+ let now = 0;
57
+ const h = harness({ fail: true, now: () => now, retry: { initialMs: 100, maxMs: 300 } });
58
+ const first = h.runtime.handle();
59
+ await settle();
60
+ h.finish();
61
+ await expect(first).rejects.toThrow('the remote said no');
62
+ expect(h.runtime.state).toBe('idle');
63
+
64
+ // Inside the window: the same failure, from memory, without a build.
65
+ now = 99;
66
+ await expect(h.runtime.handle()).rejects.toThrow('the remote said no');
67
+ expect(h.activations()).toBe(1);
68
+
69
+ // Past it: tried again; a second failure doubles the window.
70
+ now = 100;
71
+ const second = h.runtime.handle();
72
+ await settle();
73
+ expect(h.activations()).toBe(2);
74
+ h.finish();
75
+ await expect(second).rejects.toThrow();
76
+ now = 299;
77
+ await expect(h.runtime.handle()).rejects.toThrow();
78
+ expect(h.activations()).toBe(2);
79
+
80
+ // The window is capped: 100, 200, then 300 and no more.
81
+ now = 300;
82
+ const third = h.runtime.handle();
83
+ await settle();
84
+ h.finish();
85
+ await expect(third).rejects.toThrow();
86
+ now = 599;
87
+ await expect(h.runtime.handle()).rejects.toThrow();
88
+ now = 600;
89
+ const fourth = h.runtime.handle();
90
+ await settle();
91
+ expect(h.activations()).toBe(4);
92
+ h.finish();
93
+ await expect(fourth).rejects.toThrow();
94
+ now = 899;
95
+ await expect(h.runtime.handle()).rejects.toThrow();
96
+ expect(h.activations()).toBe(4);
97
+ });
98
+
99
+ it('forgets the failures once an activation succeeds', async () => {
100
+ let now = 0;
101
+ let fail = true;
102
+ const h = harness({ fail: () => fail, now: () => now, retry: { initialMs: 100, maxMs: 300 } });
103
+ const first = h.runtime.handle();
104
+ await settle();
105
+ h.finish();
106
+ await expect(first).rejects.toThrow();
107
+ fail = false;
108
+ now = 100;
109
+ const ok = h.runtime.handle();
110
+ await settle();
111
+ h.finish();
112
+ await ok;
113
+ expect(h.runtime.state).toBe('active');
114
+ // A later failure, after an eviction, starts the window from the beginning.
115
+ await h.runtime.evict();
116
+ fail = true;
117
+ const again = h.runtime.handle();
118
+ await settle();
119
+ h.finish();
120
+ await expect(again).rejects.toThrow();
121
+ now = 199;
122
+ await expect(h.runtime.handle()).rejects.toThrow();
123
+ now = 200;
124
+ const retried = h.runtime.handle();
125
+ await settle();
126
+ expect(h.activations()).toBe(4);
127
+ h.finish();
128
+ await expect(retried).rejects.toThrow();
129
+ });
130
+
131
+ it('evicts a running graph, and builds a fresh one on the next request', async () => {
132
+ const h = harness();
133
+ const pending = h.runtime.handle();
134
+ await settle();
135
+ h.finish();
136
+ await pending;
137
+ await h.runtime.evict();
138
+ expect(h.events).toEqual(['activate:1', 'stop:1']);
139
+ expect(h.runtime.state).toBe('idle');
140
+ const next = h.runtime.handle();
141
+ await settle();
142
+ h.finish();
143
+ await next;
144
+ expect(h.events).toEqual(['activate:1', 'stop:1', 'activate:2']);
145
+ });
146
+
147
+ it('an eviction that lands during an activation waits for it and stops what it built', async () => {
148
+ const h = harness();
149
+ const pending = h.runtime.handle();
150
+ await settle();
151
+ const evicting = h.runtime.evict();
152
+ expect(h.runtime.state).toBe('evicting');
153
+ h.finish();
154
+ await pending;
155
+ await evicting;
156
+ expect(h.events).toEqual(['activate:1', 'stop:1']);
157
+ expect(h.runtime.state).toBe('idle');
158
+ });
159
+
160
+ it('a second eviction joins the one under way, and evicting an idle runtime stops nothing', async () => {
161
+ const h = harness();
162
+ await h.runtime.evict();
163
+ expect(h.events).toEqual([]);
164
+ const pending = h.runtime.handle();
165
+ await settle();
166
+ h.finish();
167
+ await pending;
168
+ const a = h.runtime.evict();
169
+ const b = h.runtime.evict();
170
+ await Promise.all([a, b]);
171
+ expect(h.events).toEqual(['activate:1', 'stop:1']);
172
+ });
173
+
174
+ it('measures idleness from the last request, and reports busy only for a running graph', async () => {
175
+ let now = 1_000;
176
+ let busy = false;
177
+ const runtime = new TenantRuntime(descriptor, {
178
+ activate: async () => ({ core: {} } as unknown as TenantGraph),
179
+ stop: async () => undefined,
180
+ busy: async () => busy,
181
+ now: () => now,
182
+ });
183
+ expect(await runtime.busy()).toBe(false);
184
+ await runtime.handle();
185
+ now += 5_000;
186
+ expect(runtime.idleFor()).toBe(5_000);
187
+ await runtime.handle();
188
+ expect(runtime.idleFor()).toBe(0);
189
+ busy = true;
190
+ expect(await runtime.busy()).toBe(true);
191
+ });
192
+
193
+ it('is busy while any response is open, and counts idleness from the last one that closed', async () => {
194
+ let now = 1_000;
195
+ const runtime = new TenantRuntime(descriptor, {
196
+ activate: async () => ({ core: {} } as unknown as TenantGraph),
197
+ stop: async () => undefined,
198
+ busy: async () => false,
199
+ now: () => now,
200
+ });
201
+ await runtime.handle();
202
+ // An event stream a browser holds: entered long ago, never left.
203
+ runtime.enter();
204
+ runtime.enter();
205
+ now += 3_600_000;
206
+ expect(runtime.idleFor()).toBe(3_600_000);
207
+ expect(await runtime.busy()).toBe(true);
208
+ expect(runtime.open).toBe(2);
209
+ runtime.leave();
210
+ expect(await runtime.busy()).toBe(true);
211
+ // The last response closing is what idleness counts from.
212
+ now += 10;
213
+ runtime.leave();
214
+ expect(runtime.open).toBe(0);
215
+ expect(runtime.idleFor()).toBe(0);
216
+ expect(await runtime.busy()).toBe(false);
217
+ // A stray leave cannot make the count negative.
218
+ runtime.leave();
219
+ expect(runtime.open).toBe(0);
220
+ });
221
+ });
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { deriveTenantSecrets } from '../tenant-secrets.js';
3
+
4
+ const MASTER = 'a-master-key-that-is-long-enough-to-count-as-random';
5
+
6
+ describe('deriveTenantSecrets', () => {
7
+ it('is reproducible: the same master key and slug give the same secrets, on the day of an export too', () => {
8
+ expect(deriveTenantSecrets(MASTER, 'acme')).toEqual(deriveTenantSecrets(MASTER, 'acme'));
9
+ });
10
+
11
+ it('gives each tenant, and each of the three secrets, a value of its own', () => {
12
+ const acme = deriveTenantSecrets(MASTER, 'acme');
13
+ const globex = deriveTenantSecrets(MASTER, 'globex');
14
+ expect(acme.jwtSecret).not.toBe(globex.jwtSecret);
15
+ expect(acme.secretsEncKey).not.toBe(globex.secretsEncKey);
16
+ expect(acme.internalTokenSecret).not.toBe(globex.internalTokenSecret);
17
+ expect(new Set([acme.jwtSecret, acme.secretsEncKey, acme.internalTokenSecret]).size).toBe(3);
18
+ });
19
+
20
+ it('derives 32-byte keys, which is what the vault encryption requires', () => {
21
+ const { secretsEncKey } = deriveTenantSecrets(MASTER, 'acme');
22
+ expect(Buffer.from(secretsEncKey, 'base64')).toHaveLength(32);
23
+ });
24
+
25
+ it('changes every secret when the master key changes', () => {
26
+ const a = deriveTenantSecrets(MASTER, 'acme');
27
+ const b = deriveTenantSecrets(`${MASTER}-rotated`, 'acme');
28
+ expect(a.jwtSecret).not.toBe(b.jwtSecret);
29
+ });
30
+
31
+ it('refuses a master key too short to be random, and a missing slug', () => {
32
+ expect(() => deriveTenantSecrets('short', 'acme')).toThrow(/TENANT_MASTER_KEY/);
33
+ expect(() => deriveTenantSecrets(MASTER, '')).toThrow(/slug/);
34
+ });
35
+ });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Serving several knowledge bases from one process — see
3
+ * `docs/multi-tenant.md` and `openspec/changes/multi-tenant-runtime`.
4
+ *
5
+ * The contract (`TenantSource`) and the mechanism (runtime, host) are core's;
6
+ * where tenants come from is the caller's. Core ships a file-based source.
7
+ */
8
+ export {
9
+ assertTenantSlug,
10
+ normalizeHost,
11
+ TENANT_SLUG_PATTERN,
12
+ type TenantDescriptor,
13
+ type TenantSource,
14
+ } from './tenant-source.contract.js';
15
+ export { deriveTenantSecrets, type TenantSecrets } from './tenant-secrets.js';
16
+ export {
17
+ StaticTenantSource,
18
+ tenantConfigFrom,
19
+ tenantHostEnv,
20
+ type TenantHostEnv,
21
+ type TenantHostSettings,
22
+ type TenantRecord,
23
+ type TenantsFile,
24
+ } from './static-tenant-source.js';
25
+ export { TenantRuntime, type TenantGraph, type TenantRuntimeDeps, type TenantState } from './tenant-runtime.js';
26
+ export {
27
+ createTenantHost,
28
+ isLoopbackAddress,
29
+ loopbackTenantBaseUrl,
30
+ selectTenant,
31
+ LOOPBACK_TENANT_PREFIX,
32
+ type TenantHost,
33
+ type TenantHostOptions,
34
+ type TenantSelector,
35
+ } from './tenant-host.js';
@@ -0,0 +1,296 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { resolveDatabaseUrl, type TenantConfig } from '../core-config.js';
4
+ import { DEFAULT_DB_SCHEMA, assertSchemaName } from '../modules/database/connection.js';
5
+ import { DEFAULT_GIT_TIMEOUT_MS } from '../modules/workflow/git/node-git-runner.js';
6
+ import { defaultKbTemplateDir } from '../assets.js';
7
+ import { DEFAULT_GIT_USERNAME } from '../shared/git.contract.js';
8
+ import { deriveTenantSecrets } from './tenant-secrets.js';
9
+ import { loopbackTenantBaseUrl } from './tenant-host.js';
10
+ import { assertTenantSlug, normalizeHost, type TenantDescriptor, type TenantSource } from './tenant-source.contract.js';
11
+
12
+ /**
13
+ * What every tenant of one process shares: the process's own facts, the one
14
+ * database, the roots its folders hang off, and the master key its secrets
15
+ * derive from. A tenant's own record ({@link TenantRecord}) adds what differs.
16
+ */
17
+ export interface TenantHostSettings {
18
+ readonly databaseUrl: string;
19
+ readonly port: number;
20
+ readonly nodeEnv: string;
21
+ readonly trustProxy: string;
22
+ /** Each tenant's workspaces live under `<workspacesRoot>/<slug>`; its backups, spills and caches beside them. */
23
+ readonly workspacesRoot: string;
24
+ readonly kbTemplateDir: string;
25
+ readonly gitTimeoutMs: number;
26
+ readonly ontologySessionBlock: boolean;
27
+ readonly updateCheckEnabled: boolean;
28
+ /** See `tenant-secrets.ts`. */
29
+ readonly masterKey: string;
30
+ }
31
+
32
+ /** One entry of the tenants file. Everything optional has the single-tenant default. */
33
+ export interface TenantRecord {
34
+ slug: string;
35
+ /** Host names (a port is tolerated and ignored) that reach this tenant. At least one. */
36
+ hosts: string[];
37
+ adminEmail: string;
38
+ /** The bootstrap password; omitted, password login is off for this tenant. */
39
+ adminPassword?: string;
40
+ /** Force password login on or off; default: on exactly when `adminPassword` is set. */
41
+ loginPassword?: boolean;
42
+ kbRepoUrl?: string;
43
+ gitToken?: string;
44
+ gitUsername?: string;
45
+ kbDirName?: string;
46
+ /** The branch model; absent, the tenant's admin enters it on the setup screen. */
47
+ defaultBranch?: string;
48
+ protectedBranches?: string | string[];
49
+ /** The bearer a git host's webhook presents to `POST /api/sync`. */
50
+ kbSyncSecret?: string;
51
+ /** Default `t_<slug>` with hyphens as underscores. */
52
+ dbSchema?: string;
53
+ /** The credential-prefix brand; default: the slug without hyphens. */
54
+ tenantId?: string;
55
+ /** Default `https://<first host>`. */
56
+ publicBackendUrl?: string;
57
+ /** Default: the backend URL (the backend serves the SPA). */
58
+ publicFrontendUrl?: string;
59
+ allowedEmailDomains?: string[];
60
+ oidc?: {
61
+ issuerUrl: string;
62
+ clientId: string;
63
+ clientSecret: string;
64
+ scopes?: string;
65
+ providerLabel?: string;
66
+ };
67
+ }
68
+
69
+ /** The file's shape: a list of records. */
70
+ export interface TenantsFile {
71
+ tenants: TenantRecord[];
72
+ }
73
+
74
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
75
+ const USERNAME_PATTERN = /^[A-Za-z0-9._-]+$/;
76
+
77
+ /**
78
+ * A tenant's {@link TenantConfig} from its record and the host's settings:
79
+ * the same values `CoreConfig` reads from the environment, resolved the same
80
+ * way, with the folders and the schema named after the slug so two tenants
81
+ * can never share a path or a table.
82
+ */
83
+ export function tenantConfigFrom(record: TenantRecord, settings: TenantHostSettings): TenantConfig {
84
+ const slug = assertTenantSlug(record.slug);
85
+ if (!Array.isArray(record.hosts) || record.hosts.length === 0) {
86
+ throw new Error(`Tenant "${slug}" names no host`);
87
+ }
88
+ const hosts = record.hosts.map(normalizeHost);
89
+ const adminEmail = (record.adminEmail ?? '').trim().toLowerCase();
90
+ if (!EMAIL_PATTERN.test(adminEmail)) {
91
+ throw new Error(`Tenant "${slug}": adminEmail is not a valid email: "${record.adminEmail ?? ''}"`);
92
+ }
93
+ const gitUsername = (record.gitUsername ?? DEFAULT_GIT_USERNAME).trim();
94
+ if (!USERNAME_PATTERN.test(gitUsername)) {
95
+ throw new Error(`Tenant "${slug}": gitUsername must match [A-Za-z0-9._-]+; got "${gitUsername}"`);
96
+ }
97
+ const tenantId = (record.tenantId ?? slug.replace(/-/g, '')).trim().toLowerCase();
98
+ if (!/^[a-z0-9]+$/.test(tenantId)) {
99
+ throw new Error(`Tenant "${slug}": tenantId must be lowercase alphanumeric; got "${tenantId}"`);
100
+ }
101
+ const dbSchema = assertSchemaName(record.dbSchema ?? `t_${slug.replace(/-/g, '_')}`);
102
+ // The default schema is where a single-tenant deployment on the same
103
+ // database keeps its tables and, in drizzle's own schema beside it, its
104
+ // migration ledger; a tenant there would be no tenant at all.
105
+ if (dbSchema === DEFAULT_DB_SCHEMA) {
106
+ throw new Error(`Tenant "${slug}": dbSchema must not be "${DEFAULT_DB_SCHEMA}"; a tenant needs a schema of its own`);
107
+ }
108
+ const adminPassword = record.adminPassword ?? '';
109
+ const loginPasswordEnabled = record.loginPassword ?? adminPassword !== '';
110
+ if (loginPasswordEnabled && !adminPassword) {
111
+ throw new Error(`Tenant "${slug}": loginPassword is on but no adminPassword is set`);
112
+ }
113
+ const publicBackendUrl = (record.publicBackendUrl ?? `https://${hosts[0]}`).trim().replace(/\/+$/, '');
114
+ const publicFrontendUrl = (record.publicFrontendUrl ?? publicBackendUrl).trim().replace(/\/+$/, '');
115
+ for (const [name, value] of [
116
+ ['publicBackendUrl', publicBackendUrl],
117
+ ['publicFrontendUrl', publicFrontendUrl],
118
+ ] as const) {
119
+ try {
120
+ new URL(value);
121
+ } catch {
122
+ throw new Error(`Tenant "${slug}": ${name} is not a valid URL: "${value}"`);
123
+ }
124
+ }
125
+ const secrets = deriveTenantSecrets(settings.masterKey, slug);
126
+ const besideWorkspaces = (name: string) => path.resolve(settings.workspacesRoot, '..', name, slug);
127
+ const allowedEmailDomains = (record.allowedEmailDomains ?? [])
128
+ .map((d) => d.trim().toLowerCase().replace(/^[@.]+/, ''))
129
+ .filter((d) => d.length > 0);
130
+ const protectedBranches = Array.isArray(record.protectedBranches)
131
+ ? record.protectedBranches.join(',')
132
+ : (record.protectedBranches ?? '');
133
+ // The record's settings, as the environment a single-tenant deployment
134
+ // would pin them in: only what the record names, so the setup screen
135
+ // still collects the rest. Nothing of the host process's environment.
136
+ const settingsEnv: NodeJS.ProcessEnv = {};
137
+ for (const [name, value] of [
138
+ ['KB_REPO_URL', record.kbRepoUrl],
139
+ ['GIT_TOKEN', record.gitToken],
140
+ ['GIT_USERNAME', record.gitUsername],
141
+ ['KB_DIR_NAME', record.kbDirName],
142
+ ['DEFAULT_BRANCH', record.defaultBranch],
143
+ ['PROTECTED_BRANCHES', protectedBranches],
144
+ ['KB_SYNC_SECRET', record.kbSyncSecret],
145
+ ['OIDC_ISSUER_URL', record.oidc?.issuerUrl],
146
+ ['OIDC_CLIENT_ID', record.oidc?.clientId],
147
+ ['OIDC_CLIENT_SECRET', record.oidc?.clientSecret],
148
+ ['OIDC_SCOPES', record.oidc?.scopes],
149
+ ['OIDC_PROVIDER_LABEL', record.oidc?.providerLabel],
150
+ ['ALLOWED_EMAIL_DOMAINS', allowedEmailDomains.join(',')],
151
+ ] as const) {
152
+ const trimmed = (value ?? '').trim();
153
+ if (trimmed) settingsEnv[name] = trimmed;
154
+ }
155
+ return {
156
+ port: settings.port,
157
+ databaseUrl: settings.databaseUrl,
158
+ dbSchema,
159
+ nodeEnv: settings.nodeEnv,
160
+ tenantId,
161
+ externalApiKeyPrefix: `${tenantId}_`,
162
+ internalTokenPrefix: `${tenantId}-int_`,
163
+ uploadTokenPrefix: `${tenantId}-up_`,
164
+ mcpOAuthTokenPrefix: `${tenantId}-mcp_`,
165
+ workspacesRoot: path.resolve(settings.workspacesRoot, slug),
166
+ backupsRoot: besideWorkspaces('backups'),
167
+ spillRoot: besideWorkspaces('tool-chain-spills'),
168
+ docExtractCacheRoot: besideWorkspaces('doc-extract-cache'),
169
+ jwtSecret: secrets.jwtSecret,
170
+ secretsEncKey: secrets.secretsEncKey,
171
+ internalTokenSecret: secrets.internalTokenSecret,
172
+ adminEmail,
173
+ adminPassword,
174
+ loginPasswordEnabled,
175
+ oidcIssuerUrl: (record.oidc?.issuerUrl ?? '').trim().replace(/\/+$/, ''),
176
+ oidcClientId: (record.oidc?.clientId ?? '').trim(),
177
+ oidcClientSecret: (record.oidc?.clientSecret ?? '').trim(),
178
+ oidcScopes: (record.oidc?.scopes ?? 'openid profile email').trim(),
179
+ oidcProviderLabel: (record.oidc?.providerLabel ?? 'Single sign-on').trim(),
180
+ kbRepoUrl: (record.kbRepoUrl ?? '').trim(),
181
+ kbDirName: (record.kbDirName ?? '').trim(),
182
+ gitUsername,
183
+ gitToken: (record.gitToken ?? '').trim(),
184
+ kbTemplateDir: settings.kbTemplateDir,
185
+ ontologySessionBlock: settings.ontologySessionBlock,
186
+ updateCheckEnabled: settings.updateCheckEnabled,
187
+ allowedEmailDomains,
188
+ trustProxy: settings.trustProxy,
189
+ gitTimeoutMs: settings.gitTimeoutMs,
190
+ publicBackendUrl,
191
+ publicFrontendUrl,
192
+ configuredPublicFrontendUrl: publicFrontendUrl,
193
+ loopbackBaseUrl: loopbackTenantBaseUrl(settings.port, slug),
194
+ settingsEnv,
195
+ };
196
+ }
197
+
198
+ /**
199
+ * The reference {@link TenantSource}: every tenant known up front, from a
200
+ * JSON file (`TENANTS_FILE`). Enough for development, tests and a small
201
+ * self-hosted fleet; the cloud app brings its own source over its registry.
202
+ */
203
+ export class StaticTenantSource implements TenantSource {
204
+ private readonly bySlug = new Map<string, TenantDescriptor>();
205
+ private readonly byHost = new Map<string, TenantDescriptor>();
206
+
207
+ constructor(records: readonly TenantRecord[], settings: TenantHostSettings) {
208
+ for (const record of records) {
209
+ const config = tenantConfigFrom(record, settings);
210
+ const descriptor: TenantDescriptor = {
211
+ slug: record.slug,
212
+ hosts: record.hosts.map(normalizeHost),
213
+ config,
214
+ };
215
+ if (this.bySlug.has(descriptor.slug)) throw new Error(`Tenant "${descriptor.slug}" is listed twice`);
216
+ this.bySlug.set(descriptor.slug, descriptor);
217
+ for (const host of descriptor.hosts) {
218
+ const taken = this.byHost.get(host);
219
+ if (taken) throw new Error(`Host "${host}" is claimed by both "${taken.slug}" and "${descriptor.slug}"`);
220
+ this.byHost.set(host, descriptor);
221
+ }
222
+ }
223
+ }
224
+
225
+ static async fromFile(file: string, settings: TenantHostSettings): Promise<StaticTenantSource> {
226
+ const text = await fs.readFile(file, 'utf8');
227
+ let parsed: unknown;
228
+ try {
229
+ parsed = JSON.parse(text);
230
+ } catch (err) {
231
+ throw new Error(`${file} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
232
+ }
233
+ const tenants = (parsed as Partial<TenantsFile> | null)?.tenants;
234
+ if (!Array.isArray(tenants)) throw new Error(`${file} must be an object with a "tenants" array`);
235
+ return new StaticTenantSource(tenants as TenantRecord[], settings);
236
+ }
237
+
238
+ get slugs(): readonly string[] {
239
+ return [...this.bySlug.keys()];
240
+ }
241
+
242
+ async resolveByHost(host: string): Promise<TenantDescriptor | null> {
243
+ return this.byHost.get(normalizeHost(host)) ?? null;
244
+ }
245
+
246
+ async describe(slug: string): Promise<TenantDescriptor | null> {
247
+ return this.bySlug.get(slug) ?? null;
248
+ }
249
+ }
250
+
251
+ /** The process facts a tenant host reads from its environment — see `docs/multi-tenant.md`. */
252
+ export interface TenantHostEnv extends TenantHostSettings {
253
+ /** The tenants file, when the process runs as a host at all. */
254
+ readonly tenantsFile: string | null;
255
+ /** Minutes without a request after which a tenant's graph is stopped. */
256
+ readonly idleMinutes: number;
257
+ }
258
+
259
+ const MAX_TIMER_MS = 2_147_483_647;
260
+
261
+ /**
262
+ * The host's settings from the environment: the same variables a
263
+ * single-tenant deployment reads for these facts (`DATABASE_URL` and the
264
+ * `POSTGRES_*` parts, `PORT`, `TRUST_PROXY`, `WORKSPACES_ROOT`,
265
+ * `KB_TEMPLATE_DIR`, `GIT_TIMEOUT_MS`, …), plus the three that only a host
266
+ * has: `TENANTS_FILE`, `TENANT_MASTER_KEY` and `TENANT_IDLE_MINUTES`.
267
+ */
268
+ export function tenantHostEnv(env: NodeJS.ProcessEnv = process.env): TenantHostEnv {
269
+ const tenantsFile = (env.TENANTS_FILE || '').trim() || null;
270
+ const masterKey = (env.TENANT_MASTER_KEY || '').trim();
271
+ if (tenantsFile && !masterKey) {
272
+ throw new Error(
273
+ 'TENANT_MASTER_KEY is required with TENANTS_FILE — every tenant\'s secrets derive from it. Generate one with: ' +
274
+ `node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`,
275
+ );
276
+ }
277
+ const gitTimeout = Number(env.GIT_TIMEOUT_MS);
278
+ const idle = Number(env.TENANT_IDLE_MINUTES);
279
+ const domain = (env.DOMAIN || '').trim();
280
+ const workspacesRoot = env.WORKSPACES_ROOT || path.resolve(process.cwd(), 'workspaces');
281
+ return {
282
+ tenantsFile,
283
+ masterKey,
284
+ databaseUrl: resolveDatabaseUrl(env),
285
+ port: parseInt(env.PORT || '3001', 10),
286
+ nodeEnv: env.NODE_ENV || 'development',
287
+ trustProxy: (env.TRUST_PROXY || (domain ? '1' : '')).trim(),
288
+ workspacesRoot,
289
+ kbTemplateDir: env.KB_TEMPLATE_DIR || defaultKbTemplateDir(),
290
+ gitTimeoutMs:
291
+ Number.isFinite(gitTimeout) && gitTimeout > 0 && gitTimeout <= MAX_TIMER_MS ? gitTimeout : DEFAULT_GIT_TIMEOUT_MS,
292
+ ontologySessionBlock: (env.ONTOLOGY_SESSION_BLOCK ?? 'true').trim().toLowerCase() !== 'false',
293
+ updateCheckEnabled: (env.UPDATE_CHECK ?? 'true').trim().toLowerCase() !== 'false',
294
+ idleMinutes: Number.isFinite(idle) && idle > 0 ? idle : 30,
295
+ };
296
+ }