@rudderhq/server 0.5.2-canary.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
  2. package/dist/bootstrap/register-api-routes.js +15 -0
  3. package/dist/bootstrap/register-api-routes.js.map +1 -1
  4. package/dist/bootstrap/types.d.ts +11 -0
  5. package/dist/bootstrap/types.d.ts.map +1 -1
  6. package/dist/bundled-plugins/plugin-linear/dist/worker.js +741 -7
  7. package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +3 -3
  8. package/dist/config.d.ts +2 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +3 -0
  11. package/dist/config.js.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +28 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/local-runtime.d.ts +7 -0
  16. package/dist/local-runtime.d.ts.map +1 -1
  17. package/dist/local-runtime.js +15 -1
  18. package/dist/local-runtime.js.map +1 -1
  19. package/dist/middleware/auth.js +2 -2
  20. package/dist/middleware/auth.js.map +1 -1
  21. package/dist/middleware/error-handler.d.ts.map +1 -1
  22. package/dist/middleware/error-handler.js +3 -2
  23. package/dist/middleware/error-handler.js.map +1 -1
  24. package/dist/middleware/logger.d.ts +4 -0
  25. package/dist/middleware/logger.d.ts.map +1 -1
  26. package/dist/middleware/logger.js +82 -7
  27. package/dist/middleware/logger.js.map +1 -1
  28. package/dist/middleware/logger.test.js +94 -1
  29. package/dist/middleware/logger.test.js.map +1 -1
  30. package/dist/routes/chats.annotation-routes.d.ts +47 -0
  31. package/dist/routes/chats.annotation-routes.d.ts.map +1 -0
  32. package/dist/routes/chats.annotation-routes.js +164 -0
  33. package/dist/routes/chats.annotation-routes.js.map +1 -0
  34. package/dist/routes/chats.d.ts.map +1 -1
  35. package/dist/routes/chats.js +467 -165
  36. package/dist/routes/chats.js.map +1 -1
  37. package/dist/routes/chats.stream-routes.d.ts +1 -10
  38. package/dist/routes/chats.stream-routes.d.ts.map +1 -1
  39. package/dist/routes/chats.stream-routes.js +168 -110
  40. package/dist/routes/chats.stream-routes.js.map +1 -1
  41. package/dist/routes/chats.stream-support.d.ts +79 -0
  42. package/dist/routes/chats.stream-support.d.ts.map +1 -0
  43. package/dist/routes/chats.stream-support.js +121 -0
  44. package/dist/routes/chats.stream-support.js.map +1 -0
  45. package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
  46. package/dist/routes/issues.comments-attachments.js +16 -2
  47. package/dist/routes/issues.comments-attachments.js.map +1 -1
  48. package/dist/routes/issues.d.ts.map +1 -1
  49. package/dist/routes/issues.js +21 -4
  50. package/dist/routes/issues.js.map +1 -1
  51. package/dist/routes/issues.mutations.d.ts.map +1 -1
  52. package/dist/routes/issues.mutations.js +20 -33
  53. package/dist/routes/issues.mutations.js.map +1 -1
  54. package/dist/routes/managed-mcp-agent-bindings.d.ts +15 -0
  55. package/dist/routes/managed-mcp-agent-bindings.d.ts.map +1 -0
  56. package/dist/routes/managed-mcp-agent-bindings.js +66 -0
  57. package/dist/routes/managed-mcp-agent-bindings.js.map +1 -0
  58. package/dist/routes/managed-mcp-connections.d.ts +8 -0
  59. package/dist/routes/managed-mcp-connections.d.ts.map +1 -0
  60. package/dist/routes/managed-mcp-connections.js +189 -0
  61. package/dist/routes/managed-mcp-connections.js.map +1 -0
  62. package/dist/routes/managed-mcp-runtime.d.ts +5 -0
  63. package/dist/routes/managed-mcp-runtime.d.ts.map +1 -0
  64. package/dist/routes/managed-mcp-runtime.js +148 -0
  65. package/dist/routes/managed-mcp-runtime.js.map +1 -0
  66. package/dist/routes/messenger.d.ts.map +1 -1
  67. package/dist/routes/messenger.js +1 -1
  68. package/dist/routes/messenger.js.map +1 -1
  69. package/dist/services/access.d.ts.map +1 -1
  70. package/dist/services/access.js +91 -12
  71. package/dist/services/access.js.map +1 -1
  72. package/dist/services/agent-run-context.d.ts +1 -0
  73. package/dist/services/agent-run-context.d.ts.map +1 -1
  74. package/dist/services/agent-run-context.js +5 -1
  75. package/dist/services/agent-run-context.js.map +1 -1
  76. package/dist/services/board-auth.d.ts +2 -2
  77. package/dist/services/chat-annotation-copy-lineage.d.ts +32 -0
  78. package/dist/services/chat-annotation-copy-lineage.d.ts.map +1 -0
  79. package/dist/services/chat-annotation-copy-lineage.js +110 -0
  80. package/dist/services/chat-annotation-copy-lineage.js.map +1 -0
  81. package/dist/services/chat-assistant.annotations.d.ts +16 -0
  82. package/dist/services/chat-assistant.annotations.d.ts.map +1 -0
  83. package/dist/services/chat-assistant.annotations.js +108 -0
  84. package/dist/services/chat-assistant.annotations.js.map +1 -0
  85. package/dist/services/chat-assistant.annotations.test.d.ts +2 -0
  86. package/dist/services/chat-assistant.annotations.test.d.ts.map +1 -0
  87. package/dist/services/chat-assistant.annotations.test.js +257 -0
  88. package/dist/services/chat-assistant.annotations.test.js.map +1 -0
  89. package/dist/services/chat-assistant.d.ts.map +1 -1
  90. package/dist/services/chat-assistant.helpers.d.ts +2 -3
  91. package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
  92. package/dist/services/chat-assistant.helpers.js +37 -53
  93. package/dist/services/chat-assistant.helpers.js.map +1 -1
  94. package/dist/services/chat-assistant.js +61 -5
  95. package/dist/services/chat-assistant.js.map +1 -1
  96. package/dist/services/chat-generation-protocol.d.ts +8 -15
  97. package/dist/services/chat-generation-protocol.d.ts.map +1 -1
  98. package/dist/services/chat-generation-protocol.js +19 -59
  99. package/dist/services/chat-generation-protocol.js.map +1 -1
  100. package/dist/services/chat-generation-protocol.test.js +168 -6
  101. package/dist/services/chat-generation-protocol.test.js.map +1 -1
  102. package/dist/services/chat-generation-provenance.d.ts +14 -0
  103. package/dist/services/chat-generation-provenance.d.ts.map +1 -0
  104. package/dist/services/chat-generation-provenance.js +71 -0
  105. package/dist/services/chat-generation-provenance.js.map +1 -0
  106. package/dist/services/chat-inline-annotation-rendering.d.ts +2 -0
  107. package/dist/services/chat-inline-annotation-rendering.d.ts.map +1 -0
  108. package/dist/services/chat-inline-annotation-rendering.js +265 -0
  109. package/dist/services/chat-inline-annotation-rendering.js.map +1 -0
  110. package/dist/services/chat-inline-annotation-resolved-labels.d.ts +11 -0
  111. package/dist/services/chat-inline-annotation-resolved-labels.d.ts.map +1 -0
  112. package/dist/services/chat-inline-annotation-resolved-labels.js +490 -0
  113. package/dist/services/chat-inline-annotation-resolved-labels.js.map +1 -0
  114. package/dist/services/chat-inline-annotation-validation.d.ts +15 -0
  115. package/dist/services/chat-inline-annotation-validation.d.ts.map +1 -0
  116. package/dist/services/chat-inline-annotation-validation.js +430 -0
  117. package/dist/services/chat-inline-annotation-validation.js.map +1 -0
  118. package/dist/services/chat-inline-annotations.d.ts +18 -0
  119. package/dist/services/chat-inline-annotations.d.ts.map +1 -0
  120. package/dist/services/chat-inline-annotations.js +52 -0
  121. package/dist/services/chat-inline-annotations.js.map +1 -0
  122. package/dist/services/chat-inline-annotations.test.d.ts +2 -0
  123. package/dist/services/chat-inline-annotations.test.d.ts.map +1 -0
  124. package/dist/services/chat-inline-annotations.test.js +1306 -0
  125. package/dist/services/chat-inline-annotations.test.js.map +1 -0
  126. package/dist/services/chat-queued-message-materialization.d.ts +126 -0
  127. package/dist/services/chat-queued-message-materialization.d.ts.map +1 -0
  128. package/dist/services/chat-queued-message-materialization.js +327 -0
  129. package/dist/services/chat-queued-message-materialization.js.map +1 -0
  130. package/dist/services/chat-steer-messages.d.ts +3 -3
  131. package/dist/services/chat-steer-messages.d.ts.map +1 -1
  132. package/dist/services/chat-steer-messages.js +50 -80
  133. package/dist/services/chat-steer-messages.js.map +1 -1
  134. package/dist/services/chat-work-manifest.d.ts.map +1 -1
  135. package/dist/services/chat-work-manifest.js +5 -1
  136. package/dist/services/chat-work-manifest.js.map +1 -1
  137. package/dist/services/chats.annotation-persistence.d.ts +20 -0
  138. package/dist/services/chats.annotation-persistence.d.ts.map +1 -0
  139. package/dist/services/chats.annotation-persistence.js +206 -0
  140. package/dist/services/chats.annotation-persistence.js.map +1 -0
  141. package/dist/services/chats.d.ts +129 -96
  142. package/dist/services/chats.d.ts.map +1 -1
  143. package/dist/services/chats.helpers.d.ts +2 -2
  144. package/dist/services/chats.inline-visual-persistence.d.ts +3 -3
  145. package/dist/services/chats.inline-visual-persistence.d.ts.map +1 -1
  146. package/dist/services/chats.inline-visual-persistence.js +114 -54
  147. package/dist/services/chats.inline-visual-persistence.js.map +1 -1
  148. package/dist/services/chats.js +743 -252
  149. package/dist/services/chats.js.map +1 -1
  150. package/dist/services/finance.d.ts +1 -1
  151. package/dist/services/index.d.ts +8 -0
  152. package/dist/services/index.d.ts.map +1 -1
  153. package/dist/services/index.js +8 -0
  154. package/dist/services/index.js.map +1 -1
  155. package/dist/services/integrations/agent-integrations.d.ts +2 -2
  156. package/dist/services/integrations/custom-integrations.d.ts +3 -1
  157. package/dist/services/integrations/custom-integrations.d.ts.map +1 -1
  158. package/dist/services/integrations/custom-integrations.js +13 -2
  159. package/dist/services/integrations/custom-integrations.js.map +1 -1
  160. package/dist/services/issues.comments-attachments.d.ts +7 -1
  161. package/dist/services/issues.comments-attachments.d.ts.map +1 -1
  162. package/dist/services/issues.comments-attachments.js +13 -1
  163. package/dist/services/issues.comments-attachments.js.map +1 -1
  164. package/dist/services/issues.d.ts +6 -0
  165. package/dist/services/issues.d.ts.map +1 -1
  166. package/dist/services/issues.js +22 -3
  167. package/dist/services/issues.js.map +1 -1
  168. package/dist/services/knowledge-portability/organization-portability.core.d.ts +4 -1
  169. package/dist/services/knowledge-portability/organization-portability.core.d.ts.map +1 -1
  170. package/dist/services/knowledge-portability/organization-portability.core.js +17 -3
  171. package/dist/services/knowledge-portability/organization-portability.core.js.map +1 -1
  172. package/dist/services/knowledge-portability/organization-portability.export.d.ts.map +1 -1
  173. package/dist/services/knowledge-portability/organization-portability.export.js +2 -2
  174. package/dist/services/knowledge-portability/organization-portability.export.js.map +1 -1
  175. package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -1
  176. package/dist/services/knowledge-portability/organization-portability.import.js +13 -3
  177. package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -1
  178. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +3 -0
  179. package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -1
  180. package/dist/services/knowledge-portability/organization-skills.catalog.js +54 -23
  181. package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -1
  182. package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
  183. package/dist/services/knowledge-portability/organization-skills.js +627 -124
  184. package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
  185. package/dist/services/knowledge-portability/organization-skills.sources.d.ts +1 -0
  186. package/dist/services/knowledge-portability/organization-skills.sources.d.ts.map +1 -1
  187. package/dist/services/knowledge-portability/organization-skills.sources.js +53 -10
  188. package/dist/services/knowledge-portability/organization-skills.sources.js.map +1 -1
  189. package/dist/services/mcp/authorizer-lock.d.ts +9 -0
  190. package/dist/services/mcp/authorizer-lock.d.ts.map +1 -0
  191. package/dist/services/mcp/authorizer-lock.js +15 -0
  192. package/dist/services/mcp/authorizer-lock.js.map +1 -0
  193. package/dist/services/mcp/managed-bindings.d.ts +17 -0
  194. package/dist/services/mcp/managed-bindings.d.ts.map +1 -0
  195. package/dist/services/mcp/managed-bindings.js +332 -0
  196. package/dist/services/mcp/managed-bindings.js.map +1 -0
  197. package/dist/services/mcp/managed-client.d.ts +64 -0
  198. package/dist/services/mcp/managed-client.d.ts.map +1 -0
  199. package/dist/services/mcp/managed-client.js +238 -0
  200. package/dist/services/mcp/managed-client.js.map +1 -0
  201. package/dist/services/mcp/managed-client.test.d.ts +2 -0
  202. package/dist/services/mcp/managed-client.test.d.ts.map +1 -0
  203. package/dist/services/mcp/managed-client.test.js +398 -0
  204. package/dist/services/mcp/managed-client.test.js.map +1 -0
  205. package/dist/services/mcp/managed-connections.d.ts +81 -0
  206. package/dist/services/mcp/managed-connections.d.ts.map +1 -0
  207. package/dist/services/mcp/managed-connections.js +920 -0
  208. package/dist/services/mcp/managed-connections.js.map +1 -0
  209. package/dist/services/mcp/managed-runtime.d.ts +134 -0
  210. package/dist/services/mcp/managed-runtime.d.ts.map +1 -0
  211. package/dist/services/mcp/managed-runtime.js +282 -0
  212. package/dist/services/mcp/managed-runtime.js.map +1 -0
  213. package/dist/services/mcp/oauth-provider.d.ts +48 -0
  214. package/dist/services/mcp/oauth-provider.d.ts.map +1 -0
  215. package/dist/services/mcp/oauth-provider.js +120 -0
  216. package/dist/services/mcp/oauth-provider.js.map +1 -0
  217. package/dist/services/mcp/oauth-scope.test.d.ts +2 -0
  218. package/dist/services/mcp/oauth-scope.test.d.ts.map +1 -0
  219. package/dist/services/mcp/oauth-scope.test.js +69 -0
  220. package/dist/services/mcp/oauth-scope.test.js.map +1 -0
  221. package/dist/services/mcp/oauth-session-gc.d.ts +14 -0
  222. package/dist/services/mcp/oauth-session-gc.d.ts.map +1 -0
  223. package/dist/services/mcp/oauth-session-gc.js +34 -0
  224. package/dist/services/mcp/oauth-session-gc.js.map +1 -0
  225. package/dist/services/mcp/oauth-session-gc.test.d.ts +2 -0
  226. package/dist/services/mcp/oauth-session-gc.test.d.ts.map +1 -0
  227. package/dist/services/mcp/oauth-session-gc.test.js +66 -0
  228. package/dist/services/mcp/oauth-session-gc.test.js.map +1 -0
  229. package/dist/services/mcp/oauth.d.ts +61 -0
  230. package/dist/services/mcp/oauth.d.ts.map +1 -0
  231. package/dist/services/mcp/oauth.js +1690 -0
  232. package/dist/services/mcp/oauth.js.map +1 -0
  233. package/dist/services/mcp/pinned-fetch.d.ts +15 -0
  234. package/dist/services/mcp/pinned-fetch.d.ts.map +1 -0
  235. package/dist/services/mcp/pinned-fetch.js +400 -0
  236. package/dist/services/mcp/pinned-fetch.js.map +1 -0
  237. package/dist/services/mcp/pinned-fetch.test.d.ts +2 -0
  238. package/dist/services/mcp/pinned-fetch.test.d.ts.map +1 -0
  239. package/dist/services/mcp/pinned-fetch.test.js +190 -0
  240. package/dist/services/mcp/pinned-fetch.test.js.map +1 -0
  241. package/dist/services/mcp/provider-registry.d.ts +77 -0
  242. package/dist/services/mcp/provider-registry.d.ts.map +1 -0
  243. package/dist/services/mcp/provider-registry.js +80 -0
  244. package/dist/services/mcp/provider-registry.js.map +1 -0
  245. package/dist/services/mcp/provider-registry.test.d.ts +2 -0
  246. package/dist/services/mcp/provider-registry.test.d.ts.map +1 -0
  247. package/dist/services/mcp/provider-registry.test.js +80 -0
  248. package/dist/services/mcp/provider-registry.test.js.map +1 -0
  249. package/dist/services/mcp/security-policy.d.ts +48 -0
  250. package/dist/services/mcp/security-policy.d.ts.map +1 -0
  251. package/dist/services/mcp/security-policy.js +344 -0
  252. package/dist/services/mcp/security-policy.js.map +1 -0
  253. package/dist/services/mcp/security-policy.test.d.ts +2 -0
  254. package/dist/services/mcp/security-policy.test.d.ts.map +1 -0
  255. package/dist/services/mcp/security-policy.test.js +226 -0
  256. package/dist/services/mcp/security-policy.test.js.map +1 -0
  257. package/dist/services/mcp/tool-discovery.d.ts +49 -0
  258. package/dist/services/mcp/tool-discovery.d.ts.map +1 -0
  259. package/dist/services/mcp/tool-discovery.js +198 -0
  260. package/dist/services/mcp/tool-discovery.js.map +1 -0
  261. package/dist/services/mcp/tool-discovery.test.d.ts +2 -0
  262. package/dist/services/mcp/tool-discovery.test.d.ts.map +1 -0
  263. package/dist/services/mcp/tool-discovery.test.js +168 -0
  264. package/dist/services/mcp/tool-discovery.test.js.map +1 -0
  265. package/dist/services/messenger.d.ts +1 -1
  266. package/dist/services/messenger.d.ts.map +1 -1
  267. package/dist/services/messenger.js +21 -3
  268. package/dist/services/messenger.js.map +1 -1
  269. package/dist/services/plugin-registry.d.ts +3 -3
  270. package/dist/services/runtime-kernel/heartbeat.core.d.ts +1 -1
  271. package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
  272. package/dist/services/runtime-kernel/heartbeat.core.js +30 -4
  273. package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
  274. package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -1
  275. package/dist/services/runtime-kernel/heartbeat.wakeup.js +38 -23
  276. package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -1
  277. package/dist/services/secrets.d.ts +64 -6
  278. package/dist/services/secrets.d.ts.map +1 -1
  279. package/dist/services/secrets.js +38 -15
  280. package/dist/services/secrets.js.map +1 -1
  281. package/dist/services/side-chats.d.ts +1 -1
  282. package/dist/services/side-chats.d.ts.map +1 -1
  283. package/dist/services/side-chats.js +56 -14
  284. package/dist/services/side-chats.js.map +1 -1
  285. package/dist/services/workspace-runtime.services.d.ts +2 -2
  286. package/dist/services/workspace-web-preview.js +1 -1
  287. package/package.json +16 -13
  288. package/resources/bundled-skills/rudder-docs/references/operating-practices.md +12 -5
  289. package/skills/rudder-docs/references/operating-practices.md +12 -5
  290. package/ui-dist/assets/LibraryLiveSurface-CKOthw54.js +1 -0
  291. package/ui-dist/assets/{abnfDiagram-VRR7QNED-KGtP7zoC.js → abnfDiagram-VRR7QNED-C6a_wMho.js} +1 -1
  292. package/ui-dist/assets/{arc-JjnKyiOb.js → arc-CP0En6KA.js} +1 -1
  293. package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-AHfOdcaZ.js +36 -0
  294. package/ui-dist/assets/{blockDiagram-677ZJIJ3-D7YzMYrz.js → blockDiagram-677ZJIJ3-Bkl9lp63.js} +1 -1
  295. package/ui-dist/assets/{c4Diagram-LMCZKHZV-Dpf7UQxl.js → c4Diagram-LMCZKHZV-BpZareVt.js} +1 -1
  296. package/ui-dist/assets/channel-DqxL_Zcy.js +1 -0
  297. package/ui-dist/assets/{chunk-2Q5K7J3B-UJ3gsGlQ.js → chunk-2Q5K7J3B-BQAwVuXc.js} +1 -1
  298. package/ui-dist/assets/{chunk-32BRIVSS-BeHl2j2l.js → chunk-32BRIVSS-YkmfA08F.js} +1 -1
  299. package/ui-dist/assets/{chunk-5VM5RSS4-STE9fJd0.js → chunk-5VM5RSS4-DvvjwTnG.js} +1 -1
  300. package/ui-dist/assets/{chunk-EX3LRPZG-Bj8g8j4B.js → chunk-EX3LRPZG-TXD_Yhg5.js} +1 -1
  301. package/ui-dist/assets/{chunk-JWPE2WC7-0V4Pwta-.js → chunk-JWPE2WC7-CgMlj6XI.js} +1 -1
  302. package/ui-dist/assets/{chunk-MOJQB5TN-DNPn50gm.js → chunk-MOJQB5TN-BnJjU8Rc.js} +1 -1
  303. package/ui-dist/assets/{chunk-RYQCIY6F-Befmnv8r.js → chunk-RYQCIY6F-D9B64JKM.js} +1 -1
  304. package/ui-dist/assets/{chunk-V7JOEXUC-Boh8TjHj.js → chunk-V7JOEXUC-PS09QVpl.js} +1 -1
  305. package/ui-dist/assets/{chunk-VR4S4FIN-B_yEQSo3.js → chunk-VR4S4FIN-DQXb7pRS.js} +1 -1
  306. package/ui-dist/assets/{chunk-XXDRQBXY-Cf1GnUFx.js → chunk-XXDRQBXY-B1UdLiF2.js} +1 -1
  307. package/ui-dist/assets/classDiagram-OUVF2IWQ-DMwjzuJE.js +1 -0
  308. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-DMwjzuJE.js +1 -0
  309. package/ui-dist/assets/cose-bilkent-JH36ORCC-lqqmMB3m.js +1 -0
  310. package/ui-dist/assets/{cynefin-VYW2F7L2-Dv1yELYC.js → cynefin-VYW2F7L2-C9hu0XqZ.js} +1 -1
  311. package/ui-dist/assets/{cynefinDiagram-TSTJHNR4-CeFwjFoZ.js → cynefinDiagram-TSTJHNR4-Uuhz4c-1.js} +1 -1
  312. package/ui-dist/assets/{dagre-VKFMJZFB-8idd_4sX.js → dagre-VKFMJZFB-ChDrQYse.js} +1 -1
  313. package/ui-dist/assets/{diagram-FQU43EPY-BBVua6hM.js → diagram-FQU43EPY-DLtjkVSN.js} +1 -1
  314. package/ui-dist/assets/{diagram-G47NLZAW-D1zLQZlp.js → diagram-G47NLZAW-DCvEm4D0.js} +1 -1
  315. package/ui-dist/assets/{diagram-NH7WQ7WH-DE_FTSNv.js → diagram-NH7WQ7WH-Dz5jlsPG.js} +1 -1
  316. package/ui-dist/assets/{diagram-OA4YK3LP-61VSKBEn.js → diagram-OA4YK3LP-BXmQLO-Q.js} +1 -1
  317. package/ui-dist/assets/{diagram-WEI45ONY-C-tED6BJ.js → diagram-WEI45ONY-JnMU3xrx.js} +1 -1
  318. package/ui-dist/assets/{ebnfDiagram-CCIWWBDH-BEB8WGt0.js → ebnfDiagram-CCIWWBDH-Kryuqj4r.js} +1 -1
  319. package/ui-dist/assets/{erDiagram-Q63AITRT-AN_jm1TZ.js → erDiagram-Q63AITRT-Bn7g2mSW.js} +1 -1
  320. package/ui-dist/assets/{flowDiagram-23GEKE2U-BwLC6tI7.js → flowDiagram-23GEKE2U-DcjqTK91.js} +4 -4
  321. package/ui-dist/assets/ganttDiagram-NO4QXBWP-BRkw9-GE.js +292 -0
  322. package/ui-dist/assets/{gitGraphDiagram-IHSO6WYX-D9HCL9rY.js → gitGraphDiagram-IHSO6WYX-Cexcp6kR.js} +1 -1
  323. package/ui-dist/assets/graph-Cu7OV4Oi.js +1 -0
  324. package/ui-dist/assets/{index-BQXnpwke.js → index-ASZMPBYm.js} +5 -5
  325. package/ui-dist/assets/{index-5OM32_rf.js → index-B17BqCTj.js} +1 -1
  326. package/ui-dist/assets/{index-Cf0gLy6a.js → index-B9MccU1g.js} +1 -1
  327. package/ui-dist/assets/{index-BoSJ4Rx1.js → index-BPqbtW_X.js} +1 -1
  328. package/ui-dist/assets/{index-CpIdZ4-7.js → index-C2HjDa98.js} +1 -1
  329. package/ui-dist/assets/index-CMPtpj0A.js +1 -0
  330. package/ui-dist/assets/{index-p0ZGU1_7.js → index-CZXgOYtk.js} +1 -1
  331. package/ui-dist/assets/{index-74cx3ESJ.js → index-CknAauOP.js} +1 -1
  332. package/ui-dist/assets/index-Cu1GuH0a.js +1 -0
  333. package/ui-dist/assets/index-D8PdEImm.css +1 -0
  334. package/ui-dist/assets/{index-DrVDiJhe.js → index-DB1YX9-p.js} +1 -1
  335. package/ui-dist/assets/{index-vHMnAQSb.js → index-DBxLZXK7.js} +624 -610
  336. package/ui-dist/assets/{index-CpEfk5zb.js → index-DOCVOD97.js} +1 -1
  337. package/ui-dist/assets/{index-DMuWG0YN.js → index-DUbakRAC.js} +1 -1
  338. package/ui-dist/assets/{index-AT8juaIi.js → index-Dbg1ZwO2.js} +1 -1
  339. package/ui-dist/assets/index-DwbLsoLt.js +1 -0
  340. package/ui-dist/assets/index-hCLSotmS.js +1 -0
  341. package/ui-dist/assets/index-jrhEFc-j.js +1 -0
  342. package/ui-dist/assets/index-kz8ueXgo.js +1 -0
  343. package/ui-dist/assets/{infoDiagram-FWYZ7A6U-CyO3CAA1.js → infoDiagram-FWYZ7A6U-DugVGI3K.js} +1 -1
  344. package/ui-dist/assets/{ishikawaDiagram-FXEZZL3T-BLLZuF1_.js → ishikawaDiagram-FXEZZL3T-CHy06Oev.js} +1 -1
  345. package/ui-dist/assets/{journeyDiagram-5HDEW3XC-CWzQocvE.js → journeyDiagram-5HDEW3XC-BI0w53rb.js} +1 -1
  346. package/ui-dist/assets/{kanban-definition-HUTT4EX6-Dz4SFU-I.js → kanban-definition-HUTT4EX6-v_bLwx7B.js} +1 -1
  347. package/ui-dist/assets/layout-Dwp1s_bo.js +1 -0
  348. package/ui-dist/assets/{linear-BlzDlAaL.js → linear-DAV4STLZ.js} +1 -1
  349. package/ui-dist/assets/map-Bs6QpaaQ.js +1 -0
  350. package/ui-dist/assets/{mermaid.core-BtCydd-w.js → mermaid.core-fVmaL47V.js} +4 -4
  351. package/ui-dist/assets/{mindmap-definition-LN4V7U3C-BeIkKRBx.js → mindmap-definition-LN4V7U3C-5dWD4dxm.js} +1 -1
  352. package/ui-dist/assets/{pegDiagram-2B236MQR-BZgpT4TN.js → pegDiagram-2B236MQR-DB2XsDPN.js} +1 -1
  353. package/ui-dist/assets/{pieDiagram-ENE6RG2P-BRFggcg2.js → pieDiagram-ENE6RG2P-Bc5teLCS.js} +1 -1
  354. package/ui-dist/assets/{quadrantDiagram-ABIIQ3AL-CibUo3gT.js → quadrantDiagram-ABIIQ3AL-BlfNdETt.js} +1 -1
  355. package/ui-dist/assets/{railroadDiagram-RFXS5EU6-DINJqhgn.js → railroadDiagram-RFXS5EU6-BbXywkq3.js} +1 -1
  356. package/ui-dist/assets/{requirementDiagram-TGXJPOKE-NegluQIe.js → requirementDiagram-TGXJPOKE-DU-CapdF.js} +1 -1
  357. package/ui-dist/assets/{sankeyDiagram-HTMAVEWB-Kq41yHAd.js → sankeyDiagram-HTMAVEWB-Ftvh3MqA.js} +1 -1
  358. package/ui-dist/assets/{sequenceDiagram-DBY2YBRQ-CngfEI4f.js → sequenceDiagram-DBY2YBRQ-CCtHt9V4.js} +1 -1
  359. package/ui-dist/assets/{sizeCapture-X5ZJPWSS-DUWRNlme.js → sizeCapture-X5ZJPWSS-x0jnIKHx.js} +1 -1
  360. package/ui-dist/assets/{stateDiagram-2N3HPSRC-BwHC_Fz4.js → stateDiagram-2N3HPSRC-2qCrydE7.js} +1 -1
  361. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-CvGRUgRf.js +1 -0
  362. package/ui-dist/assets/{swimlanes-5IMT3BWC-Cqp2rT1K.js → swimlanes-5IMT3BWC-6kqJq5Xf.js} +2 -2
  363. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-EBa7-qSc.js +8 -0
  364. package/ui-dist/assets/{timeline-definition-FHXFAJF6-BG3GYPCu.js → timeline-definition-FHXFAJF6-BuRz8KHZ.js} +1 -1
  365. package/ui-dist/assets/{vennDiagram-L72KCM5P-D1IFU27N.js → vennDiagram-L72KCM5P-BKzMZ_Rv.js} +1 -1
  366. package/ui-dist/assets/{wardleyDiagram-EHGQE667-DboO2kYu.js → wardleyDiagram-EHGQE667-8uXlFtBV.js} +1 -1
  367. package/ui-dist/assets/{xychartDiagram-FW5EYKEG-vI2BGrXP.js → xychartDiagram-FW5EYKEG-CWKGlpF9.js} +1 -1
  368. package/ui-dist/index.html +2 -2
  369. package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-DHyfFgK0.js +0 -36
  370. package/ui-dist/assets/channel-TMGI9Qt9.js +0 -1
  371. package/ui-dist/assets/classDiagram-OUVF2IWQ-r5zIpY6G.js +0 -1
  372. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-r5zIpY6G.js +0 -1
  373. package/ui-dist/assets/cose-bilkent-JH36ORCC-fSNs9447.js +0 -1
  374. package/ui-dist/assets/ganttDiagram-NO4QXBWP-CJYhBF1C.js +0 -292
  375. package/ui-dist/assets/graph-DdBT9W3J.js +0 -1
  376. package/ui-dist/assets/index-6UIO-gg7.js +0 -1
  377. package/ui-dist/assets/index-BVkQVMFn.js +0 -1
  378. package/ui-dist/assets/index-BaAPxkqh.css +0 -1
  379. package/ui-dist/assets/index-CmCERTmS.js +0 -1
  380. package/ui-dist/assets/index-Cr4lUIaJ.js +0 -1
  381. package/ui-dist/assets/index-D0fAqlES.js +0 -1
  382. package/ui-dist/assets/index-DfNcAyZq.js +0 -1
  383. package/ui-dist/assets/layout-D3tY9q4Q.js +0 -1
  384. package/ui-dist/assets/map-Ctg1-a9N.js +0 -1
  385. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-C6P3NY96.js +0 -1
  386. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-BNdDt62J.js +0 -8
