@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.
- package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
- package/dist/bootstrap/register-api-routes.js +15 -0
- package/dist/bootstrap/register-api-routes.js.map +1 -1
- package/dist/bootstrap/types.d.ts +11 -0
- package/dist/bootstrap/types.d.ts.map +1 -1
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +741 -7
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +3 -3
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.d.ts +7 -0
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +15 -1
- package/dist/local-runtime.js.map +1 -1
- package/dist/middleware/auth.js +2 -2
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/error-handler.d.ts.map +1 -1
- package/dist/middleware/error-handler.js +3 -2
- package/dist/middleware/error-handler.js.map +1 -1
- package/dist/middleware/logger.d.ts +4 -0
- package/dist/middleware/logger.d.ts.map +1 -1
- package/dist/middleware/logger.js +82 -7
- package/dist/middleware/logger.js.map +1 -1
- package/dist/middleware/logger.test.js +94 -1
- package/dist/middleware/logger.test.js.map +1 -1
- package/dist/routes/chats.annotation-routes.d.ts +47 -0
- package/dist/routes/chats.annotation-routes.d.ts.map +1 -0
- package/dist/routes/chats.annotation-routes.js +164 -0
- package/dist/routes/chats.annotation-routes.js.map +1 -0
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +467 -165
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/chats.stream-routes.d.ts +1 -10
- package/dist/routes/chats.stream-routes.d.ts.map +1 -1
- package/dist/routes/chats.stream-routes.js +168 -110
- package/dist/routes/chats.stream-routes.js.map +1 -1
- package/dist/routes/chats.stream-support.d.ts +79 -0
- package/dist/routes/chats.stream-support.d.ts.map +1 -0
- package/dist/routes/chats.stream-support.js +121 -0
- package/dist/routes/chats.stream-support.js.map +1 -0
- package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
- package/dist/routes/issues.comments-attachments.js +16 -2
- package/dist/routes/issues.comments-attachments.js.map +1 -1
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +21 -4
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/issues.mutations.d.ts.map +1 -1
- package/dist/routes/issues.mutations.js +20 -33
- package/dist/routes/issues.mutations.js.map +1 -1
- package/dist/routes/managed-mcp-agent-bindings.d.ts +15 -0
- package/dist/routes/managed-mcp-agent-bindings.d.ts.map +1 -0
- package/dist/routes/managed-mcp-agent-bindings.js +66 -0
- package/dist/routes/managed-mcp-agent-bindings.js.map +1 -0
- package/dist/routes/managed-mcp-connections.d.ts +8 -0
- package/dist/routes/managed-mcp-connections.d.ts.map +1 -0
- package/dist/routes/managed-mcp-connections.js +189 -0
- package/dist/routes/managed-mcp-connections.js.map +1 -0
- package/dist/routes/managed-mcp-runtime.d.ts +5 -0
- package/dist/routes/managed-mcp-runtime.d.ts.map +1 -0
- package/dist/routes/managed-mcp-runtime.js +148 -0
- package/dist/routes/managed-mcp-runtime.js.map +1 -0
- package/dist/routes/messenger.d.ts.map +1 -1
- package/dist/routes/messenger.js +1 -1
- package/dist/routes/messenger.js.map +1 -1
- package/dist/services/access.d.ts.map +1 -1
- package/dist/services/access.js +91 -12
- package/dist/services/access.js.map +1 -1
- package/dist/services/agent-run-context.d.ts +1 -0
- package/dist/services/agent-run-context.d.ts.map +1 -1
- package/dist/services/agent-run-context.js +5 -1
- package/dist/services/agent-run-context.js.map +1 -1
- package/dist/services/board-auth.d.ts +2 -2
- package/dist/services/chat-annotation-copy-lineage.d.ts +32 -0
- package/dist/services/chat-annotation-copy-lineage.d.ts.map +1 -0
- package/dist/services/chat-annotation-copy-lineage.js +110 -0
- package/dist/services/chat-annotation-copy-lineage.js.map +1 -0
- package/dist/services/chat-assistant.annotations.d.ts +16 -0
- package/dist/services/chat-assistant.annotations.d.ts.map +1 -0
- package/dist/services/chat-assistant.annotations.js +108 -0
- package/dist/services/chat-assistant.annotations.js.map +1 -0
- package/dist/services/chat-assistant.annotations.test.d.ts +2 -0
- package/dist/services/chat-assistant.annotations.test.d.ts.map +1 -0
- package/dist/services/chat-assistant.annotations.test.js +257 -0
- package/dist/services/chat-assistant.annotations.test.js.map +1 -0
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.d.ts +2 -3
- package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.js +37 -53
- package/dist/services/chat-assistant.helpers.js.map +1 -1
- package/dist/services/chat-assistant.js +61 -5
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chat-generation-protocol.d.ts +8 -15
- package/dist/services/chat-generation-protocol.d.ts.map +1 -1
- package/dist/services/chat-generation-protocol.js +19 -59
- package/dist/services/chat-generation-protocol.js.map +1 -1
- package/dist/services/chat-generation-protocol.test.js +168 -6
- package/dist/services/chat-generation-protocol.test.js.map +1 -1
- package/dist/services/chat-generation-provenance.d.ts +14 -0
- package/dist/services/chat-generation-provenance.d.ts.map +1 -0
- package/dist/services/chat-generation-provenance.js +71 -0
- package/dist/services/chat-generation-provenance.js.map +1 -0
- package/dist/services/chat-inline-annotation-rendering.d.ts +2 -0
- package/dist/services/chat-inline-annotation-rendering.d.ts.map +1 -0
- package/dist/services/chat-inline-annotation-rendering.js +265 -0
- package/dist/services/chat-inline-annotation-rendering.js.map +1 -0
- package/dist/services/chat-inline-annotation-resolved-labels.d.ts +11 -0
- package/dist/services/chat-inline-annotation-resolved-labels.d.ts.map +1 -0
- package/dist/services/chat-inline-annotation-resolved-labels.js +490 -0
- package/dist/services/chat-inline-annotation-resolved-labels.js.map +1 -0
- package/dist/services/chat-inline-annotation-validation.d.ts +15 -0
- package/dist/services/chat-inline-annotation-validation.d.ts.map +1 -0
- package/dist/services/chat-inline-annotation-validation.js +430 -0
- package/dist/services/chat-inline-annotation-validation.js.map +1 -0
- package/dist/services/chat-inline-annotations.d.ts +18 -0
- package/dist/services/chat-inline-annotations.d.ts.map +1 -0
- package/dist/services/chat-inline-annotations.js +52 -0
- package/dist/services/chat-inline-annotations.js.map +1 -0
- package/dist/services/chat-inline-annotations.test.d.ts +2 -0
- package/dist/services/chat-inline-annotations.test.d.ts.map +1 -0
- package/dist/services/chat-inline-annotations.test.js +1306 -0
- package/dist/services/chat-inline-annotations.test.js.map +1 -0
- package/dist/services/chat-queued-message-materialization.d.ts +126 -0
- package/dist/services/chat-queued-message-materialization.d.ts.map +1 -0
- package/dist/services/chat-queued-message-materialization.js +327 -0
- package/dist/services/chat-queued-message-materialization.js.map +1 -0
- package/dist/services/chat-steer-messages.d.ts +3 -3
- package/dist/services/chat-steer-messages.d.ts.map +1 -1
- package/dist/services/chat-steer-messages.js +50 -80
- package/dist/services/chat-steer-messages.js.map +1 -1
- package/dist/services/chat-work-manifest.d.ts.map +1 -1
- package/dist/services/chat-work-manifest.js +5 -1
- package/dist/services/chat-work-manifest.js.map +1 -1
- package/dist/services/chats.annotation-persistence.d.ts +20 -0
- package/dist/services/chats.annotation-persistence.d.ts.map +1 -0
- package/dist/services/chats.annotation-persistence.js +206 -0
- package/dist/services/chats.annotation-persistence.js.map +1 -0
- package/dist/services/chats.d.ts +129 -96
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.helpers.d.ts +2 -2
- package/dist/services/chats.inline-visual-persistence.d.ts +3 -3
- package/dist/services/chats.inline-visual-persistence.d.ts.map +1 -1
- package/dist/services/chats.inline-visual-persistence.js +114 -54
- package/dist/services/chats.inline-visual-persistence.js.map +1 -1
- package/dist/services/chats.js +743 -252
- package/dist/services/chats.js.map +1 -1
- package/dist/services/finance.d.ts +1 -1
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +8 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/integrations/agent-integrations.d.ts +2 -2
- package/dist/services/integrations/custom-integrations.d.ts +3 -1
- package/dist/services/integrations/custom-integrations.d.ts.map +1 -1
- package/dist/services/integrations/custom-integrations.js +13 -2
- package/dist/services/integrations/custom-integrations.js.map +1 -1
- package/dist/services/issues.comments-attachments.d.ts +7 -1
- package/dist/services/issues.comments-attachments.d.ts.map +1 -1
- package/dist/services/issues.comments-attachments.js +13 -1
- package/dist/services/issues.comments-attachments.js.map +1 -1
- package/dist/services/issues.d.ts +6 -0
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.js +22 -3
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.core.d.ts +4 -1
- package/dist/services/knowledge-portability/organization-portability.core.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.core.js +17 -3
- package/dist/services/knowledge-portability/organization-portability.core.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.export.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.export.js +2 -2
- package/dist/services/knowledge-portability/organization-portability.export.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.import.js +13 -3
- package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +3 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.catalog.js +54 -23
- package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.js +627 -124
- package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts +1 -0
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.sources.js +53 -10
- package/dist/services/knowledge-portability/organization-skills.sources.js.map +1 -1
- package/dist/services/mcp/authorizer-lock.d.ts +9 -0
- package/dist/services/mcp/authorizer-lock.d.ts.map +1 -0
- package/dist/services/mcp/authorizer-lock.js +15 -0
- package/dist/services/mcp/authorizer-lock.js.map +1 -0
- package/dist/services/mcp/managed-bindings.d.ts +17 -0
- package/dist/services/mcp/managed-bindings.d.ts.map +1 -0
- package/dist/services/mcp/managed-bindings.js +332 -0
- package/dist/services/mcp/managed-bindings.js.map +1 -0
- package/dist/services/mcp/managed-client.d.ts +64 -0
- package/dist/services/mcp/managed-client.d.ts.map +1 -0
- package/dist/services/mcp/managed-client.js +238 -0
- package/dist/services/mcp/managed-client.js.map +1 -0
- package/dist/services/mcp/managed-client.test.d.ts +2 -0
- package/dist/services/mcp/managed-client.test.d.ts.map +1 -0
- package/dist/services/mcp/managed-client.test.js +398 -0
- package/dist/services/mcp/managed-client.test.js.map +1 -0
- package/dist/services/mcp/managed-connections.d.ts +81 -0
- package/dist/services/mcp/managed-connections.d.ts.map +1 -0
- package/dist/services/mcp/managed-connections.js +920 -0
- package/dist/services/mcp/managed-connections.js.map +1 -0
- package/dist/services/mcp/managed-runtime.d.ts +134 -0
- package/dist/services/mcp/managed-runtime.d.ts.map +1 -0
- package/dist/services/mcp/managed-runtime.js +282 -0
- package/dist/services/mcp/managed-runtime.js.map +1 -0
- package/dist/services/mcp/oauth-provider.d.ts +48 -0
- package/dist/services/mcp/oauth-provider.d.ts.map +1 -0
- package/dist/services/mcp/oauth-provider.js +120 -0
- package/dist/services/mcp/oauth-provider.js.map +1 -0
- package/dist/services/mcp/oauth-scope.test.d.ts +2 -0
- package/dist/services/mcp/oauth-scope.test.d.ts.map +1 -0
- package/dist/services/mcp/oauth-scope.test.js +69 -0
- package/dist/services/mcp/oauth-scope.test.js.map +1 -0
- package/dist/services/mcp/oauth-session-gc.d.ts +14 -0
- package/dist/services/mcp/oauth-session-gc.d.ts.map +1 -0
- package/dist/services/mcp/oauth-session-gc.js +34 -0
- package/dist/services/mcp/oauth-session-gc.js.map +1 -0
- package/dist/services/mcp/oauth-session-gc.test.d.ts +2 -0
- package/dist/services/mcp/oauth-session-gc.test.d.ts.map +1 -0
- package/dist/services/mcp/oauth-session-gc.test.js +66 -0
- package/dist/services/mcp/oauth-session-gc.test.js.map +1 -0
- package/dist/services/mcp/oauth.d.ts +61 -0
- package/dist/services/mcp/oauth.d.ts.map +1 -0
- package/dist/services/mcp/oauth.js +1690 -0
- package/dist/services/mcp/oauth.js.map +1 -0
- package/dist/services/mcp/pinned-fetch.d.ts +15 -0
- package/dist/services/mcp/pinned-fetch.d.ts.map +1 -0
- package/dist/services/mcp/pinned-fetch.js +400 -0
- package/dist/services/mcp/pinned-fetch.js.map +1 -0
- package/dist/services/mcp/pinned-fetch.test.d.ts +2 -0
- package/dist/services/mcp/pinned-fetch.test.d.ts.map +1 -0
- package/dist/services/mcp/pinned-fetch.test.js +190 -0
- package/dist/services/mcp/pinned-fetch.test.js.map +1 -0
- package/dist/services/mcp/provider-registry.d.ts +77 -0
- package/dist/services/mcp/provider-registry.d.ts.map +1 -0
- package/dist/services/mcp/provider-registry.js +80 -0
- package/dist/services/mcp/provider-registry.js.map +1 -0
- package/dist/services/mcp/provider-registry.test.d.ts +2 -0
- package/dist/services/mcp/provider-registry.test.d.ts.map +1 -0
- package/dist/services/mcp/provider-registry.test.js +80 -0
- package/dist/services/mcp/provider-registry.test.js.map +1 -0
- package/dist/services/mcp/security-policy.d.ts +48 -0
- package/dist/services/mcp/security-policy.d.ts.map +1 -0
- package/dist/services/mcp/security-policy.js +344 -0
- package/dist/services/mcp/security-policy.js.map +1 -0
- package/dist/services/mcp/security-policy.test.d.ts +2 -0
- package/dist/services/mcp/security-policy.test.d.ts.map +1 -0
- package/dist/services/mcp/security-policy.test.js +226 -0
- package/dist/services/mcp/security-policy.test.js.map +1 -0
- package/dist/services/mcp/tool-discovery.d.ts +49 -0
- package/dist/services/mcp/tool-discovery.d.ts.map +1 -0
- package/dist/services/mcp/tool-discovery.js +198 -0
- package/dist/services/mcp/tool-discovery.js.map +1 -0
- package/dist/services/mcp/tool-discovery.test.d.ts +2 -0
- package/dist/services/mcp/tool-discovery.test.d.ts.map +1 -0
- package/dist/services/mcp/tool-discovery.test.js +168 -0
- package/dist/services/mcp/tool-discovery.test.js.map +1 -0
- package/dist/services/messenger.d.ts +1 -1
- package/dist/services/messenger.d.ts.map +1 -1
- package/dist/services/messenger.js +21 -3
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/plugin-registry.d.ts +3 -3
- package/dist/services/runtime-kernel/heartbeat.core.d.ts +1 -1
- package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.core.js +30 -4
- package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.wakeup.js +38 -23
- package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -1
- package/dist/services/secrets.d.ts +64 -6
- package/dist/services/secrets.d.ts.map +1 -1
- package/dist/services/secrets.js +38 -15
- package/dist/services/secrets.js.map +1 -1
- package/dist/services/side-chats.d.ts +1 -1
- package/dist/services/side-chats.d.ts.map +1 -1
- package/dist/services/side-chats.js +56 -14
- package/dist/services/side-chats.js.map +1 -1
- package/dist/services/workspace-runtime.services.d.ts +2 -2
- package/dist/services/workspace-web-preview.js +1 -1
- package/package.json +16 -13
- package/resources/bundled-skills/rudder-docs/references/operating-practices.md +12 -5
- package/skills/rudder-docs/references/operating-practices.md +12 -5
- package/ui-dist/assets/LibraryLiveSurface-CKOthw54.js +1 -0
- package/ui-dist/assets/{abnfDiagram-VRR7QNED-KGtP7zoC.js → abnfDiagram-VRR7QNED-C6a_wMho.js} +1 -1
- package/ui-dist/assets/{arc-JjnKyiOb.js → arc-CP0En6KA.js} +1 -1
- package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-AHfOdcaZ.js +36 -0
- package/ui-dist/assets/{blockDiagram-677ZJIJ3-D7YzMYrz.js → blockDiagram-677ZJIJ3-Bkl9lp63.js} +1 -1
- package/ui-dist/assets/{c4Diagram-LMCZKHZV-Dpf7UQxl.js → c4Diagram-LMCZKHZV-BpZareVt.js} +1 -1
- package/ui-dist/assets/channel-DqxL_Zcy.js +1 -0
- package/ui-dist/assets/{chunk-2Q5K7J3B-UJ3gsGlQ.js → chunk-2Q5K7J3B-BQAwVuXc.js} +1 -1
- package/ui-dist/assets/{chunk-32BRIVSS-BeHl2j2l.js → chunk-32BRIVSS-YkmfA08F.js} +1 -1
- package/ui-dist/assets/{chunk-5VM5RSS4-STE9fJd0.js → chunk-5VM5RSS4-DvvjwTnG.js} +1 -1
- package/ui-dist/assets/{chunk-EX3LRPZG-Bj8g8j4B.js → chunk-EX3LRPZG-TXD_Yhg5.js} +1 -1
- package/ui-dist/assets/{chunk-JWPE2WC7-0V4Pwta-.js → chunk-JWPE2WC7-CgMlj6XI.js} +1 -1
- package/ui-dist/assets/{chunk-MOJQB5TN-DNPn50gm.js → chunk-MOJQB5TN-BnJjU8Rc.js} +1 -1
- package/ui-dist/assets/{chunk-RYQCIY6F-Befmnv8r.js → chunk-RYQCIY6F-D9B64JKM.js} +1 -1
- package/ui-dist/assets/{chunk-V7JOEXUC-Boh8TjHj.js → chunk-V7JOEXUC-PS09QVpl.js} +1 -1
- package/ui-dist/assets/{chunk-VR4S4FIN-B_yEQSo3.js → chunk-VR4S4FIN-DQXb7pRS.js} +1 -1
- package/ui-dist/assets/{chunk-XXDRQBXY-Cf1GnUFx.js → chunk-XXDRQBXY-B1UdLiF2.js} +1 -1
- package/ui-dist/assets/classDiagram-OUVF2IWQ-DMwjzuJE.js +1 -0
- package/ui-dist/assets/classDiagram-v2-EOCWNBFH-DMwjzuJE.js +1 -0
- package/ui-dist/assets/cose-bilkent-JH36ORCC-lqqmMB3m.js +1 -0
- package/ui-dist/assets/{cynefin-VYW2F7L2-Dv1yELYC.js → cynefin-VYW2F7L2-C9hu0XqZ.js} +1 -1
- package/ui-dist/assets/{cynefinDiagram-TSTJHNR4-CeFwjFoZ.js → cynefinDiagram-TSTJHNR4-Uuhz4c-1.js} +1 -1
- package/ui-dist/assets/{dagre-VKFMJZFB-8idd_4sX.js → dagre-VKFMJZFB-ChDrQYse.js} +1 -1
- package/ui-dist/assets/{diagram-FQU43EPY-BBVua6hM.js → diagram-FQU43EPY-DLtjkVSN.js} +1 -1
- package/ui-dist/assets/{diagram-G47NLZAW-D1zLQZlp.js → diagram-G47NLZAW-DCvEm4D0.js} +1 -1
- package/ui-dist/assets/{diagram-NH7WQ7WH-DE_FTSNv.js → diagram-NH7WQ7WH-Dz5jlsPG.js} +1 -1
- package/ui-dist/assets/{diagram-OA4YK3LP-61VSKBEn.js → diagram-OA4YK3LP-BXmQLO-Q.js} +1 -1
- package/ui-dist/assets/{diagram-WEI45ONY-C-tED6BJ.js → diagram-WEI45ONY-JnMU3xrx.js} +1 -1
- package/ui-dist/assets/{ebnfDiagram-CCIWWBDH-BEB8WGt0.js → ebnfDiagram-CCIWWBDH-Kryuqj4r.js} +1 -1
- package/ui-dist/assets/{erDiagram-Q63AITRT-AN_jm1TZ.js → erDiagram-Q63AITRT-Bn7g2mSW.js} +1 -1
- package/ui-dist/assets/{flowDiagram-23GEKE2U-BwLC6tI7.js → flowDiagram-23GEKE2U-DcjqTK91.js} +4 -4
- package/ui-dist/assets/ganttDiagram-NO4QXBWP-BRkw9-GE.js +292 -0
- package/ui-dist/assets/{gitGraphDiagram-IHSO6WYX-D9HCL9rY.js → gitGraphDiagram-IHSO6WYX-Cexcp6kR.js} +1 -1
- package/ui-dist/assets/graph-Cu7OV4Oi.js +1 -0
- package/ui-dist/assets/{index-BQXnpwke.js → index-ASZMPBYm.js} +5 -5
- package/ui-dist/assets/{index-5OM32_rf.js → index-B17BqCTj.js} +1 -1
- package/ui-dist/assets/{index-Cf0gLy6a.js → index-B9MccU1g.js} +1 -1
- package/ui-dist/assets/{index-BoSJ4Rx1.js → index-BPqbtW_X.js} +1 -1
- package/ui-dist/assets/{index-CpIdZ4-7.js → index-C2HjDa98.js} +1 -1
- package/ui-dist/assets/index-CMPtpj0A.js +1 -0
- package/ui-dist/assets/{index-p0ZGU1_7.js → index-CZXgOYtk.js} +1 -1
- package/ui-dist/assets/{index-74cx3ESJ.js → index-CknAauOP.js} +1 -1
- package/ui-dist/assets/index-Cu1GuH0a.js +1 -0
- package/ui-dist/assets/index-D8PdEImm.css +1 -0
- package/ui-dist/assets/{index-DrVDiJhe.js → index-DB1YX9-p.js} +1 -1
- package/ui-dist/assets/{index-vHMnAQSb.js → index-DBxLZXK7.js} +624 -610
- package/ui-dist/assets/{index-CpEfk5zb.js → index-DOCVOD97.js} +1 -1
- package/ui-dist/assets/{index-DMuWG0YN.js → index-DUbakRAC.js} +1 -1
- package/ui-dist/assets/{index-AT8juaIi.js → index-Dbg1ZwO2.js} +1 -1
- package/ui-dist/assets/index-DwbLsoLt.js +1 -0
- package/ui-dist/assets/index-hCLSotmS.js +1 -0
- package/ui-dist/assets/index-jrhEFc-j.js +1 -0
- package/ui-dist/assets/index-kz8ueXgo.js +1 -0
- package/ui-dist/assets/{infoDiagram-FWYZ7A6U-CyO3CAA1.js → infoDiagram-FWYZ7A6U-DugVGI3K.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-FXEZZL3T-BLLZuF1_.js → ishikawaDiagram-FXEZZL3T-CHy06Oev.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-5HDEW3XC-CWzQocvE.js → journeyDiagram-5HDEW3XC-BI0w53rb.js} +1 -1
- package/ui-dist/assets/{kanban-definition-HUTT4EX6-Dz4SFU-I.js → kanban-definition-HUTT4EX6-v_bLwx7B.js} +1 -1
- package/ui-dist/assets/layout-Dwp1s_bo.js +1 -0
- package/ui-dist/assets/{linear-BlzDlAaL.js → linear-DAV4STLZ.js} +1 -1
- package/ui-dist/assets/map-Bs6QpaaQ.js +1 -0
- package/ui-dist/assets/{mermaid.core-BtCydd-w.js → mermaid.core-fVmaL47V.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-LN4V7U3C-BeIkKRBx.js → mindmap-definition-LN4V7U3C-5dWD4dxm.js} +1 -1
- package/ui-dist/assets/{pegDiagram-2B236MQR-BZgpT4TN.js → pegDiagram-2B236MQR-DB2XsDPN.js} +1 -1
- package/ui-dist/assets/{pieDiagram-ENE6RG2P-BRFggcg2.js → pieDiagram-ENE6RG2P-Bc5teLCS.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-ABIIQ3AL-CibUo3gT.js → quadrantDiagram-ABIIQ3AL-BlfNdETt.js} +1 -1
- package/ui-dist/assets/{railroadDiagram-RFXS5EU6-DINJqhgn.js → railroadDiagram-RFXS5EU6-BbXywkq3.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-TGXJPOKE-NegluQIe.js → requirementDiagram-TGXJPOKE-DU-CapdF.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-HTMAVEWB-Kq41yHAd.js → sankeyDiagram-HTMAVEWB-Ftvh3MqA.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-DBY2YBRQ-CngfEI4f.js → sequenceDiagram-DBY2YBRQ-CCtHt9V4.js} +1 -1
- package/ui-dist/assets/{sizeCapture-X5ZJPWSS-DUWRNlme.js → sizeCapture-X5ZJPWSS-x0jnIKHx.js} +1 -1
- package/ui-dist/assets/{stateDiagram-2N3HPSRC-BwHC_Fz4.js → stateDiagram-2N3HPSRC-2qCrydE7.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-CvGRUgRf.js +1 -0
- package/ui-dist/assets/{swimlanes-5IMT3BWC-Cqp2rT1K.js → swimlanes-5IMT3BWC-6kqJq5Xf.js} +2 -2
- package/ui-dist/assets/swimlanesDiagram-G3AALYLV-EBa7-qSc.js +8 -0
- package/ui-dist/assets/{timeline-definition-FHXFAJF6-BG3GYPCu.js → timeline-definition-FHXFAJF6-BuRz8KHZ.js} +1 -1
- package/ui-dist/assets/{vennDiagram-L72KCM5P-D1IFU27N.js → vennDiagram-L72KCM5P-BKzMZ_Rv.js} +1 -1
- package/ui-dist/assets/{wardleyDiagram-EHGQE667-DboO2kYu.js → wardleyDiagram-EHGQE667-8uXlFtBV.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-FW5EYKEG-vI2BGrXP.js → xychartDiagram-FW5EYKEG-CWKGlpF9.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-DHyfFgK0.js +0 -36
- package/ui-dist/assets/channel-TMGI9Qt9.js +0 -1
- package/ui-dist/assets/classDiagram-OUVF2IWQ-r5zIpY6G.js +0 -1
- package/ui-dist/assets/classDiagram-v2-EOCWNBFH-r5zIpY6G.js +0 -1
- package/ui-dist/assets/cose-bilkent-JH36ORCC-fSNs9447.js +0 -1
- package/ui-dist/assets/ganttDiagram-NO4QXBWP-CJYhBF1C.js +0 -292
- package/ui-dist/assets/graph-DdBT9W3J.js +0 -1
- package/ui-dist/assets/index-6UIO-gg7.js +0 -1
- package/ui-dist/assets/index-BVkQVMFn.js +0 -1
- package/ui-dist/assets/index-BaAPxkqh.css +0 -1
- package/ui-dist/assets/index-CmCERTmS.js +0 -1
- package/ui-dist/assets/index-Cr4lUIaJ.js +0 -1
- package/ui-dist/assets/index-D0fAqlES.js +0 -1
- package/ui-dist/assets/index-DfNcAyZq.js +0 -1
- package/ui-dist/assets/layout-D3tY9q4Q.js +0 -1
- package/ui-dist/assets/map-Ctg1-a9N.js +0 -1
- package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-C6P3NY96.js +0 -1
- 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,
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
?
|
|
580
|
-
: await
|
|
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 =
|
|
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
|
-
|
|
716
|
-
|
|
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 (
|
|
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
|
|
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
|
|
1123
|
+
if (!source.editable) {
|
|
814
1124
|
throw unprocessable(source.editableReason ?? "This skill cannot be edited.");
|
|
815
1125
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
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
|
-
|
|
894
|
-
|
|
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
|
|
897
|
-
const
|
|
898
|
-
if (
|
|
899
|
-
|
|
900
|
-
|
|
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
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
const
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const
|
|
924
|
-
if (
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
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
|
-
?
|
|
948
|
-
: await
|
|
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
|
-
|
|
1463
|
+
const packageDir = skill.packageDir ? normalizePortablePath(skill.packageDir) : null;
|
|
1464
|
+
if (!packageDir)
|
|
971
1465
|
continue;
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
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
|
|
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
|
|
1650
|
+
const imported = await installAndUpsertImportedSkills(orgId, filteredSkills);
|
|
1151
1651
|
return { imported, warnings };
|
|
1152
1652
|
}
|
|
1153
1653
|
async function deleteSkill(orgId, skillId) {
|
|
1154
|
-
const
|
|
1155
|
-
|
|
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
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
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,
|