@bevel-software/platform-core-backend 0.13.6 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/THIRD-PARTY-NOTICES.md +6 -6
  2. package/dist/core/catalog-cache-invalidation.d.ts +29 -0
  3. package/dist/core/catalog-cache-invalidation.d.ts.map +1 -0
  4. package/dist/core/catalog-cache-invalidation.js +71 -0
  5. package/dist/core/catalog-cache-invalidation.js.map +1 -0
  6. package/dist/core/create-core-server.d.ts.map +1 -1
  7. package/dist/core/create-core-server.js +101 -9
  8. package/dist/core/create-core-server.js.map +1 -1
  9. package/dist/core/create-core-services.d.ts +20 -1
  10. package/dist/core/create-core-services.d.ts.map +1 -1
  11. package/dist/core/create-core-services.js +105 -43
  12. package/dist/core/create-core-services.js.map +1 -1
  13. package/dist/core-config.d.ts.map +1 -1
  14. package/dist/core-config.js +12 -5
  15. package/dist/core-config.js.map +1 -1
  16. package/dist/modules/access/access-control.interface.d.ts +18 -6
  17. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  18. package/dist/modules/access/access-control.service.d.ts +26 -1
  19. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  20. package/dist/modules/access/access-control.service.js +213 -71
  21. package/dist/modules/access/access-control.service.js.map +1 -1
  22. package/dist/modules/access/access.routes.d.ts.map +1 -1
  23. package/dist/modules/access/access.routes.js +59 -7
  24. package/dist/modules/access/access.routes.js.map +1 -1
  25. package/dist/modules/access-model/access-grammar.d.ts +51 -1
  26. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  27. package/dist/modules/access-model/access-grammar.js +95 -1
  28. package/dist/modules/access-model/access-grammar.js.map +1 -1
  29. package/dist/modules/access-model/group-files.d.ts.map +1 -1
  30. package/dist/modules/access-model/group-files.js +4 -1
  31. package/dist/modules/access-model/group-files.js.map +1 -1
  32. package/dist/modules/access-model/kb-read-filter.d.ts +12 -0
  33. package/dist/modules/access-model/kb-read-filter.d.ts.map +1 -1
  34. package/dist/modules/access-model/kb-read-filter.js +15 -0
  35. package/dist/modules/access-model/kb-read-filter.js.map +1 -1
  36. package/dist/modules/access-model/plugin-principals.d.ts +49 -0
  37. package/dist/modules/access-model/plugin-principals.d.ts.map +1 -0
  38. package/dist/modules/access-model/plugin-principals.js +125 -0
  39. package/dist/modules/access-model/plugin-principals.js.map +1 -0
  40. package/dist/modules/connection-probe/connection-probe.contract.d.ts +43 -0
  41. package/dist/modules/connection-probe/connection-probe.contract.d.ts.map +1 -0
  42. package/dist/modules/connection-probe/connection-probe.contract.js +2 -0
  43. package/dist/modules/connection-probe/connection-probe.contract.js.map +1 -0
  44. package/dist/modules/connection-probe/connection-probe.service.d.ts +94 -0
  45. package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -0
  46. package/dist/modules/connection-probe/connection-probe.service.js +684 -0
  47. package/dist/modules/connection-probe/connection-probe.service.js.map +1 -0
  48. package/dist/modules/connection-probe/index.d.ts +3 -0
  49. package/dist/modules/connection-probe/index.d.ts.map +1 -0
  50. package/dist/modules/connection-probe/index.js +3 -0
  51. package/dist/modules/connection-probe/index.js.map +1 -0
  52. package/dist/modules/database/core-schema.d.ts +326 -0
  53. package/dist/modules/database/core-schema.d.ts.map +1 -1
  54. package/dist/modules/database/core-schema.js +52 -0
  55. package/dist/modules/database/core-schema.js.map +1 -1
  56. package/dist/modules/diff/diff.routes.d.ts.map +1 -1
  57. package/dist/modules/diff/diff.routes.js +3 -5
  58. package/dist/modules/diff/diff.routes.js.map +1 -1
  59. package/dist/modules/kb-fs/mutex.d.ts +37 -0
  60. package/dist/modules/kb-fs/mutex.d.ts.map +1 -1
  61. package/dist/modules/kb-fs/mutex.js +48 -5
  62. package/dist/modules/kb-fs/mutex.js.map +1 -1
  63. package/dist/modules/kb-sync/index.d.ts +6 -0
  64. package/dist/modules/kb-sync/index.d.ts.map +1 -0
  65. package/dist/modules/kb-sync/index.js +5 -0
  66. package/dist/modules/kb-sync/index.js.map +1 -0
  67. package/dist/modules/kb-sync/kb-sync.interface.d.ts +77 -0
  68. package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -0
  69. package/dist/modules/kb-sync/kb-sync.interface.js +2 -0
  70. package/dist/modules/kb-sync/kb-sync.interface.js.map +1 -0
  71. package/dist/modules/kb-sync/kb-sync.routes.d.ts +70 -0
  72. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -0
  73. package/dist/modules/kb-sync/kb-sync.routes.js +135 -0
  74. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -0
  75. package/dist/modules/kb-sync/kb-sync.service.d.ts +45 -0
  76. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -0
  77. package/dist/modules/kb-sync/kb-sync.service.js +183 -0
  78. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -0
  79. package/dist/modules/kb-sync/sync-auth.d.ts +59 -0
  80. package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -0
  81. package/dist/modules/kb-sync/sync-auth.js +62 -0
  82. package/dist/modules/kb-sync/sync-auth.js.map +1 -0
  83. package/dist/modules/kb-sync/sync-payload.d.ts +32 -0
  84. package/dist/modules/kb-sync/sync-payload.d.ts.map +1 -0
  85. package/dist/modules/kb-sync/sync-payload.js +105 -0
  86. package/dist/modules/kb-sync/sync-payload.js.map +1 -0
  87. package/dist/modules/marketplace/git-http.routes.d.ts +42 -0
  88. package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -0
  89. package/dist/modules/marketplace/git-http.routes.js +190 -0
  90. package/dist/modules/marketplace/git-http.routes.js.map +1 -0
  91. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +23 -0
  92. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -0
  93. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +59 -0
  94. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -0
  95. package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts +59 -0
  96. package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts.map +1 -0
  97. package/dist/modules/marketplace/github-facade/github-facade-codes.store.js +96 -0
  98. package/dist/modules/marketplace/github-facade/github-facade-codes.store.js.map +1 -0
  99. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +99 -0
  100. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -0
  101. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +147 -0
  102. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -0
  103. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts +47 -0
  104. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -0
  105. package/dist/modules/marketplace/github-facade/github-facade.routes.js +228 -0
  106. package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -0
  107. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +109 -0
  108. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -0
  109. package/dist/modules/marketplace/github-facade/github-facade.service.js +172 -0
  110. package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -0
  111. package/dist/modules/marketplace/github-facade/index.d.ts +6 -0
  112. package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -0
  113. package/dist/modules/marketplace/github-facade/index.js +6 -0
  114. package/dist/modules/marketplace/github-facade/index.js.map +1 -0
  115. package/dist/modules/marketplace/index.d.ts +4 -0
  116. package/dist/modules/marketplace/index.d.ts.map +1 -0
  117. package/dist/modules/marketplace/index.js +4 -0
  118. package/dist/modules/marketplace/index.js.map +1 -0
  119. package/dist/modules/marketplace/marketplace-repo.service.d.ts +122 -0
  120. package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -0
  121. package/dist/modules/marketplace/marketplace-repo.service.js +300 -0
  122. package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -0
  123. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  124. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +4 -0
  125. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  126. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +14 -8
  127. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  128. package/dist/modules/mcp/oauth/oauth-consent.routes.js +13 -1
  129. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  130. package/dist/modules/mcp/oauth/oauth-state.d.ts +13 -2
  131. package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -1
  132. package/dist/modules/mcp/oauth/oauth-state.js +3 -1
  133. package/dist/modules/mcp/oauth/oauth-state.js.map +1 -1
  134. package/dist/modules/plugins/compile/compile-marketplace.d.ts +97 -0
  135. package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -0
  136. package/dist/modules/plugins/compile/compile-marketplace.js +240 -0
  137. package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -0
  138. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +66 -0
  139. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -0
  140. package/dist/modules/plugins/compile/marketplace-compiler.service.js +132 -0
  141. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -0
  142. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +37 -0
  143. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -0
  144. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +135 -0
  145. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -0
  146. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +45 -0
  147. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -0
  148. package/dist/modules/plugins/discovery/bundle-dialect/registry.js +147 -0
  149. package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -0
  150. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +37 -0
  151. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -0
  152. package/dist/modules/plugins/discovery/kb-plugin-source.js +107 -0
  153. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -0
  154. package/dist/modules/plugins/discovery/native.source.d.ts +15 -0
  155. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -0
  156. package/dist/modules/plugins/discovery/native.source.js +102 -0
  157. package/dist/modules/plugins/discovery/native.source.js.map +1 -0
  158. package/dist/modules/plugins/discovery/plugin-source.d.ts +96 -0
  159. package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -0
  160. package/dist/modules/plugins/discovery/plugin-source.js +2 -0
  161. package/dist/modules/plugins/discovery/plugin-source.js.map +1 -0
  162. package/dist/modules/plugins/index.d.ts +10 -1
  163. package/dist/modules/plugins/index.d.ts.map +1 -1
  164. package/dist/modules/plugins/index.js +8 -0
  165. package/dist/modules/plugins/index.js.map +1 -1
  166. package/dist/modules/plugins/plugin-links.d.ts +66 -0
  167. package/dist/modules/plugins/plugin-links.d.ts.map +1 -0
  168. package/dist/modules/plugins/plugin-links.js +160 -0
  169. package/dist/modules/plugins/plugin-links.js.map +1 -0
  170. package/dist/modules/plugins/plugin-links.service.d.ts +114 -0
  171. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -0
  172. package/dist/modules/plugins/plugin-links.service.js +304 -0
  173. package/dist/modules/plugins/plugin-links.service.js.map +1 -0
  174. package/dist/modules/plugins/plugin-provision.service.d.ts +39 -3
  175. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  176. package/dist/modules/plugins/plugin-provision.service.js +152 -52
  177. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  178. package/dist/modules/plugins/plugin-rename.service.d.ts +69 -0
  179. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -0
  180. package/dist/modules/plugins/plugin-rename.service.js +245 -0
  181. package/dist/modules/plugins/plugin-rename.service.js.map +1 -0
  182. package/dist/modules/plugins/plugins.contract.d.ts +27 -1
  183. package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
  184. package/dist/modules/plugins/plugins.routes.d.ts +7 -1
  185. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  186. package/dist/modules/plugins/plugins.routes.js +120 -12
  187. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  188. package/dist/modules/plugins/plugins.service.d.ts +50 -3
  189. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  190. package/dist/modules/plugins/plugins.service.js +108 -47
  191. package/dist/modules/plugins/plugins.service.js.map +1 -1
  192. package/dist/modules/secrets-vault/db-secrets-vault.service.js +1 -1
  193. package/dist/modules/secrets-vault/db-secrets-vault.service.js.map +1 -1
  194. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +6 -0
  195. package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
  196. package/dist/modules/secrets-vault/secrets-vault.routes.js +31 -1
  197. package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
  198. package/dist/modules/settings/deployment-settings.service.d.ts +9 -0
  199. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  200. package/dist/modules/settings/deployment-settings.service.js +82 -2
  201. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  202. package/dist/modules/settings/setup.routes.d.ts +26 -0
  203. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  204. package/dist/modules/settings/setup.routes.js +9 -1
  205. package/dist/modules/settings/setup.routes.js.map +1 -1
  206. package/dist/modules/skills/index.d.ts +1 -0
  207. package/dist/modules/skills/index.d.ts.map +1 -1
  208. package/dist/modules/skills/index.js +1 -0
  209. package/dist/modules/skills/index.js.map +1 -1
  210. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  211. package/dist/modules/skills/pending-skills.service.js +9 -4
  212. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  213. package/dist/modules/skills/skill-access-requests.routes.d.ts +38 -0
  214. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -0
  215. package/dist/modules/skills/skill-access-requests.routes.js +168 -0
  216. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -0
  217. package/dist/modules/skills/skills.contract.d.ts +31 -0
  218. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  219. package/dist/modules/skills/skills.routes.d.ts +28 -3
  220. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  221. package/dist/modules/skills/skills.routes.js +42 -3
  222. package/dist/modules/skills/skills.routes.js.map +1 -1
  223. package/dist/modules/skills/skills.service.d.ts +4 -0
  224. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  225. package/dist/modules/skills/skills.service.js +83 -22
  226. package/dist/modules/skills/skills.service.js.map +1 -1
  227. package/dist/modules/tool-auth/external-api-key.interface.d.ts +18 -1
  228. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  229. package/dist/modules/tool-auth/external-api-key.interface.js +2 -1
  230. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  231. package/dist/modules/tool-auth/external-api-key.service.d.ts +13 -4
  232. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  233. package/dist/modules/tool-auth/external-api-key.service.js +26 -9
  234. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  235. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +36 -0
  236. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  237. package/dist/modules/tool-manuals/mcp-json-discovery.js +130 -83
  238. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  239. package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
  240. package/dist/modules/tool-manuals/mcp-server-edit.service.js +10 -4
  241. package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
  242. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts +83 -0
  243. package/dist/modules/tool-manuals/tool-manuals.contract.d.ts.map +1 -1
  244. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +15 -2
  245. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  246. package/dist/modules/tool-manuals/tool-manuals.service.js +199 -38
  247. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  248. package/dist/modules/workflow/git/git.service.d.ts +58 -3
  249. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  250. package/dist/modules/workflow/git/git.service.js +325 -60
  251. package/dist/modules/workflow/git/git.service.js.map +1 -1
  252. package/dist/modules/workflow/git/pull-request.service.d.ts +6 -0
  253. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  254. package/dist/modules/workflow/git/pull-request.service.js +8 -0
  255. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  256. package/dist/modules/workflow/workflow.routes.d.ts +2 -1
  257. package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
  258. package/dist/modules/workflow/workflow.routes.js +98 -9
  259. package/dist/modules/workflow/workflow.routes.js.map +1 -1
  260. package/dist/modules/workflow/workflow.service.d.ts +133 -9
  261. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  262. package/dist/modules/workflow/workflow.service.js +462 -52
  263. package/dist/modules/workflow/workflow.service.js.map +1 -1
  264. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +35 -0
  265. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -0
  266. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +121 -0
  267. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -0
  268. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  269. package/dist/modules/workspace/startup/steps/seed-tree.js +46 -2
  270. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  271. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  272. package/dist/modules/workspace/startup/steps/template-files.step.js +112 -25
  273. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  274. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  275. package/dist/modules/workspace/workspace.routes.js +11 -14
  276. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  277. package/dist/modules/workspace/workspace.service.d.ts +38 -1
  278. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  279. package/dist/modules/workspace/workspace.service.js +131 -20
  280. package/dist/modules/workspace/workspace.service.js.map +1 -1
  281. package/dist/shared/domain-errors.d.ts +20 -0
  282. package/dist/shared/domain-errors.d.ts.map +1 -1
  283. package/dist/shared/domain-errors.js +29 -0
  284. package/dist/shared/domain-errors.js.map +1 -1
  285. package/dist/shared/fs-errors.d.ts +8 -0
  286. package/dist/shared/fs-errors.d.ts.map +1 -0
  287. package/dist/shared/fs-errors.js +11 -0
  288. package/dist/shared/fs-errors.js.map +1 -0
  289. package/dist/shared/fs-walk.d.ts +20 -6
  290. package/dist/shared/fs-walk.d.ts.map +1 -1
  291. package/dist/shared/fs-walk.js +35 -27
  292. package/dist/shared/fs-walk.js.map +1 -1
  293. package/dist/shared/kb-walk.d.ts +52 -0
  294. package/dist/shared/kb-walk.d.ts.map +1 -0
  295. package/dist/shared/kb-walk.js +76 -0
  296. package/dist/shared/kb-walk.js.map +1 -0
  297. package/dist/shared/path-order.d.ts +18 -0
  298. package/dist/shared/path-order.d.ts.map +1 -0
  299. package/dist/shared/path-order.js +35 -0
  300. package/dist/shared/path-order.js.map +1 -0
  301. package/dist/shared/token-crypto.d.ts +17 -2
  302. package/dist/shared/token-crypto.d.ts.map +1 -1
  303. package/dist/shared/token-crypto.js +25 -8
  304. package/dist/shared/token-crypto.js.map +1 -1
  305. package/dist/shared/ttl-cache.d.ts +28 -1
  306. package/dist/shared/ttl-cache.d.ts.map +1 -1
  307. package/dist/shared/ttl-cache.js +33 -1
  308. package/dist/shared/ttl-cache.js.map +1 -1
  309. package/kb-template/.bevelignore +1 -1
  310. package/kb-template/AGENTS.md +95 -30
  311. package/kb-template/Skills/.gitkeep +0 -0
  312. package/migrations/0005_github_facade.sql +25 -0
  313. package/migrations/meta/0005_snapshot.json +1717 -0
  314. package/migrations/meta/_journal.json +7 -0
  315. package/package.json +6 -6
  316. package/src/__tests__/core-config.admin.test.ts +21 -0
  317. package/src/__tests__/kb-layout-config.test.ts +110 -0
  318. package/src/core/__tests__/catalog-cache-invalidation.test.ts +146 -0
  319. package/src/core/catalog-cache-invalidation.ts +92 -0
  320. package/src/core/create-core-server.ts +126 -7
  321. package/src/core/create-core-services.ts +173 -41
  322. package/src/core-config.ts +14 -7
  323. package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +86 -0
  324. package/src/modules/access/__tests__/access-control.service.test.ts +19 -3
  325. package/src/modules/access/__tests__/access-plugin-principals.test.ts +368 -0
  326. package/src/modules/access/__tests__/groups-admin.service.test.ts +8 -0
  327. package/src/modules/access/access-control.interface.ts +20 -9
  328. package/src/modules/access/access-control.service.ts +240 -78
  329. package/src/modules/access/access.routes.ts +96 -14
  330. package/src/modules/access-model/__tests__/access-grammar.test.ts +18 -0
  331. package/src/modules/access-model/__tests__/plugin-token-canonical.test.ts +40 -0
  332. package/src/modules/access-model/access-grammar.ts +114 -2
  333. package/src/modules/access-model/group-files.ts +4 -0
  334. package/src/modules/access-model/kb-read-filter.ts +22 -0
  335. package/src/modules/access-model/plugin-principals.ts +137 -0
  336. package/src/modules/connection-probe/__tests__/connection-probe.service.test.ts +685 -0
  337. package/src/modules/connection-probe/connection-probe.contract.ts +44 -0
  338. package/src/modules/connection-probe/connection-probe.service.ts +734 -0
  339. package/src/modules/connection-probe/index.ts +2 -0
  340. package/src/modules/database/core-schema.ts +54 -0
  341. package/src/modules/diff/diff.routes.ts +9 -5
  342. package/src/modules/kb-fs/__tests__/mutex.test.ts +107 -0
  343. package/src/modules/kb-fs/mutex.ts +50 -5
  344. package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +297 -0
  345. package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +295 -0
  346. package/src/modules/kb-sync/__tests__/sync-auth.test.ts +118 -0
  347. package/src/modules/kb-sync/__tests__/sync-payload.test.ts +148 -0
  348. package/src/modules/kb-sync/index.ts +19 -0
  349. package/src/modules/kb-sync/kb-sync.interface.ts +79 -0
  350. package/src/modules/kb-sync/kb-sync.routes.ts +197 -0
  351. package/src/modules/kb-sync/kb-sync.service.ts +201 -0
  352. package/src/modules/kb-sync/sync-auth.ts +118 -0
  353. package/src/modules/kb-sync/sync-payload.ts +143 -0
  354. package/src/modules/marketplace/__tests__/github-facade.test.ts +443 -0
  355. package/src/modules/marketplace/__tests__/marketplace-git.test.ts +179 -0
  356. package/src/modules/marketplace/git-http.routes.ts +193 -0
  357. package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +78 -0
  358. package/src/modules/marketplace/github-facade/github-facade-codes.store.ts +145 -0
  359. package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +208 -0
  360. package/src/modules/marketplace/github-facade/github-facade.routes.ts +268 -0
  361. package/src/modules/marketplace/github-facade/github-facade.service.ts +221 -0
  362. package/src/modules/marketplace/github-facade/index.ts +25 -0
  363. package/src/modules/marketplace/index.ts +3 -0
  364. package/src/modules/marketplace/marketplace-repo.service.ts +337 -0
  365. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -0
  366. package/src/modules/mcp/oauth/oauth-consent.routes.ts +24 -2
  367. package/src/modules/mcp/oauth/oauth-state.ts +15 -3
  368. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +163 -0
  369. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +294 -0
  370. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +56 -12
  371. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +287 -0
  372. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +205 -5
  373. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +394 -0
  374. package/src/modules/plugins/__tests__/plugins.routes.test.ts +80 -33
  375. package/src/modules/plugins/compile/compile-marketplace.ts +403 -0
  376. package/src/modules/plugins/compile/marketplace-compiler.service.ts +150 -0
  377. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +329 -0
  378. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +140 -0
  379. package/src/modules/plugins/discovery/bundle-dialect/registry.ts +175 -0
  380. package/src/modules/plugins/discovery/kb-plugin-source.ts +114 -0
  381. package/src/modules/plugins/discovery/native.source.ts +122 -0
  382. package/src/modules/plugins/discovery/plugin-source.ts +99 -0
  383. package/src/modules/plugins/index.ts +10 -0
  384. package/src/modules/plugins/plugin-links.service.ts +363 -0
  385. package/src/modules/plugins/plugin-links.ts +184 -0
  386. package/src/modules/plugins/plugin-provision.service.ts +160 -54
  387. package/src/modules/plugins/plugin-rename.service.ts +298 -0
  388. package/src/modules/plugins/plugins.contract.ts +28 -1
  389. package/src/modules/plugins/plugins.routes.ts +128 -11
  390. package/src/modules/plugins/plugins.service.ts +106 -53
  391. package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +12 -0
  392. package/src/modules/secrets-vault/__tests__/oauth-return-to.route.test.ts +10 -3
  393. package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +26 -0
  394. package/src/modules/secrets-vault/db-secrets-vault.service.ts +1 -1
  395. package/src/modules/secrets-vault/secrets-vault.routes.ts +35 -1
  396. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +57 -0
  397. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +77 -0
  398. package/src/modules/settings/deployment-settings.service.ts +95 -2
  399. package/src/modules/settings/setup.routes.ts +24 -0
  400. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +128 -0
  401. package/src/modules/skills/__tests__/skills.service.test.ts +85 -0
  402. package/src/modules/skills/index.ts +1 -0
  403. package/src/modules/skills/pending-skills.service.ts +7 -5
  404. package/src/modules/skills/skill-access-requests.routes.ts +199 -0
  405. package/src/modules/skills/skills.contract.ts +32 -0
  406. package/src/modules/skills/skills.routes.ts +59 -3
  407. package/src/modules/skills/skills.service.ts +84 -22
  408. package/src/modules/tool-auth/external-api-key.interface.ts +20 -1
  409. package/src/modules/tool-auth/external-api-key.service.ts +31 -13
  410. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +35 -1
  411. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +2 -0
  412. package/src/modules/tool-manuals/__tests__/tool-manuals.health-check.test.ts +104 -0
  413. package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +56 -0
  414. package/src/modules/tool-manuals/mcp-json-discovery.ts +154 -76
  415. package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -4
  416. package/src/modules/tool-manuals/tool-manuals.contract.ts +89 -0
  417. package/src/modules/tool-manuals/tool-manuals.service.ts +210 -37
  418. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +1 -1
  419. package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +139 -0
  420. package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +337 -0
  421. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +1 -0
  422. package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +7 -1
  423. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +97 -10
  424. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +119 -2
  425. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +437 -0
  426. package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +86 -0
  427. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +3 -3
  428. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +47 -4
  429. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +253 -0
  430. package/src/modules/workflow/git/git.service.ts +354 -63
  431. package/src/modules/workflow/git/pull-request.service.ts +9 -0
  432. package/src/modules/workflow/workflow.routes.ts +104 -4
  433. package/src/modules/workflow/workflow.service.ts +505 -64
  434. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +6 -7
  435. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +93 -0
  436. package/src/modules/workspace/__tests__/workspace.service.missing-branch.test.ts +28 -0
  437. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +42 -12
  438. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +219 -4
  439. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +129 -0
  440. package/src/modules/workspace/startup/steps/seed-tree.ts +44 -2
  441. package/src/modules/workspace/startup/steps/template-files.step.ts +131 -27
  442. package/src/modules/workspace/workspace.routes.ts +17 -21
  443. package/src/modules/workspace/workspace.service.ts +128 -19
  444. package/src/shared/__tests__/filename.test.ts +0 -0
  445. package/src/shared/__tests__/fs-walk.test.ts +22 -1
  446. package/src/shared/__tests__/kb-walk.test.ts +107 -0
  447. package/src/shared/__tests__/path-order.test.ts +24 -0
  448. package/src/shared/__tests__/token-crypto.test.ts +34 -0
  449. package/src/shared/__tests__/ttl-cache.test.ts +55 -0
  450. package/src/shared/domain-errors.ts +29 -0
  451. package/src/shared/fs-errors.ts +10 -0
  452. package/src/shared/fs-walk.ts +38 -23
  453. package/src/shared/kb-walk.ts +95 -0
  454. package/src/shared/path-order.ts +33 -0
  455. package/src/shared/token-crypto.ts +28 -10
  456. package/src/shared/ttl-cache.ts +33 -1
