@bevel-software/platform-core-backend 0.14.0 → 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 (385) 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 +99 -8
  8. package/dist/core/create-core-server.js.map +1 -1
  9. package/dist/core/create-core-services.d.ts +18 -1
  10. package/dist/core/create-core-services.d.ts.map +1 -1
  11. package/dist/core/create-core-services.js +96 -43
  12. package/dist/core/create-core-services.js.map +1 -1
  13. package/dist/modules/access/access-control.interface.d.ts +18 -6
  14. package/dist/modules/access/access-control.interface.d.ts.map +1 -1
  15. package/dist/modules/access/access-control.service.d.ts +26 -1
  16. package/dist/modules/access/access-control.service.d.ts.map +1 -1
  17. package/dist/modules/access/access-control.service.js +213 -71
  18. package/dist/modules/access/access-control.service.js.map +1 -1
  19. package/dist/modules/access/access.routes.d.ts.map +1 -1
  20. package/dist/modules/access/access.routes.js +59 -7
  21. package/dist/modules/access/access.routes.js.map +1 -1
  22. package/dist/modules/access-model/access-grammar.d.ts +51 -1
  23. package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
  24. package/dist/modules/access-model/access-grammar.js +95 -1
  25. package/dist/modules/access-model/access-grammar.js.map +1 -1
  26. package/dist/modules/access-model/group-files.d.ts.map +1 -1
  27. package/dist/modules/access-model/group-files.js +4 -1
  28. package/dist/modules/access-model/group-files.js.map +1 -1
  29. package/dist/modules/access-model/plugin-principals.d.ts +49 -0
  30. package/dist/modules/access-model/plugin-principals.d.ts.map +1 -0
  31. package/dist/modules/access-model/plugin-principals.js +125 -0
  32. package/dist/modules/access-model/plugin-principals.js.map +1 -0
  33. package/dist/modules/database/core-schema.d.ts +326 -0
  34. package/dist/modules/database/core-schema.d.ts.map +1 -1
  35. package/dist/modules/database/core-schema.js +52 -0
  36. package/dist/modules/database/core-schema.js.map +1 -1
  37. package/dist/modules/kb-sync/index.d.ts +6 -0
  38. package/dist/modules/kb-sync/index.d.ts.map +1 -0
  39. package/dist/modules/kb-sync/index.js +5 -0
  40. package/dist/modules/kb-sync/index.js.map +1 -0
  41. package/dist/modules/kb-sync/kb-sync.interface.d.ts +77 -0
  42. package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -0
  43. package/dist/modules/kb-sync/kb-sync.interface.js +2 -0
  44. package/dist/modules/kb-sync/kb-sync.interface.js.map +1 -0
  45. package/dist/modules/kb-sync/kb-sync.routes.d.ts +70 -0
  46. package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -0
  47. package/dist/modules/kb-sync/kb-sync.routes.js +135 -0
  48. package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -0
  49. package/dist/modules/kb-sync/kb-sync.service.d.ts +45 -0
  50. package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -0
  51. package/dist/modules/kb-sync/kb-sync.service.js +183 -0
  52. package/dist/modules/kb-sync/kb-sync.service.js.map +1 -0
  53. package/dist/modules/kb-sync/sync-auth.d.ts +59 -0
  54. package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -0
  55. package/dist/modules/kb-sync/sync-auth.js +62 -0
  56. package/dist/modules/kb-sync/sync-auth.js.map +1 -0
  57. package/dist/modules/kb-sync/sync-payload.d.ts +32 -0
  58. package/dist/modules/kb-sync/sync-payload.d.ts.map +1 -0
  59. package/dist/modules/kb-sync/sync-payload.js +105 -0
  60. package/dist/modules/kb-sync/sync-payload.js.map +1 -0
  61. package/dist/modules/marketplace/git-http.routes.d.ts +42 -0
  62. package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -0
  63. package/dist/modules/marketplace/git-http.routes.js +190 -0
  64. package/dist/modules/marketplace/git-http.routes.js.map +1 -0
  65. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +23 -0
  66. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -0
  67. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +59 -0
  68. package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -0
  69. package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts +59 -0
  70. package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts.map +1 -0
  71. package/dist/modules/marketplace/github-facade/github-facade-codes.store.js +96 -0
  72. package/dist/modules/marketplace/github-facade/github-facade-codes.store.js.map +1 -0
  73. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +99 -0
  74. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -0
  75. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +147 -0
  76. package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -0
  77. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts +47 -0
  78. package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -0
  79. package/dist/modules/marketplace/github-facade/github-facade.routes.js +228 -0
  80. package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -0
  81. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +109 -0
  82. package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -0
  83. package/dist/modules/marketplace/github-facade/github-facade.service.js +172 -0
  84. package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -0
  85. package/dist/modules/marketplace/github-facade/index.d.ts +6 -0
  86. package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -0
  87. package/dist/modules/marketplace/github-facade/index.js +6 -0
  88. package/dist/modules/marketplace/github-facade/index.js.map +1 -0
  89. package/dist/modules/marketplace/index.d.ts +4 -0
  90. package/dist/modules/marketplace/index.d.ts.map +1 -0
  91. package/dist/modules/marketplace/index.js +4 -0
  92. package/dist/modules/marketplace/index.js.map +1 -0
  93. package/dist/modules/marketplace/marketplace-repo.service.d.ts +122 -0
  94. package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -0
  95. package/dist/modules/marketplace/marketplace-repo.service.js +300 -0
  96. package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -0
  97. package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
  98. package/dist/modules/mcp/oauth/bevel-oauth-provider.js +4 -0
  99. package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
  100. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +14 -8
  101. package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
  102. package/dist/modules/mcp/oauth/oauth-consent.routes.js +13 -1
  103. package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
  104. package/dist/modules/mcp/oauth/oauth-state.d.ts +13 -2
  105. package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -1
  106. package/dist/modules/mcp/oauth/oauth-state.js +3 -1
  107. package/dist/modules/mcp/oauth/oauth-state.js.map +1 -1
  108. package/dist/modules/plugins/compile/compile-marketplace.d.ts +97 -0
  109. package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -0
  110. package/dist/modules/plugins/compile/compile-marketplace.js +240 -0
  111. package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -0
  112. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +66 -0
  113. package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -0
  114. package/dist/modules/plugins/compile/marketplace-compiler.service.js +132 -0
  115. package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -0
  116. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +37 -0
  117. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -0
  118. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +135 -0
  119. package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -0
  120. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +45 -0
  121. package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -0
  122. package/dist/modules/plugins/discovery/bundle-dialect/registry.js +147 -0
  123. package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -0
  124. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +37 -0
  125. package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -0
  126. package/dist/modules/plugins/discovery/kb-plugin-source.js +107 -0
  127. package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -0
  128. package/dist/modules/plugins/discovery/native.source.d.ts +15 -0
  129. package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -0
  130. package/dist/modules/plugins/discovery/native.source.js +102 -0
  131. package/dist/modules/plugins/discovery/native.source.js.map +1 -0
  132. package/dist/modules/plugins/discovery/plugin-source.d.ts +96 -0
  133. package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -0
  134. package/dist/modules/plugins/discovery/plugin-source.js +2 -0
  135. package/dist/modules/plugins/discovery/plugin-source.js.map +1 -0
  136. package/dist/modules/plugins/index.d.ts +10 -1
  137. package/dist/modules/plugins/index.d.ts.map +1 -1
  138. package/dist/modules/plugins/index.js +8 -0
  139. package/dist/modules/plugins/index.js.map +1 -1
  140. package/dist/modules/plugins/plugin-links.d.ts +66 -0
  141. package/dist/modules/plugins/plugin-links.d.ts.map +1 -0
  142. package/dist/modules/plugins/plugin-links.js +160 -0
  143. package/dist/modules/plugins/plugin-links.js.map +1 -0
  144. package/dist/modules/plugins/plugin-links.service.d.ts +114 -0
  145. package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -0
  146. package/dist/modules/plugins/plugin-links.service.js +304 -0
  147. package/dist/modules/plugins/plugin-links.service.js.map +1 -0
  148. package/dist/modules/plugins/plugin-provision.service.d.ts +39 -3
  149. package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
  150. package/dist/modules/plugins/plugin-provision.service.js +152 -52
  151. package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
  152. package/dist/modules/plugins/plugin-rename.service.d.ts +69 -0
  153. package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -0
  154. package/dist/modules/plugins/plugin-rename.service.js +245 -0
  155. package/dist/modules/plugins/plugin-rename.service.js.map +1 -0
  156. package/dist/modules/plugins/plugins.contract.d.ts +27 -1
  157. package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
  158. package/dist/modules/plugins/plugins.routes.d.ts +7 -1
  159. package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
  160. package/dist/modules/plugins/plugins.routes.js +120 -12
  161. package/dist/modules/plugins/plugins.routes.js.map +1 -1
  162. package/dist/modules/plugins/plugins.service.d.ts +50 -3
  163. package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
  164. package/dist/modules/plugins/plugins.service.js +108 -47
  165. package/dist/modules/plugins/plugins.service.js.map +1 -1
  166. package/dist/modules/settings/deployment-settings.service.d.ts +9 -0
  167. package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
  168. package/dist/modules/settings/deployment-settings.service.js +82 -2
  169. package/dist/modules/settings/deployment-settings.service.js.map +1 -1
  170. package/dist/modules/settings/setup.routes.d.ts +26 -0
  171. package/dist/modules/settings/setup.routes.d.ts.map +1 -1
  172. package/dist/modules/settings/setup.routes.js +9 -1
  173. package/dist/modules/settings/setup.routes.js.map +1 -1
  174. package/dist/modules/skills/index.d.ts +1 -0
  175. package/dist/modules/skills/index.d.ts.map +1 -1
  176. package/dist/modules/skills/index.js +1 -0
  177. package/dist/modules/skills/index.js.map +1 -1
  178. package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
  179. package/dist/modules/skills/pending-skills.service.js +9 -4
  180. package/dist/modules/skills/pending-skills.service.js.map +1 -1
  181. package/dist/modules/skills/skill-access-requests.routes.d.ts +38 -0
  182. package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -0
  183. package/dist/modules/skills/skill-access-requests.routes.js +168 -0
  184. package/dist/modules/skills/skill-access-requests.routes.js.map +1 -0
  185. package/dist/modules/skills/skills.contract.d.ts +31 -0
  186. package/dist/modules/skills/skills.contract.d.ts.map +1 -1
  187. package/dist/modules/skills/skills.routes.d.ts +28 -3
  188. package/dist/modules/skills/skills.routes.d.ts.map +1 -1
  189. package/dist/modules/skills/skills.routes.js +42 -3
  190. package/dist/modules/skills/skills.routes.js.map +1 -1
  191. package/dist/modules/skills/skills.service.d.ts +4 -0
  192. package/dist/modules/skills/skills.service.d.ts.map +1 -1
  193. package/dist/modules/skills/skills.service.js +83 -22
  194. package/dist/modules/skills/skills.service.js.map +1 -1
  195. package/dist/modules/tool-auth/external-api-key.interface.d.ts +18 -1
  196. package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
  197. package/dist/modules/tool-auth/external-api-key.interface.js +2 -1
  198. package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
  199. package/dist/modules/tool-auth/external-api-key.service.d.ts +13 -4
  200. package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
  201. package/dist/modules/tool-auth/external-api-key.service.js +26 -9
  202. package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
  203. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +36 -0
  204. package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
  205. package/dist/modules/tool-manuals/mcp-json-discovery.js +130 -92
  206. package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
  207. package/dist/modules/tool-manuals/tool-manuals.service.d.ts +6 -1
  208. package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
  209. package/dist/modules/tool-manuals/tool-manuals.service.js +18 -25
  210. package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
  211. package/dist/modules/workflow/git/git.service.d.ts +40 -2
  212. package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
  213. package/dist/modules/workflow/git/git.service.js +227 -54
  214. package/dist/modules/workflow/git/git.service.js.map +1 -1
  215. package/dist/modules/workflow/git/pull-request.service.d.ts +6 -0
  216. package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
  217. package/dist/modules/workflow/git/pull-request.service.js +8 -0
  218. package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
  219. package/dist/modules/workflow/workflow.service.d.ts +52 -1
  220. package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
  221. package/dist/modules/workflow/workflow.service.js +242 -9
  222. package/dist/modules/workflow/workflow.service.js.map +1 -1
  223. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +35 -0
  224. package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -0
  225. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +121 -0
  226. package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -0
  227. package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
  228. package/dist/modules/workspace/startup/steps/seed-tree.js +46 -2
  229. package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
  230. package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
  231. package/dist/modules/workspace/startup/steps/template-files.step.js +112 -25
  232. package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
  233. package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
  234. package/dist/modules/workspace/workspace.routes.js +9 -9
  235. package/dist/modules/workspace/workspace.routes.js.map +1 -1
  236. package/dist/modules/workspace/workspace.service.d.ts +33 -0
  237. package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
  238. package/dist/modules/workspace/workspace.service.js +110 -16
  239. package/dist/modules/workspace/workspace.service.js.map +1 -1
  240. package/dist/shared/domain-errors.d.ts +20 -0
  241. package/dist/shared/domain-errors.d.ts.map +1 -1
  242. package/dist/shared/domain-errors.js +29 -0
  243. package/dist/shared/domain-errors.js.map +1 -1
  244. package/dist/shared/fs-errors.d.ts +8 -0
  245. package/dist/shared/fs-errors.d.ts.map +1 -0
  246. package/dist/shared/fs-errors.js +11 -0
  247. package/dist/shared/fs-errors.js.map +1 -0
  248. package/dist/shared/fs-walk.d.ts +20 -6
  249. package/dist/shared/fs-walk.d.ts.map +1 -1
  250. package/dist/shared/fs-walk.js +35 -27
  251. package/dist/shared/fs-walk.js.map +1 -1
  252. package/dist/shared/kb-walk.d.ts +52 -0
  253. package/dist/shared/kb-walk.d.ts.map +1 -0
  254. package/dist/shared/kb-walk.js +76 -0
  255. package/dist/shared/kb-walk.js.map +1 -0
  256. package/dist/shared/path-order.d.ts +18 -0
  257. package/dist/shared/path-order.d.ts.map +1 -0
  258. package/dist/shared/path-order.js +35 -0
  259. package/dist/shared/path-order.js.map +1 -0
  260. package/dist/shared/ttl-cache.d.ts +28 -1
  261. package/dist/shared/ttl-cache.d.ts.map +1 -1
  262. package/dist/shared/ttl-cache.js +33 -1
  263. package/dist/shared/ttl-cache.js.map +1 -1
  264. package/kb-template/.bevelignore +1 -1
  265. package/kb-template/AGENTS.md +95 -30
  266. package/kb-template/Skills/.gitkeep +0 -0
  267. package/migrations/0005_github_facade.sql +25 -0
  268. package/migrations/meta/0005_snapshot.json +1717 -0
  269. package/migrations/meta/_journal.json +7 -0
  270. package/package.json +6 -6
  271. package/src/__tests__/kb-layout-config.test.ts +110 -0
  272. package/src/core/__tests__/catalog-cache-invalidation.test.ts +146 -0
  273. package/src/core/catalog-cache-invalidation.ts +92 -0
  274. package/src/core/create-core-server.ts +123 -7
  275. package/src/core/create-core-services.ts +162 -41
  276. package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +86 -0
  277. package/src/modules/access/__tests__/access-control.service.test.ts +19 -3
  278. package/src/modules/access/__tests__/access-plugin-principals.test.ts +368 -0
  279. package/src/modules/access/__tests__/groups-admin.service.test.ts +8 -0
  280. package/src/modules/access/access-control.interface.ts +20 -9
  281. package/src/modules/access/access-control.service.ts +240 -78
  282. package/src/modules/access/access.routes.ts +96 -14
  283. package/src/modules/access-model/__tests__/access-grammar.test.ts +18 -0
  284. package/src/modules/access-model/__tests__/plugin-token-canonical.test.ts +40 -0
  285. package/src/modules/access-model/access-grammar.ts +114 -2
  286. package/src/modules/access-model/group-files.ts +4 -0
  287. package/src/modules/access-model/plugin-principals.ts +137 -0
  288. package/src/modules/database/core-schema.ts +54 -0
  289. package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +297 -0
  290. package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +295 -0
  291. package/src/modules/kb-sync/__tests__/sync-auth.test.ts +118 -0
  292. package/src/modules/kb-sync/__tests__/sync-payload.test.ts +148 -0
  293. package/src/modules/kb-sync/index.ts +19 -0
  294. package/src/modules/kb-sync/kb-sync.interface.ts +79 -0
  295. package/src/modules/kb-sync/kb-sync.routes.ts +197 -0
  296. package/src/modules/kb-sync/kb-sync.service.ts +201 -0
  297. package/src/modules/kb-sync/sync-auth.ts +118 -0
  298. package/src/modules/kb-sync/sync-payload.ts +143 -0
  299. package/src/modules/marketplace/__tests__/github-facade.test.ts +443 -0
  300. package/src/modules/marketplace/__tests__/marketplace-git.test.ts +179 -0
  301. package/src/modules/marketplace/git-http.routes.ts +193 -0
  302. package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +78 -0
  303. package/src/modules/marketplace/github-facade/github-facade-codes.store.ts +145 -0
  304. package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +208 -0
  305. package/src/modules/marketplace/github-facade/github-facade.routes.ts +268 -0
  306. package/src/modules/marketplace/github-facade/github-facade.service.ts +221 -0
  307. package/src/modules/marketplace/github-facade/index.ts +25 -0
  308. package/src/modules/marketplace/index.ts +3 -0
  309. package/src/modules/marketplace/marketplace-repo.service.ts +337 -0
  310. package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -0
  311. package/src/modules/mcp/oauth/oauth-consent.routes.ts +24 -2
  312. package/src/modules/mcp/oauth/oauth-state.ts +15 -3
  313. package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +163 -0
  314. package/src/modules/plugins/__tests__/compile-marketplace.test.ts +294 -0
  315. package/src/modules/plugins/__tests__/plugin-index.service.test.ts +56 -12
  316. package/src/modules/plugins/__tests__/plugin-links.service.test.ts +287 -0
  317. package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +205 -5
  318. package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +394 -0
  319. package/src/modules/plugins/__tests__/plugins.routes.test.ts +80 -33
  320. package/src/modules/plugins/compile/compile-marketplace.ts +403 -0
  321. package/src/modules/plugins/compile/marketplace-compiler.service.ts +150 -0
  322. package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +329 -0
  323. package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +140 -0
  324. package/src/modules/plugins/discovery/bundle-dialect/registry.ts +175 -0
  325. package/src/modules/plugins/discovery/kb-plugin-source.ts +114 -0
  326. package/src/modules/plugins/discovery/native.source.ts +122 -0
  327. package/src/modules/plugins/discovery/plugin-source.ts +99 -0
  328. package/src/modules/plugins/index.ts +10 -0
  329. package/src/modules/plugins/plugin-links.service.ts +363 -0
  330. package/src/modules/plugins/plugin-links.ts +184 -0
  331. package/src/modules/plugins/plugin-provision.service.ts +160 -54
  332. package/src/modules/plugins/plugin-rename.service.ts +298 -0
  333. package/src/modules/plugins/plugins.contract.ts +28 -1
  334. package/src/modules/plugins/plugins.routes.ts +128 -11
  335. package/src/modules/plugins/plugins.service.ts +106 -53
  336. package/src/modules/settings/__tests__/deployment-settings.service.test.ts +57 -0
  337. package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +77 -0
  338. package/src/modules/settings/deployment-settings.service.ts +95 -2
  339. package/src/modules/settings/setup.routes.ts +24 -0
  340. package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +128 -0
  341. package/src/modules/skills/__tests__/skills.service.test.ts +85 -0
  342. package/src/modules/skills/index.ts +1 -0
  343. package/src/modules/skills/pending-skills.service.ts +7 -5
  344. package/src/modules/skills/skill-access-requests.routes.ts +199 -0
  345. package/src/modules/skills/skills.contract.ts +32 -0
  346. package/src/modules/skills/skills.routes.ts +59 -3
  347. package/src/modules/skills/skills.service.ts +84 -22
  348. package/src/modules/tool-auth/external-api-key.interface.ts +20 -1
  349. package/src/modules/tool-auth/external-api-key.service.ts +31 -13
  350. package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +24 -1
  351. package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +2 -0
  352. package/src/modules/tool-manuals/mcp-json-discovery.ts +154 -88
  353. package/src/modules/tool-manuals/tool-manuals.service.ts +14 -23
  354. package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +1 -1
  355. package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +1 -0
  356. package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +2 -2
  357. package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +119 -2
  358. package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +437 -0
  359. package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +3 -3
  360. package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +47 -4
  361. package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +253 -0
  362. package/src/modules/workflow/git/git.service.ts +239 -56
  363. package/src/modules/workflow/git/pull-request.service.ts +9 -0
  364. package/src/modules/workflow/workflow.service.ts +260 -9
  365. package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +6 -7
  366. package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +93 -0
  367. package/src/modules/workspace/__tests__/workspace.service.missing-branch.test.ts +28 -0
  368. package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +42 -12
  369. package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +219 -4
  370. package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +129 -0
  371. package/src/modules/workspace/startup/steps/seed-tree.ts +44 -2
  372. package/src/modules/workspace/startup/steps/template-files.step.ts +131 -27
  373. package/src/modules/workspace/workspace.routes.ts +9 -17
  374. package/src/modules/workspace/workspace.service.ts +107 -16
  375. package/src/shared/__tests__/filename.test.ts +0 -0
  376. package/src/shared/__tests__/fs-walk.test.ts +22 -1
  377. package/src/shared/__tests__/kb-walk.test.ts +107 -0
  378. package/src/shared/__tests__/path-order.test.ts +24 -0
  379. package/src/shared/__tests__/ttl-cache.test.ts +55 -0
  380. package/src/shared/domain-errors.ts +29 -0
  381. package/src/shared/fs-errors.ts +10 -0
  382. package/src/shared/fs-walk.ts +38 -23
  383. package/src/shared/kb-walk.ts +95 -0
  384. package/src/shared/path-order.ts +33 -0
  385. package/src/shared/ttl-cache.ts +33 -1