@@ -2,6 +2,7 @@ import { readRudderSkillSyncPreference, writeRudderSkillSyncPreference } from "@
2
2
  import { agents as agentRows, organizationSkills } from "@rudderhq/db";
3
3
  import { getActiveRudderBundledSkillSlugs, getBundledRudderSkillSlug, isRetiredRudderCreationSkillReference, toBundledRudderSkillKey, } from "@rudderhq/shared";
4
4
  import { and, asc, eq } from "drizzle-orm";
5
+ import { randomUUID } from "node:crypto";
5
6
  import { promises as fs } from "node:fs";
6
7
  import path from "node:path";
7
8
  import { conflict, notFound, unprocessable } from "../../errors.js";
@@ -11,9 +12,12 @@ import { agentService } from "../agents.js";
11
12
  import { resolveBrowserCapability, resolveBrowserCapabilityDeployment, } from "../browser-capability.js";
12
13
  import { instanceSettingsService } from "../instance-settings.js";
13
14
  import { projectService } from "../projects.js";
14
- import { ADAPTER_SKILL_HOME_DEFINITIONS, COMMUNITY_PRESET_SKILLS, COMMUNITY_PRESET_SKILL_SLUGS, applyDesiredSelectionsToCatalog, arraysEqual, asString, buildAgentPrivateSkillEntry, buildAgentSelectionKey, buildBundledSelectionKey, buildDraftSkillMarkdown, buildGlobalSelectionKey, buildOrganizationSelectionKey, buildSkillRuntimeName, compareOrganizationSkillListItems, deriveCanonicalSkillKey, deriveSkillSourceInfo, enrichSkill, findMissingLocalSkillIds, getRequiredBundledSkillKeys, getSkillMeta, inferLanguageFromPath, isBundledRudderSkillKey, isBundledRudderSourceKind, isMarkdownPath, isPlainRecord, listLegacyUserHomeLocalScanSkillIds, listStaleBundledSkillIds, listStaleCommunityPresetSkillIds, normalizeGitHubSkillDirectory, normalizePackageFileMap, normalizePortablePath, normalizeSelectionRef, normalizeSkillDescription, normalizeSkillDirectory, normalizeSkillSlug, parseSelectionKey, readAdapterSkillCatalogEntries, readDiscoveredSkillEntries, resolveConfiguredHomeDir, resolveLocalSkillFilePath, resolveManagedSkillsRoot, resolveRequestedSkillKeysOrThrow, resolveWorkspaceEditPath, serializeFileInventory, skillInventoryRefreshPromises, sortUniqueSelectionRefs, statPath, toCompanySkill, toCompanySkillListItem, uniqueImportedSkillKey, uniqueSkillSlug } from "./organization-skills.catalog.js";
15
+ import { ADAPTER_SKILL_HOME_DEFINITIONS, COMMUNITY_PRESET_SKILLS, COMMUNITY_PRESET_SKILL_SLUGS, applyDesiredSelectionsToCatalog, arraysEqual, asString, buildAgentPrivateSkillEntry, buildAgentSelectionKey, buildBundledSelectionKey, buildDraftSkillMarkdown, buildGlobalSelectionKey, buildOrganizationSelectionKey, buildSkillRuntimeName, compareOrganizationSkillListItems, deriveCanonicalSkillKey, deriveSkillSourceInfo, enrichSkill, findMissingLocalSkillIds, getRequiredBundledSkillKeys, getSkillMeta, inferLanguageFromPath, isBundledRudderSkillKey, isBundledRudderSourceKind, isMarkdownPath, isPlainRecord, listLegacyUserHomeLocalScanSkillIds, listStaleBundledSkillIds, listStaleCommunityPresetSkillIds, normalizePackageFileMap, normalizePortablePath, normalizeSafeRelativeSkillPath, normalizeSelectionRef, normalizeSkillDescription, normalizeSkillDirectory, normalizeSkillSlug, parseSelectionKey, readAdapterSkillCatalogEntries, readDiscoveredSkillEntries, resolveConfiguredHomeDir, resolveLocalSkillFilePath, resolveManagedSkillsRoot, resolveRequestedSkillKeysOrThrow, resolveWorkspaceEditPath, serializeFileInventory, skillInventoryRefreshPromises, sortUniqueSelectionRefs, statPath, toCompanySkill, toCompanySkillListItem, uniqueImportedSkillKey, uniqueSkillSlug } from "./organization-skills.catalog.js";
15
16
  import { createOrganizationSkillScanHandlers } from "./organization-skills.scans.js";