@@ -1,3 +1,4 @@
1
+ import path from 'node:path';
1
2
  import type { AuthProviderPlugin } from '../modules/auth/auth.routes.js';
2
3
  import type { AuthUser } from '@bevel-software/platform-shared';
3
4
  import {
@@ -5,7 +6,7 @@ import {
5
6
  configureBranchModel,
6
7
  validateBranchModel,
7
8
  PROTECTED_BRANCHES,
8
- PLUGINS_DIR,
9
+ configureKbLayout,
9
10
  } from '@bevel-software/platform-shared';
10
11
  import { CoreConfig } from '../core-config.js';
11
12
  import { getDb, type Database } from '../modules/database/connection.js';
@@ -15,10 +16,20 @@ import { WorkspaceService } from '../modules/workspace/workspace.service.js';
15
16
  import { RoutineWritePolicyService } from '../modules/workspace/routine-write-policy.js';
16
17
  import { KbStartupRunner } from '../modules/workspace/startup/kb-startup-runner.js';
17
18
  import { GroupsToPluginsStep } from '../modules/workspace/startup/steps/groups-to-plugins.step.js';
19
+ import { PluginManifestsStep } from '../modules/workspace/startup/steps/plugin-manifests.step.js';
18
20
  import { TemplateFilesStep } from '../modules/workspace/startup/steps/template-files.step.js';
19
21
  import { RolesYamlStep } from '../modules/workspace/startup/steps/roles-yaml.step.js';
20
22
  import { buildSeedTree } from '../modules/workspace/startup/steps/seed-tree.js';
21
23
  import { DeploymentSettingsService } from '../modules/settings/deployment-settings.service.js';
24
+ import { KbSyncService } from '../modules/kb-sync/kb-sync.service.js';
25
+
26
+ /** The hosted MCP endpoint at a deployment address, with any userinfo stripped. */
27
+ function mcpEndpointUrl(publicBackendUrl: string): string {
28
+ const url = new URL('/api/mcp', publicBackendUrl);
29
+ url.username = '';
30
+ url.password = '';
31
+ return url.toString();
32
+ }
22
33
 
23
34
  /** `a.com, b.com` → `['a.com','b.com']`, tolerating a leading `@` or `.`. */
24
35
  function parseDomainList(raw: string): string[] {
@@ -40,12 +51,32 @@ import { GroupsAdminService } from '../modules/access/groups-admin.service.js';
40
51
  import { PendingSkillsService, SkillService } from '../modules/skills/index.js';
41
52
  import { ToolManualService } from '../modules/tool-manuals/index.js';
42
53
  import { McpServerEditService } from '../modules/tool-manuals/mcp-server-edit.service.js';
43
- import { PluginIndexService, PluginProvisionService, JoinRequestsService } from '../modules/plugins/index.js';
54
+ import {
55
+ PluginIndexService,
56
+ PluginProvisionService,
57
+ JoinRequestsService,
58
+ PluginLinkIndex,
59
+ PluginLinksService,
60
+ PluginRenameService,
61
+ MarketplaceCompilerService,
62
+ KbPluginSource,
63
+ } from '../modules/plugins/index.js';
64
+ import { MarketplaceRepoService } from '../modules/marketplace/index.js';
65
+ import {
66
+ GitHubFacadeCredentialsService,
67
+ GitHubFacade,
68
+ DbGitHubFacadeCodeStore,
69
+ DbGitHubFacadeCredentialsStore,
70
+ CLAUDE_CONSUMER,
71
+ GITHUB_LINK_KEY_KIND,
72
+ GITHUB_LINK_KEY_PREFIX,
73
+ } from '../modules/marketplace/github-facade/index.js';
44
74
  import {
45
75
  DbSecretsVaultService,
46
76
  McpOAuthDiscoveryService,
47
77
  registerBevelSecretsVariableLoader,
48
78
  } from '../modules/secrets-vault/index.js';
79
+ import { ConnectionProbeService } from '../modules/connection-probe/index.js';
49
80
  import { GitService } from '../modules/workflow/git/git.service.js';
50
81
  import { PullRequestService } from '../modules/workflow/git/pull-request.service.js';
51
82
  import { WorkspaceMutex } from '../modules/kb-fs/mutex.js';
@@ -93,6 +124,7 @@ import { TokenCrypto } from '../shared/token-crypto.js';
93
124
  import { UpdateCheckService } from '../modules/update-check/update-check.service.js';
94
125
  import { resolveAppVersion } from '../version.js';
95
126
  import { noopRecoveryAgent, type CorePorts } from './core-ports.js';
127
+ import { registerCatalogCacheInvalidation } from './catalog-cache-invalidation.js';
96
128
 
97
129
  /**
98
130
  * Everything the CORE composition builds: the core services `createCoreServer`
@@ -127,6 +159,18 @@ export interface CoreServices {
127
159
  pluginIndexService: PluginIndexService;
128
160
  pluginProvisionService: PluginProvisionService;
129
161
  joinRequestsService: JoinRequestsService;
162
+ /** Which plugins hold which skills (inline or linked) — see `PluginLinkIndex`. */
163
+ pluginLinkIndex: PluginLinkIndex;
164
+ /** Link / unlink / repair shared skills into plugins. */
165
+ pluginLinksService: PluginLinksService;
166
+ pluginRenameService: PluginRenameService;
167
+ /** Source layout → distribution layout, for one audience. */
168
+ marketplaceCompiler: MarketplaceCompilerService;
169
+ /** The bare repository the per-user marketplace git endpoint serves from. */
170
+ marketplaceRepo: MarketplaceRepoService;
171
+ /** The GitHub Enterprise facade products such as claude.ai add the marketplace through. */
172
+ githubFacade: GitHubFacade;
173
+ githubFacadeCredentials: GitHubFacadeCredentialsService;
130
174
  authService: AuthService;
131
175
  authMiddleware: ReturnType<typeof createAuthMiddleware>;
132
176
  accountErasureService: AccountErasureService;
@@ -136,6 +180,8 @@ export interface CoreServices {
136
180
  reviewWorkflowService: ReviewWorkflowService;
137
181
  fileLockService: FileLockService;
138
182
  workflowService: WorkflowService;
183
+ /** Remote sync (`POST /api/sync`): a git host or pipeline makes the clones catch up. */
184
+ kbSyncService: KbSyncService;
139
185
  eventBus: WorkflowEventBus;
140
186
  fileChangeNotifier: FileChangeNotifier;
141
187
  pendingCommitsService: PendingCommitsService;
@@ -169,6 +215,7 @@ export interface CoreServices {
169
215
  */
170
216
  kbDirName: string;
171
217
  secretsVaultService: DbSecretsVaultService;
218
+ connectionProbeService: ConnectionProbeService;
172
219
  externalApiKeyService: ExternalApiKeyService;
173
220
  internalTokenService: InternalTokenService;
174
221
  mcpService: McpService;
@@ -240,6 +287,11 @@ export async function createCoreServices(
240
287
  protectedBranches: settings.resolve('protectedBranches'),
241
288
  };
242
289
  if (!validateBranchModel(branchModel)) configureBranchModel(branchModel);
290
+ // The KB layout — the three renameable roots — applied the same way and at
291
+ // the same moment, before any service captures a root name. Unlike the
292
+ // branch model it always resolves (every root has a default), so an invalid
293
+ // value is a real misconfiguration and stops the boot.
294
+ configureKbLayout(settings.resolveKbLayout());
243
295
  // A token supplied through the setup screen has to reach the credential
244
296
  // helper, which reads `$GITHUB_TOKEN` at call time.
245
297
  settings.syncGitTokenEnv();
@@ -267,6 +319,7 @@ export async function createCoreServices(
267
319
  // whatever the distribution appends.
268
320
  const kbStartupSteps = [
269
321
  new GroupsToPluginsStep(),
322
+ new PluginManifestsStep(),
270
323
  new TemplateFilesStep(extraDirs),
271
324
  new RolesYamlStep([config.adminEmail]),
272
325
  ...(ports.kbStartupSteps ?? []),
@@ -322,17 +375,27 @@ export async function createCoreServices(
322
375
  // Tool manuals: user-authored `*.tool` files under `Plugins/` in the default
323
376
  // branch — access-controlled like Skills, served to external agents via
324
377
  // `GET /api/agent/all-tools` and registered on the MCP proxy's UTCP client.
325
- const toolManualService = new ToolManualService(workspaceService, accessControl, kbDirName);
378
+ // Where plugins come from: one walk of the plugins root that reads every
379
+ // plugin folder in whichever file shape it carries (see
380
+ // modules/plugins/discovery). Nothing to configure, nothing to document.
381
+ const pluginSource = new KbPluginSource();
382
+ const toolManualService = new ToolManualService(workspaceService, accessControl, kbDirName, Date.now, pluginSource);
326
383
  // Plugins: the folders under `Plugins/` that carry a
327
384
  // team's skills AND the tools they need. Enumerated for EVERY authenticated
328
385
  // caller — a plugin they cannot read still exists for them, as a locked one —
329
386
  // with the counts read off the two catalogs above rather than a second scan.
387
+ // The link index resolves manifests against the released catalog, and the
388
+ // plugin index counts through it (inline + linked), so it comes first.
389
+ const pluginLinkIndex = new PluginLinkIndex(workspaceService, skillService, accessControl, kbDirName, Date.now, pluginSource);
330
390
  const pluginIndexService = new PluginIndexService(
331
391
  workspaceService,
332
392
  accessControl,
333
393
  skillService,
334
394
  toolManualService,
335
395
  kbDirName,
396
+ Date.now,
397
+ pluginLinkIndex,
398
+ pluginSource,
336
399
  );
337
400
  // Auth service — resolves identities for login, PR author attribution, and
338
401
  // access lookups. (Change requests now store the author email directly, so
@@ -448,6 +511,62 @@ export async function createCoreServices(
448
511
  // only needs to read files at refs and to close a request whose proposals
449
512
  // have all landed.
450
513
  const joinRequestsService = new JoinRequestsService(workspaceService, workflowService);
514
+ // Links land as ordinary default-branch commits and change what the plugin
515
+ // index counts, so a link drops that cache too.
516
+ const pluginLinksService = new PluginLinksService(
517
+ workspaceService,
518
+ workflowService,
519
+ accessControl,
520
+ skillService,
521
+ pluginLinkIndex,
522
+ kbDirName,
523
+ eventBus,
524
+ () => pluginIndexService.invalidate(),
525
+ );
526
+ // A rename rewrites grants across the knowledge base in one batch commit,
527
+ // then drops every cache that keyed on the old identity.
528
+ const pluginRenameService = new PluginRenameService(
529
+ workspaceService,
530
+ workflowService,
531
+ accessControl,
532
+ pluginSource,
533
+ kbDirName,
534
+ eventBus,
535
+ () => {
536
+ pluginIndexService.invalidate();
537
+ pluginLinkIndex.invalidate();
538
+ },
539
+ );
540
+ // Source → distribution. The marketplace's identity is fixed for now; the
541
+ // per-user git endpoint and any mirror both compile through this.
542
+ const marketplaceCompiler = new MarketplaceCompilerService(
543
+ workspaceService,
544
+ accessControl,
545
+ skillService,
546
+ pluginLinkIndex,
547
+ kbDirName,
548
+ {
549
+ name: 'hexis',
550
+ owner: 'Hexis',
551
+ description: 'Skills and plugins from this knowledge base',
552
+ // The knowledge base as a tool, shipped in the skills plugin so an
553
+ // agent installing from the marketplace can read what its skills refer
554
+ // to. The same address `/api/config` and the OAuth metadata publish,
555
+ // userinfo stripped; the client's OAuth flow supplies the identity.
556
+ knowledgeBaseMcp: { name: 'hexis', url: mcpEndpointUrl(config.publicBackendUrl) },
557
+ },
558
+ pluginSource,
559
+ );
560
+ // Sibling of the workspaces root, like the spill store: one bare repo, one
561
+ // git namespace per caller (see marketplace-repo.service.ts).
562
+ const marketplaceRepo = new MarketplaceRepoService(
563
+ path.resolve(config.workspacesRoot, '..', 'marketplace.git'),
564
+ marketplaceCompiler,
565
+ );
566
+
567
+ // Remote sync. Drives the workflow module's per-branch pull-and-announce
568
+ // over every clone the workspace service knows about; owns no git of its own.
569
+ const kbSyncService = new KbSyncService(workflowService, workspaceService);
451
570
  // Server-scoped MCP editing — the tool page's edit form. One server's truth
452
571
  // spans a plugin's mcp.json AND plugin.json extensions block, and this is
453
572
  // the ONE writer that rewrites both entries and commits them together (see
@@ -484,43 +603,16 @@ export async function createCoreServices(
484
603
  workflowService,
485
604
  );
486
605
 
487
- // Subscriber A — catalog freshness: a committed change drops the affected
488
- // caches immediately instead of waiting out their TTLs. The tool/skill
489
- // catalogs are global but read the DEFAULT branch only, so only
490
- // default-branch changes under their folders matter. (The kb-graph id-index
491
- // invalidation that used to live in this subscriber is registered by the
492
- // enterprise overlay, next to the kb-graph service it belongs to — the
493
- // caches are independent, so the split preserves behavior.)
494
- fileChangeNotifier.onFilesChanged(({ branch, paths }) => {
495
- if (branch !== DEFAULT_BRANCH) return;
496
- // Skills, tools and the plugin index all live under `Plugins/`, so one
497
- // touch check drives all three caches. An access grant lands as a
498
- // default-branch change to `Plugins/<plugin>/access.md`, so this is also
499
- // what makes a newly-granted plugin unlock within one round-trip instead
500
- // of one TTL.
501
- const touched = paths.some((p) => p.startsWith(`${kbDirName}/${PLUGINS_DIR}/`));
502
- if (touched) {
503
- toolManualService.invalidate();
504
- skillService.invalidate();
505
- pluginIndexService.invalidate();
506
- }
507
-
508
- });
509
-
510
- // Subscriber B — WRITE-time freshness for the same three caches. The
511
- // workspace routes emit `fs-tree-changed` the moment bytes hit a working
512
- // tree; Subscriber A above fires only when the ASYNC commit lands. Between
513
- // the two, "create a skill, reload the catalog" raced the commit pipeline
514
- // and lost — the new skill's card stayed invisible until a refresh outlived
515
- // the TTL. The catalogs scan the working tree anyway, so invalidating at
516
- // write time makes the very next read see the file. No path filter: this
517
- // event carries none, and a spurious drop only costs one re-scan.
518
- eventBus.onEmit((event) => {
519
- if (event.kind !== 'fs-tree-changed') return;
520
- if (!('branch' in event) || event.branch !== DEFAULT_BRANCH) return;
521
- toolManualService.invalidate();
522
- skillService.invalidate();
523
- pluginIndexService.invalidate();
606
+ // Catalog freshness: the skill / tool-manual / plugin-index caches all scan
607
+ // the DEFAULT branch's working tree, and all three go stale on the same
608
+ // events (a commit, a working-tree write, a merge). Wired in one place so a
609
+ // new way of reaching the default branch cannot refresh two of them and
610
+ // leave the third serving last minute's answer.
611
+ registerCatalogCacheInvalidation({
612
+ eventBus,
613
+ fileChangeNotifier,
614
+ kbDirName,
615
+ catalogs: [toolManualService, skillService, pluginIndexService, pluginLinkIndex],
524
616
  });
525
617
 
526
618
  // Admin = `Admin` role in roles.yaml, resolved through the access model on the
@@ -562,6 +654,14 @@ export async function createCoreServices(
562
654
  // UTCP client can resolve `${VAR}` from the caller's secrets at tool-call time.
563
655
  registerBevelSecretsVariableLoader(secretsVaultService);
564
656
 
657
+ // The credential probe: whether a tool's stored credential actually WORKS, as
658
+ // distinct from whether one is stored. Built here — after the vault and the
659
+ // tool catalog — because a probe needs both: the catalog to know what to
660
+ // call, the vault to resolve the credential to call it with. Holds no state
661
+ // and takes no `db`: a verdict is returned to the caller that asked for it and
662
+ // never outlives their page (see `ProbeVerdict`).
663
+ const connectionProbeService = new ConnectionProbeService(toolManualService, secretsVaultService);
664
+
565
665
  // Zero-config OAuth for bare `type: mcp` `.tool`s: when the remote server
566
666
  // demands OAuth (MCP authorization spec), discover its authorization server,
567
667
  // dynamically register a public PKCE client, and persist the provider as the
@@ -599,7 +699,30 @@ export async function createCoreServices(
599
699
  // GENERIC proxy: per session it discovers the UTCP manual at /api/agent/utcp
600
700
  // over loopback and re-exposes every tool, dispatching calls back through the
601
701
  // REST tool surface (so agent logic + metering live there, once).
602
- const externalApiKeyService = new ExternalApiKeyService(db, config.externalApiKeyPrefix);
702
+ // Connection keys also come as GitHub-shaped links (`gho_…`, kind
703
+ // `github-link`): the same key, minted by the marketplace facade below when
704
+ // a person connects an account on claude.ai, told apart by its stored kind.
705
+ const externalApiKeyService = new ExternalApiKeyService(db, config.externalApiKeyPrefix, {
706
+ [GITHUB_LINK_KEY_KIND]: GITHUB_LINK_KEY_PREFIX,
707
+ });
708
+
709
+ // The facade that lets products which sync marketplaces only from a GitHub
710
+ // Enterprise Server (claude.ai, Cowork) add the per-user marketplace:
711
+ // generated app credentials an Owner registers once, and the connect flow
712
+ // that turns a hexis session into a connection key the product holds.
713
+ // Reuses the MCP flow's signed state and /connect page — see
714
+ // modules/marketplace/github-facade.
715
+ const githubFacadeCredentials = new GitHubFacadeCredentialsService(
716
+ new DbGitHubFacadeCredentialsStore(db, config.secretsEncKey ? new TokenCrypto(config.secretsEncKey) : null),
717
+ );
718
+ const githubFacade = new GitHubFacade({
719
+ credentials: githubFacadeCredentials,
720
+ codes: new DbGitHubFacadeCodeStore(db),
721
+ keys: externalApiKeyService,
722
+ consumers: [CLAUDE_CONSUMER],
723
+ stateSecret: config.jwtSecret,
724
+ publicFrontendUrl: config.publicFrontendUrl,
725
+ });
603
726
  const mcpSessionStore = new McpSessionStore();
604
727
  const mcpService = new McpService(
605
728
  mcpSessionStore,
@@ -816,6 +939,13 @@ export async function createCoreServices(
816
939
  pluginIndexService,
817
940
  pluginProvisionService,
818
941
  joinRequestsService,
942
+ pluginLinkIndex,
943
+ pluginLinksService,
944
+ pluginRenameService,
945
+ marketplaceCompiler,
946
+ marketplaceRepo,
947
+ githubFacade,
948
+ githubFacadeCredentials,
819
949
  authService,
820
950
  authMiddleware,
821
951
  accountErasureService,
@@ -825,6 +955,7 @@ export async function createCoreServices(
825
955
  reviewWorkflowService,
826
956
  fileLockService,
827
957
  workflowService,
958
+ kbSyncService,
828
959
  eventBus,
829
960
  fileChangeNotifier,
830
961
  pendingCommitsService,
@@ -835,6 +966,7 @@ export async function createCoreServices(
835
966
  createSyncedGroupsMaterializer,
836
967
  updateCheckService,
837
968
  secretsVaultService,
969
+ connectionProbeService,
838
970
  externalApiKeyService,
839
971
  internalTokenService,
840
972
  mcpService,
@@ -2,6 +2,7 @@ import dotenv from 'dotenv';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { defaultKbTemplateDir } from './assets.js';
5
+ import { assertKeyDecodesTo32Bytes } from './shared/token-crypto.js';
5
6
 
6
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
8
  dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
@@ -337,19 +338,25 @@ export class CoreConfig {
337
338
  .split(/[\s,]+/)
338
339
  .map((d) => d.trim().toLowerCase().replace(/^[@.]+/, ''))
339
340
  .filter((d) => d.length > 0);
340
- this.secretsEncKey = (
341
- process.env.SECRETS_ENC_KEY ||
342
- process.env.CONNECTOR_CONFIG_ENC_KEY ||
343
- process.env.SHAREPOINT_TOKEN_ENC_KEY ||
344
- ''
345
- ).trim();
346
- if (!this.secretsEncKey) {
341
+ // Which variable actually supplied the key decides whose name a bad key
342
+ // is reported under: a legacy deploy still on the fallback chain must be
343
+ // told to fix the variable it set, not one it never heard of.
344
+ const secretsKeySource = (
345
+ ['SECRETS_ENC_KEY', 'CONNECTOR_CONFIG_ENC_KEY', 'SHAREPOINT_TOKEN_ENC_KEY'] as const
346
+ ).find((name) => (process.env[name] ?? '').trim() !== '');
347
+ this.secretsEncKey = secretsKeySource ? process.env[secretsKeySource]!.trim() : '';
348
+ if (!secretsKeySource) {
347
349
  throw new Error(
348
350
  'SECRETS_ENC_KEY is required — it encrypts the secrets vault, the MCP OAuth ' +
349
351
  'tokens and the git credential saved from the setup screen. Generate one ' +
350
352
  `with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`,
351
353
  );
352
354
  }
355
+ // Validate the LENGTH here too, while the winning variable's name is in
356
+ // hand. Every later `new TokenCrypto(config.secretsEncKey)` blames
357
+ // SECRETS_ENC_KEY by default, which is only certainly right because a bad
358
+ // key never gets past this line.
359
+ assertKeyDecodesTo32Bytes(this.secretsEncKey, secretsKeySource);
353
360
  this.internalTokenSecret = (process.env.INTERNAL_TOKEN_SECRET || '').trim();
354
361
  // Setting DOMAIN declares "the bundled Caddy `https` profile fronts this
355
362
  // deployment" — one proxy hop, and the public origin IS that domain. The
@@ -0,0 +1,86 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+
6
+ import type { WorkspaceService } from '../../workspace/workspace.service.js';
7
+ import { AccessControlService } from '../access-control.service.js';
8
+
9
+ const KB_DIR = 'knowledge-base';
10
+
11
+ async function writeFile(repo: string, rel: string, contents: string): Promise<void> {
12
+ const abs = path.join(repo, rel);
13
+ await fs.mkdir(path.dirname(abs), { recursive: true });
14
+ await fs.writeFile(abs, contents);
15
+ }
16
+
17
+ /**
18
+ * `invalidate()` fires exactly when the tree changes under a model load that
19
+ * is still reading it: a marketplace compile for one caller drops the caches
20
+ * while a compile for another caller is mid-load, or a commit lands while the
21
+ * explorer builds. The load that started on the OLD tree must not store its
22
+ * model after the drop — otherwise every read for the next TTL resolves
23
+ * against a tree that is already gone.
24
+ */
25
+ describe('AccessControlService — a load that straddles invalidate() never repopulates the cache', () => {
26
+ let root: string;
27
+ const workspaceId = 'ws-invalidate-race';
28
+
29
+ beforeEach(async () => {
30
+ root = await fs.mkdtemp(path.join(os.tmpdir(), 'bevel-invalidate-race-'));
31
+ });
32
+ afterEach(async () => {
33
+ vi.restoreAllMocks();
34
+ await fs.rm(root, { recursive: true, force: true });
35
+ });
36
+
37
+ it('a read that starts after the drop keeps seeing the new tree once the old load lands', async () => {
38
+ const workspaceDir = path.join(root, workspaceId);
39
+ const repo = path.join(workspaceDir, KB_DIR);
40
+ await writeFile(repo, 'roles.yaml', 'roles:\n Admin:\n - admin@x.io\n');
41
+ await writeFile(repo, 'access.md', '---\nwrite:\n - Admin\n---\n');
42
+ // Notes is closed to zoe on the old tree.
43
+ await writeFile(repo, 'Notes/access.md', '---\nread:\n - Admin\n---\n');
44
+ await writeFile(repo, 'Notes/plan.md', '# plan\n');
45
+ const service = new AccessControlService(
46
+ { getWorkspacePath: async () => workspaceDir } as unknown as WorkspaceService,
47
+ KB_DIR,
48
+ );
49
+
50
+ // Hold the OLD load exactly where the walk reads Notes/access.md: the
51
+ // bytes are read now (the closed rules), but the load cannot finish until
52
+ // released — which is what a slow disk or a large tree does for free.
53
+ const realReadFile = fs.readFile.bind(fs);
54
+ let release: () => void = () => undefined;
55
+ const held = new Promise<void>((resolve) => (release = resolve));
56
+ let reachedHold: () => void = () => undefined;
57
+ const reached = new Promise<void>((resolve) => (reachedHold = resolve));
58
+ let holdOnce = true;
59
+ vi.spyOn(fs, 'readFile').mockImplementation(async (file, ...rest) => {
60
+ const result = await (realReadFile as (...a: unknown[]) => Promise<unknown>)(file, ...rest);
61
+ if (holdOnce && String(file).endsWith(path.join('Notes', 'access.md'))) {
62
+ holdOnce = false;
63
+ reachedHold();
64
+ await held;
65
+ }
66
+ return result as never;
67
+ });
68
+ const oldLoad = service.canRead(workspaceId, 'zoe@x.io', 'Notes/plan.md');
69
+ await reached;
70
+
71
+ // The tree moves and the caches are dropped while that load is in flight.
72
+ await writeFile(repo, 'Notes/access.md', '---\nread:\n - Admin\n - Zoe <zoe@x.io>\n---\n');
73
+ service.invalidate(workspaceId);
74
+ try {
75
+ await expect(service.canRead(workspaceId, 'zoe@x.io', 'Notes/plan.md')).resolves.toBe(true);
76
+ } finally {
77
+ // The old load lands (also on a failed assertion, so the test cannot hang).
78
+ release();
79
+ }
80
+
81
+ // It answers for the tree it read — closed — but must not become the
82
+ // cached model for the reads that follow.
83
+ await expect(oldLoad).resolves.toBe(false);
84
+ await expect(service.canRead(workspaceId, 'zoe@x.io', 'Notes/plan.md')).resolves.toBe(true);
85
+ });
86
+ });
@@ -953,7 +953,7 @@ describe('AccessControlService', () => {
953
953
 
954
954
  const svc = new AccessControlService(stubWorkspaceService(workspaceId, workspaceDir), PROCESS_MAP_DIR);
955
955
  const e = await svc.eligibleReaders(workspaceId, 'Knowledge/Foo.md');
956
- expect(e).toEqual({ restricted: true, principals: [], roles: [], users: [] });
956
+ expect(e).toEqual({ restricted: true, principals: [], roles: [], users: [], publicVia: [] });
957
957
  });
958
958
 
959
959
  it('reports restricted=false when read: everyone applies', async () => {
@@ -963,7 +963,17 @@ describe('AccessControlService', () => {
963
963
 
964
964
  const svc = new AccessControlService(stubWorkspaceService(workspaceId, workspaceDir), PROCESS_MAP_DIR);
965
965
  const e = await svc.eligibleReaders(workspaceId, 'Knowledge/Foo.md');
966
- expect(e).toEqual({ restricted: false, principals: [], roles: [], users: [] });
966
+ // The flag says public; the lists still name the grants (write folds into read).
967
+ expect(e).toEqual({
968
+ restricted: false,
969
+ principals: [
970
+ { name: 'Admin', kind: 'role' },
971
+ { name: 'everyone', kind: 'role' },
972
+ ],
973
+ roles: ['Admin', 'everyone'],
974
+ users: [],
975
+ publicVia: [], // public by a literal line, through no plugin
976
+ });
967
977
  });
968
978
 
969
979
  it('restricted=false when a closer everyone grant shadows a farther by-name deny', async () => {
@@ -978,7 +988,13 @@ describe('AccessControlService', () => {
978
988
  // node really is readable by everyone — not restricted.
979
989
  expect(await svc.canRead(workspaceId, 'felix@example.com', 'Knowledge/Open/Foo.md')).toBe(true);
980
990
  const e = await svc.eligibleReaders(workspaceId, 'Knowledge/Open/Foo.md');
981
- expect(e).toEqual({ restricted: false, principals: [], roles: [], users: [] });
991
+ expect(e).toEqual({
992
+ restricted: false,
993
+ principals: [{ name: 'everyone', kind: 'role' }],
994
+ roles: ['everyone'],
995
+ users: [],
996
+ publicVia: [],
997
+ });
982
998
  });
983
999
 
984
1000
  it('restricted=true when a same-scope by-name deny carves someone out of read: everyone', async () => {