@@ -0,0 +1,245 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { PERSONAL_PLUGIN_PREFIX, PLUGIN_MANIFEST_FILE, isPluginIdentifier, pluginDisplayNameOf, pluginManifestName, } from '@bevel-software/platform-shared';
4
+ import { PushNeedsAgentResolutionError } from '../../shared/domain-errors.js';
5
+ import { walkKb } from '../../shared/kb-walk.js';
6
+ import { PLUGIN_TOKEN_PREFIX, canonicalPluginToken, hasAccessFrontmatterExtension, parsePluginPrincipalKey, } from '../access-model/access-grammar.js';
7
+ import { linksWorkspaceId } from './plugin-links.js';
8
+ export class PluginRenameError extends Error {
9
+ status;
10
+ payload;
11
+ constructor(message, status, payload = {}) {
12
+ super(message);
13
+ this.status = status;
14
+ this.payload = payload;
15
+ this.name = 'PluginRenameError';
16
+ }
17
+ }
18
+ /**
19
+ * Renaming a plugin — its identifier, its display name, or both.
20
+ *
21
+ * The identifier (the manifest's `name`) IS the plugin: the marketplace
22
+ * publishes it, the URLs key on it, and every grant of `plugin/<name>/<verb>`
23
+ * spells it. Changing it is therefore a rename of a PRINCIPAL, and a
24
+ * principal that is renamed in one file and not another is two principals,
25
+ * one of which nobody is a member of. So the rename is one commit: the
26
+ * manifest, plus every access entry in the knowledge base that names the old
27
+ * identifier, rewritten to the new one. Membership does not change — the
28
+ * same people hold the same verbs through the new spelling — but the caller
29
+ * still needs write on each file the commit touches: the protected-branch
30
+ * gate checks every path, and a rename that lands in some files and is
31
+ * refused in others is the split it exists to prevent. The refusal names the
32
+ * files, so the manager can ask their editors or an admin.
33
+ *
34
+ * The display name is free text with no consequences: the manifest changes,
35
+ * nothing else does.
36
+ */
37
+ export class PluginRenameService {
38
+ workspaceService;
39
+ commits;
40
+ accessControl;
41
+ source;
42
+ kbDirName;
43
+ events;
44
+ onChanged;
45
+ constructor(workspaceService, commits, accessControl, source, kbDirName, events, onChanged) {
46
+ this.workspaceService = workspaceService;
47
+ this.commits = commits;
48
+ this.accessControl = accessControl;
49
+ this.source = source;
50
+ this.kbDirName = kbDirName;
51
+ this.events = events;
52
+ this.onChanged = onChanged;
53
+ }
54
+ noteChanged(wsId) {
55
+ noteChangedFor(this.accessControl, this.events, this.onChanged, wsId);
56
+ }
57
+ /** The READ phase must have seen everything it needed; a hole refuses the rename before any write. */
58
+ assertComplete(unreadable) {
59
+ if (unreadable.length === 0)
60
+ return;
61
+ throw new PluginRenameError(`Some of the knowledge base could not be read (${unreadable.join(', ')}), so the rename cannot be checked against every plugin and grant. Try again, or ask an admin.`, 503, { kind: 'incomplete-discovery', unreadable });
62
+ }
63
+ async rename(user, current, patch) {
64
+ const wsId = linksWorkspaceId();
65
+ const kbRoot = path.join(await this.workspaceService.getWorkspacePath(wsId), this.kbDirName);
66
+ // ONE walk of the checkout for everything the rename needs to know:
67
+ // the plugins (discovery's listener) and every file that can carry a
68
+ // grant (this one's), seeing one set of holes. Listing is all that
69
+ // happens here — no file is read, nothing is decided — so it can run
70
+ // before authorization without telling anyone anything.
71
+ const grantFiles = [];
72
+ const grantListener = {
73
+ onFile(dir, name) {
74
+ if (hasAccessFrontmatterExtension(name))
75
+ grantFiles.push(dir ? `${dir}/${name}` : name);
76
+ },
77
+ };
78
+ const { discovery, holes } = this.source.walkWith
79
+ ? await this.source.walkWith(kbRoot, [grantListener])
80
+ : { discovery: await this.source.discover(kbRoot), holes: (await walkKb(kbRoot, [grantListener])).holes };
81
+ const { plugins } = discovery;
82
+ const unreadable = [...new Set([...discovery.unreadable, ...holes])];
83
+ // Three phases: READ everything, DECIDE, then WRITE. Nothing about the
84
+ // tree — not even that part of it could not be read — reaches a caller
85
+ // before they are known to manage the plugin they name.
86
+ const plugin = plugins.find((p) => p.name === current.trim() && !p.personal && p.exists);
87
+ // Fail closed, like every plugin surface: unknown and not-yours answer alike.
88
+ if (!plugin || !(await this.accessControl.canWrite(wsId, user.email, `${plugin.folder}/access.md`))) {
89
+ throw new PluginRenameError('Unknown plugin', 404, { kind: 'unknown-plugin' });
90
+ }
91
+ if (!plugin.linksAreManaged) {
92
+ throw new PluginRenameError(`${plugin.name} is read from an external plugin format; rename it in that repository.`, 409, { kind: 'read-only' });
93
+ }
94
+ const nextName = patch.name === undefined ? plugin.name : String(patch.name).trim();
95
+ // The rules govern a NEW identifier only. The current one is whatever the
96
+ // manifest says — a hand-written name that predates a rule (a reserved
97
+ // prefix, say) must not block a display-name change, and cannot be
98
+ // "fixed" by a rename that keeps it.
99
+ const identifierChanges = nextName !== plugin.name;
100
+ // A NEW identifier is claimed against EVERY plugin there is and rewritten
101
+ // into every grant there is. A listing with a hole in it — a folder or
102
+ // manifest that exists but could not be read — is neither set: the name
103
+ // could belong to what was not seen, the grant could live there. Fail
104
+ // closed; nothing is written. A display-name change claims nothing and
105
+ // touches one manifest, so it needs no more than its own plugin read.
106
+ if (identifierChanges)
107
+ this.assertComplete(unreadable);
108
+ // An identifier must be its own slug: the marketplace and the collision
109
+ // checks key on `pluginManifestName(name)`, so a name that does not
110
+ // round-trip (longer than the slug's 64 characters, say) would be one
111
+ // thing here and another everywhere it is published.
112
+ if (identifierChanges && (!isPluginIdentifier(nextName) || pluginManifestName(nextName) !== nextName)) {
113
+ throw new PluginRenameError('A plugin identifier is lowercase kebab-case: letters, digits and single hyphens, like "sales-team".', 422, { kind: 'bad-name' });
114
+ }
115
+ if (identifierChanges && nextName.startsWith(PERSONAL_PLUGIN_PREFIX)) {
116
+ throw new PluginRenameError(`The "${PERSONAL_PLUGIN_PREFIX}" namespace is reserved for personal folders. Pick another identifier.`, 422, { kind: 'bad-name' });
117
+ }
118
+ // Taken by SLUG, not by spelling: a bundle may declare a name that is not
119
+ // an identifier, and the marketplace folds every name to its slug — a
120
+ // collision there drops one of the two from the catalog.
121
+ if (identifierChanges && plugins.some((p) => p !== plugin && pluginManifestName(p.name) === nextName)) {
122
+ throw new PluginRenameError(`A plugin named "${nextName}" already exists.`, 409, { kind: 'name-taken' });
123
+ }
124
+ const folderName = path.posix.basename(plugin.folder);
125
+ const nextDisplay = patch.displayName === undefined ? plugin.displayName : String(patch.displayName).trim();
126
+ // The manifest, with everything else in it kept.
127
+ const manifest = { ...(plugin.manifest ?? {}), name: nextName };
128
+ if (nextDisplay && nextDisplay !== folderName)
129
+ manifest.displayName = nextDisplay;
130
+ else
131
+ delete manifest.displayName;
132
+ const manifestRel = `${this.kbDirName}/${plugin.folder}/${PLUGIN_MANIFEST_FILE}`;
133
+ const writes = [
134
+ { rel: manifestRel, text: `${JSON.stringify(manifest, null, 2)}\n`, before: plugin.manifestText },
135
+ ];
136
+ if (identifierChanges) {
137
+ // Every access entry in the knowledge base that names the old
138
+ // principal — in a folder's access.md or in the own frontmatter of any
139
+ // file kind the resolver reads grants from (`.md`, `.tool`, whatever
140
+ // an overlay registered). The files came from the walk above, whose
141
+ // holes have already refused: a folder that could not be listed would
142
+ // leave the old spelling live in it, and a principal renamed in some
143
+ // files and not others is two principals. A file the walk listed but
144
+ // that cannot be opened is the same hole: every such file is
145
+ // collected, then refused together.
146
+ const unopened = [];
147
+ for (const rel of grantFiles) {
148
+ const abs = path.join(kbRoot, rel);
149
+ let before;
150
+ try {
151
+ before = await fs.readFile(abs, 'utf-8');
152
+ }
153
+ catch {
154
+ unopened.push(rel);
155
+ continue;
156
+ }
157
+ const after = renamePluginPrincipalInText(before, plugin.name, nextName);
158
+ if (after !== before)
159
+ writes.push({ rel: `${this.kbDirName}/${rel}`, text: after, before });
160
+ }
161
+ this.assertComplete(unopened);
162
+ const denied = [];
163
+ for (const w of writes.slice(1)) {
164
+ const repoRel = w.rel.slice(this.kbDirName.length + 1);
165
+ if (!(await this.accessControl.canWrite(wsId, user.email, repoRel)))
166
+ denied.push(repoRel);
167
+ }
168
+ if (denied.length > 0) {
169
+ throw new PluginRenameError(`Renaming ${plugin.name} would rewrite grants in files you can't edit: ${denied.join(', ')}. Ask their editors, or an admin.`, 409, { kind: 'needs-write', files: denied });
170
+ }
171
+ }
172
+ // Writes and commit share ONE failure envelope: whichever of them fails,
173
+ // the working tree goes back to what origin has — a half-written batch
174
+ // is as much a split principal as a refused commit.
175
+ try {
176
+ for (const w of writes)
177
+ await this.workspaceService.writeFile(wsId, w.rel, w.text);
178
+ const summary = identifierChanges
179
+ ? `Rename plugin ${plugin.name} to ${nextName}`
180
+ : `Rename plugin ${plugin.name}: display name`;
181
+ await this.commits.commitChanges(wsId, user, summary, writes.map((w) => w.rel));
182
+ }
183
+ catch (err) {
184
+ // One failure is NOT a failed commit: the commit landed locally and only
185
+ // the push did not (the typed hand-off the workflow throws). Restoring
186
+ // the old bytes then would stack an uncommitted inverse of a real
187
+ // commit; the recovery flow owns that state, so it is left alone — and
188
+ // since the tree DID change, every cache keyed on the old identity is
189
+ // dropped before the error goes up, exactly as on success.
190
+ if (err instanceof PushNeedsAgentResolutionError) {
191
+ this.noteChanged(wsId);
192
+ }
193
+ else {
194
+ for (const w of writes) {
195
+ if (w.before !== null)
196
+ await this.workspaceService.writeFile(wsId, w.rel, w.before).catch(() => undefined);
197
+ }
198
+ }
199
+ throw err;
200
+ }
201
+ this.noteChanged(wsId);
202
+ return {
203
+ name: nextName,
204
+ displayName: pluginDisplayNameOf(manifest, folderName),
205
+ rewritten: writes.slice(1).map((w) => w.rel.slice(this.kbDirName.length + 1)),
206
+ };
207
+ }
208
+ }
209
+ /**
210
+ * The tree changed: drop the resolver's model and the plugin caches, and
211
+ * tell every session. The ONE place this happens, for the landed commit and
212
+ * for the committed-but-unpushed one alike.
213
+ */
214
+ function noteChangedFor(accessControl, events, onChanged, wsId) {
215
+ accessControl.invalidate(wsId);
216
+ events?.emit({ kind: 'fs-tree-changed', workspaceId: wsId, branch: linksWorkspaceIdBranch() });
217
+ onChanged?.();
218
+ }
219
+ /**
220
+ * Every access entry line naming `plugin/<old>/<verb>` — in ANY spelling the
221
+ * parser accepts — rewritten to `plugin/<next>/<verb>`, the rest of the
222
+ * line (indent, `- `, a `deny` prefix, the line ending) kept. Lines that are
223
+ * not such an entry are untouched, so the file's own formatting survives.
224
+ */
225
+ export function renamePluginPrincipalInText(text, oldName, nextName) {
226
+ const oldSlug = pluginManifestName(oldName);
227
+ return text
228
+ .split('\n')
229
+ .map((line) => {
230
+ const m = /^(\s*-\s+(?:deny\s+)?)(\S.*?)(\s*)$/i.exec(line);
231
+ if (!m)
232
+ return line;
233
+ const key = canonicalPluginToken(m[2]);
234
+ const parsed = key === null ? null : parsePluginPrincipalKey(key);
235
+ if (!parsed || parsed.slug !== oldSlug)
236
+ return line;
237
+ return `${m[1]}${PLUGIN_TOKEN_PREFIX}${nextName}/${parsed.verb}${m[3]}`;
238
+ })
239
+ .join('\n');
240
+ }
241
+ function linksWorkspaceIdBranch() {
242
+ // The default branch's workspace id IS its encoded branch name.
243
+ return decodeURIComponent(linksWorkspaceId());
244
+ }
245
+ //# sourceMappingURL=plugin-rename.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-rename.service.js","sourceRoot":"","sources":["../../../src/modules/plugins/plugin-rename.service.ts"],"names":[],"mappings":"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"}
@@ -32,6 +32,7 @@
32
32
  * via Manage access, which makes the CR moot). To agents these are plain