16
- import { fetchText, parseFrontmatterMarkdown, parseSkillImportSourceInput, readCommunityPresetFallbackImport, readInlineSkillImports, readLocalSkillImportFromDirectory, readLocalSkillImports, readUrlSkillImports, resolveBundledSkillsRoot, resolveCommunityPresetSkillsRoot, resolveGitHubCommitSha, resolveRawGitHubUrl, } from "./organization-skills.sources.js";
17
+ import { parseFrontmatterMarkdown, parseSkillImportSourceInput, readCommunityPresetFallbackImport, readInlineSkillImports, readLocalSkillImportFromDirectory, readLocalSkillImports, readUrlSkillImports, resolveBundledSkillsRoot, resolveCommunityPresetSkillsRoot, resolveGitHubCommitSha, } from "./organization-skills.sources.js";
18
+ const ORGANIZATION_SKILL_INSTALLATION_VERSION = 1;
19
+ const skillInstallationPromises = new Map();
20
+ const skillMutationLocks = new Map();
17
21
  export function organizationSkillService(db, options = {}) {
18
22
  const deploymentMode = resolveBrowserCapabilityDeployment(db, options.deploymentMode);
19
23
  const agents = agentService(db);
@@ -25,6 +29,318 @@ export function organizationSkillService(db, options = {}) {
25
29
  projects,
26
30
  upsertImportedSkills,
27
31
  });