33
33
  * change requests; only the UI dresses them up.
34
34
  */
35
+ export type { PluginMembership } from '../skills/skills.contract.js';
35
36
  /** Access-rule principals as the resolver hands them back. */
36
37
  export interface ResolvedPrincipals {
37
38
  roles: string[];
@@ -45,10 +46,14 @@ export interface ResolvedReaders extends ResolvedPrincipals {
45
46
  }
46
47
  /** One plugin as `GET /api/plugins` reports it, resolved for ONE caller. */
47
48
  export interface PluginSummary {
48
- /** Plugin folder name, e.g. `GTM`. */
49
+ /** The plugin's identity — its manifest name, e.g. `gtm`. */
49
50
  name: string;
51
+ /** What a person sees it called, e.g. `GTM`. */
52
+ displayName: string;
50
53
  /** Repo-relative constituent folders, e.g. `['Plugins/GTM']`. */
51
54
  folders: string[];
55
+ /** Whether this platform writes the plugin's links — see `PluginCatalogEntry`. */
56
+ linksAreManaged: boolean;
52
57
  /**
53
58
  * Per-caller: the caller can read the FOLDER (membership). Every returned
54
59
  * plugin has at least one of `canRead` / `canWrite` / discoverability; a
@@ -71,6 +76,16 @@ export interface PluginSummary {
71
76
  /** Caller-INDEPENDENT total (the plugin's whole content, not the caller's slice). */
72
77
  skillCount: number;
73
78
  toolCount: number;
79
+ /**
80
+ * Caller-INDEPENDENT: how many of the plugin's LINKED skills its members
81
+ * cannot read — the link is in the manifest, but the skill folder no
82
+ * longer grants the plugin's readers. Counted here, from the unfiltered
83
+ * link index, because the people who most need the warning are the
84
+ * plugin's managers, and they are exactly the people a missing grant
85
+ * locks out: a count derived from what the caller can read would be zero
86
+ * for them. Zero for a plugin whose links are not managed here.
87
+ */
88
+ brokenLinks: number;
74
89
  /** For display: "Run by …" (fallback chain: owners → writers → 'the workspace admins'). */
75
90
  owners: ResolvedPrincipals;
76
91
  writers: ResolvedPrincipals;
@@ -89,10 +104,21 @@ export interface PluginSummary {
89
104
  * appears at all) are resolved per request in the route.
90
105
  */
91
106
  export interface PluginCatalogEntry {
107
+ /** The plugin's identity — its manifest name. */
92
108
  name: string;
109
+ /** What a person sees it called — the manifest's `displayName`, else the folder name. */
110
+ displayName: string;
93
111
  folders: string[];
112
+ /**
113
+ * Whether this platform writes the plugin's links (a native `plugin.json`)
114
+ * — false for a plugin read from an external format, whose links are
115
+ * edited in that repository and which the link endpoints refuse.
116
+ */
117
+ linksAreManaged: boolean;
94
118
  skillCount: number;
95
119
  toolCount: number;
120
+ /** Linked skills the members cannot read — see `PluginSummary.brokenLinks`. */
121
+ brokenLinks: number;
96
122
  owners: ResolvedPrincipals;
97
123
  writers: ResolvedPrincipals;
98
124
  readers: ResolvedReaders;
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.contract.d.ts","sourceRoot":"","sources":["../../../src/modules/plugins/plugins.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;IACzD,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,yEAAyE;IACzE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,OAAO,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACzC,wFAAwF;IACxF,UAAU,IAAI,IAAI,CAAC;CACpB"}
1
+ {"version":3,"file":"plugins.contract.d.ts","sourceRoot":"","sources":["../../../src/modules/plugins/plugins.contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;IACzD,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kFAAkF;IAClF,eAAe,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,yEAAyE;IACzE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,OAAO,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACzC,wFAAwF;IACxF,UAAU,IAAI,IAAI,CAAC;CACpB"}
@@ -4,6 +4,8 @@ import { type AuthUser, type IWorkflowService } from '@bevel-software/platform-s
4
4
  import type { IAccessControl } from '../access/access-control.interface.js';
5
5
  import type { WorkspaceService } from '../workspace/workspace.service.js';
6
6
  import { type PluginProvisionService } from './plugin-provision.service.js';
7
+ import { type PluginLinksService } from './plugin-links.service.js';
8
+ import { type PluginRenameService } from './plugin-rename.service.js';
7
9
  import type { JoinRequestsService } from './join-requests.service.js';
8
10
  import type { IPluginIndexService } from './plugins.contract.js';
9
11
  /**
@@ -38,5 +40,9 @@ import type { IPluginIndexService } from './plugins.contract.js';
38
40
  * middleware change can never silently un-gate one. Nothing here is reachable
39
41
  * with an agent connection key or a manual-auth bearer.
40
42
  */
41
- export declare function createPluginsRoutes(pluginIndex: IPluginIndexService, accessControl: IAccessControl, workflow: IWorkflowService, workspaceService: WorkspaceService, joinRequests: JoinRequestsService, provision: PluginProvisionService, kbDirName: string, resolveUser: (req: express.Request) => Promise<AuthUser | null>): express.Router;
43
+ export declare function createPluginsRoutes(pluginIndex: IPluginIndexService, accessControl: IAccessControl, workflow: IWorkflowService, workspaceService: WorkspaceService, joinRequests: JoinRequestsService, provision: PluginProvisionService, kbDirName: string, resolveUser: (req: express.Request) => Promise<AuthUser | null>,
44
+ /** Optional: a host without the link machinery simply has no link routes. */
45
+ links?: PluginLinksService,
46
+ /** Optional: a host without it has no rename route. */
47
+ rename?: PluginRenameService): express.Router;
42
48
  //# sourceMappingURL=plugins.routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/plugins/plugins.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,4BAA4B,CAAC;AACpC,OAAO,EAGL,KAAK,QAAQ,EAEb,KAAK,gBAAgB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAG5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,EAAwB,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAClG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,mBAAmB,EAChC,aAAa,EAAE,cAAc,EAC7B,QAAQ,EAAE,gBAAgB,EAC1B,gBAAgB,EAAE,gBAAgB,EAClC,YAAY,EAAE,mBAAmB,EACjC,SAAS,EAAE,sBAAsB,EACjC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,GAC9D,OAAO,CAAC,MAAM,CAiXhB"}
1
+ {"version":3,"file":"plugins.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/plugins/plugins.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,4BAA4B,CAAC;AACpC,OAAO,EAGL,KAAK,QAAQ,EAEb,KAAK,gBAAgB,EACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAG5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,OAAO,EAAwB,KAAK,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAClG,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,mBAAmB,EAChC,aAAa,EAAE,cAAc,EAC7B,QAAQ,EAAE,gBAAgB,EAC1B,gBAAgB,EAAE,gBAAgB,EAClC,YAAY,EAAE,mBAAmB,EACjC,SAAS,EAAE,sBAAsB,EACjC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC/D,6EAA6E;AAC7E,KAAK,CAAC,EAAE,kBAAkB;AAC1B,uDAAuD;AACvD,MAAM,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAgehB"}
@@ -5,6 +5,8 @@ import { spliceGrant } from '../access-model/access-splice.js';
5
5
  import { WorkflowDomainError } from '../../shared/domain-errors.js';
6
6
  import { pluginsWorkspaceId } from './plugins.service.js';
7
7
  import { PluginProvisionError } from './plugin-provision.service.js';
8
+ import { PluginLinkError } from './plugin-links.service.js';
9
+ import { PluginRenameError } from './plugin-rename.service.js';
8
10
  /**
9
11
  * Browser-facing (JWT) plugin routes, mounted behind `authMiddleware`:
10
12
  *
@@ -37,12 +39,111 @@ import { PluginProvisionError } from './plugin-provision.service.js';
37
39
  * middleware change can never silently un-gate one. Nothing here is reachable
38
40
  * with an agent connection key or a manual-auth bearer.
39
41
  */
40
- export function createPluginsRoutes(pluginIndex, accessControl, workflow, workspaceService, joinRequests, provision, kbDirName, resolveUser) {
42
+ export function createPluginsRoutes(pluginIndex, accessControl, workflow, workspaceService, joinRequests, provision, kbDirName, resolveUser,
43
+ /** Optional: a host without the link machinery simply has no link routes. */
44
+ links,
45
+ /** Optional: a host without it has no rename route. */
46
+ rename) {
41
47
  const router = express.Router();
48
+ /**
49
+ * Rename a plugin — its identifier, its display name, or both. The
50
+ * MANAGER's verb (write on the folder's access.md, the gate linking uses).
51
+ * An identifier change rewrites every grant naming the old identifier in
52
+ * the same commit; see `PluginRenameService`.
53
+ *
54
+ * PATCH /api/plugins/:name { name?, displayName? } → { name, displayName, rewritten }
55
+ */
56
+ if (rename) {
57
+ router.patch('/plugins/:name', async (req, res) => {
58
+ if (!req.userEmail) {
59
+ res.status(401).json({ error: 'Unauthenticated' });
60
+ return;
61
+ }
62
+ const user = await resolveUser(req);
63
+ if (!user) {
64
+ res.status(401).json({ error: 'Unauthenticated' });
65
+ return;
66
+ }
67
+ const body = (req.body ?? {});
68
+ try {
69
+ res.json(await rename.rename(user, String(req.params.name), { name: body.name, displayName: body.displayName }));
70
+ }
71
+ catch (err) {
72
+ if (err instanceof PluginRenameError) {
73
+ res.status(err.status).json({ error: err.message, ...err.payload });
74
+ return;
75
+ }
76
+ if (err instanceof WorkflowDomainError) {
77
+ res.status(err.status).json({ error: err.message, ...(err.payload ?? {}) });
78
+ return;
79
+ }
80
+ console.error('[plugins] rename failed:', err);
81
+ res.status(500).json({ error: 'Failed to rename the plugin' });
82
+ }
83
+ });
84
+ }
85
+ /**
86
+ * Linking shared skills — see `PluginLinksService` for the two-sided write.
87
+ *
88
+ * POST /api/plugins/:name/links { skillPath } → { root, skills }
89
+ * DELETE /api/plugins/:name/links?skillPath= → { root, revoked }
90
+ * POST /api/plugins/:name/links/repair { skillPath } → { root }
91
+ *
92
+ * Refusals carry a `kind` the UI branches on — `needs-skill-write` (409) is
93
+ * the one that becomes "request write access".
94
+ */
95
+ if (links) {
96
+ const linkOp = async (req, res, op, skillPathOf) => {
97
+ if (!req.userEmail) {
98
+ res.status(401).json({ error: 'Unauthenticated' });
99
+ return;
100
+ }
101
+ const user = await resolveUser(req);
102
+ if (!user) {
103
+ res.status(401).json({ error: 'Unauthenticated' });
104
+ return;
105
+ }
106
+ const skillPath = skillPathOf(req);
107
+ if (typeof skillPath !== 'string' || !skillPath.trim()) {
108
+ res.status(400).json({ error: 'skillPath is required' });
109
+ return;
110
+ }
111
+ try {
112
+ res.json(await op(user, String(req.params.name), skillPath));
113
+ }
114
+ catch (err) {
115
+ if (err instanceof PluginLinkError) {
116
+ res.status(err.status).json({ error: err.message, ...err.payload });
117
+ return;
118
+ }
119
+ if (err instanceof WorkflowDomainError) {
120
+ res.status(err.status).json({ error: err.message, ...(err.payload ?? {}) });
121
+ return;
122
+ }
123
+ console.error('[plugins] link operation failed:', err);
124
+ res.status(500).json({ error: 'Failed to update the plugin\'s links' });
125
+ }
126
+ };
127
+ const bodyPath = (req) => (req.body ?? {}).skillPath;
128
+ router.post('/plugins/:name/links', (req, res) => linkOp(req, res, (u, p, s) => links.link(u, p, s), bodyPath));
129
+ router.post('/plugins/:name/links/repair', (req, res) => linkOp(req, res, (u, p, s) => links.repair(u, p, s), bodyPath));
130
+ router.delete('/plugins/:name/links', (req, res) => linkOp(req, res, (u, p, s) => links.unlink(u, p, s), (r) => r.query.skillPath));
131
+ }
42
132
  /** The folder-chain probe for MEMBERSHIP — the folder itself. */
43
133
  const memberProbe = (folder) => folder;
44
134
  /** The FILE probe for discovery/management — the folder's access.md. */
45
135
  const accessMdOf = (folder) => `${folder}/access.md`;
136
+ /** A plugin folder's path BELOW the plugins root: `GTM`, or `teams/deep`. */
137
+ const folderBelowRoot = (folder) => folder.slice(folder.indexOf('/') + 1);
138
+ /**
139
+ * What a join request is keyed by: the plugin's primary FOLDER path below
140
+ * the root, not its identity. The request writes into that folder's rules;
141
+ * a top-level folder keys exactly as every join branch already on a remote
142
+ * was cut (its name), a nested one by its whole path, so two plugins whose
143
+ * folders share a basename never share a branch; and a rename of the
144
+ * identity moves no folder, so it orphans no open request.
145
+ */
146
+ const joinKeyOf = (g) => folderBelowRoot(g.folders[0]);
46
147
  const probesFor = (plugins) => [
47
148
  ...new Set(plugins.flatMap((g) => g.folders.flatMap((f) => [memberProbe(f), accessMdOf(f)]))),
48
149
  ];
@@ -90,15 +191,18 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
90
191
  const discoverable = member || any(readable, g, accessMdOf);
91
192
  if (!member && !manager && !discoverable)
92
193
  continue; // absent — fail closed
93
- const joinCr = member ? null : openJoinCr(mine, email, g.name);
194
+ const joinCr = member ? null : openJoinCr(mine, email, joinKeyOf(g));
94
195
  plugins.push({
95
196
  name: g.name,
197
+ displayName: g.displayName,
96
198
  folders: g.folders,
97
199
  canRead: member,
98
200
  canWrite: manager,
99
201
  isOwner: owner,
202
+ linksAreManaged: g.linksAreManaged,
100
203
  skillCount: g.skillCount,
101
204
  toolCount: g.toolCount,
205
+ brokenLinks: g.brokenLinks,
102
206
  owners: g.owners,
103
207
  writers: g.writers,
104
208
  readers: g.readers,
@@ -198,7 +302,7 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
198
302
  res.status(401).json({ error: 'Unauthenticated' });
199
303
  return;
200
304
  }
201
- // Case-sensitive, like `pluginOfPath` — the plugin name IS the folder name.
305
+ // By identity — the manifest name the catalog keys on.
202
306
  const plugin = (await pluginIndex.catalog()).find((g) => g.name === req.params.name);
203
307
  const wsId = pluginsWorkspaceId();
204
308
  const ownerVerdicts = plugin
@@ -208,7 +312,10 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
208
312
  res.status(404).json({ error: 'Unknown plugin', kind: 'unknown-plugin' });
209
313
  return;
210
314
  }
211
- await provision.deletePlugin(user, plugin.name);
315
+ // Provisioning works on FOLDERS (it created one); the identity only
316
+ // found the plugin. The whole path below the root, so a nested plugin
317
+ // is deleted where it is.
318
+ await provision.deletePlugin(user, folderBelowRoot(plugin.folders[0]));
212
319
  pluginIndex.invalidate();
213
320
  res.json({ ok: true });
214
321
  }
@@ -234,7 +341,7 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
234
341
  return;
235
342
  }
236
343
  const catalog = await pluginIndex.catalog();
237
- // Case-sensitive, like `pluginOfPath` — the plugin name IS the folder name.
344
+ // By identity — the manifest name the catalog keys on.
238
345
  const plugin = catalog.find((g) => g.name === req.params.name);
239
346
  const wsId = pluginsWorkspaceId();
240
347
  // Same ANY-folder shape `GET /plugins` resolves with, so a plugin can
@@ -258,8 +365,8 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
258
365
  // summary's `folders[0]` names and the banner's touched-path check
259
366
  // expects.
260
367
  const folder = plugin.folders[0];
261
- const branch = joinBranchFor(email, plugin.name);
262
- const existing = openJoinCr(await workflow.listChangeRequestsAuthoredBy(email), email, plugin.name);
368
+ const branch = joinBranchFor(email, joinKeyOf(plugin));
369
+ const existing = openJoinCr(await workflow.listChangeRequestsAuthoredBy(email), email, joinKeyOf(plugin));
263
370
  if (existing) {
264
371
  res.json({ ok: true, number: existing.number });
265
372
  return;
@@ -278,13 +385,14 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
278
385
  const spliced = spliceGrant(current, 'read', { kind: 'user', email: user.email, displayName: user.name }, { target: 'folder' });
279
386
  if (spliced.changed) {
280
387
  await workspaceService.writeFile(ws.id, accessPath, spliced.text);
281
- await workflow.commitChanges(ws.id, user, `Request access to ${plugin.name}`);
388
+ await workflow.commitChanges(ws.id, user, `Request access to ${plugin.displayName}`);
282
389
  }
283
390
  const detail = await workflow.openChangeRequest(ws.id, user, {
284
391
  sourceBranch: branch,
285
392
  targetBranch: DEFAULT_BRANCH,
286
- title: `Join request: ${plugin.name}`,
287
- description: `${user.name} asked to join ${plugin.name}. A manager of the plugin accepts by ` +
393
+ // People read these: the display name, not the identifier.
394
+ title: `Join request: ${plugin.displayName}`,
395
+ description: `${user.name} asked to join ${plugin.displayName}. A manager of the plugin accepts by ` +
288
396
  `granting the access this branch proposes; the request closes itself once ` +
289
397
  `every proposal has landed.`,
290
398
  });
@@ -338,7 +446,7 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
338
446
  return;
339
447
  const crs = await workflow.listChangeRequests();
340
448
  res.json({
341
- requests: await joinRequests.list(ctx.plugin.name, ctx.folder, crs, ctx.user),
449
+ requests: await joinRequests.list(joinKeyOf(ctx.plugin), ctx.folder, crs, ctx.user),
342
450
  });
343
451
  }
344
452
  catch (err) {
@@ -367,7 +475,7 @@ export function createPluginsRoutes(pluginIndex, accessControl, workflow, worksp
367
475
  res.status(404).json({ error: 'Not found' });
368
476
  return;
369
477
  }
370
- res.json({ closed: await joinRequests.reconcile(ctx.plugin.name, ctx.folder, cr, ctx.user) });
478
+ res.json({ closed: await joinRequests.reconcile(joinKeyOf(ctx.plugin), ctx.folder, cr, ctx.user) });
371
479
  }
372
480
  catch (err) {
373
481
  console.error('[plugins] failed to reconcile a join request:', err);