32
+ function normalizeInstalledFilePath(relativePath) {
33
+ const normalized = normalizeSafeRelativeSkillPath(relativePath);
34
+ if (!normalized) {
35
+ throw unprocessable(`Invalid skill file path: ${relativePath}`);
36
+ }
37
+ return normalized;
38
+ }
39
+ function resolveInstalledSkillDirectory(orgId, key, slug) {
40
+ const managedRoot = path.resolve(resolveManagedSkillsRoot(orgId));
41
+ const installedRoot = path.resolve(managedRoot, "__installed__");
42
+ const target = path.resolve(installedRoot, buildSkillRuntimeName(key, slug));
43
+ const relative = path.relative(installedRoot, target);
44
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
45
+ throw unprocessable("Invalid organization skill installation path.");
46
+ }
47
+ return target;
48
+ }
49
+ async function removeInstalledSkillDirectory(orgId, skill) {
50
+ if (getSkillMeta(skill).installationVersion !== ORGANIZATION_SKILL_INSTALLATION_VERSION)
51
+ return;
52
+ await fs.rm(resolveInstalledSkillDirectory(orgId, skill.key, skill.slug), { recursive: true, force: true });
53
+ }
54
+ async function mapWithConcurrency(values, concurrency, operation) {
55
+ let nextIndex = 0;
56
+ const workers = Array.from({ length: Math.min(Math.max(1, concurrency), values.length) }, async () => {
57
+ while (nextIndex < values.length) {
58
+ const index = nextIndex;
59
+ nextIndex += 1;
60
+ await operation(values[index]);
61
+ }
62
+ });
63
+ await Promise.all(workers);
64
+ }
65
+ async function withSkillMutationLock(orgId, skillKey, operation) {
66
+ const lockKey = `${orgId}:${skillKey}`;
67
+ const previous = skillMutationLocks.get(lockKey) ?? Promise.resolve();
68
+ let release;
69
+ const current = new Promise((resolve) => {
70
+ release = resolve;
71
+ });
72
+ skillMutationLocks.set(lockKey, current);
73
+ await previous.catch(() => { });
74
+ try {
75
+ return await operation();
76
+ }
77
+ finally {
78
+ release();
79
+ if (skillMutationLocks.get(lockKey) === current) {
80
+ skillMutationLocks.delete(lockKey);
81
+ }
82
+ }
83
+ }
84
+ async function readImportedSkillFiles(skill) {
85
+ const files = new Map();
86
+ for (const [rawPath, content] of Object.entries(skill.files ?? {})) {
87
+ files.set(normalizeInstalledFilePath(rawPath), content);
88
+ }
89
+ if (skill.packageDir) {
90
+ await mapWithConcurrency(skill.fileInventory, 8, async (entry) => {
91
+ const relativePath = normalizeInstalledFilePath(entry.path);
92
+ if (files.has(relativePath))
93
+ return;
94
+ files.set(relativePath, await fs.readFile(path.resolve(skill.packageDir, relativePath)));
95
+ });
96
+ }
97
+ for (const entry of skill.fileInventory) {
98
+ const relativePath = normalizeInstalledFilePath(entry.path);
99
+ if (!files.has(relativePath)) {
100
+ throw unprocessable(`Skill source did not provide declared file: ${relativePath}`);
101
+ }
102
+ }
103
+ return files;
104
+ }
105
+ async function replaceInstalledSkillDirectory(orgId, skill, files, persist) {
106
+ const target = resolveInstalledSkillDirectory(orgId, skill.key, skill.slug);
107
+ const parent = path.dirname(target);
108
+ const staging = path.join(parent, `.${path.basename(target)}.staging-${randomUUID()}`);
109
+ const backup = path.join(parent, `.${path.basename(target)}.backup-${randomUUID()}`);
110
+ let movedExisting = false;
111
+ async function restorePreviousInstallation(originalError) {
112
+ const displaced = path.join(parent, `.${path.basename(target)}.rollback-displaced-${randomUUID()}`);
113
+ let movedCurrentTarget = false;
114
+ try {
115
+ if (await fs.stat(target).catch(() => null)) {
116
+ await fs.rename(target, displaced);
117
+ movedCurrentTarget = true;
118
+ }
119
+ await fs.rename(backup, target);
120
+ movedExisting = false;
121
+ if (movedCurrentTarget) {
122
+ await fs.rm(displaced, { recursive: true, force: true });
123
+ movedCurrentTarget = false;
124
+ }
125
+ }
126
+ catch (rollbackError) {
127
+ if (movedCurrentTarget
128
+ && !(await fs.stat(target).catch(() => null))) {
129
+ await fs.rename(displaced, target).catch(() => { });
130
+ }
131
+ throw conflict("Organization skill installation consistency error: rollback could not restore the previous installation.", {
132
+ target,
133
+ backup,
134
+ originalError: originalError instanceof Error
135
+ ? originalError.message
136
+ : String(originalError),
137
+ rollbackError: rollbackError instanceof Error
138
+ ? rollbackError.message
139
+ : String(rollbackError),
140
+ });
141
+ }
142
+ }
143
+ async function removeFailedInstallation(originalError) {
144
+ try {
145
+ await fs.rm(target, { recursive: true, force: true });
146
+ }
147
+ catch (rollbackError) {
148
+ throw conflict("Organization skill installation consistency error: rollback could not remove the failed installation.", {
149
+ target,
150
+ originalError: originalError instanceof Error
151
+ ? originalError.message
152
+ : String(originalError),
153
+ rollbackError: rollbackError instanceof Error
154
+ ? rollbackError.message
155
+ : String(rollbackError),
156
+ });
157
+ }
158
+ }
159
+ await fs.mkdir(staging, { recursive: true });
160
+ try {
161
+ await mapWithConcurrency(skill.fileInventory, 8, async (entry) => {
162
+ const relativePath = normalizeInstalledFilePath(entry.path);
163
+ const targetPath = path.resolve(staging, relativePath);
164
+ const relativeTarget = path.relative(staging, targetPath);
165
+ if (relativeTarget.startsWith("..") || path.isAbsolute(relativeTarget)) {
166
+ throw unprocessable(`Invalid skill file path: ${entry.path}`);
167
+ }
168
+ const content = files.get(relativePath);
169
+ if (content === undefined) {
170
+ throw unprocessable(`Skill source did not provide declared file: ${relativePath}`);
171
+ }
172
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
173
+ await fs.writeFile(targetPath, content);
174
+ });
175
+ const skillFile = await fs.stat(path.join(staging, "SKILL.md")).catch(() => null);
176
+ if (!skillFile?.isFile()) {
177
+ throw unprocessable("Installed skill is missing SKILL.md.");
178
+ }
179
+ await fs.mkdir(parent, { recursive: true });
180
+ const existing = await fs.stat(target).catch(() => null);
181
+ if (existing) {
182
+ await fs.rename(target, backup);
183
+ movedExisting = true;
184
+ }
185
+ try {
186
+ await fs.rename(staging, target);
187
+ }
188
+ catch (error) {
189
+ if (movedExisting) {
190
+ await restorePreviousInstallation(error);
191
+ }
192
+ throw error;
193
+ }
194
+ let persisted;
195
+ try {
196
+ persisted = await persist();
197
+ }
198
+ catch (error) {
199
+ if (movedExisting) {
200
+ await restorePreviousInstallation(error);
201
+ }
202
+ else {
203
+ await removeFailedInstallation(error);
204
+ }
205
+ throw error;
206
+ }
207
+ if (movedExisting) {
208
+ await fs.rm(backup, { recursive: true, force: true });
209
+ movedExisting = false;
210
+ }
211
+ return persisted;
212
+ }
213
+ finally {
214
+ await fs.rm(staging, { recursive: true, force: true });
215
+ }
216
+ }
217
+ async function installImportedSkill(orgId, skill, persist) {
218
+ const sourceKind = asString(skill.metadata?.sourceKind);
219
+ if (isBundledRudderSourceKind(sourceKind))
220
+ return persist(skill);
221
+ const installed = markImportedSkillInstalled(skill);
222
+ const files = await readImportedSkillFiles(installed);
223
+ return withSkillMutationLock(orgId, installed.key, () => replaceInstalledSkillDirectory(orgId, installed, files, () => persist(installed)));
224
+ }
225
+ function markImportedSkillInstalled(skill) {
226
+ return {
227
+ ...skill,
228
+ metadata: {
229
+ ...(skill.metadata ?? {}),
230
+ installationVersion: ORGANIZATION_SKILL_INSTALLATION_VERSION,
231
+ },
232
+ };
233
+ }
234
+ function hasUnchangedInstallationSource(current, snapshot) {
235
+ return current.sourceType === snapshot.sourceType
236
+ && current.sourceLocator === snapshot.sourceLocator
237
+ && current.sourceRef === snapshot.sourceRef
238
+ && getSkillMeta(current).installationVersion
239
+ === getSkillMeta(snapshot).installationVersion
240
+ && current.updatedAt.getTime() === snapshot.updatedAt.getTime();
241
+ }
242
+ async function resolveCurrentSkillDirectory(skill) {
243
+ const directory = normalizeSkillDirectory(skill);
244
+ if (directory
245
+ && (await fs.stat(path.join(directory, "SKILL.md")).catch(() => null))?.isFile()) {
246
+ return directory;
247
+ }
248
+ return null;
249
+ }
250
+ function isStrictlyWithinPath(root, target) {
251
+ const relative = path.relative(root, target);
252
+ return Boolean(relative)
253
+ && !relative.startsWith("..")
254
+ && !path.isAbsolute(relative);
255
+ }
256
+ async function resolveValidatedLocalSkillFilePath(skill, relativePath) {
257
+ const absolutePath = resolveLocalSkillFilePath(skill, relativePath);
258
+ const skillRoot = normalizeSkillDirectory(skill);
259
+ if (!absolutePath || !skillRoot)
260
+ return null;
261
+ const realRoot = await fs.realpath(skillRoot).catch(() => null);
262
+ if (!realRoot)
263
+ return null;
264
+ const targetEntry = await fs.lstat(absolutePath).catch(() => null);
265
+ if (targetEntry) {
266
+ const realTarget = await fs.realpath(absolutePath).catch(() => null);
267
+ return realTarget && isStrictlyWithinPath(realRoot, realTarget)
268
+ ? absolutePath
269
+ : null;
270
+ }
271
+ let existingAncestor = path.dirname(absolutePath);
272
+ while (!(await fs.lstat(existingAncestor).catch(() => null))
273
+ && existingAncestor !== path.dirname(existingAncestor)) {
274
+ existingAncestor = path.dirname(existingAncestor);
275
+ }
276
+ const realAncestor = await fs.realpath(existingAncestor).catch(() => null);
277
+ if (!realAncestor
278
+ || (realAncestor !== realRoot && !isStrictlyWithinPath(realRoot, realAncestor))) {
279
+ return null;
280
+ }
281
+ const projectedTarget = path.resolve(realAncestor, path.relative(existingAncestor, absolutePath));
282
+ return isStrictlyWithinPath(realRoot, projectedTarget) ? absolutePath : null;
283
+ }
284
+ async function assertDirectLocalSourceIsUnmanaged(orgId, skill) {
285
+ const sourcePath = skill.packageDir ?? skill.sourceLocator;
286
+ if (!sourcePath)
287
+ throw unprocessable("Local skill source path is missing.");
288
+ const sourceDirectory = path.basename(sourcePath).toLowerCase() === "skill.md"
289
+ ? path.dirname(sourcePath)
290
+ : sourcePath;
291
+ const managedInstalledRoot = path.resolve(resolveManagedSkillsRoot(orgId), "__installed__");
292
+ const [resolvedSource, resolvedManagedRoot] = await Promise.all([
293
+ fs.realpath(path.resolve(sourceDirectory)).catch(() => path.resolve(sourceDirectory)),
294
+ fs.realpath(managedInstalledRoot).catch(() => managedInstalledRoot),
295
+ ]);
296
+ const relative = path.relative(resolvedManagedRoot, resolvedSource);
297
+ if (!relative || (!relative.startsWith("..") && !path.isAbsolute(relative))) {
298
+ throw unprocessable("Managed organization skill installations cannot be imported as direct local sources.");
299
+ }
300
+ }
301
+ async function upsertDirectLocalSkill(orgId, skill) {
302
+ return withSkillMutationLock(orgId, skill.key, async () => {
303
+ await assertDirectLocalSourceIsUnmanaged(orgId, skill);
304
+ const existing = await getByKey(orgId, skill.key);
305
+ if (existing
306
+ && isBundledRudderSourceKind(asString(getSkillMeta(existing).sourceKind))) {
307
+ return existing;
308
+ }
309
+ const [persisted] = await upsertImportedSkills(orgId, [skill]);
310
+ if (!persisted)
311
+ throw notFound("Failed to persist organization skill");
312
+ // The database transition must succeed before retiring the managed copy so
313
+ // a failed local-path upsert leaves the previous installation runnable.
314
+ if (existing
315
+ && getSkillMeta(existing).installationVersion
316
+ === ORGANIZATION_SKILL_INSTALLATION_VERSION) {
317
+ await removeInstalledSkillDirectory(orgId, existing);
318
+ }
319
+ return persisted;
320
+ });
321
+ }
322
+ async function installAndUpsertImportedSkills(orgId, skills) {
323
+ const out = [];
324
+ for (const skill of skills) {
325
+ const existing = await getByKey(orgId, skill.key);
326
+ if (existing
327
+ && isBundledRudderSourceKind(asString(getSkillMeta(existing).sourceKind))) {
328
+ out.push(existing);
329
+ continue;
330
+ }
331
+ if (skill.sourceType === "local_path") {
332
+ out.push(await upsertDirectLocalSkill(orgId, skill));
333
+ continue;
334
+ }
335
+ out.push(await installImportedSkill(orgId, skill, async (installed) => {
336
+ const [persisted] = await upsertImportedSkills(orgId, [installed]);
337
+ if (!persisted)
338
+ throw notFound("Failed to persist organization skill");
339
+ return persisted;
340
+ }));
341
+ }
342
+ return out;
343
+ }
28
344
  async function getAgentWorkspaceRow(orgId, agentId) {
29
345
  const row = await db
30
346
  .select({
@@ -187,7 +503,9 @@ export function organizationSkillService(db, options = {}) {
187
503
  const existing = existingByKey.get(skill.key);
188
504
  if (!existing)
189
505
  return true;
190
- return asString(existing.metadata?.sourceKind) === "community_preset";
506
+ const existingMetadata = isPlainRecord(existing.metadata) ? existing.metadata : {};
507
+ return asString(existingMetadata.sourceKind) === "community_preset"
508
+ && existingMetadata.installationVersion !== ORGANIZATION_SKILL_INSTALLATION_VERSION;
191
509
  });
192
510
  const persisted = toPersist.length > 0 ? await upsertImportedSkills(orgId, toPersist) : [];
193
511
  const stalePresetIds = listStaleCommunityPresetSkillIds(existingRows, currentCommunityPresetKeys);
@@ -217,7 +535,7 @@ export function organizationSkillService(db, options = {}) {
217
535
  await db
218
536
  .delete(organizationSkills)
219
537
  .where(eq(organizationSkills.id, skill.id));
220
- await fs.rm(resolveRuntimeSkillMaterializedPath(orgId, skill), { recursive: true, force: true });
538
+ await removeInstalledSkillDirectory(orgId, skill);
221
539
  }
222
540
  }
223
541
  async function pruneLegacyUserHomeLocalScanSkills(orgId) {
@@ -239,7 +557,7 @@ export function organizationSkillService(db, options = {}) {
239
557
  await db
240
558
  .delete(organizationSkills)
241
559
  .where(eq(organizationSkills.id, skill.id));
242
- await fs.rm(resolveRuntimeSkillMaterializedPath(orgId, skill), { recursive: true, force: true });
560
+ await removeInstalledSkillDirectory(orgId, skill);
243
561
  }
244
562
  }
245
563
  async function backfillMissingSkillDescriptions(orgId) {
@@ -574,10 +892,14 @@ export function organizationSkillService(db, options = {}) {
574
892
  if (!skill)
575
893
  continue;
576
894
  let source = normalizeSkillDirectory(skill);
895
+ if (source
896
+ && !(await fs.stat(path.join(source, "SKILL.md")).catch(() => null))?.isFile()) {
897
+ source = null;
898
+ }
577
899
  if (!source) {
578
900
  source = options.materializeMissing === false
579
- ? resolveRuntimeSkillMaterializedPath(orgId, skill)
580
- : await materializeRuntimeSkillFiles(orgId, skill).catch(() => null);
901
+ ? null
902
+ : await ensureInstalledSkill(orgId, skill).catch(() => null);
581
903
  }
582
904
  if (!source)
583
905
  continue;
@@ -705,15 +1027,18 @@ export function organizationSkillService(db, options = {}) {
705
1027
  const skill = await getById(skillId);
706
1028
  if (!skill || skill.orgId !== orgId)
707
1029
  return null;
708
- const normalizedPath = normalizePortablePath(relativePath || "SKILL.md");
1030
+ const normalizedPath = normalizeSafeRelativeSkillPath(relativePath || "SKILL.md");
1031
+ if (!normalizedPath)
1032
+ throw notFound("Skill file not found");
709
1033
  const fileEntry = skill.fileInventory.find((entry) => entry.path === normalizedPath);
710
1034
  if (!fileEntry) {
711
1035
  throw notFound("Skill file not found");
712
1036
  }
713
1037
  const source = deriveSkillSourceInfo(skill);
714
1038
  let content = "";
715
- if (skill.sourceType === "local_path" || skill.sourceType === "catalog") {
716
- const absolutePath = resolveLocalSkillFilePath(skill, normalizedPath);
1039
+ const localSkillDirectory = normalizeSkillDirectory(skill);
1040
+ if (localSkillDirectory) {
1041
+ const absolutePath = await resolveValidatedLocalSkillFilePath(skill, normalizedPath);
717
1042
  if (absolutePath) {
718
1043
  content = await fs.readFile(absolutePath, "utf8");
719
1044
  }
@@ -724,26 +1049,11 @@ export function organizationSkillService(db, options = {}) {
724
1049
  throw notFound("Skill file not found");
725
1050
  }
726
1051
  }
727
- else if (skill.sourceType === "github" || skill.sourceType === "skills_sh") {
728
- const metadata = getSkillMeta(skill);
729
- const owner = asString(metadata.owner);
730
- const repo = asString(metadata.repo);
731
- const ref = skill.sourceRef ?? asString(metadata.ref) ?? "main";
732
- const repoSkillDir = normalizeGitHubSkillDirectory(asString(metadata.repoSkillDir), skill.slug);
733
- if (!owner || !repo) {
734
- throw unprocessable("Skill source metadata is incomplete.");
735
- }
736
- const repoPath = normalizePortablePath(path.posix.join(repoSkillDir, normalizedPath));
737
- content = await fetchText(resolveRawGitHubUrl(owner, repo, ref, repoPath));
738
- }
739
- else if (skill.sourceType === "url") {
740
- if (normalizedPath !== "SKILL.md") {
741
- throw notFound("This skill source only exposes SKILL.md");
742
- }
1052
+ else if (normalizedPath === "SKILL.md") {
743
1053
  content = skill.markdown;
744
1054
  }
745
1055
  else {
746
- throw unprocessable("Unsupported skill source.");
1056
+ throw notFound("Skill file is not installed yet.");
747
1057
  }
748
1058
  return {
749
1059
  skillId: skill.id,
@@ -810,37 +1120,90 @@ export function organizationSkillService(db, options = {}) {
810
1120
  if (!skill || skill.orgId !== orgId)
811
1121
  throw notFound("Skill not found");
812
1122
  const source = deriveSkillSourceInfo(skill);
813
- if (!source.editable || skill.sourceType !== "local_path") {
1123
+ if (!source.editable) {
814
1124
  throw unprocessable(source.editableReason ?? "This skill cannot be edited.");
815
1125
  }
816
- const normalizedPath = normalizePortablePath(relativePath);
817
- const absolutePath = resolveLocalSkillFilePath(skill, normalizedPath);
818
- if (!absolutePath)
819
- throw notFound("Skill file not found");
820
- await fs.mkdir(path.dirname(absolutePath), { recursive: true });
821
- await fs.writeFile(absolutePath, content, "utf8");
822
- if (normalizedPath === "SKILL.md") {
823
- const parsed = parseFrontmatterMarkdown(content);
824
- await db
825
- .update(organizationSkills)
826
- .set({
827
- name: asString(parsed.frontmatter.name) ?? skill.name,
828
- description: normalizeSkillDescription(parsed.frontmatter.description) ?? skill.description,
829
- markdown: content,
830
- updatedAt: new Date(),
831
- })
832
- .where(eq(organizationSkills.id, skill.id));
833
- }
834
- else {
835
- await db
836
- .update(organizationSkills)
837
- .set({ updatedAt: new Date() })
838
- .where(eq(organizationSkills.id, skill.id));
839
- }
840
- const detail = await readFile(orgId, skillId, normalizedPath);
841
- if (!detail)
842
- throw notFound("Skill file not found");
843
- return detail;
1126
+ await ensureInstalledSkill(orgId, skill);
1127
+ return withSkillMutationLock(orgId, skill.key, async () => {
1128
+ const current = await getById(skillId);
1129
+ if (!current || current.orgId !== orgId)
1130
+ throw notFound("Skill not found");
1131
+ const currentSource = deriveSkillSourceInfo(current);
1132
+ if (!currentSource.editable) {
1133
+ throw unprocessable(currentSource.editableReason ?? "This skill cannot be edited.");
1134
+ }
1135
+ const normalizedPath = normalizeSafeRelativeSkillPath(relativePath);
1136
+ if (!normalizedPath)
1137
+ throw notFound("Skill file not found");
1138
+ if (!current.fileInventory.some((entry) => entry.path === normalizedPath)) {
1139
+ throw notFound("Skill file not found");
1140
+ }
1141
+ const absolutePath = await resolveValidatedLocalSkillFilePath(current, normalizedPath);
1142
+ if (!absolutePath)
1143
+ throw notFound("Skill file not found");
1144
+ let previousContent = null;
1145
+ let fileExisted = false;
1146
+ try {
1147
+ previousContent = await fs.readFile(absolutePath);
1148
+ fileExisted = true;
1149
+ }
1150
+ catch (error) {
1151
+ if (!(error instanceof Error)
1152
+ || !("code" in error)
1153
+ || error.code !== "ENOENT") {
1154
+ throw error;
1155
+ }
1156
+ }
1157
+ await fs.mkdir(path.dirname(absolutePath), { recursive: true });
1158
+ await fs.writeFile(absolutePath, content, "utf8");
1159
+ try {
1160
+ if (normalizedPath === "SKILL.md") {
1161
+ const parsed = parseFrontmatterMarkdown(content);
1162
+ await db
1163
+ .update(organizationSkills)
1164
+ .set({
1165
+ name: asString(parsed.frontmatter.name) ?? current.name,
1166
+ description: normalizeSkillDescription(parsed.frontmatter.description)
1167
+ ?? current.description,
1168
+ markdown: content,
1169
+ updatedAt: new Date(),
1170
+ })
1171
+ .where(eq(organizationSkills.id, current.id));
1172
+ }
1173
+ else {
1174
+ await db
1175
+ .update(organizationSkills)
1176
+ .set({ updatedAt: new Date() })
1177
+ .where(eq(organizationSkills.id, current.id));
1178
+ }
1179
+ }
1180
+ catch (persistenceError) {
1181
+ try {
1182
+ if (fileExisted) {
1183
+ await fs.writeFile(absolutePath, previousContent);
1184
+ }
1185
+ else {
1186
+ await fs.rm(absolutePath, { force: true });
1187
+ }
1188
+ }
1189
+ catch (rollbackError) {
1190
+ throw conflict("Organization skill edit consistency error: rollback could not restore the previous file.", {
1191
+ path: normalizedPath,
1192
+ persistenceError: persistenceError instanceof Error
1193
+ ? persistenceError.message
1194
+ : String(persistenceError),
1195
+ rollbackError: rollbackError instanceof Error
1196
+ ? rollbackError.message
1197
+ : String(rollbackError),
1198
+ });
1199
+ }
1200
+ throw persistenceError;
1201
+ }
1202
+ const detail = await readFile(orgId, skillId, normalizedPath);
1203
+ if (!detail)
1204
+ throw notFound("Skill file not found");
1205
+ return detail;
1206
+ });
844
1207
  }
845
1208
  async function syncWorkspaceFileChange(orgId, workspaceFilePath, content) {
846
1209
  await ensureSkillInventoryCurrent(orgId);
@@ -890,48 +1253,174 @@ export function organizationSkillService(db, options = {}) {
890
1253
  if (!matching) {
891
1254
  throw unprocessable(`Skill ${skill.key} could not be re-imported from its source.`);
892
1255
  }
893
- const imported = await upsertImportedSkills(orgId, [matching]);
894
- return imported[0] ?? null;
1256
+ return installImportedSkill(orgId, {
1257
+ ...matching,
1258
+ key: skill.key,
1259
+ slug: skill.slug,
1260
+ sourceType: skill.sourceType,
1261
+ sourceLocator: skill.sourceLocator,
1262
+ metadata: {
1263
+ ...(skill.metadata ?? {}),
1264
+ ...(matching.metadata ?? {}),
1265
+ sourceKind: asString(skill.metadata?.sourceKind)
1266
+ ?? asString(matching.metadata?.sourceKind),
1267
+ },
1268
+ }, async (installed) => {
1269
+ const current = await getById(skill.id);
1270
+ if (!current)
1271
+ throw conflict(`Skill ${skill.key} no longer exists.`);
1272
+ if (!hasUnchangedInstallationSource(current, skill)) {
1273
+ throw conflict(`Skill ${skill.key} changed while update was downloading.`);
1274
+ }
1275
+ const [persisted] = await upsertImportedSkills(orgId, [installed]);
1276
+ return persisted ?? null;
1277
+ });
895
1278
  }
896
- async function materializeCatalogSkillFiles(orgId, skill, normalizedFiles) {
897
- const packageDir = skill.packageDir ? normalizePortablePath(skill.packageDir) : null;
898
- if (!packageDir)
899
- return null;
900
- const catalogRoot = path.resolve(resolveManagedSkillsRoot(orgId), "__catalog__");
901
- const skillDir = path.resolve(catalogRoot, buildSkillRuntimeName(skill.key, skill.slug));
902
- await fs.rm(skillDir, { recursive: true, force: true });
903
- await fs.mkdir(skillDir, { recursive: true });
904
- for (const entry of skill.fileInventory) {
905
- const sourcePath = entry.path === "SKILL.md"
906
- ? `${packageDir}/SKILL.md`
907
- : `${packageDir}/${entry.path}`;
908
- const content = normalizedFiles[sourcePath];
909
- if (typeof content !== "string")
910
- continue;
911
- const targetPath = path.resolve(skillDir, entry.path);
912
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
913
- await fs.writeFile(targetPath, content, "utf8");
1279
+ async function ensureInstalledSkill(orgId, skill) {
1280
+ const existingDirectory = normalizeSkillDirectory(skill);
1281
+ if (existingDirectory
1282
+ && (await fs.stat(path.join(existingDirectory, "SKILL.md")).catch(() => null))?.isFile()) {
1283
+ return existingDirectory;
914
1284
  }
915
- return skillDir;
916
- }
917
- async function materializeRuntimeSkillFiles(orgId, skill) {
918
- const runtimeRoot = path.resolve(resolveManagedSkillsRoot(orgId), "__runtime__");
919
- const skillDir = path.resolve(runtimeRoot, buildSkillRuntimeName(skill.key, skill.slug));
920
- await fs.rm(skillDir, { recursive: true, force: true });
921
- await fs.mkdir(skillDir, { recursive: true });
922
- for (const entry of skill.fileInventory) {
923
- const detail = await readFile(orgId, skill.id, entry.path).catch(() => null);
924
- if (!detail)
925
- continue;
926
- const targetPath = path.resolve(skillDir, entry.path);
927
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
928
- await fs.writeFile(targetPath, detail.content, "utf8");
1285
+ if (isBundledRudderSourceKind(asString(skill.metadata?.sourceKind))) {
1286
+ throw unprocessable("Bundled Rudder skill files are unavailable.");
1287
+ }
1288
+ const lockKey = `${orgId}:${skill.id}`;
1289
+ const existingInstall = skillInstallationPromises.get(lockKey);
1290
+ if (existingInstall)
1291
+ return existingInstall;
1292
+ const installation = (async () => {
1293
+ const target = resolveInstalledSkillDirectory(orgId, skill.key, skill.slug);
1294
+ if ((await fs.stat(path.join(target, "SKILL.md")).catch(() => null))?.isFile()) {
1295
+ return withSkillMutationLock(orgId, skill.key, async () => {
1296
+ const current = await getById(skill.id);
1297
+ if (!current)
1298
+ throw notFound(`Skill ${skill.key} no longer exists.`);
1299
+ if (!hasUnchangedInstallationSource(current, skill)) {
1300
+ const currentDirectory = await resolveCurrentSkillDirectory(current);
1301
+ if (currentDirectory)
1302
+ return currentDirectory;
1303
+ throw conflict(`Skill ${skill.key} changed while installation was prepared.`);
1304
+ }
1305
+ if (getSkillMeta(current).installationVersion
1306
+ !== ORGANIZATION_SKILL_INSTALLATION_VERSION) {
1307
+ await db
1308
+ .update(organizationSkills)
1309
+ .set({
1310
+ metadata: {
1311
+ ...(current.metadata ?? {}),
1312
+ installationVersion: ORGANIZATION_SKILL_INSTALLATION_VERSION,
1313
+ },
1314
+ updatedAt: new Date(),
1315
+ })
1316
+ .where(eq(organizationSkills.id, current.id));
1317
+ }
1318
+ return target;
1319
+ });
1320
+ }
1321
+ let imported = null;
1322
+ if (skill.sourceType === "github"
1323
+ || skill.sourceType === "skills_sh"
1324
+ || skill.sourceType === "url") {
1325
+ if (!skill.sourceLocator) {
1326
+ throw unprocessable("Skill source locator is missing.");
1327
+ }
1328
+ const skillMetadata = getSkillMeta(skill);
1329
+ const owner = asString(skillMetadata.owner);
1330
+ const repo = asString(skillMetadata.repo);
1331
+ const repoSkillDir = asString(skillMetadata.repoSkillDir);
1332
+ const migrationSource = ((skill.sourceType === "github" || skill.sourceType === "skills_sh")
1333
+ && skill.sourceRef
1334
+ && owner
1335
+ && repo)
1336
+ ? `https://github.com/${owner}/${repo}/tree/${skill.sourceRef}${repoSkillDir ? `/${repoSkillDir}` : ""}`
1337
+ : skill.sourceLocator;
1338
+ const parsed = parseSkillImportSourceInput(migrationSource);
1339
+ const result = await readUrlSkillImports(orgId, parsed.resolvedSource, parsed.requestedSkillSlug ?? skill.slug);
1340
+ imported = result.skills.find((entry) => entry.key === skill.key)
1341
+ ?? result.skills.find((entry) => entry.slug === skill.slug)
1342
+ ?? result.skills[0]
1343
+ ?? null;
1344
+ }
1345
+ else if ((skill.sourceType === "local_path" || skill.sourceType === "catalog")
1346
+ && skill.sourceLocator) {
1347
+ const sourceDirectory = path.basename(skill.sourceLocator).toLowerCase() === "skill.md"
1348
+ ? path.dirname(skill.sourceLocator)
1349
+ : skill.sourceLocator;
1350
+ imported = {
1351
+ key: skill.key,
1352
+ slug: skill.slug,
1353
+ name: skill.name,
1354
+ description: skill.description,
1355
+ markdown: skill.markdown,
1356
+ packageDir: path.resolve(sourceDirectory),
1357
+ sourceType: skill.sourceType,
1358
+ sourceLocator: skill.sourceLocator,
1359
+ sourceRef: skill.sourceRef,
1360
+ trustLevel: skill.trustLevel,
1361
+ compatibility: skill.compatibility,
1362
+ fileInventory: skill.fileInventory,
1363
+ metadata: skill.metadata ?? {},
1364
+ };
1365
+ }
1366
+ if (!imported) {
1367
+ throw unprocessable(`Skill ${skill.key} could not be installed from its source.`);
1368
+ }
1369
+ const installed = markImportedSkillInstalled({
1370
+ ...imported,
1371
+ key: skill.key,
1372
+ slug: skill.slug,
1373
+ sourceType: skill.sourceType,
1374
+ sourceLocator: skill.sourceLocator,
1375
+ sourceRef: skill.sourceRef ?? imported.sourceRef,
1376
+ metadata: {
1377
+ ...(skill.metadata ?? {}),
1378
+ ...(imported.metadata ?? {}),
1379
+ sourceKind: asString(skill.metadata?.sourceKind)
1380
+ ?? asString(imported.metadata?.sourceKind),
1381
+ trackingRef: asString(skill.metadata?.trackingRef)
1382
+ ?? asString(imported.metadata?.trackingRef),
1383
+ },
1384
+ });
1385
+ const files = await readImportedSkillFiles(installed);
1386
+ return withSkillMutationLock(orgId, skill.key, async () => {
1387
+ const current = await getById(skill.id);
1388
+ if (!current)
1389
+ throw notFound(`Skill ${skill.key} no longer exists.`);
1390
+ if (!hasUnchangedInstallationSource(current, skill)) {
1391
+ const currentDirectory = await resolveCurrentSkillDirectory(current);
1392
+ if (currentDirectory)
1393
+ return currentDirectory;
1394
+ throw conflict(`Skill ${skill.key} changed while installation was prepared.`);
1395
+ }
1396
+ return replaceInstalledSkillDirectory(orgId, installed, files, async () => {
1397
+ await db
1398
+ .update(organizationSkills)
1399
+ .set({
1400
+ name: installed.name,
1401
+ description: installed.description,
1402
+ markdown: installed.markdown,
1403
+ sourceRef: installed.sourceRef,
1404
+ trustLevel: installed.trustLevel,
1405
+ compatibility: installed.compatibility,
1406
+ fileInventory: serializeFileInventory(installed.fileInventory),
1407
+ metadata: installed.metadata,
1408
+ updatedAt: new Date(),
1409
+ })
1410
+ .where(eq(organizationSkills.id, skill.id));
1411
+ return resolveInstalledSkillDirectory(orgId, skill.key, skill.slug);
1412
+ });
1413
+ });
1414
+ })();
1415
+ skillInstallationPromises.set(lockKey, installation);
1416
+ try {
1417
+ return await installation;
1418
+ }
1419
+ finally {
1420
+ if (skillInstallationPromises.get(lockKey) === installation) {
1421
+ skillInstallationPromises.delete(lockKey);
1422
+ }
929
1423
  }
930
- return skillDir;
931
- }
932
- function resolveRuntimeSkillMaterializedPath(orgId, skill) {
933
- const runtimeRoot = path.resolve(resolveManagedSkillsRoot(orgId), "__runtime__");
934
- return path.resolve(runtimeRoot, buildSkillRuntimeName(skill.key, skill.slug));
935
1424
  }
936
1425
  async function listRuntimeSkillEntries(orgId, options = {}) {
937
1426
  const skills = await listFull(orgId);
@@ -942,10 +1431,14 @@ export function organizationSkillService(db, options = {}) {
942
1431
  continue;
943
1432
  }
944
1433
  let source = normalizeSkillDirectory(skill);
1434
+ if (source
1435
+ && !(await fs.stat(path.join(source, "SKILL.md")).catch(() => null))?.isFile()) {
1436
+ source = null;
1437
+ }
945
1438
  if (!source) {
946
1439
  source = options.materializeMissing === false
947
- ? resolveRuntimeSkillMaterializedPath(orgId, skill)
948
- : await materializeRuntimeSkillFiles(orgId, skill).catch(() => null);
1440
+ ? null
1441
+ : await ensureInstalledSkill(orgId, skill).catch(() => null);
949
1442
  }
950
1443
  if (!source)
951
1444
  continue;
@@ -967,12 +1460,22 @@ export function organizationSkillService(db, options = {}) {
967
1460
  if (importedSkills.length === 0)
968
1461
  return [];
969
1462
  for (const skill of importedSkills) {
970
- if (skill.sourceType !== "catalog")
1463
+ const packageDir = skill.packageDir ? normalizePortablePath(skill.packageDir) : null;
1464
+ if (!packageDir)
971
1465
  continue;
972
- const materializedDir = await materializeCatalogSkillFiles(orgId, skill, normalizedFiles);
973
- if (materializedDir) {
974
- skill.sourceLocator = materializedDir;
1466
+ const packageFiles = {};
1467
+ for (const entry of skill.fileInventory) {
1468
+ const sourcePath = entry.path === "SKILL.md"
1469
+ ? `${packageDir}/SKILL.md`
1470
+ : `${packageDir}/${entry.path}`;
1471
+ const content = normalizedFiles[sourcePath];
1472
+ if (typeof content !== "string") {
1473
+ throw unprocessable(`Skill package did not provide declared file: ${sourcePath}`);
1474
+ }
1475
+ packageFiles[entry.path] = content;
975
1476
  }
1477
+ skill.files = packageFiles;
1478
+ skill.packageDir = null;
976
1479
  }
977
1480
  const conflictStrategy = options?.onConflict ?? "replace";
978
1481
  const existingSkills = await listFull(orgId);
@@ -1042,7 +1545,7 @@ export function organizationSkillService(db, options = {}) {
1042
1545
  }
1043
1546
  if (toPersist.length === 0)
1044
1547
  return out;
1045
- const persisted = await upsertImportedSkills(orgId, toPersist);
1548
+ const persisted = await installAndUpsertImportedSkills(orgId, toPersist);
1046
1549
  for (let index = 0; index < prepared.length; index += 1) {
1047
1550
  const persistedSkill = persisted[index];
1048
1551
  const preparedSkill = prepared[index];
@@ -1064,19 +1567,16 @@ export function organizationSkillService(db, options = {}) {
1064
1567
  for (const skill of imported) {
1065
1568
  const existing = await getByKey(orgId, skill.key);
1066
1569
  const existingMeta = existing ? getSkillMeta(existing) : {};
1067
- const incomingMeta = skill.metadata && isPlainRecord(skill.metadata) ? skill.metadata : {};
1068
- const incomingOwner = asString(incomingMeta.owner);
1069
- const incomingRepo = asString(incomingMeta.repo);
1070
- const incomingKind = asString(incomingMeta.sourceKind);
1071
1570
  if (existing
1072
- && isBundledRudderSourceKind(asString(existingMeta.sourceKind))
1073
- && incomingKind === "github"
1074
- && incomingOwner === "rudder"
1075
- && incomingRepo === "rudder") {
1571
+ && isBundledRudderSourceKind(asString(existingMeta.sourceKind))) {
1076
1572
  out.push(existing);
1077
1573
  continue;
1078
1574
  }
1079
1575
  const metadata = {
1576
+ ...(skill.sourceType !== "local_path"
1577
+ && existingMeta.installationVersion === ORGANIZATION_SKILL_INSTALLATION_VERSION
1578
+ ? { installationVersion: ORGANIZATION_SKILL_INSTALLATION_VERSION }
1579
+ : {}),
1080
1580
  ...(skill.metadata ?? {}),
1081
1581
  skillKey: skill.key,
1082
1582
  };
@@ -1147,24 +1647,27 @@ export function organizationSkillService(db, options = {}) {
1147
1647
  skill.key = deriveCanonicalSkillKey(orgId, skill);
1148
1648
  }
1149
1649
  }
1150
- const imported = await upsertImportedSkills(orgId, filteredSkills);
1650
+ const imported = await installAndUpsertImportedSkills(orgId, filteredSkills);
1151
1651
  return { imported, warnings };
1152
1652
  }
1153
1653
  async function deleteSkill(orgId, skillId) {
1154
- const row = await db
1155
- .select()
1156
- .from(organizationSkills)
1157
- .where(and(eq(organizationSkills.id, skillId), eq(organizationSkills.orgId, orgId)))
1158
- .then((rows) => rows[0] ?? null);
1159
- if (!row)
1654
+ const initial = await getById(skillId);
1655
+ if (!initial || initial.orgId !== orgId)
1160
1656
  return null;
1161
- const skill = toCompanySkill(row);
1162
- await enabledSkills.removeSkillKeys(orgId, [skill.key]);
1163
- await db
1164
- .delete(organizationSkills)
1165
- .where(eq(organizationSkills.id, skillId));
1166
- await fs.rm(resolveRuntimeSkillMaterializedPath(orgId, skill), { recursive: true, force: true });
1167
- return skill;
1657
+ return withSkillMutationLock(orgId, initial.key, async () => {
1658
+ const skill = await getById(skillId);
1659
+ if (!skill || skill.orgId !== orgId)
1660
+ return null;
1661
+ if (isBundledRudderSourceKind(asString(getSkillMeta(skill).sourceKind))) {
1662
+ throw unprocessable("Bundled Rudder skills are read-only.");
1663
+ }
1664
+ await enabledSkills.removeSkillKeys(orgId, [skill.key]);
1665
+ await db
1666
+ .delete(organizationSkills)
1667
+ .where(eq(organizationSkills.id, skillId));
1668
+ await removeInstalledSkillDirectory(orgId, skill);
1669
+ return skill;
1670
+ });
1168
1671
  }
1169
1672
  return {
1170
1673
  list,