@rudderhq/server 0.2.5-canary.9 → 0.2.5
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/plugin-host-runtime.d.ts +39 -39
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +101 -147
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
- package/dist/bundled-plugins/plugin-linear/package.json +1 -1
- package/dist/routes/access-onboarding.helpers.d.ts +142 -0
- package/dist/routes/access-onboarding.helpers.d.ts.map +1 -0
- package/dist/routes/access-onboarding.helpers.js +762 -0
- package/dist/routes/access-onboarding.helpers.js.map +1 -0
- package/dist/routes/access.d.ts +2 -48
- package/dist/routes/access.d.ts.map +1 -1
- package/dist/routes/access.helpers.d.ts +109 -0
- package/dist/routes/access.helpers.d.ts.map +1 -0
- package/dist/routes/access.helpers.js +460 -0
- package/dist/routes/access.helpers.js.map +1 -0
- package/dist/routes/access.js +6 -1218
- package/dist/routes/access.js.map +1 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +55 -1057
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/agents.management-routes.d.ts +12 -0
- package/dist/routes/agents.management-routes.d.ts.map +1 -0
- package/dist/routes/agents.management-routes.js +1067 -0
- package/dist/routes/agents.management-routes.js.map +1 -0
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +42 -652
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/chats.stream-routes.d.ts +12 -0
- package/dist/routes/chats.stream-routes.d.ts.map +1 -0
- package/dist/routes/chats.stream-routes.js +666 -0
- package/dist/routes/chats.stream-routes.js.map +1 -0
- package/dist/routes/issues.comments-attachments.d.ts +12 -0
- package/dist/routes/issues.comments-attachments.d.ts.map +1 -0
- package/dist/routes/issues.comments-attachments.js +511 -0
- package/dist/routes/issues.comments-attachments.js.map +1 -0
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +43 -1128
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/issues.mutations.d.ts +12 -0
- package/dist/routes/issues.mutations.d.ts.map +1 -0
- package/dist/routes/issues.mutations.js +635 -0
- package/dist/routes/issues.mutations.js.map +1 -0
- package/dist/routes/plugins.d.ts.map +1 -1
- package/dist/routes/plugins.js +14 -694
- package/dist/routes/plugins.js.map +1 -1
- package/dist/routes/plugins.operations-routes.d.ts +28 -0
- package/dist/routes/plugins.operations-routes.d.ts.map +1 -0
- package/dist/routes/plugins.operations-routes.js +720 -0
- package/dist/routes/plugins.operations-routes.js.map +1 -0
- package/dist/services/access.d.ts +21 -21
- package/dist/services/activity.d.ts +19 -19
- package/dist/services/agents.d.ts +158 -158
- package/dist/services/approvals.d.ts +29 -29
- package/dist/services/assets.d.ts +8 -8
- package/dist/services/automations.d.ts +41 -27
- package/dist/services/automations.d.ts.map +1 -1
- package/dist/services/automations.js +287 -110
- package/dist/services/automations.js.map +1 -1
- package/dist/services/automations.scheduler.d.ts +9 -0
- package/dist/services/automations.scheduler.d.ts.map +1 -0
- package/dist/services/automations.scheduler.js +101 -0
- package/dist/services/automations.scheduler.js.map +1 -0
- package/dist/services/board-auth.d.ts +32 -32
- package/dist/services/calendar.d.ts +26 -26
- package/dist/services/chat-assistant.d.ts +3 -47
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.d.ts +156 -0
- package/dist/services/chat-assistant.helpers.d.ts.map +1 -0
- package/dist/services/chat-assistant.helpers.js +862 -0
- package/dist/services/chat-assistant.helpers.js.map +1 -0
- package/dist/services/chat-assistant.js +2 -861
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chats.d.ts +149 -247
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.helpers.d.ts +117 -0
- package/dist/services/chats.helpers.d.ts.map +1 -0
- package/dist/services/chats.helpers.js +285 -0
- package/dist/services/chats.helpers.js.map +1 -0
- package/dist/services/chats.js +6 -286
- package/dist/services/chats.js.map +1 -1
- package/dist/services/costs.d.ts +8 -8
- package/dist/services/finance.d.ts +18 -18
- package/dist/services/goals.d.ts +30 -30
- package/dist/services/heartbeat.d.ts +3 -1
- package/dist/services/heartbeat.d.ts.map +1 -1
- package/dist/services/heartbeat.js +3 -1
- package/dist/services/heartbeat.js.map +1 -1
- package/dist/services/issue-approvals.d.ts +4 -4
- package/dist/services/issue-review-wakeup.d.ts +3 -3
- package/dist/services/issues.comments-attachments.d.ts +141 -0
- package/dist/services/issues.comments-attachments.d.ts.map +1 -0
- package/dist/services/issues.comments-attachments.js +313 -0
- package/dist/services/issues.comments-attachments.js.map +1 -0
- package/dist/services/issues.d.ts +205 -256
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.helpers.d.ts +87 -0
- package/dist/services/issues.helpers.d.ts.map +1 -0
- package/dist/services/issues.helpers.js +270 -0
- package/dist/services/issues.helpers.js.map +1 -0
- package/dist/services/issues.js +5 -569
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.core.d.ts +210 -0
- package/dist/services/knowledge-portability/organization-portability.core.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.core.js +997 -0
- package/dist/services/knowledge-portability/organization-portability.core.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.d.ts +6 -28
- package/dist/services/knowledge-portability/organization-portability.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.export.d.ts +24 -0
- package/dist/services/knowledge-portability/organization-portability.export.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.export.js +607 -0
- package/dist/services/knowledge-portability/organization-portability.export.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.files.d.ts +69 -0
- package/dist/services/knowledge-portability/organization-portability.files.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.files.js +597 -0
- package/dist/services/knowledge-portability/organization-portability.files.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.import.d.ts +31 -0
- package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.import.js +575 -0
- package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.js +37 -3848
- package/dist/services/knowledge-portability/organization-portability.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.package.d.ts +72 -0
- package/dist/services/knowledge-portability/organization-portability.package.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.package.js +749 -0
- package/dist/services/knowledge-portability/organization-portability.package.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.preview.d.ts +18 -0
- package/dist/services/knowledge-portability/organization-portability.preview.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.preview.js +333 -0
- package/dist/services/knowledge-portability/organization-portability.preview.js.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.d.ts +4 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.js +86 -0
- package/dist/services/knowledge-portability/organization-portability.resolve-source.js.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +221 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.js +999 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.d.ts +4 -75
- package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.js +11 -2008
- package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.scans.d.ts +16 -0
- package/dist/services/knowledge-portability/organization-skills.scans.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.scans.js +300 -0
- package/dist/services/knowledge-portability/organization-skills.scans.js.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts +68 -0
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts.map +1 -0
- package/dist/services/knowledge-portability/organization-skills.sources.js +728 -0
- package/dist/services/knowledge-portability/organization-skills.sources.js.map +1 -0
- package/dist/services/messenger.d.ts +2 -2
- package/dist/services/messenger.js +2 -2
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/organization-skills.d.ts +3 -1
- package/dist/services/organization-skills.d.ts.map +1 -1
- package/dist/services/organization-skills.js +3 -1
- package/dist/services/organization-skills.js.map +1 -1
- package/dist/services/orgs.d.ts +9 -9
- package/dist/services/plugin-loader.core.d.ts +14 -0
- package/dist/services/plugin-loader.core.d.ts.map +1 -0
- package/dist/services/plugin-loader.core.js +905 -0
- package/dist/services/plugin-loader.core.js.map +1 -0
- package/dist/services/plugin-loader.d.ts +3 -440
- package/dist/services/plugin-loader.d.ts.map +1 -1
- package/dist/services/plugin-loader.helpers.d.ts +468 -0
- package/dist/services/plugin-loader.helpers.d.ts.map +1 -0
- package/dist/services/plugin-loader.helpers.js +263 -0
- package/dist/services/plugin-loader.helpers.js.map +1 -0
- package/dist/services/plugin-loader.js +3 -1191
- package/dist/services/plugin-loader.js.map +1 -1
- package/dist/services/plugin-loader.worker-paths.d.ts +7 -0
- package/dist/services/plugin-loader.worker-paths.d.ts.map +1 -0
- package/dist/services/plugin-loader.worker-paths.js +85 -0
- package/dist/services/plugin-loader.worker-paths.js.map +1 -0
- package/dist/services/plugin-registry.d.ts +123 -123
- package/dist/services/projects.d.ts +8 -8
- package/dist/services/runtime-kernel/heartbeat.core.d.ts +725 -0
- package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.core.js +525 -0
- package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.d.ts +38 -259
- package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.execute.d.ts +5 -0
- package/dist/services/runtime-kernel/heartbeat.execute.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.execute.js +1052 -0
- package/dist/services/runtime-kernel/heartbeat.execute.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.js +50 -4142
- package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.misc.d.ts +30 -0
- package/dist/services/runtime-kernel/heartbeat.misc.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.misc.js +483 -0
- package/dist/services/runtime-kernel/heartbeat.misc.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.d.ts +38 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.js +605 -0
- package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.release.d.ts +6 -0
- package/dist/services/runtime-kernel/heartbeat.release.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.release.js +398 -0
- package/dist/services/runtime-kernel/heartbeat.release.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.d.ts +229 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.js +708 -0
- package/dist/services/runtime-kernel/heartbeat.sessions.js.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts +5 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.js +552 -0
- package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -0
- package/dist/services/secrets.d.ts +25 -25
- package/dist/services/sidebar-badges.js +1 -1
- package/dist/services/sidebar-badges.js.map +1 -1
- package/dist/services/workspace-runtime.comments.d.ts +6 -0
- package/dist/services/workspace-runtime.comments.d.ts.map +1 -0
- package/dist/services/workspace-runtime.comments.js +17 -0
- package/dist/services/workspace-runtime.comments.js.map +1 -0
- package/dist/services/workspace-runtime.d.ts +4 -163
- package/dist/services/workspace-runtime.d.ts.map +1 -1
- package/dist/services/workspace-runtime.helpers.d.ts +163 -0
- package/dist/services/workspace-runtime.helpers.d.ts.map +1 -0
- package/dist/services/workspace-runtime.helpers.js +360 -0
- package/dist/services/workspace-runtime.helpers.js.map +1 -0
- package/dist/services/workspace-runtime.js +4 -1236
- package/dist/services/workspace-runtime.js.map +1 -1
- package/dist/services/workspace-runtime.lifecycle.d.ts +35 -0
- package/dist/services/workspace-runtime.lifecycle.d.ts.map +1 -0
- package/dist/services/workspace-runtime.lifecycle.js +266 -0
- package/dist/services/workspace-runtime.lifecycle.js.map +1 -0
- package/dist/services/workspace-runtime.services.d.ts +140 -0
- package/dist/services/workspace-runtime.services.d.ts.map +1 -0
- package/dist/services/workspace-runtime.services.js +606 -0
- package/dist/services/workspace-runtime.services.js.map +1 -0
- package/package.json +21 -15
- package/ui-dist/assets/{_basePickBy-B5mJzzqZ.js → _basePickBy-N8I9ml5Y.js} +1 -1
- package/ui-dist/assets/{_baseUniq-B10Ec09o.js → _baseUniq-BuSlpRSQ.js} +1 -1
- package/ui-dist/assets/{arc-Bw7wimOa.js → arc-qX-dPyA1.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-DZr0XEvv.js → architectureDiagram-2XIMDMQ5-DhjkbXsp.js} +1 -1
- package/ui-dist/assets/{blockDiagram-WCTKOSBZ-D0jl0LgB.js → blockDiagram-WCTKOSBZ-JS-tTu3J.js} +1 -1
- package/ui-dist/assets/{c4Diagram-IC4MRINW-BEFxBnEm.js → c4Diagram-IC4MRINW-4DqwCWIx.js} +1 -1
- package/ui-dist/assets/channel-CccCW5_a.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-Cbul1GoA.js → chunk-4BX2VUAB-T37SqBpp.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-DuouC3bT.js → chunk-55IACEB6-BSj9hdqK.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-bN1jF9xw.js → chunk-FMBD7UC4-Dkrlh0Wk.js} +1 -1
- package/ui-dist/assets/{chunk-JSJVCQXG-B0-Ij6ZF.js → chunk-JSJVCQXG-C0ZE3QdB.js} +1 -1
- package/ui-dist/assets/{chunk-KX2RTZJC-BjI3IEjI.js → chunk-KX2RTZJC-DOZQM9gW.js} +1 -1
- package/ui-dist/assets/{chunk-NQ4KR5QH-MUoGr46n.js → chunk-NQ4KR5QH-5Yr3U2k8.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-CQoI9Ouy.js → chunk-QZHKN3VN-CvKTufwF.js} +1 -1
- package/ui-dist/assets/{chunk-WL4C6EOR-DSJh3iDp.js → chunk-WL4C6EOR-IoEM0jyx.js} +1 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-JKk4tCW2.js +1 -0
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-JKk4tCW2.js +1 -0
- package/ui-dist/assets/clone-Onaweg8D.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-BPepglgB.js → cose-bilkent-S5V4N54A-CTvr1OFj.js} +1 -1
- package/ui-dist/assets/{dagre-KLK3FWXG-DhnHVZkt.js → dagre-KLK3FWXG-UZ-SNjVK.js} +1 -1
- package/ui-dist/assets/{diagram-E7M64L7V-DNvXtoOO.js → diagram-E7M64L7V-D7RAN0Hr.js} +1 -1
- package/ui-dist/assets/{diagram-IFDJBPK2-DhGlDTgn.js → diagram-IFDJBPK2-B4LViaFR.js} +1 -1
- package/ui-dist/assets/{diagram-P4PSJMXO-BmXEloWS.js → diagram-P4PSJMXO-CY1be7ak.js} +1 -1
- package/ui-dist/assets/{erDiagram-INFDFZHY-BTYVzaLM.js → erDiagram-INFDFZHY-Dca0KkvJ.js} +1 -1
- package/ui-dist/assets/{flowDiagram-PKNHOUZH-CqMNQUVv.js → flowDiagram-PKNHOUZH-i-qMvfwg.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-A5KZAMGK-B2le_64a.js → ganttDiagram-A5KZAMGK-Wxq2lhbh.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-BtxOBq5A.js → gitGraphDiagram-K3NZZRJ6-DwzgPlAY.js} +1 -1
- package/ui-dist/assets/{graph-C5E6qFfm.js → graph-BAqf89Tz.js} +1 -1
- package/ui-dist/assets/{index-Piq-IPXt.js → index-4eCzaLuY.js} +1 -1
- package/ui-dist/assets/{index-DT6UN2ec.js → index-8uu-nKqK.js} +1 -1
- package/ui-dist/assets/{index-T5NVZ3nR.js → index-B-1NEcI_.js} +1 -1
- package/ui-dist/assets/{index-D-MoarxG.js → index-B0b_3Eu5.js} +1 -1
- package/ui-dist/assets/{index-CZiP3FBQ.js → index-B8v0eZjP.js} +1 -1
- package/ui-dist/assets/{index-C1Ga66FM.js → index-BN7Moj3u.js} +1 -1
- package/ui-dist/assets/{index-xBUfBdQn.js → index-BSpxh3cY.js} +1 -1
- package/ui-dist/assets/{index-CQcMWp51.js → index-BY44RIi9.js} +1 -1
- package/ui-dist/assets/{index-3a93sZNI.js → index-BhyQJhdZ.js} +1 -1
- package/ui-dist/assets/{index-BsVDit5y.js → index-BkPL_iGU.js} +1 -1
- package/ui-dist/assets/{index-88lBSTsW.js → index-BsPfoHXS.js} +1 -1
- package/ui-dist/assets/{index-CyJtcUF0.js → index-BstW7nmv.js} +1 -1
- package/ui-dist/assets/{index-BvZ0Ptfl.js → index-BwB67Zyz.js} +1 -1
- package/ui-dist/assets/index-C2peSkmT.css +1 -0
- package/ui-dist/assets/{index-vkCrQLeX.js → index-C3ktOsS_.js} +1 -1
- package/ui-dist/assets/{index-D2hZpQJT.js → index-CMyABlS-.js} +1 -1
- package/ui-dist/assets/{index-C4WCPEY4.js → index-CyBJ8ujC.js} +1 -1
- package/ui-dist/assets/{index-Bf7NB_lK.js → index-DAxM2W3O.js} +1 -1
- package/ui-dist/assets/{index-Dq7H6-Lm.js → index-DVZXPmhk.js} +1 -1
- package/ui-dist/assets/{index-CskDu6A3.js → index-Dc19uAyw.js} +1 -1
- package/ui-dist/assets/index-DzHrwZu1.js +1511 -0
- package/ui-dist/assets/{index-B20JneLK.js → index-LJuf53Ye.js} +1 -1
- package/ui-dist/assets/{index-D6McTDMQ.js → index-Ugw5VWWz.js} +1 -1
- package/ui-dist/assets/{index-CcVGS6HJ.js → index-YGraEFR7.js} +1 -1
- package/ui-dist/assets/{infoDiagram-LFFYTUFH-BiCCZcIW.js → infoDiagram-LFFYTUFH-jLmDtFVR.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-BiwBemM5.js → ishikawaDiagram-PHBUUO56-6OGMyLT8.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-4ABVD52K-D8RGr2xl.js → journeyDiagram-4ABVD52K-yQjl6E0t.js} +1 -1
- package/ui-dist/assets/{kanban-definition-K7BYSVSG-C733Fj-E.js → kanban-definition-K7BYSVSG-DkdCeQlS.js} +1 -1
- package/ui-dist/assets/{layout-CM4c3NA_.js → layout-CqSYvZ_w.js} +1 -1
- package/ui-dist/assets/{linear-DzH21Xsf.js → linear-B8xGZaoi.js} +1 -1
- package/ui-dist/assets/{mermaid.core-Z2rpoVP2.js → mermaid.core-AKL_cdyk.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-YRQLILUH-DylLLj9w.js → mindmap-definition-YRQLILUH-Zr-dXC0x.js} +1 -1
- package/ui-dist/assets/{pieDiagram-SKSYHLDU-617wI_rr.js → pieDiagram-SKSYHLDU-BvDAU-Nk.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-337W2JSQ-lxoCPJIL.js → quadrantDiagram-337W2JSQ-Dn9kM62o.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-C5XydQ9-.js → requirementDiagram-Z7DCOOCP-GIsIh7Sd.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK--grmq-Q8.js → sankeyDiagram-WA2Y5GQK-CUCuBkuf.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-BS2PeYH-.js → sequenceDiagram-2WXFIKYE-MDpUY2HM.js} +1 -1
- package/ui-dist/assets/{stateDiagram-RAJIS63D-CeuZtj2z.js → stateDiagram-RAJIS63D-BymMpuUU.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-Bi2oCU6d.js +1 -0
- package/ui-dist/assets/{timeline-definition-YZTLITO2-DxHdMpRr.js → timeline-definition-YZTLITO2-B6ofPhhy.js} +1 -1
- package/ui-dist/assets/{treemap-KZPCXAKY-Bv1ZlC5h.js → treemap-KZPCXAKY-DnLO6w1l.js} +1 -1
- package/ui-dist/assets/{vennDiagram-LZ73GAT5-DvpZSXY2.js → vennDiagram-LZ73GAT5-D0MyZIDl.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-JWTSCODW-DttOu1GC.js → xychartDiagram-JWTSCODW-rADY1iUG.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/channel-DGUh6rEi.js +0 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-1ntk2IOV.js +0 -1
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-1ntk2IOV.js +0 -1
- package/ui-dist/assets/clone-BpddY88c.js +0 -1
- package/ui-dist/assets/index-C8AD6s7S.js +0 -1510
- package/ui-dist/assets/index-Ded0dPwB.css +0 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DXq0yC5C.js +0 -1
|
@@ -1,1732 +1,27 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { promises as fs } from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
2
|
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
3
|
import { and, asc, eq } from "drizzle-orm";
|
|
7
4
|
import { agents as agentRows, organizationSkills } from "@rudderhq/db";
|
|
8
5
|
import { readRudderSkillSyncPreference, writeRudderSkillSyncPreference } from "@rudderhq/agent-runtime-utils/server-utils";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { resolveAgentSkillsDir, resolveOrganizationSkillsDir, resolveOrganizationWorkspaceRoot, } from "../../home-paths.js";
|
|
6
|
+
import { RUDDER_BUNDLED_SKILL_SLUGS, getBundledRudderSkillSlug, toBundledRudderSkillKey, } from "@rudderhq/shared";
|
|
7
|
+
import { resolveAgentSkillsDir, resolveOrganizationWorkspaceRoot, } from "../../home-paths.js";
|
|
12
8
|
import { conflict, notFound, unprocessable } from "../../errors.js";
|
|
13
9
|
import { agentEnabledSkillsService } from "../agent-enabled-skills.js";
|
|
14
10
|
import { agentService } from "../agents.js";
|
|
15
11
|
import { projectService } from "../projects.js";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
slug: "deep-research",
|
|
21
|
-
source: "repo",
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
slug: "software-product-advisor",
|
|
25
|
-
source: "repo",
|
|
26
|
-
},
|
|
27
|
-
];
|
|
28
|
-
const COMMUNITY_PRESET_SKILL_SLUGS = COMMUNITY_PRESET_SKILLS.map((preset) => preset.slug);
|
|
29
|
-
const BUNDLED_SELECTION_PREFIX = "bundled:";
|
|
30
|
-
const ORGANIZATION_SELECTION_PREFIX = "org:";
|
|
31
|
-
const AGENT_SELECTION_PREFIX = "agent:";
|
|
32
|
-
const GLOBAL_SELECTION_PREFIX = "global:";
|
|
33
|
-
const ADAPTER_SELECTION_PREFIX = "adapter:";
|
|
34
|
-
const AGENT_SKILL_SOURCE_CLASS_ORDER = {
|
|
35
|
-
bundled: 0,
|
|
36
|
-
organization: 1,
|
|
37
|
-
agent_home: 2,
|
|
38
|
-
global: 3,
|
|
39
|
-
adapter_home: 4,
|
|
40
|
-
};
|
|
41
|
-
const ADAPTER_SKILL_HOME_DEFINITIONS = {
|
|
42
|
-
claude_local: {
|
|
43
|
-
mode: "ephemeral",
|
|
44
|
-
label: "Adapter skill",
|
|
45
|
-
locationLabel: "~/.claude/skills",
|
|
46
|
-
resolveRoot: (config) => path.join(resolveConfiguredHomeDir(config), ".claude", "skills"),
|
|
47
|
-
},
|
|
48
|
-
opencode_local: {
|
|
49
|
-
mode: "ephemeral",
|
|
50
|
-
label: "Adapter skill",
|
|
51
|
-
locationLabel: "~/.claude/skills",
|
|
52
|
-
resolveRoot: (config) => path.join(resolveConfiguredHomeDir(config), ".claude", "skills"),
|
|
53
|
-
},
|
|
54
|
-
codex_local: {
|
|
55
|
-
mode: "persistent",
|
|
56
|
-
label: "Adapter skill",
|
|
57
|
-
locationLabel: "~/.codex/skills",
|
|
58
|
-
resolveRoot: (config) => path.join(resolveConfiguredCodexHomeDir(config), "skills"),
|
|
59
|
-
},
|
|
60
|
-
cursor: {
|
|
61
|
-
mode: "persistent",
|
|
62
|
-
label: "Adapter skill",
|
|
63
|
-
locationLabel: "~/.cursor/skills",
|
|
64
|
-
resolveRoot: (config) => path.join(resolveConfiguredHomeDir(config), ".cursor", "skills"),
|
|
65
|
-
},
|
|
66
|
-
gemini_local: {
|
|
67
|
-
mode: "persistent",
|
|
68
|
-
label: "Adapter skill",
|
|
69
|
-
locationLabel: "~/.gemini/skills",
|
|
70
|
-
resolveRoot: (config) => path.join(resolveConfiguredHomeDir(config), ".gemini", "skills"),
|
|
71
|
-
},
|
|
72
|
-
pi_local: {
|
|
73
|
-
mode: "persistent",
|
|
74
|
-
label: "Adapter skill",
|
|
75
|
-
locationLabel: "~/.pi/agent/skills",
|
|
76
|
-
resolveRoot: (config) => path.join(resolveConfiguredHomeDir(config), ".pi", "agent", "skills"),
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
const PROJECT_SCAN_DIRECTORY_ROOTS = [
|
|
80
|
-
"skills",
|
|
81
|
-
"skills/.curated",
|
|
82
|
-
"skills/.experimental",
|
|
83
|
-
"skills/.system",
|
|
84
|
-
".agents/skills",
|
|
85
|
-
".agent/skills",
|
|
86
|
-
".augment/skills",
|
|
87
|
-
".claude/skills",
|
|
88
|
-
".codebuddy/skills",
|
|
89
|
-
".commandcode/skills",
|
|
90
|
-
".continue/skills",
|
|
91
|
-
".cortex/skills",
|
|
92
|
-
".crush/skills",
|
|
93
|
-
".factory/skills",
|
|
94
|
-
".goose/skills",
|
|
95
|
-
".junie/skills",
|
|
96
|
-
".iflow/skills",
|
|
97
|
-
".kilocode/skills",
|
|
98
|
-
".kiro/skills",
|
|
99
|
-
".kode/skills",
|
|
100
|
-
".mcpjam/skills",
|
|
101
|
-
".vibe/skills",
|
|
102
|
-
".mux/skills",
|
|
103
|
-
".openhands/skills",
|
|
104
|
-
".pi/skills",
|
|
105
|
-
".qoder/skills",
|
|
106
|
-
".qwen/skills",
|
|
107
|
-
".roo/skills",
|
|
108
|
-
".trae/skills",
|
|
109
|
-
".windsurf/skills",
|
|
110
|
-
".zencoder/skills",
|
|
111
|
-
".neovate/skills",
|
|
112
|
-
".pochi/skills",
|
|
113
|
-
".adal/skills",
|
|
114
|
-
];
|
|
115
|
-
const PROJECT_ROOT_SKILL_SUBDIRECTORIES = [
|
|
116
|
-
"references",
|
|
117
|
-
"scripts",
|
|
118
|
-
"assets",
|
|
119
|
-
];
|
|
120
|
-
function asString(value) {
|
|
121
|
-
if (typeof value !== "string")
|
|
122
|
-
return null;
|
|
123
|
-
const trimmed = value.trim();
|
|
124
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
125
|
-
}
|
|
126
|
-
function normalizeSkillDescription(value) {
|
|
127
|
-
const description = asString(value);
|
|
128
|
-
if (!description)
|
|
129
|
-
return null;
|
|
130
|
-
return /^[>|][+-]?$/.test(description) ? null : description;
|
|
131
|
-
}
|
|
132
|
-
function isPlainRecord(value) {
|
|
133
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
134
|
-
}
|
|
135
|
-
function resolveConfigEnvRecord(config) {
|
|
136
|
-
return isPlainRecord(config.env) ? config.env : {};
|
|
137
|
-
}
|
|
138
|
-
function resolveConfiguredHomeDir(config) {
|
|
139
|
-
const env = resolveConfigEnvRecord(config);
|
|
140
|
-
const configuredHome = asString(env.HOME);
|
|
141
|
-
return configuredHome ? path.resolve(configuredHome) : os.homedir();
|
|
142
|
-
}
|
|
143
|
-
function resolveConfiguredCodexHomeDir(config) {
|
|
144
|
-
const env = resolveConfigEnvRecord(config);
|
|
145
|
-
const configuredCodexHome = asString(env.CODEX_HOME);
|
|
146
|
-
return configuredCodexHome
|
|
147
|
-
? path.resolve(configuredCodexHome)
|
|
148
|
-
: path.join(resolveConfiguredHomeDir(config), ".codex");
|
|
149
|
-
}
|
|
150
|
-
function normalizePortablePath(input) {
|
|
151
|
-
const parts = [];
|
|
152
|
-
for (const segment of input.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/^\/+/, "").split("/")) {
|
|
153
|
-
if (!segment || segment === ".")
|
|
154
|
-
continue;
|
|
155
|
-
if (segment === "..") {
|
|
156
|
-
if (parts.length > 0)
|
|
157
|
-
parts.pop();
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
parts.push(segment);
|
|
161
|
-
}
|
|
162
|
-
return parts.join("/");
|
|
163
|
-
}
|
|
164
|
-
function normalizePackageFileMap(files) {
|
|
165
|
-
const out = {};
|
|
166
|
-
for (const [rawPath, content] of Object.entries(files)) {
|
|
167
|
-
const nextPath = normalizePortablePath(rawPath);
|
|
168
|
-
if (!nextPath)
|
|
169
|
-
continue;
|
|
170
|
-
out[nextPath] = content;
|
|
171
|
-
}
|
|
172
|
-
return out;
|
|
173
|
-
}
|
|
174
|
-
function normalizeSkillSlug(value) {
|
|
175
|
-
return value ? normalizeAgentUrlKey(value) ?? null : null;
|
|
176
|
-
}
|
|
177
|
-
function normalizeSkillKey(value) {
|
|
178
|
-
if (!value)
|
|
179
|
-
return null;
|
|
180
|
-
const segments = value
|
|
181
|
-
.split("/")
|
|
182
|
-
.map((segment) => normalizeSkillSlug(segment))
|
|
183
|
-
.filter((segment) => Boolean(segment));
|
|
184
|
-
return segments.length > 0 ? segments.join("/") : null;
|
|
185
|
-
}
|
|
186
|
-
function isBundledRudderSourceKind(value) {
|
|
187
|
-
return value === "rudder_bundled" || value === "paperclip_bundled";
|
|
188
|
-
}
|
|
189
|
-
function isBundledRudderSkillKey(value) {
|
|
190
|
-
return isCanonicalBundledRudderSkillKey(value);
|
|
191
|
-
}
|
|
192
|
-
function buildBundledSelectionKey(skillKey) {
|
|
193
|
-
return `${BUNDLED_SELECTION_PREFIX}${skillKey}`;
|
|
194
|
-
}
|
|
195
|
-
function buildOrganizationSelectionKey(skillKey) {
|
|
196
|
-
return `${ORGANIZATION_SELECTION_PREFIX}${skillKey}`;
|
|
197
|
-
}
|
|
198
|
-
function buildAgentSelectionKey(slug) {
|
|
199
|
-
return `${AGENT_SELECTION_PREFIX}${slug}`;
|
|
200
|
-
}
|
|
201
|
-
function buildGlobalSelectionKey(slug) {
|
|
202
|
-
return `${GLOBAL_SELECTION_PREFIX}${slug}`;
|
|
203
|
-
}
|
|
204
|
-
function buildAdapterSelectionKey(agentRuntimeType, slug) {
|
|
205
|
-
return `${ADAPTER_SELECTION_PREFIX}${agentRuntimeType}:${slug}`;
|
|
206
|
-
}
|
|
207
|
-
function parseSelectionKey(selectionKey) {
|
|
208
|
-
const trimmed = selectionKey.trim();
|
|
209
|
-
if (!trimmed) {
|
|
210
|
-
return { sourceClass: null, orgKey: null, slug: null, agentRuntimeType: null };
|
|
211
|
-
}
|
|
212
|
-
if (trimmed.startsWith(BUNDLED_SELECTION_PREFIX)) {
|
|
213
|
-
const orgKey = trimmed.slice(BUNDLED_SELECTION_PREFIX.length).trim();
|
|
214
|
-
return {
|
|
215
|
-
sourceClass: "bundled",
|
|
216
|
-
orgKey: orgKey || null,
|
|
217
|
-
slug: normalizeSkillSlug(orgKey.split("/").pop() ?? null),
|
|
218
|
-
agentRuntimeType: null,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
if (trimmed.startsWith(ORGANIZATION_SELECTION_PREFIX)) {
|
|
222
|
-
const orgKey = trimmed.slice(ORGANIZATION_SELECTION_PREFIX.length).trim();
|
|
223
|
-
return {
|
|
224
|
-
sourceClass: "organization",
|
|
225
|
-
orgKey: orgKey || null,
|
|
226
|
-
slug: normalizeSkillSlug(orgKey.split("/").pop() ?? null),
|
|
227
|
-
agentRuntimeType: null,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
if (trimmed.startsWith(AGENT_SELECTION_PREFIX)) {
|
|
231
|
-
const slug = normalizeSkillSlug(trimmed.slice(AGENT_SELECTION_PREFIX.length));
|
|
232
|
-
return {
|
|
233
|
-
sourceClass: "agent_home",
|
|
234
|
-
orgKey: null,
|
|
235
|
-
slug,
|
|
236
|
-
agentRuntimeType: null,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
if (trimmed.startsWith(GLOBAL_SELECTION_PREFIX)) {
|
|
240
|
-
const slug = normalizeSkillSlug(trimmed.slice(GLOBAL_SELECTION_PREFIX.length));
|
|
241
|
-
return {
|
|
242
|
-
sourceClass: "global",
|
|
243
|
-
orgKey: null,
|
|
244
|
-
slug,
|
|
245
|
-
agentRuntimeType: null,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
if (trimmed.startsWith(ADAPTER_SELECTION_PREFIX)) {
|
|
249
|
-
const payload = trimmed.slice(ADAPTER_SELECTION_PREFIX.length);
|
|
250
|
-
const delimiter = payload.indexOf(":");
|
|
251
|
-
if (delimiter <= 0) {
|
|
252
|
-
return { sourceClass: "adapter_home", orgKey: null, slug: null, agentRuntimeType: null };
|
|
253
|
-
}
|
|
254
|
-
return {
|
|
255
|
-
sourceClass: "adapter_home",
|
|
256
|
-
orgKey: null,
|
|
257
|
-
slug: normalizeSkillSlug(payload.slice(delimiter + 1)),
|
|
258
|
-
agentRuntimeType: payload.slice(0, delimiter).trim() || null,
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
return { sourceClass: null, orgKey: null, slug: null, agentRuntimeType: null };
|
|
262
|
-
}
|
|
263
|
-
function normalizeSelectionRef(reference, skills, orgId, agentRuntimeType) {
|
|
264
|
-
const trimmed = reference.trim();
|
|
265
|
-
if (!trimmed)
|
|
266
|
-
return null;
|
|
267
|
-
const parsedSelection = parseSelectionKey(trimmed);
|
|
268
|
-
if (parsedSelection.sourceClass === "bundled") {
|
|
269
|
-
return parsedSelection.orgKey ? buildBundledSelectionKey(parsedSelection.orgKey) : null;
|
|
270
|
-
}
|
|
271
|
-
if (parsedSelection.sourceClass === "organization") {
|
|
272
|
-
return parsedSelection.orgKey ? buildOrganizationSelectionKey(parsedSelection.orgKey) : null;
|
|
273
|
-
}
|
|
274
|
-
if (parsedSelection.sourceClass === "agent_home") {
|
|
275
|
-
return parsedSelection.slug ? buildAgentSelectionKey(parsedSelection.slug) : null;
|
|
276
|
-
}
|
|
277
|
-
if (parsedSelection.sourceClass === "global") {
|
|
278
|
-
return parsedSelection.slug ? buildGlobalSelectionKey(parsedSelection.slug) : null;
|
|
279
|
-
}
|
|
280
|
-
if (parsedSelection.sourceClass === "adapter_home") {
|
|
281
|
-
if (!parsedSelection.slug || !parsedSelection.agentRuntimeType)
|
|
282
|
-
return null;
|
|
283
|
-
return buildAdapterSelectionKey(parsedSelection.agentRuntimeType, parsedSelection.slug);
|
|
284
|
-
}
|
|
285
|
-
const orgMatch = resolveSkillReference(skills, trimmed, orgId);
|
|
286
|
-
if (orgMatch.skill) {
|
|
287
|
-
if (isBundledRudderSkillKey(orgMatch.skill.key)) {
|
|
288
|
-
return buildBundledSelectionKey(orgMatch.skill.key);
|
|
289
|
-
}
|
|
290
|
-
return buildOrganizationSelectionKey(orgMatch.skill.key);
|
|
291
|
-
}
|
|
292
|
-
const bundledSlug = getBundledRudderSkillSlug(trimmed);
|
|
293
|
-
if (bundledSlug) {
|
|
294
|
-
const bundledKey = toBundledRudderSkillKey(bundledSlug);
|
|
295
|
-
return bundledKey ? buildBundledSelectionKey(bundledKey) : null;
|
|
296
|
-
}
|
|
297
|
-
const normalizedSlug = normalizeSkillSlug(trimmed);
|
|
298
|
-
if (!normalizedSlug)
|
|
299
|
-
return null;
|
|
300
|
-
return buildAdapterSelectionKey(agentRuntimeType, normalizedSlug);
|
|
301
|
-
}
|
|
302
|
-
async function discoverLocalSkillDirectories(root) {
|
|
303
|
-
const discovered = new Set();
|
|
304
|
-
for (const candidateRoot of [root, path.join(root, "skills")]) {
|
|
305
|
-
const candidateStat = await statPath(candidateRoot);
|
|
306
|
-
if (!candidateStat?.isDirectory())
|
|
307
|
-
continue;
|
|
308
|
-
const entries = await fs.readdir(candidateRoot, { withFileTypes: true }).catch(() => []);
|
|
309
|
-
for (const entry of entries) {
|
|
310
|
-
if (!entry.isDirectory())
|
|
311
|
-
continue;
|
|
312
|
-
const skillDir = path.resolve(candidateRoot, entry.name);
|
|
313
|
-
if (!(await statPath(path.join(skillDir, "SKILL.md")))?.isFile())
|
|
314
|
-
continue;
|
|
315
|
-
discovered.add(skillDir);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return Array.from(discovered).sort((left, right) => left.localeCompare(right));
|
|
319
|
-
}
|
|
320
|
-
async function readDiscoveredSkillEntries(orgId, root, selectionKeyForSlug, options) {
|
|
321
|
-
const out = [];
|
|
322
|
-
const seenSelectionKeys = new Set();
|
|
323
|
-
for (const skillDir of await discoverLocalSkillDirectories(root)) {
|
|
324
|
-
const slug = normalizeSkillSlug(path.basename(skillDir));
|
|
325
|
-
if (!slug)
|
|
326
|
-
continue;
|
|
327
|
-
const selectionKey = selectionKeyForSlug(slug);
|
|
328
|
-
if (seenSelectionKeys.has(selectionKey))
|
|
329
|
-
continue;
|
|
330
|
-
seenSelectionKeys.add(selectionKey);
|
|
331
|
-
const metadata = await readSkillMetadataFromPath(skillDir).catch(() => ({ name: null, description: null }));
|
|
332
|
-
out.push({
|
|
333
|
-
key: slug,
|
|
334
|
-
selectionKey,
|
|
335
|
-
runtimeName: slug,
|
|
336
|
-
description: metadata.description ?? null,
|
|
337
|
-
desired: false,
|
|
338
|
-
configurable: true,
|
|
339
|
-
alwaysEnabled: false,
|
|
340
|
-
managed: false,
|
|
341
|
-
state: "external",
|
|
342
|
-
sourceClass: options.sourceClass,
|
|
343
|
-
origin: "user_installed",
|
|
344
|
-
originLabel: options.originLabel,
|
|
345
|
-
locationLabel: options.locationLabel,
|
|
346
|
-
readOnly: false,
|
|
347
|
-
sourcePath: skillDir,
|
|
348
|
-
targetPath: null,
|
|
349
|
-
workspaceEditPath: resolveWorkspaceEditPath(orgId, skillDir),
|
|
350
|
-
detail: null,
|
|
351
|
-
organizationSkillKey: null,
|
|
352
|
-
runtimeSourcePath: skillDir,
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
return out;
|
|
356
|
-
}
|
|
357
|
-
function buildDraftSkillMarkdown(input) {
|
|
358
|
-
return (input.markdown?.trim().length
|
|
359
|
-
? input.markdown
|
|
360
|
-
: [
|
|
361
|
-
"---",
|
|
362
|
-
`name: ${input.name}`,
|
|
363
|
-
...(input.description?.trim() ? [`description: ${input.description.trim()}`] : []),
|
|
364
|
-
"---",
|
|
365
|
-
"",
|
|
366
|
-
`# ${input.name}`,
|
|
367
|
-
"",
|
|
368
|
-
input.description?.trim() ? input.description.trim() : "Describe what this skill does.",
|
|
369
|
-
"",
|
|
370
|
-
].join("\n"));
|
|
371
|
-
}
|
|
372
|
-
function buildAgentPrivateSkillEntry(orgId, slug, skillDir, description) {
|
|
373
|
-
return {
|
|
374
|
-
key: slug,
|
|
375
|
-
selectionKey: buildAgentSelectionKey(slug),
|
|
376
|
-
runtimeName: slug,
|
|
377
|
-
description,
|
|
378
|
-
desired: false,
|
|
379
|
-
configurable: true,
|
|
380
|
-
alwaysEnabled: false,
|
|
381
|
-
managed: false,
|
|
382
|
-
state: "external",
|
|
383
|
-
sourceClass: "agent_home",
|
|
384
|
-
origin: "user_installed",
|
|
385
|
-
originLabel: "Agent skill",
|
|
386
|
-
locationLabel: "AGENT_HOME/skills",
|
|
387
|
-
readOnly: false,
|
|
388
|
-
sourcePath: skillDir,
|
|
389
|
-
targetPath: null,
|
|
390
|
-
workspaceEditPath: resolveWorkspaceEditPath(orgId, skillDir),
|
|
391
|
-
detail: "Installed, not enabled. Future runs will not load it until enabled.",
|
|
392
|
-
organizationSkillKey: null,
|
|
393
|
-
runtimeSourcePath: skillDir,
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
export function normalizeGitHubSkillDirectory(value, fallback) {
|
|
397
|
-
const normalized = normalizePortablePath(value ?? "");
|
|
398
|
-
if (!normalized)
|
|
399
|
-
return normalizePortablePath(fallback);
|
|
400
|
-
if (path.posix.basename(normalized).toLowerCase() === "skill.md") {
|
|
401
|
-
return normalizePortablePath(path.posix.dirname(normalized));
|
|
402
|
-
}
|
|
403
|
-
return normalized;
|
|
404
|
-
}
|
|
405
|
-
export function listStaleBundledSkillIds(existingSkills, currentBundledKeys) {
|
|
406
|
-
const currentKeysSet = new Set(currentBundledKeys.map((key) => {
|
|
407
|
-
const bundledKey = toBundledRudderSkillKey(getBundledRudderSkillSlug(key));
|
|
408
|
-
return bundledKey ?? key;
|
|
409
|
-
}));
|
|
410
|
-
return existingSkills
|
|
411
|
-
.filter((skill) => {
|
|
412
|
-
const sourceKind = skill.metadata?.sourceKind;
|
|
413
|
-
if (sourceKind !== "rudder_bundled" && sourceKind !== "paperclip_bundled") {
|
|
414
|
-
return false;
|
|
415
|
-
}
|
|
416
|
-
const canonicalKey = toBundledRudderSkillKey(getBundledRudderSkillSlug(skill.key)) ?? skill.key;
|
|
417
|
-
return !currentKeysSet.has(canonicalKey);
|
|
418
|
-
})
|
|
419
|
-
.map((skill) => skill.id);
|
|
420
|
-
}
|
|
421
|
-
export function listStaleCommunityPresetSkillIds(existingSkills, currentCommunityPresetKeys) {
|
|
422
|
-
const currentKeysSet = new Set(currentCommunityPresetKeys);
|
|
423
|
-
return existingSkills
|
|
424
|
-
.filter((skill) => skill.metadata?.sourceKind === "community_preset")
|
|
425
|
-
.filter((skill) => !currentKeysSet.has(skill.key))
|
|
426
|
-
.map((skill) => skill.id);
|
|
427
|
-
}
|
|
428
|
-
function hashSkillValue(value) {
|
|
429
|
-
return createHash("sha256").update(value).digest("hex").slice(0, 10);
|
|
430
|
-
}
|
|
431
|
-
function uniqueSkillSlug(baseSlug, usedSlugs) {
|
|
432
|
-
if (!usedSlugs.has(baseSlug))
|
|
433
|
-
return baseSlug;
|
|
434
|
-
let attempt = 2;
|
|
435
|
-
let candidate = `${baseSlug}-${attempt}`;
|
|
436
|
-
while (usedSlugs.has(candidate)) {
|
|
437
|
-
attempt += 1;
|
|
438
|
-
candidate = `${baseSlug}-${attempt}`;
|
|
439
|
-
}
|
|
440
|
-
return candidate;
|
|
441
|
-
}
|
|
442
|
-
function uniqueImportedSkillKey(orgId, baseSlug, usedKeys) {
|
|
443
|
-
const initial = `organization/${orgId}/${baseSlug}`;
|
|
444
|
-
if (!usedKeys.has(initial))
|
|
445
|
-
return initial;
|
|
446
|
-
let attempt = 2;
|
|
447
|
-
let candidate = `organization/${orgId}/${baseSlug}-${attempt}`;
|
|
448
|
-
while (usedKeys.has(candidate)) {
|
|
449
|
-
attempt += 1;
|
|
450
|
-
candidate = `organization/${orgId}/${baseSlug}-${attempt}`;
|
|
451
|
-
}
|
|
452
|
-
return candidate;
|
|
453
|
-
}
|
|
454
|
-
function buildSkillRuntimeName(key, slug) {
|
|
455
|
-
if (getBundledRudderSkillSlug(key))
|
|
456
|
-
return slug;
|
|
457
|
-
return `${slug}--${hashSkillValue(key)}`;
|
|
458
|
-
}
|
|
459
|
-
function readCanonicalSkillKey(frontmatter, metadata) {
|
|
460
|
-
const direct = normalizeSkillKey(asString(frontmatter.key)
|
|
461
|
-
?? asString(frontmatter.skillKey)
|
|
462
|
-
?? asString(metadata?.skillKey)
|
|
463
|
-
?? asString(metadata?.canonicalKey)
|
|
464
|
-
?? asString(metadata?.rudderSkillKey));
|
|
465
|
-
if (direct)
|
|
466
|
-
return direct;
|
|
467
|
-
const rudder = isPlainRecord(metadata?.rudder) ? metadata?.rudder : null;
|
|
468
|
-
return normalizeSkillKey(asString(rudder?.skillKey)
|
|
469
|
-
?? asString(rudder?.key));
|
|
470
|
-
}
|
|
471
|
-
function deriveCanonicalSkillKey(orgId, input) {
|
|
472
|
-
const slug = normalizeSkillSlug(input.slug) ?? "skill";
|
|
473
|
-
const metadata = isPlainRecord(input.metadata) ? input.metadata : null;
|
|
474
|
-
const sourceKind = asString(metadata?.sourceKind);
|
|
475
|
-
const explicitKey = readCanonicalSkillKey({}, metadata);
|
|
476
|
-
if (explicitKey) {
|
|
477
|
-
if (isBundledRudderSourceKind(sourceKind)) {
|
|
478
|
-
return toBundledRudderSkillKey(getBundledRudderSkillSlug(explicitKey) ?? slug) ?? explicitKey;
|
|
479
|
-
}
|
|
480
|
-
return explicitKey;
|
|
481
|
-
}
|
|
482
|
-
if (isBundledRudderSourceKind(sourceKind)) {
|
|
483
|
-
return toBundledRudderSkillKey(slug) ?? `rudder/${slug}`;
|
|
484
|
-
}
|
|
485
|
-
if (sourceKind === "community_preset") {
|
|
486
|
-
return `organization/${orgId}/${slug}`;
|
|
487
|
-
}
|
|
488
|
-
const owner = normalizeSkillSlug(asString(metadata?.owner));
|
|
489
|
-
const repo = normalizeSkillSlug(asString(metadata?.repo));
|
|
490
|
-
if ((input.sourceType === "github" || input.sourceType === "skills_sh" || sourceKind === "github" || sourceKind === "skills_sh") && owner && repo) {
|
|
491
|
-
return `${owner}/${repo}/${slug}`;
|
|
492
|
-
}
|
|
493
|
-
if (input.sourceType === "url" || sourceKind === "url") {
|
|
494
|
-
const locator = asString(input.sourceLocator);
|
|
495
|
-
if (locator) {
|
|
496
|
-
try {
|
|
497
|
-
const url = new URL(locator);
|
|
498
|
-
const host = normalizeSkillSlug(url.host) ?? "url";
|
|
499
|
-
return `url/${host}/${hashSkillValue(locator)}/${slug}`;
|
|
500
|
-
}
|
|
501
|
-
catch {
|
|
502
|
-
return `url/unknown/${hashSkillValue(locator)}/${slug}`;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
if (input.sourceType === "local_path") {
|
|
507
|
-
if (sourceKind === "managed_local") {
|
|
508
|
-
return `organization/${orgId}/${slug}`;
|
|
509
|
-
}
|
|
510
|
-
const locator = asString(input.sourceLocator);
|
|
511
|
-
if (locator) {
|
|
512
|
-
return `local/${hashSkillValue(path.resolve(locator))}/${slug}`;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
return `organization/${orgId}/${slug}`;
|
|
516
|
-
}
|
|
517
|
-
function classifyInventoryKind(relativePath) {
|
|
518
|
-
const normalized = normalizePortablePath(relativePath).toLowerCase();
|
|
519
|
-
if (normalized.endsWith("/skill.md") || normalized === "skill.md")
|
|
520
|
-
return "skill";
|
|
521
|
-
if (normalized.startsWith("references/"))
|
|
522
|
-
return "reference";
|
|
523
|
-
if (normalized.startsWith("scripts/"))
|
|
524
|
-
return "script";
|
|
525
|
-
if (normalized.startsWith("assets/"))
|
|
526
|
-
return "asset";
|
|
527
|
-
if (normalized.endsWith(".md"))
|
|
528
|
-
return "markdown";
|
|
529
|
-
const fileName = path.posix.basename(normalized);
|
|
530
|
-
if (fileName.endsWith(".sh")
|
|
531
|
-
|| fileName.endsWith(".js")
|
|
532
|
-
|| fileName.endsWith(".mjs")
|
|
533
|
-
|| fileName.endsWith(".cjs")
|
|
534
|
-
|| fileName.endsWith(".ts")
|
|
535
|
-
|| fileName.endsWith(".py")
|
|
536
|
-
|| fileName.endsWith(".rb")
|
|
537
|
-
|| fileName.endsWith(".bash")) {
|
|
538
|
-
return "script";
|
|
539
|
-
}
|
|
540
|
-
if (fileName.endsWith(".png")
|
|
541
|
-
|| fileName.endsWith(".jpg")
|
|
542
|
-
|| fileName.endsWith(".jpeg")
|
|
543
|
-
|| fileName.endsWith(".gif")
|
|
544
|
-
|| fileName.endsWith(".svg")
|
|
545
|
-
|| fileName.endsWith(".webp")
|
|
546
|
-
|| fileName.endsWith(".pdf")) {
|
|
547
|
-
return "asset";
|
|
548
|
-
}
|
|
549
|
-
return "other";
|
|
550
|
-
}
|
|
551
|
-
function deriveTrustLevel(fileInventory) {
|
|
552
|
-
if (fileInventory.some((entry) => entry.kind === "script"))
|
|
553
|
-
return "scripts_executables";
|
|
554
|
-
if (fileInventory.some((entry) => entry.kind === "asset" || entry.kind === "other"))
|
|
555
|
-
return "assets";
|
|
556
|
-
return "markdown_only";
|
|
557
|
-
}
|
|
558
|
-
function prepareYamlLines(raw) {
|
|
559
|
-
return raw
|
|
560
|
-
.split("\n")
|
|
561
|
-
.map((line) => ({
|
|
562
|
-
indent: line.match(/^ */)?.[0].length ?? 0,
|
|
563
|
-
content: line.trim(),
|
|
564
|
-
}))
|
|
565
|
-
.filter((line) => line.content.length > 0 && !line.content.startsWith("#"));
|
|
566
|
-
}
|
|
567
|
-
function parseYamlScalar(rawValue) {
|
|
568
|
-
const trimmed = rawValue.trim();
|
|
569
|
-
if (trimmed === "")
|
|
570
|
-
return "";
|
|
571
|
-
if (trimmed === "null" || trimmed === "~")
|
|
572
|
-
return null;
|
|
573
|
-
if (trimmed === "true")
|
|
574
|
-
return true;
|
|
575
|
-
if (trimmed === "false")
|
|
576
|
-
return false;
|
|
577
|
-
if (trimmed === "[]")
|
|
578
|
-
return [];
|
|
579
|
-
if (trimmed === "{}")
|
|
580
|
-
return {};
|
|
581
|
-
if (/^-?\d+(\.\d+)?$/.test(trimmed))
|
|
582
|
-
return Number(trimmed);
|
|
583
|
-
if (trimmed.startsWith("\"") || trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
|
584
|
-
try {
|
|
585
|
-
return JSON.parse(trimmed);
|
|
586
|
-
}
|
|
587
|
-
catch {
|
|
588
|
-
return trimmed;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
return trimmed;
|
|
592
|
-
}
|
|
593
|
-
function parseYamlBlockScalar(lines, startIndex, indentLevel, style) {
|
|
594
|
-
const parts = [];
|
|
595
|
-
let index = startIndex;
|
|
596
|
-
while (index < lines.length) {
|
|
597
|
-
const line = lines[index];
|
|
598
|
-
if (line.indent < indentLevel)
|
|
599
|
-
break;
|
|
600
|
-
parts.push(line.content);
|
|
601
|
-
index += 1;
|
|
602
|
-
}
|
|
603
|
-
if (style === ">") {
|
|
604
|
-
return { value: parts.join(" "), nextIndex: index };
|
|
605
|
-
}
|
|
606
|
-
return { value: parts.join("\n"), nextIndex: index };
|
|
607
|
-
}
|
|
608
|
-
function parseYamlBlock(lines, startIndex, indentLevel) {
|
|
609
|
-
let index = startIndex;
|
|
610
|
-
while (index < lines.length && lines[index].content.length === 0)
|
|
611
|
-
index += 1;
|
|
612
|
-
if (index >= lines.length || lines[index].indent < indentLevel) {
|
|
613
|
-
return { value: {}, nextIndex: index };
|
|
614
|
-
}
|
|
615
|
-
const isArray = lines[index].indent === indentLevel && lines[index].content.startsWith("-");
|
|
616
|
-
if (isArray) {
|
|
617
|
-
const values = [];
|
|
618
|
-
while (index < lines.length) {
|
|
619
|
-
const line = lines[index];
|
|
620
|
-
if (line.indent < indentLevel)
|
|
621
|
-
break;
|
|
622
|
-
if (line.indent !== indentLevel || !line.content.startsWith("-"))
|
|
623
|
-
break;
|
|
624
|
-
const remainder = line.content.slice(1).trim();
|
|
625
|
-
index += 1;
|
|
626
|
-
if (!remainder) {
|
|
627
|
-
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
|
628
|
-
values.push(nested.value);
|
|
629
|
-
index = nested.nextIndex;
|
|
630
|
-
continue;
|
|
631
|
-
}
|
|
632
|
-
const inlineObjectSeparator = remainder.indexOf(":");
|
|
633
|
-
if (inlineObjectSeparator > 0 &&
|
|
634
|
-
!remainder.startsWith("\"") &&
|
|
635
|
-
!remainder.startsWith("{") &&
|
|
636
|
-
!remainder.startsWith("[")) {
|
|
637
|
-
const key = remainder.slice(0, inlineObjectSeparator).trim();
|
|
638
|
-
const rawValue = remainder.slice(inlineObjectSeparator + 1).trim();
|
|
639
|
-
const nextObject = {
|
|
640
|
-
[key]: parseYamlScalar(rawValue),
|
|
641
|
-
};
|
|
642
|
-
if (index < lines.length && lines[index].indent > indentLevel) {
|
|
643
|
-
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
|
644
|
-
if (isPlainRecord(nested.value)) {
|
|
645
|
-
Object.assign(nextObject, nested.value);
|
|
646
|
-
}
|
|
647
|
-
index = nested.nextIndex;
|
|
648
|
-
}
|
|
649
|
-
values.push(nextObject);
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
values.push(parseYamlScalar(remainder));
|
|
653
|
-
}
|
|
654
|
-
return { value: values, nextIndex: index };
|
|
655
|
-
}
|
|
656
|
-
const record = {};
|
|
657
|
-
while (index < lines.length) {
|
|
658
|
-
const line = lines[index];
|
|
659
|
-
if (line.indent < indentLevel)
|
|
660
|
-
break;
|
|
661
|
-
if (line.indent !== indentLevel) {
|
|
662
|
-
index += 1;
|
|
663
|
-
continue;
|
|
664
|
-
}
|
|
665
|
-
const separatorIndex = line.content.indexOf(":");
|
|
666
|
-
if (separatorIndex <= 0) {
|
|
667
|
-
index += 1;
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
const key = line.content.slice(0, separatorIndex).trim();
|
|
671
|
-
const remainder = line.content.slice(separatorIndex + 1).trim();
|
|
672
|
-
index += 1;
|
|
673
|
-
if (/^[>|][+-]?$/.test(remainder)) {
|
|
674
|
-
const blockScalar = parseYamlBlockScalar(lines, index, indentLevel + 2, remainder.startsWith(">") ? ">" : "|");
|
|
675
|
-
record[key] = blockScalar.value;
|
|
676
|
-
index = blockScalar.nextIndex;
|
|
677
|
-
continue;
|
|
678
|
-
}
|
|
679
|
-
if (!remainder) {
|
|
680
|
-
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
|
681
|
-
record[key] = nested.value;
|
|
682
|
-
index = nested.nextIndex;
|
|
683
|
-
continue;
|
|
684
|
-
}
|
|
685
|
-
record[key] = parseYamlScalar(remainder);
|
|
686
|
-
}
|
|
687
|
-
return { value: record, nextIndex: index };
|
|
688
|
-
}
|
|
689
|
-
function parseYamlFrontmatter(raw) {
|
|
690
|
-
const prepared = prepareYamlLines(raw);
|
|
691
|
-
if (prepared.length === 0)
|
|
692
|
-
return {};
|
|
693
|
-
const parsed = parseYamlBlock(prepared, 0, prepared[0].indent);
|
|
694
|
-
return isPlainRecord(parsed.value) ? parsed.value : {};
|
|
695
|
-
}
|
|
696
|
-
function parseFrontmatterMarkdown(raw) {
|
|
697
|
-
const normalized = raw.replace(/\r\n/g, "\n");
|
|
698
|
-
if (!normalized.startsWith("---\n")) {
|
|
699
|
-
return { frontmatter: {}, body: normalized.trim() };
|
|
700
|
-
}
|
|
701
|
-
const closing = normalized.indexOf("\n---\n", 4);
|
|
702
|
-
if (closing < 0) {
|
|
703
|
-
return { frontmatter: {}, body: normalized.trim() };
|
|
704
|
-
}
|
|
705
|
-
const frontmatterRaw = normalized.slice(4, closing).trim();
|
|
706
|
-
const body = normalized.slice(closing + 5).trim();
|
|
707
|
-
return {
|
|
708
|
-
frontmatter: parseYamlFrontmatter(frontmatterRaw),
|
|
709
|
-
body,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
async function fetchText(url) {
|
|
713
|
-
const response = await fetch(url);
|
|
714
|
-
if (!response.ok) {
|
|
715
|
-
throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
|
|
716
|
-
}
|
|
717
|
-
return response.text();
|
|
718
|
-
}
|
|
719
|
-
async function fetchJson(url) {
|
|
720
|
-
const response = await fetch(url, {
|
|
721
|
-
headers: {
|
|
722
|
-
accept: "application/vnd.github+json",
|
|
723
|
-
},
|
|
724
|
-
});
|
|
725
|
-
if (!response.ok) {
|
|
726
|
-
throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
|
|
727
|
-
}
|
|
728
|
-
return response.json();
|
|
729
|
-
}
|
|
730
|
-
async function resolveGitHubDefaultBranch(owner, repo) {
|
|
731
|
-
const response = await fetchJson(`https://api.github.com/repos/${owner}/${repo}`);
|
|
732
|
-
return asString(response.default_branch) ?? "main";
|
|
733
|
-
}
|
|
734
|
-
async function resolveGitHubCommitSha(owner, repo, ref) {
|
|
735
|
-
const response = await fetchJson(`https://api.github.com/repos/${owner}/${repo}/commits/${encodeURIComponent(ref)}`);
|
|
736
|
-
const sha = asString(response.sha);
|
|
737
|
-
if (!sha) {
|
|
738
|
-
throw unprocessable(`Failed to resolve GitHub ref ${ref}`);
|
|
739
|
-
}
|
|
740
|
-
return sha;
|
|
741
|
-
}
|
|
742
|
-
function parseGitHubSourceUrl(rawUrl) {
|
|
743
|
-
const url = new URL(rawUrl);
|
|
744
|
-
if (url.hostname !== "github.com") {
|
|
745
|
-
throw unprocessable("GitHub source must use github.com URL");
|
|
746
|
-
}
|
|
747
|
-
const parts = url.pathname.split("/").filter(Boolean);
|
|
748
|
-
if (parts.length < 2) {
|
|
749
|
-
throw unprocessable("Invalid GitHub URL");
|
|
750
|
-
}
|
|
751
|
-
const owner = parts[0];
|
|
752
|
-
const repo = parts[1].replace(/\.git$/i, "");
|
|
753
|
-
let ref = "main";
|
|
754
|
-
let basePath = "";
|
|
755
|
-
let filePath = null;
|
|
756
|
-
let explicitRef = false;
|
|
757
|
-
if (parts[2] === "tree") {
|
|
758
|
-
ref = parts[3] ?? "main";
|
|
759
|
-
basePath = parts.slice(4).join("/");
|
|
760
|
-
explicitRef = true;
|
|
761
|
-
}
|
|
762
|
-
else if (parts[2] === "blob") {
|
|
763
|
-
ref = parts[3] ?? "main";
|
|
764
|
-
filePath = parts.slice(4).join("/");
|
|
765
|
-
basePath = filePath ? path.posix.dirname(filePath) : "";
|
|
766
|
-
explicitRef = true;
|
|
767
|
-
}
|
|
768
|
-
return { owner, repo, ref, basePath, filePath, explicitRef };
|
|
769
|
-
}
|
|
770
|
-
async function resolveGitHubPinnedRef(parsed) {
|
|
771
|
-
if (/^[0-9a-f]{40}$/i.test(parsed.ref.trim())) {
|
|
772
|
-
return {
|
|
773
|
-
pinnedRef: parsed.ref,
|
|
774
|
-
trackingRef: parsed.explicitRef ? parsed.ref : null,
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
const trackingRef = parsed.explicitRef
|
|
778
|
-
? parsed.ref
|
|
779
|
-
: await resolveGitHubDefaultBranch(parsed.owner, parsed.repo);
|
|
780
|
-
const pinnedRef = await resolveGitHubCommitSha(parsed.owner, parsed.repo, trackingRef);
|
|
781
|
-
return { pinnedRef, trackingRef };
|
|
782
|
-
}
|
|
783
|
-
function resolveRawGitHubUrl(owner, repo, ref, filePath) {
|
|
784
|
-
return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filePath.replace(/^\/+/, "")}`;
|
|
785
|
-
}
|
|
786
|
-
function extractCommandTokens(raw) {
|
|
787
|
-
const matches = raw.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
788
|
-
return matches.map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
789
|
-
}
|
|
790
|
-
export function parseSkillImportSourceInput(rawInput) {
|
|
791
|
-
const trimmed = rawInput.trim();
|
|
792
|
-
if (!trimmed) {
|
|
793
|
-
throw unprocessable("Skill source is required.");
|
|
794
|
-
}
|
|
795
|
-
const warnings = [];
|
|
796
|
-
let source = trimmed;
|
|
797
|
-
let requestedSkillSlug = null;
|
|
798
|
-
if (/^npx\s+skills\s+add\s+/i.test(trimmed)) {
|
|
799
|
-
const tokens = extractCommandTokens(trimmed);
|
|
800
|
-
const addIndex = tokens.findIndex((token, index) => token === "add"
|
|
801
|
-
&& index > 0
|
|
802
|
-
&& tokens[index - 1]?.toLowerCase() === "skills");
|
|
803
|
-
if (addIndex >= 0) {
|
|
804
|
-
source = tokens[addIndex + 1] ?? "";
|
|
805
|
-
for (let index = addIndex + 2; index < tokens.length; index += 1) {
|
|
806
|
-
const token = tokens[index];
|
|
807
|
-
if (token === "--skill") {
|
|
808
|
-
requestedSkillSlug = normalizeSkillSlug(tokens[index + 1] ?? null);
|
|
809
|
-
index += 1;
|
|
810
|
-
continue;
|
|
811
|
-
}
|
|
812
|
-
if (token.startsWith("--skill=")) {
|
|
813
|
-
requestedSkillSlug = normalizeSkillSlug(token.slice("--skill=".length));
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
const normalizedSource = source.trim();
|
|
819
|
-
if (!normalizedSource) {
|
|
820
|
-
throw unprocessable("Skill source is required.");
|
|
821
|
-
}
|
|
822
|
-
// Key-style imports (org/repo/skill) originate from the skills.sh registry
|
|
823
|
-
if (!/^https?:\/\//i.test(normalizedSource) && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalizedSource)) {
|
|
824
|
-
const [owner, repo, skillSlugRaw] = normalizedSource.split("/");
|
|
825
|
-
return {
|
|
826
|
-
resolvedSource: `https://github.com/${owner}/${repo}`,
|
|
827
|
-
requestedSkillSlug: normalizeSkillSlug(skillSlugRaw),
|
|
828
|
-
originalSkillsShUrl: `https://skills.sh/${owner}/${repo}/${skillSlugRaw}`,
|
|
829
|
-
warnings,
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
if (!/^https?:\/\//i.test(normalizedSource) && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalizedSource)) {
|
|
833
|
-
return {
|
|
834
|
-
resolvedSource: `https://github.com/${normalizedSource}`,
|
|
835
|
-
requestedSkillSlug,
|
|
836
|
-
originalSkillsShUrl: null,
|
|
837
|
-
warnings,
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
// Detect skills.sh URLs and resolve to GitHub: https://skills.sh/org/repo/skill → org/repo/skill key
|
|
841
|
-
const skillsShMatch = normalizedSource.match(/^https?:\/\/(?:www\.)?skills\.sh\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(?:\/([A-Za-z0-9_.-]+))?(?:[?#].*)?$/i);
|
|
842
|
-
if (skillsShMatch) {
|
|
843
|
-
const [, owner, repo, skillSlugRaw] = skillsShMatch;
|
|
844
|
-
return {
|
|
845
|
-
resolvedSource: `https://github.com/${owner}/${repo}`,
|
|
846
|
-
requestedSkillSlug: skillSlugRaw ? normalizeSkillSlug(skillSlugRaw) : requestedSkillSlug,
|
|
847
|
-
originalSkillsShUrl: normalizedSource,
|
|
848
|
-
warnings,
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
return {
|
|
852
|
-
resolvedSource: normalizedSource,
|
|
853
|
-
requestedSkillSlug,
|
|
854
|
-
originalSkillsShUrl: null,
|
|
855
|
-
warnings,
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
function resolveBundledSkillsRoot() {
|
|
859
|
-
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
860
|
-
return [
|
|
861
|
-
path.resolve(moduleDir, "../../../resources/bundled-skills"),
|
|
862
|
-
path.resolve(process.cwd(), "server/resources/bundled-skills"),
|
|
863
|
-
];
|
|
864
|
-
}
|
|
865
|
-
async function readCommunityPresetFallbackImport(orgId, slug, skillKey, sourceUrl) {
|
|
866
|
-
for (const skillsRoot of resolveCommunityPresetSkillsRoot()) {
|
|
867
|
-
const stats = await fs.stat(skillsRoot).catch(() => null);
|
|
868
|
-
if (!stats?.isDirectory())
|
|
869
|
-
continue;
|
|
870
|
-
const skillDir = path.join(skillsRoot, slug);
|
|
871
|
-
const skillStats = await fs.stat(skillDir).catch(() => null);
|
|
872
|
-
if (!skillStats?.isDirectory())
|
|
873
|
-
continue;
|
|
874
|
-
const imported = await readLocalSkillImportFromDirectory(orgId, skillDir, {
|
|
875
|
-
metadata: {
|
|
876
|
-
sourceKind: "community_preset",
|
|
877
|
-
skillKey,
|
|
878
|
-
},
|
|
879
|
-
}).catch(() => null);
|
|
880
|
-
if (!imported)
|
|
881
|
-
continue;
|
|
882
|
-
return {
|
|
883
|
-
...imported,
|
|
884
|
-
key: skillKey,
|
|
885
|
-
slug,
|
|
886
|
-
sourceType: "github",
|
|
887
|
-
sourceLocator: sourceUrl,
|
|
888
|
-
sourceRef: imported.sourceRef ?? null,
|
|
889
|
-
metadata: {
|
|
890
|
-
...(imported.metadata ?? {}),
|
|
891
|
-
sourceKind: "community_preset",
|
|
892
|
-
skillKey,
|
|
893
|
-
},
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
return null;
|
|
897
|
-
}
|
|
898
|
-
function resolveCommunityPresetSkillsRoot() {
|
|
899
|
-
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
900
|
-
return [
|
|
901
|
-
path.resolve(moduleDir, "../../../resources/community-skills"),
|
|
902
|
-
path.resolve(process.cwd(), "server/resources/community-skills"),
|
|
903
|
-
];
|
|
904
|
-
}
|
|
905
|
-
function matchesRequestedSkill(relativeSkillPath, requestedSkillSlug) {
|
|
906
|
-
if (!requestedSkillSlug)
|
|
907
|
-
return true;
|
|
908
|
-
const skillDir = path.posix.dirname(relativeSkillPath);
|
|
909
|
-
return normalizeSkillSlug(path.posix.basename(skillDir)) === requestedSkillSlug;
|
|
910
|
-
}
|
|
911
|
-
function deriveImportedSkillSlug(frontmatter, fallback) {
|
|
912
|
-
return normalizeSkillSlug(asString(frontmatter.slug))
|
|
913
|
-
?? normalizeSkillSlug(asString(frontmatter.name))
|
|
914
|
-
?? normalizeAgentUrlKey(fallback)
|
|
915
|
-
?? "skill";
|
|
916
|
-
}
|
|
917
|
-
function deriveImportedSkillSource(frontmatter, fallbackSlug) {
|
|
918
|
-
const metadata = isPlainRecord(frontmatter.metadata) ? frontmatter.metadata : null;
|
|
919
|
-
const canonicalKey = readCanonicalSkillKey(frontmatter, metadata);
|
|
920
|
-
const rawSources = metadata && Array.isArray(metadata.sources) ? metadata.sources : [];
|
|
921
|
-
const sourceEntry = rawSources.find((entry) => isPlainRecord(entry));
|
|
922
|
-
const kind = asString(sourceEntry?.kind);
|
|
923
|
-
if (kind === "github-dir" || kind === "github-file") {
|
|
924
|
-
const repo = asString(sourceEntry?.repo);
|
|
925
|
-
const repoPath = asString(sourceEntry?.path);
|
|
926
|
-
const commit = asString(sourceEntry?.commit);
|
|
927
|
-
const trackingRef = asString(sourceEntry?.trackingRef);
|
|
928
|
-
const url = asString(sourceEntry?.url)
|
|
929
|
-
?? (repo
|
|
930
|
-
? `https://github.com/${repo}${repoPath ? `/tree/${trackingRef ?? commit ?? "main"}/${repoPath}` : ""}`
|
|
931
|
-
: null);
|
|
932
|
-
const [owner, repoName] = (repo ?? "").split("/");
|
|
933
|
-
if (repo && owner && repoName) {
|
|
934
|
-
return {
|
|
935
|
-
sourceType: "github",
|
|
936
|
-
sourceLocator: url,
|
|
937
|
-
sourceRef: commit,
|
|
938
|
-
metadata: {
|
|
939
|
-
...(canonicalKey ? { skillKey: canonicalKey } : {}),
|
|
940
|
-
sourceKind: "github",
|
|
941
|
-
owner,
|
|
942
|
-
repo: repoName,
|
|
943
|
-
ref: commit,
|
|
944
|
-
trackingRef,
|
|
945
|
-
repoSkillDir: repoPath ?? `skills/${fallbackSlug}`,
|
|
946
|
-
},
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
if (kind === "url") {
|
|
951
|
-
const url = asString(sourceEntry?.url) ?? asString(sourceEntry?.rawUrl);
|
|
952
|
-
if (url) {
|
|
953
|
-
return {
|
|
954
|
-
sourceType: "url",
|
|
955
|
-
sourceLocator: url,
|
|
956
|
-
sourceRef: null,
|
|
957
|
-
metadata: {
|
|
958
|
-
...(canonicalKey ? { skillKey: canonicalKey } : {}),
|
|
959
|
-
sourceKind: "url",
|
|
960
|
-
},
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
return {
|
|
965
|
-
sourceType: "catalog",
|
|
966
|
-
sourceLocator: null,
|
|
967
|
-
sourceRef: null,
|
|
968
|
-
metadata: {
|
|
969
|
-
...(canonicalKey ? { skillKey: canonicalKey } : {}),
|
|
970
|
-
sourceKind: "catalog",
|
|
971
|
-
},
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
function readInlineSkillImports(orgId, files) {
|
|
975
|
-
const normalizedFiles = normalizePackageFileMap(files);
|
|
976
|
-
const skillPaths = Object.keys(normalizedFiles).filter((entry) => path.posix.basename(entry).toLowerCase() === "skill.md");
|
|
977
|
-
const imports = [];
|
|
978
|
-
for (const skillPath of skillPaths) {
|
|
979
|
-
const dir = path.posix.dirname(skillPath);
|
|
980
|
-
const skillDir = dir === "." ? "" : dir;
|
|
981
|
-
const slugFallback = path.posix.basename(skillDir || path.posix.dirname(skillPath));
|
|
982
|
-
const markdown = normalizedFiles[skillPath];
|
|
983
|
-
const parsed = parseFrontmatterMarkdown(markdown);
|
|
984
|
-
const slug = deriveImportedSkillSlug(parsed.frontmatter, slugFallback);
|
|
985
|
-
const source = deriveImportedSkillSource(parsed.frontmatter, slug);
|
|
986
|
-
const inventory = Object.keys(normalizedFiles)
|
|
987
|
-
.filter((entry) => entry === skillPath || (skillDir ? entry.startsWith(`${skillDir}/`) : false))
|
|
988
|
-
.map((entry) => {
|
|
989
|
-
const relative = entry === skillPath ? "SKILL.md" : entry.slice(skillDir.length + 1);
|
|
990
|
-
return {
|
|
991
|
-
path: normalizePortablePath(relative),
|
|
992
|
-
kind: classifyInventoryKind(relative),
|
|
993
|
-
};
|
|
994
|
-
})
|
|
995
|
-
.sort((left, right) => left.path.localeCompare(right.path));
|
|
996
|
-
imports.push({
|
|
997
|
-
key: "",
|
|
998
|
-
slug,
|
|
999
|
-
name: asString(parsed.frontmatter.name) ?? slug,
|
|
1000
|
-
description: normalizeSkillDescription(parsed.frontmatter.description),
|
|
1001
|
-
markdown,
|
|
1002
|
-
packageDir: skillDir,
|
|
1003
|
-
sourceType: source.sourceType,
|
|
1004
|
-
sourceLocator: source.sourceLocator,
|
|
1005
|
-
sourceRef: source.sourceRef,
|
|
1006
|
-
trustLevel: deriveTrustLevel(inventory),
|
|
1007
|
-
compatibility: "compatible",
|
|
1008
|
-
fileInventory: inventory,
|
|
1009
|
-
metadata: source.metadata,
|
|
1010
|
-
});
|
|
1011
|
-
imports[imports.length - 1].key = deriveCanonicalSkillKey(orgId, imports[imports.length - 1]);
|
|
1012
|
-
}
|
|
1013
|
-
return imports;
|
|
1014
|
-
}
|
|
1015
|
-
async function walkLocalFiles(root, current, out) {
|
|
1016
|
-
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
1017
|
-
for (const entry of entries) {
|
|
1018
|
-
if (entry.name === ".git" || entry.name === "node_modules")
|
|
1019
|
-
continue;
|
|
1020
|
-
const absolutePath = path.join(current, entry.name);
|
|
1021
|
-
if (entry.isDirectory()) {
|
|
1022
|
-
await walkLocalFiles(root, absolutePath, out);
|
|
1023
|
-
continue;
|
|
1024
|
-
}
|
|
1025
|
-
if (!entry.isFile())
|
|
1026
|
-
continue;
|
|
1027
|
-
out.push(normalizePortablePath(path.relative(root, absolutePath)));
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
async function statPath(targetPath) {
|
|
1031
|
-
return fs.stat(targetPath).catch(() => null);
|
|
1032
|
-
}
|
|
1033
|
-
async function collectLocalSkillInventory(skillDir, mode = "full") {
|
|
1034
|
-
const skillFilePath = path.join(skillDir, "SKILL.md");
|
|
1035
|
-
const skillFileStat = await statPath(skillFilePath);
|
|
1036
|
-
if (!skillFileStat?.isFile()) {
|
|
1037
|
-
throw unprocessable(`No SKILL.md file was found in ${skillDir}.`);
|
|
1038
|
-
}
|
|
1039
|
-
const allFiles = new Set(["SKILL.md"]);
|
|
1040
|
-
if (mode === "full") {
|
|
1041
|
-
const discoveredFiles = [];
|
|
1042
|
-
await walkLocalFiles(skillDir, skillDir, discoveredFiles);
|
|
1043
|
-
for (const relativePath of discoveredFiles) {
|
|
1044
|
-
allFiles.add(relativePath);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
else {
|
|
1048
|
-
for (const relativeDir of PROJECT_ROOT_SKILL_SUBDIRECTORIES) {
|
|
1049
|
-
const absoluteDir = path.join(skillDir, relativeDir);
|
|
1050
|
-
const dirStat = await statPath(absoluteDir);
|
|
1051
|
-
if (!dirStat?.isDirectory())
|
|
1052
|
-
continue;
|
|
1053
|
-
const discoveredFiles = [];
|
|
1054
|
-
await walkLocalFiles(skillDir, absoluteDir, discoveredFiles);
|
|
1055
|
-
for (const relativePath of discoveredFiles) {
|
|
1056
|
-
allFiles.add(relativePath);
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
return Array.from(allFiles)
|
|
1061
|
-
.map((relativePath) => ({
|
|
1062
|
-
path: normalizePortablePath(relativePath),
|
|
1063
|
-
kind: classifyInventoryKind(relativePath),
|
|
1064
|
-
}))
|
|
1065
|
-
.sort((left, right) => left.path.localeCompare(right.path));
|
|
1066
|
-
}
|
|
1067
|
-
export async function readLocalSkillImportFromDirectory(orgId, skillDir, options) {
|
|
1068
|
-
const resolvedSkillDir = path.resolve(skillDir);
|
|
1069
|
-
const skillFilePath = path.join(resolvedSkillDir, "SKILL.md");
|
|
1070
|
-
const markdown = await fs.readFile(skillFilePath, "utf8");
|
|
1071
|
-
const parsed = parseFrontmatterMarkdown(markdown);
|
|
1072
|
-
const slug = deriveImportedSkillSlug(parsed.frontmatter, path.basename(resolvedSkillDir));
|
|
1073
|
-
const parsedMetadata = isPlainRecord(parsed.frontmatter.metadata) ? parsed.frontmatter.metadata : null;
|
|
1074
|
-
const skillKey = readCanonicalSkillKey(parsed.frontmatter, parsedMetadata);
|
|
1075
|
-
const metadata = {
|
|
1076
|
-
...(skillKey ? { skillKey } : {}),
|
|
1077
|
-
...(parsedMetadata ?? {}),
|
|
1078
|
-
sourceKind: "local_path",
|
|
1079
|
-
...(options?.metadata ?? {}),
|
|
1080
|
-
};
|
|
1081
|
-
const inventory = await collectLocalSkillInventory(resolvedSkillDir, options?.inventoryMode ?? "full");
|
|
1082
|
-
return {
|
|
1083
|
-
key: deriveCanonicalSkillKey(orgId, {
|
|
1084
|
-
slug,
|
|
1085
|
-
sourceType: "local_path",
|
|
1086
|
-
sourceLocator: resolvedSkillDir,
|
|
1087
|
-
metadata,
|
|
1088
|
-
}),
|
|
1089
|
-
slug,
|
|
1090
|
-
name: asString(parsed.frontmatter.name) ?? slug,
|
|
1091
|
-
description: normalizeSkillDescription(parsed.frontmatter.description),
|
|
1092
|
-
markdown,
|
|
1093
|
-
packageDir: resolvedSkillDir,
|
|
1094
|
-
sourceType: "local_path",
|
|
1095
|
-
sourceLocator: resolvedSkillDir,
|
|
1096
|
-
sourceRef: null,
|
|
1097
|
-
trustLevel: deriveTrustLevel(inventory),
|
|
1098
|
-
compatibility: "compatible",
|
|
1099
|
-
fileInventory: inventory,
|
|
1100
|
-
metadata,
|
|
1101
|
-
};
|
|
1102
|
-
}
|
|
1103
|
-
export async function discoverProjectWorkspaceSkillDirectories(target) {
|
|
1104
|
-
const discovered = new Map();
|
|
1105
|
-
const rootSkillPath = path.join(target.workspaceCwd, "SKILL.md");
|
|
1106
|
-
if ((await statPath(rootSkillPath))?.isFile()) {
|
|
1107
|
-
discovered.set(path.resolve(target.workspaceCwd), "project_root");
|
|
1108
|
-
}
|
|
1109
|
-
for (const relativeRoot of PROJECT_SCAN_DIRECTORY_ROOTS) {
|
|
1110
|
-
const absoluteRoot = path.join(target.workspaceCwd, relativeRoot);
|
|
1111
|
-
const rootStat = await statPath(absoluteRoot);
|
|
1112
|
-
if (!rootStat?.isDirectory())
|
|
1113
|
-
continue;
|
|
1114
|
-
const entries = await fs.readdir(absoluteRoot, { withFileTypes: true }).catch(() => []);
|
|
1115
|
-
for (const entry of entries) {
|
|
1116
|
-
if (!entry.isDirectory())
|
|
1117
|
-
continue;
|
|
1118
|
-
const absoluteSkillDir = path.resolve(absoluteRoot, entry.name);
|
|
1119
|
-
if (!(await statPath(path.join(absoluteSkillDir, "SKILL.md")))?.isFile())
|
|
1120
|
-
continue;
|
|
1121
|
-
discovered.set(absoluteSkillDir, "full");
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
return Array.from(discovered.entries())
|
|
1125
|
-
.map(([skillDir, inventoryMode]) => ({ skillDir, inventoryMode }))
|
|
1126
|
-
.sort((left, right) => left.skillDir.localeCompare(right.skillDir));
|
|
1127
|
-
}
|
|
1128
|
-
async function readLocalSkillImports(orgId, sourcePath) {
|
|
1129
|
-
const resolvedPath = path.resolve(sourcePath);
|
|
1130
|
-
const stat = await fs.stat(resolvedPath).catch(() => null);
|
|
1131
|
-
if (!stat) {
|
|
1132
|
-
throw unprocessable(`Skill source path does not exist: ${sourcePath}`);
|
|
1133
|
-
}
|
|
1134
|
-
if (stat.isFile()) {
|
|
1135
|
-
if (path.basename(resolvedPath).toLowerCase() !== "skill.md") {
|
|
1136
|
-
throw unprocessable("Local skill imports must point at SKILL.md or a directory that contains skill folders.");
|
|
1137
|
-
}
|
|
1138
|
-
return [await readLocalSkillImportFromDirectory(orgId, path.dirname(resolvedPath))];
|
|
1139
|
-
}
|
|
1140
|
-
const discovered = new Set();
|
|
1141
|
-
if ((await statPath(path.join(resolvedPath, "SKILL.md")))?.isFile()) {
|
|
1142
|
-
discovered.add(resolvedPath);
|
|
1143
|
-
}
|
|
1144
|
-
for (const candidateRoot of [resolvedPath, path.join(resolvedPath, "skills")]) {
|
|
1145
|
-
const candidateStat = await statPath(candidateRoot);
|
|
1146
|
-
if (!candidateStat?.isDirectory())
|
|
1147
|
-
continue;
|
|
1148
|
-
const entries = await fs.readdir(candidateRoot, { withFileTypes: true }).catch(() => []);
|
|
1149
|
-
for (const entry of entries) {
|
|
1150
|
-
if (!entry.isDirectory())
|
|
1151
|
-
continue;
|
|
1152
|
-
const skillDir = path.join(candidateRoot, entry.name);
|
|
1153
|
-
if ((await statPath(path.join(skillDir, "SKILL.md")))?.isFile()) {
|
|
1154
|
-
discovered.add(path.resolve(skillDir));
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
if (discovered.size === 0) {
|
|
1159
|
-
throw unprocessable("No SKILL.md files were found in the provided path.");
|
|
1160
|
-
}
|
|
1161
|
-
return Promise.all(Array.from(discovered)
|
|
1162
|
-
.sort((left, right) => left.localeCompare(right))
|
|
1163
|
-
.map((skillDir) => readLocalSkillImportFromDirectory(orgId, skillDir)));
|
|
1164
|
-
}
|
|
1165
|
-
async function readUrlSkillImports(orgId, sourceUrl, requestedSkillSlug = null) {
|
|
1166
|
-
const url = sourceUrl.trim();
|
|
1167
|
-
const warnings = [];
|
|
1168
|
-
if (url.includes("github.com/")) {
|
|
1169
|
-
const parsed = parseGitHubSourceUrl(url);
|
|
1170
|
-
const { pinnedRef, trackingRef } = await resolveGitHubPinnedRef(parsed);
|
|
1171
|
-
let ref = pinnedRef;
|
|
1172
|
-
const tree = await fetchJson(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/git/trees/${ref}?recursive=1`).catch(() => {
|
|
1173
|
-
throw unprocessable(`Failed to read GitHub tree for ${url}`);
|
|
1174
|
-
});
|
|
1175
|
-
const allPaths = (tree.tree ?? [])
|
|
1176
|
-
.filter((entry) => entry.type === "blob")
|
|
1177
|
-
.map((entry) => entry.path)
|
|
1178
|
-
.filter((entry) => typeof entry === "string");
|
|
1179
|
-
const basePrefix = parsed.basePath ? `${parsed.basePath.replace(/^\/+|\/+$/g, "")}/` : "";
|
|
1180
|
-
const scopedPaths = basePrefix
|
|
1181
|
-
? allPaths.filter((entry) => entry.startsWith(basePrefix))
|
|
1182
|
-
: allPaths;
|
|
1183
|
-
const relativePaths = scopedPaths.map((entry) => basePrefix ? entry.slice(basePrefix.length) : entry);
|
|
1184
|
-
const filteredPaths = parsed.filePath
|
|
1185
|
-
? relativePaths.filter((entry) => entry === path.posix.relative(parsed.basePath || ".", parsed.filePath))
|
|
1186
|
-
: relativePaths;
|
|
1187
|
-
const skillPaths = filteredPaths.filter((entry) => path.posix.basename(entry).toLowerCase() === "skill.md");
|
|
1188
|
-
if (skillPaths.length === 0) {
|
|
1189
|
-
throw unprocessable("No SKILL.md files were found in the provided GitHub source.");
|
|
1190
|
-
}
|
|
1191
|
-
const skills = [];
|
|
1192
|
-
for (const relativeSkillPath of skillPaths) {
|
|
1193
|
-
const repoSkillPath = basePrefix ? `${basePrefix}${relativeSkillPath}` : relativeSkillPath;
|
|
1194
|
-
const markdown = await fetchText(resolveRawGitHubUrl(parsed.owner, parsed.repo, ref, repoSkillPath));
|
|
1195
|
-
const parsedMarkdown = parseFrontmatterMarkdown(markdown);
|
|
1196
|
-
const skillDir = path.posix.dirname(relativeSkillPath);
|
|
1197
|
-
const slug = deriveImportedSkillSlug(parsedMarkdown.frontmatter, path.posix.basename(skillDir));
|
|
1198
|
-
const skillKey = readCanonicalSkillKey(parsedMarkdown.frontmatter, isPlainRecord(parsedMarkdown.frontmatter.metadata) ? parsedMarkdown.frontmatter.metadata : null);
|
|
1199
|
-
if (requestedSkillSlug && !matchesRequestedSkill(relativeSkillPath, requestedSkillSlug) && slug !== requestedSkillSlug) {
|
|
1200
|
-
continue;
|
|
1201
|
-
}
|
|
1202
|
-
const metadata = {
|
|
1203
|
-
...(skillKey ? { skillKey } : {}),
|
|
1204
|
-
sourceKind: "github",
|
|
1205
|
-
owner: parsed.owner,
|
|
1206
|
-
repo: parsed.repo,
|
|
1207
|
-
ref: ref,
|
|
1208
|
-
trackingRef,
|
|
1209
|
-
repoSkillDir: normalizeGitHubSkillDirectory(basePrefix ? `${basePrefix}${skillDir}` : skillDir, slug),
|
|
1210
|
-
};
|
|
1211
|
-
const inventory = filteredPaths
|
|
1212
|
-
.filter((entry) => entry === relativeSkillPath || entry.startsWith(`${skillDir}/`))
|
|
1213
|
-
.map((entry) => ({
|
|
1214
|
-
path: entry === relativeSkillPath ? "SKILL.md" : entry.slice(skillDir.length + 1),
|
|
1215
|
-
kind: classifyInventoryKind(entry === relativeSkillPath ? "SKILL.md" : entry.slice(skillDir.length + 1)),
|
|
1216
|
-
}))
|
|
1217
|
-
.sort((left, right) => left.path.localeCompare(right.path));
|
|
1218
|
-
skills.push({
|
|
1219
|
-
key: deriveCanonicalSkillKey(orgId, {
|
|
1220
|
-
slug,
|
|
1221
|
-
sourceType: "github",
|
|
1222
|
-
sourceLocator: sourceUrl,
|
|
1223
|
-
metadata,
|
|
1224
|
-
}),
|
|
1225
|
-
slug,
|
|
1226
|
-
name: asString(parsedMarkdown.frontmatter.name) ?? slug,
|
|
1227
|
-
description: normalizeSkillDescription(parsedMarkdown.frontmatter.description),
|
|
1228
|
-
markdown,
|
|
1229
|
-
sourceType: "github",
|
|
1230
|
-
sourceLocator: sourceUrl,
|
|
1231
|
-
sourceRef: ref,
|
|
1232
|
-
trustLevel: deriveTrustLevel(inventory),
|
|
1233
|
-
compatibility: "compatible",
|
|
1234
|
-
fileInventory: inventory,
|
|
1235
|
-
metadata,
|
|
1236
|
-
});
|
|
1237
|
-
}
|
|
1238
|
-
if (skills.length === 0) {
|
|
1239
|
-
throw unprocessable(requestedSkillSlug
|
|
1240
|
-
? `Skill ${requestedSkillSlug} was not found in the provided GitHub source.`
|
|
1241
|
-
: "No SKILL.md files were found in the provided GitHub source.");
|
|
1242
|
-
}
|
|
1243
|
-
return { skills, warnings };
|
|
1244
|
-
}
|
|
1245
|
-
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
1246
|
-
const markdown = await fetchText(url);
|
|
1247
|
-
const parsedMarkdown = parseFrontmatterMarkdown(markdown);
|
|
1248
|
-
const urlObj = new URL(url);
|
|
1249
|
-
const fileName = path.posix.basename(urlObj.pathname);
|
|
1250
|
-
const slug = deriveImportedSkillSlug(parsedMarkdown.frontmatter, fileName.replace(/\.md$/i, ""));
|
|
1251
|
-
const skillKey = readCanonicalSkillKey(parsedMarkdown.frontmatter, isPlainRecord(parsedMarkdown.frontmatter.metadata) ? parsedMarkdown.frontmatter.metadata : null);
|
|
1252
|
-
const metadata = {
|
|
1253
|
-
...(skillKey ? { skillKey } : {}),
|
|
1254
|
-
sourceKind: "url",
|
|
1255
|
-
};
|
|
1256
|
-
const inventory = [{ path: "SKILL.md", kind: "skill" }];
|
|
1257
|
-
return {
|
|
1258
|
-
skills: [{
|
|
1259
|
-
key: deriveCanonicalSkillKey(orgId, {
|
|
1260
|
-
slug,
|
|
1261
|
-
sourceType: "url",
|
|
1262
|
-
sourceLocator: url,
|
|
1263
|
-
metadata,
|
|
1264
|
-
}),
|
|
1265
|
-
slug,
|
|
1266
|
-
name: asString(parsedMarkdown.frontmatter.name) ?? slug,
|
|
1267
|
-
description: normalizeSkillDescription(parsedMarkdown.frontmatter.description),
|
|
1268
|
-
markdown,
|
|
1269
|
-
sourceType: "url",
|
|
1270
|
-
sourceLocator: url,
|
|
1271
|
-
sourceRef: null,
|
|
1272
|
-
trustLevel: deriveTrustLevel(inventory),
|
|
1273
|
-
compatibility: "compatible",
|
|
1274
|
-
fileInventory: inventory,
|
|
1275
|
-
metadata,
|
|
1276
|
-
}],
|
|
1277
|
-
warnings,
|
|
1278
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
throw unprocessable("Unsupported skill source. Use a local path or URL.");
|
|
1281
|
-
}
|
|
1282
|
-
function toCompanySkill(row) {
|
|
1283
|
-
return {
|
|
1284
|
-
...row,
|
|
1285
|
-
description: row.description ?? null,
|
|
1286
|
-
sourceType: row.sourceType,
|
|
1287
|
-
sourceLocator: row.sourceLocator ?? null,
|
|
1288
|
-
sourceRef: row.sourceRef ?? null,
|
|
1289
|
-
trustLevel: row.trustLevel,
|
|
1290
|
-
compatibility: row.compatibility,
|
|
1291
|
-
fileInventory: Array.isArray(row.fileInventory)
|
|
1292
|
-
? row.fileInventory.flatMap((entry) => {
|
|
1293
|
-
if (!isPlainRecord(entry))
|
|
1294
|
-
return [];
|
|
1295
|
-
return [{
|
|
1296
|
-
path: String(entry.path ?? ""),
|
|
1297
|
-
kind: String(entry.kind ?? "other"),
|
|
1298
|
-
}];
|
|
1299
|
-
})
|
|
1300
|
-
: [],
|
|
1301
|
-
metadata: isPlainRecord(row.metadata) ? row.metadata : null,
|
|
1302
|
-
};
|
|
1303
|
-
}
|
|
1304
|
-
function serializeFileInventory(fileInventory) {
|
|
1305
|
-
return fileInventory.map((entry) => ({
|
|
1306
|
-
path: entry.path,
|
|
1307
|
-
kind: entry.kind,
|
|
1308
|
-
}));
|
|
1309
|
-
}
|
|
1310
|
-
function getSkillMeta(skill) {
|
|
1311
|
-
return isPlainRecord(skill.metadata) ? skill.metadata : {};
|
|
1312
|
-
}
|
|
1313
|
-
function resolveSkillReference(skills, reference, orgId) {
|
|
1314
|
-
const trimmed = reference.trim();
|
|
1315
|
-
if (!trimmed) {
|
|
1316
|
-
return { skill: null, ambiguous: false };
|
|
1317
|
-
}
|
|
1318
|
-
const byId = skills.find((skill) => skill.id === trimmed);
|
|
1319
|
-
if (byId) {
|
|
1320
|
-
return { skill: byId, ambiguous: false };
|
|
1321
|
-
}
|
|
1322
|
-
return resolveOrganizationSkillReference(skills, trimmed, { orgId });
|
|
1323
|
-
}
|
|
1324
|
-
function resolveRequestedSkillKeysOrThrow(skills, requestedReferences, orgId) {
|
|
1325
|
-
const missing = new Set();
|
|
1326
|
-
const ambiguous = new Set();
|
|
1327
|
-
const resolved = new Set();
|
|
1328
|
-
for (const reference of requestedReferences) {
|
|
1329
|
-
const trimmed = reference.trim();
|
|
1330
|
-
if (!trimmed)
|
|
1331
|
-
continue;
|
|
1332
|
-
const match = resolveSkillReference(skills, trimmed, orgId);
|
|
1333
|
-
if (match.skill) {
|
|
1334
|
-
resolved.add(match.skill.key);
|
|
1335
|
-
continue;
|
|
1336
|
-
}
|
|
1337
|
-
if (match.ambiguous) {
|
|
1338
|
-
ambiguous.add(trimmed);
|
|
1339
|
-
continue;
|
|
1340
|
-
}
|
|
1341
|
-
missing.add(trimmed);
|
|
1342
|
-
}
|
|
1343
|
-
if (ambiguous.size > 0 || missing.size > 0) {
|
|
1344
|
-
const problems = [];
|
|
1345
|
-
if (ambiguous.size > 0) {
|
|
1346
|
-
problems.push(`ambiguous references: ${Array.from(ambiguous).sort().join(", ")}`);
|
|
1347
|
-
}
|
|
1348
|
-
if (missing.size > 0) {
|
|
1349
|
-
problems.push(`unknown references: ${Array.from(missing).sort().join(", ")}`);
|
|
1350
|
-
}
|
|
1351
|
-
throw unprocessable(`Invalid organization skill selection (${problems.join("; ")}).`);
|
|
1352
|
-
}
|
|
1353
|
-
return Array.from(resolved);
|
|
1354
|
-
}
|
|
1355
|
-
function resolveDesiredSkillKeys(skills, config, orgId) {
|
|
1356
|
-
const preference = readRudderSkillSyncPreference(config);
|
|
1357
|
-
return Array.from(new Set(preference.desiredSkills
|
|
1358
|
-
.map((reference) => {
|
|
1359
|
-
const resolved = resolveSkillReference(skills, reference, orgId).skill?.key;
|
|
1360
|
-
if (resolved)
|
|
1361
|
-
return resolved;
|
|
1362
|
-
const bundledKey = toBundledRudderSkillKey(getBundledRudderSkillSlug(reference));
|
|
1363
|
-
return bundledKey ?? normalizeSkillKey(reference);
|
|
1364
|
-
})
|
|
1365
|
-
.filter((value) => Boolean(value))));
|
|
1366
|
-
}
|
|
1367
|
-
function getRequiredBundledSkillKeys(skills) {
|
|
1368
|
-
const availableKeys = new Set(skills.map((skill) => skill.key));
|
|
1369
|
-
return RUDDER_BUNDLED_SKILL_SLUGS
|
|
1370
|
-
.map((slug) => `rudder/${slug}`)
|
|
1371
|
-
.filter((key) => availableKeys.has(key));
|
|
1372
|
-
}
|
|
1373
|
-
function sortUniqueSkillKeys(skillKeys) {
|
|
1374
|
-
return Array.from(new Set(skillKeys
|
|
1375
|
-
.map((value) => value.trim())
|
|
1376
|
-
.filter(Boolean))).sort((left, right) => left.localeCompare(right));
|
|
1377
|
-
}
|
|
1378
|
-
function sortUniqueSelectionRefs(selectionRefs) {
|
|
1379
|
-
return Array.from(new Set(selectionRefs
|
|
1380
|
-
.map((value) => value.trim())
|
|
1381
|
-
.filter(Boolean))).sort((left, right) => left.localeCompare(right));
|
|
1382
|
-
}
|
|
1383
|
-
function arraysEqual(left, right) {
|
|
1384
|
-
if (left === right)
|
|
1385
|
-
return true;
|
|
1386
|
-
if (left.length !== right.length)
|
|
1387
|
-
return false;
|
|
1388
|
-
return left.every((value, index) => value === right[index]);
|
|
1389
|
-
}
|
|
1390
|
-
function buildMissingSelectionEntry(selectionKey, agentRuntimeType) {
|
|
1391
|
-
const parsed = parseSelectionKey(selectionKey);
|
|
1392
|
-
const key = parsed.slug ?? parsed.orgKey ?? selectionKey;
|
|
1393
|
-
const runtimeTypeMismatch = parsed.sourceClass === "adapter_home"
|
|
1394
|
-
&& parsed.agentRuntimeType
|
|
1395
|
-
&& parsed.agentRuntimeType !== agentRuntimeType;
|
|
1396
|
-
const locationLabel = (() => {
|
|
1397
|
-
if (parsed.sourceClass === "agent_home")
|
|
1398
|
-
return "AGENT_HOME/skills";
|
|
1399
|
-
if (parsed.sourceClass === "global")
|
|
1400
|
-
return "~/.agents/skills";
|
|
1401
|
-
if (parsed.sourceClass === "adapter_home" && parsed.agentRuntimeType) {
|
|
1402
|
-
return ADAPTER_SKILL_HOME_DEFINITIONS[parsed.agentRuntimeType]?.locationLabel ?? null;
|
|
1403
|
-
}
|
|
1404
|
-
return null;
|
|
1405
|
-
})();
|
|
1406
|
-
const detail = runtimeTypeMismatch
|
|
1407
|
-
? `This adapter-specific skill was saved for ${parsed.agentRuntimeType} and is unavailable on ${agentRuntimeType}.`
|
|
1408
|
-
: "Rudder cannot find this enabled skill in the current Rudder-owned catalog.";
|
|
1409
|
-
return {
|
|
1410
|
-
key,
|
|
1411
|
-
selectionKey,
|
|
1412
|
-
runtimeName: parsed.slug ?? key,
|
|
1413
|
-
description: null,
|
|
1414
|
-
desired: true,
|
|
1415
|
-
configurable: parsed.sourceClass !== "bundled",
|
|
1416
|
-
alwaysEnabled: parsed.sourceClass === "bundled",
|
|
1417
|
-
managed: parsed.sourceClass === "bundled" || parsed.sourceClass === "organization",
|
|
1418
|
-
state: "missing",
|
|
1419
|
-
sourceClass: parsed.sourceClass ?? "adapter_home",
|
|
1420
|
-
origin: "external_unknown",
|
|
1421
|
-
originLabel: runtimeTypeMismatch ? "Unavailable for this runtime" : "Unavailable",
|
|
1422
|
-
locationLabel,
|
|
1423
|
-
readOnly: parsed.sourceClass === "bundled",
|
|
1424
|
-
sourcePath: null,
|
|
1425
|
-
targetPath: null,
|
|
1426
|
-
detail,
|
|
1427
|
-
organizationSkillKey: parsed.orgKey ?? null,
|
|
1428
|
-
runtimeSourcePath: null,
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
function applyDesiredSelectionsToCatalog(entries, desiredSelectionRefs, agentRuntimeType) {
|
|
1432
|
-
const desiredSet = new Set(desiredSelectionRefs);
|
|
1433
|
-
const warnings = [];
|
|
1434
|
-
const out = entries.map((entry) => {
|
|
1435
|
-
const desired = entry.alwaysEnabled || desiredSet.has(entry.selectionKey);
|
|
1436
|
-
const state = entry.alwaysEnabled
|
|
1437
|
-
? "configured"
|
|
1438
|
-
: desired
|
|
1439
|
-
? "configured"
|
|
1440
|
-
: entry.sourceClass === "agent_home" || entry.sourceClass === "global" || entry.sourceClass === "adapter_home"
|
|
1441
|
-
? "external"
|
|
1442
|
-
: "available";
|
|
1443
|
-
return {
|
|
1444
|
-
...entry,
|
|
1445
|
-
desired,
|
|
1446
|
-
state,
|
|
1447
|
-
detail: desired
|
|
1448
|
-
? entry.alwaysEnabled
|
|
1449
|
-
? (entry.detail ?? "Always loaded by Rudder for every agent run.")
|
|
1450
|
-
: "Enabled for this agent and loaded on the next run."
|
|
1451
|
-
: (entry.detail ?? null),
|
|
1452
|
-
};
|
|
1453
|
-
});
|
|
1454
|
-
const knownSelectionKeys = new Set(out.map((entry) => entry.selectionKey));
|
|
1455
|
-
for (const selectionKey of desiredSelectionRefs) {
|
|
1456
|
-
if (knownSelectionKeys.has(selectionKey))
|
|
1457
|
-
continue;
|
|
1458
|
-
warnings.push(`Enabled skill "${selectionKey}" is no longer available in the current skill catalog.`);
|
|
1459
|
-
out.push(buildMissingSelectionEntry(selectionKey, agentRuntimeType));
|
|
1460
|
-
}
|
|
1461
|
-
out.sort((left, right) => {
|
|
1462
|
-
const orderDelta = AGENT_SKILL_SOURCE_CLASS_ORDER[left.sourceClass] - AGENT_SKILL_SOURCE_CLASS_ORDER[right.sourceClass];
|
|
1463
|
-
if (orderDelta !== 0)
|
|
1464
|
-
return orderDelta;
|
|
1465
|
-
return left.key.localeCompare(right.key) || left.selectionKey.localeCompare(right.selectionKey);
|
|
1466
|
-
});
|
|
1467
|
-
const conflictGroups = new Map();
|
|
1468
|
-
for (const entry of out) {
|
|
1469
|
-
if (!entry.desired || entry.alwaysEnabled)
|
|
1470
|
-
continue;
|
|
1471
|
-
const existing = conflictGroups.get(entry.key) ?? [];
|
|
1472
|
-
existing.push(entry.selectionKey);
|
|
1473
|
-
conflictGroups.set(entry.key, existing);
|
|
1474
|
-
}
|
|
1475
|
-
for (const [skillKey, selectionKeys] of conflictGroups.entries()) {
|
|
1476
|
-
if (selectionKeys.length <= 1)
|
|
1477
|
-
continue;
|
|
1478
|
-
warnings.push(`Enabled skill collision for "${skillKey}": ${selectionKeys.join(", ")}`);
|
|
1479
|
-
}
|
|
1480
|
-
return {
|
|
1481
|
-
desiredSkills: sortUniqueSelectionRefs(desiredSelectionRefs),
|
|
1482
|
-
entries: out,
|
|
1483
|
-
warnings,
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
function stripBundledRequiredSkillKeys(skillKeys) {
|
|
1487
|
-
return sortUniqueSkillKeys(skillKeys).filter((skillKey) => !isBundledRudderSkillKey(skillKey));
|
|
1488
|
-
}
|
|
1489
|
-
function mergeRequiredBundledSkillKeys(skills, skillKeys) {
|
|
1490
|
-
return sortUniqueSkillKeys([
|
|
1491
|
-
...stripBundledRequiredSkillKeys(skillKeys),
|
|
1492
|
-
...getRequiredBundledSkillKeys(skills),
|
|
1493
|
-
]);
|
|
1494
|
-
}
|
|
1495
|
-
function normalizeSkillDirectory(skill) {
|
|
1496
|
-
if ((skill.sourceType !== "local_path" && skill.sourceType !== "catalog") || !skill.sourceLocator)
|
|
1497
|
-
return null;
|
|
1498
|
-
const resolved = path.resolve(skill.sourceLocator);
|
|
1499
|
-
if (path.basename(resolved).toLowerCase() === "skill.md") {
|
|
1500
|
-
return path.dirname(resolved);
|
|
1501
|
-
}
|
|
1502
|
-
return resolved;
|
|
1503
|
-
}
|
|
1504
|
-
function normalizeSourceLocatorDirectory(sourceLocator) {
|
|
1505
|
-
if (!sourceLocator)
|
|
1506
|
-
return null;
|
|
1507
|
-
const resolved = path.resolve(sourceLocator);
|
|
1508
|
-
return path.basename(resolved).toLowerCase() === "skill.md" ? path.dirname(resolved) : resolved;
|
|
1509
|
-
}
|
|
1510
|
-
export async function findMissingLocalSkillIds(skills) {
|
|
1511
|
-
const missingIds = [];
|
|
1512
|
-
for (const skill of skills) {
|
|
1513
|
-
if (skill.sourceType !== "local_path")
|
|
1514
|
-
continue;
|
|
1515
|
-
const skillDir = normalizeSourceLocatorDirectory(skill.sourceLocator);
|
|
1516
|
-
if (!skillDir) {
|
|
1517
|
-
missingIds.push(skill.id);
|
|
1518
|
-
continue;
|
|
1519
|
-
}
|
|
1520
|
-
const skillDirStat = await statPath(skillDir);
|
|
1521
|
-
const skillFileStat = await statPath(path.join(skillDir, "SKILL.md"));
|
|
1522
|
-
if (!skillDirStat?.isDirectory() || !skillFileStat?.isFile()) {
|
|
1523
|
-
missingIds.push(skill.id);
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
return missingIds;
|
|
1527
|
-
}
|
|
1528
|
-
function resolveManagedSkillsRoot(orgId) {
|
|
1529
|
-
return resolveOrganizationSkillsDir(orgId);
|
|
1530
|
-
}
|
|
1531
|
-
function resolveWorkspaceEditPath(orgId, sourcePath) {
|
|
1532
|
-
if (!sourcePath)
|
|
1533
|
-
return null;
|
|
1534
|
-
const workspaceRoot = path.resolve(resolveOrganizationWorkspaceRoot(orgId));
|
|
1535
|
-
const skillDir = path.resolve(sourcePath);
|
|
1536
|
-
const entryFilePath = path.resolve(skillDir, "SKILL.md");
|
|
1537
|
-
const relativePath = path.relative(workspaceRoot, entryFilePath);
|
|
1538
|
-
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
|
1539
|
-
return null;
|
|
1540
|
-
}
|
|
1541
|
-
return normalizePortablePath(relativePath);
|
|
1542
|
-
}
|
|
1543
|
-
function resolveLocalSkillFilePath(skill, relativePath) {
|
|
1544
|
-
const normalized = normalizePortablePath(relativePath);
|
|
1545
|
-
const skillDir = normalizeSkillDirectory(skill);
|
|
1546
|
-
if (skillDir) {
|
|
1547
|
-
return path.resolve(skillDir, normalized);
|
|
1548
|
-
}
|
|
1549
|
-
if (!skill.sourceLocator)
|
|
1550
|
-
return null;
|
|
1551
|
-
const fallbackRoot = path.resolve(skill.sourceLocator);
|
|
1552
|
-
const directPath = path.resolve(fallbackRoot, normalized);
|
|
1553
|
-
return directPath;
|
|
1554
|
-
}
|
|
1555
|
-
function inferLanguageFromPath(filePath) {
|
|
1556
|
-
const fileName = path.posix.basename(filePath).toLowerCase();
|
|
1557
|
-
if (fileName === "skill.md" || fileName.endsWith(".md"))
|
|
1558
|
-
return "markdown";
|
|
1559
|
-
if (fileName.endsWith(".ts"))
|
|
1560
|
-
return "typescript";
|
|
1561
|
-
if (fileName.endsWith(".tsx"))
|
|
1562
|
-
return "tsx";
|
|
1563
|
-
if (fileName.endsWith(".js"))
|
|
1564
|
-
return "javascript";
|
|
1565
|
-
if (fileName.endsWith(".jsx"))
|
|
1566
|
-
return "jsx";
|
|
1567
|
-
if (fileName.endsWith(".json"))
|
|
1568
|
-
return "json";
|
|
1569
|
-
if (fileName.endsWith(".yml") || fileName.endsWith(".yaml"))
|
|
1570
|
-
return "yaml";
|
|
1571
|
-
if (fileName.endsWith(".sh"))
|
|
1572
|
-
return "bash";
|
|
1573
|
-
if (fileName.endsWith(".py"))
|
|
1574
|
-
return "python";
|
|
1575
|
-
if (fileName.endsWith(".html"))
|
|
1576
|
-
return "html";
|
|
1577
|
-
if (fileName.endsWith(".css"))
|
|
1578
|
-
return "css";
|
|
1579
|
-
return null;
|
|
1580
|
-
}
|
|
1581
|
-
function isMarkdownPath(filePath) {
|
|
1582
|
-
const fileName = path.posix.basename(filePath).toLowerCase();
|
|
1583
|
-
return fileName === "skill.md" || fileName.endsWith(".md");
|
|
1584
|
-
}
|
|
1585
|
-
function deriveSkillSourceInfo(skill) {
|
|
1586
|
-
const metadata = getSkillMeta(skill);
|
|
1587
|
-
const localSkillDir = normalizeSkillDirectory(skill);
|
|
1588
|
-
if (isBundledRudderSourceKind(asString(metadata.sourceKind))) {
|
|
1589
|
-
return {
|
|
1590
|
-
editable: false,
|
|
1591
|
-
editableReason: "Bundled Rudder skills are read-only.",
|
|
1592
|
-
sourceLabel: "Bundled by Rudder",
|
|
1593
|
-
sourceBadge: "rudder",
|
|
1594
|
-
sourcePath: null,
|
|
1595
|
-
};
|
|
1596
|
-
}
|
|
1597
|
-
if (asString(metadata.sourceKind) === "community_preset") {
|
|
1598
|
-
return {
|
|
1599
|
-
editable: false,
|
|
1600
|
-
editableReason: "Community preset skills are read-only.",
|
|
1601
|
-
sourceLabel: "Community preset",
|
|
1602
|
-
sourceBadge: "community",
|
|
1603
|
-
sourcePath: null,
|
|
1604
|
-
};
|
|
1605
|
-
}
|
|
1606
|
-
if (skill.sourceType === "skills_sh") {
|
|
1607
|
-
const owner = asString(metadata.owner) ?? null;
|
|
1608
|
-
const repo = asString(metadata.repo) ?? null;
|
|
1609
|
-
return {
|
|
1610
|
-
editable: false,
|
|
1611
|
-
editableReason: "Skills.sh-managed skills are read-only.",
|
|
1612
|
-
sourceLabel: skill.sourceLocator ?? (owner && repo ? `${owner}/${repo}` : null),
|
|
1613
|
-
sourceBadge: "skills_sh",
|
|
1614
|
-
sourcePath: null,
|
|
1615
|
-
};
|
|
1616
|
-
}
|
|
1617
|
-
if (skill.sourceType === "github") {
|
|
1618
|
-
const owner = asString(metadata.owner) ?? null;
|
|
1619
|
-
const repo = asString(metadata.repo) ?? null;
|
|
1620
|
-
return {
|
|
1621
|
-
editable: false,
|
|
1622
|
-
editableReason: "Remote GitHub skills are read-only. Fork or import locally to edit them.",
|
|
1623
|
-
sourceLabel: owner && repo ? `${owner}/${repo}` : skill.sourceLocator,
|
|
1624
|
-
sourceBadge: "github",
|
|
1625
|
-
sourcePath: null,
|
|
1626
|
-
};
|
|
1627
|
-
}
|
|
1628
|
-
if (skill.sourceType === "url") {
|
|
1629
|
-
return {
|
|
1630
|
-
editable: false,
|
|
1631
|
-
editableReason: "URL-based skills are read-only. Save them locally to edit them.",
|
|
1632
|
-
sourceLabel: skill.sourceLocator,
|
|
1633
|
-
sourceBadge: "url",
|
|
1634
|
-
sourcePath: null,
|
|
1635
|
-
};
|
|
1636
|
-
}
|
|
1637
|
-
if (skill.sourceType === "local_path") {
|
|
1638
|
-
const managedRoot = resolveManagedSkillsRoot(skill.orgId);
|
|
1639
|
-
const projectName = asString(metadata.projectName);
|
|
1640
|
-
const workspaceName = asString(metadata.workspaceName);
|
|
1641
|
-
const isProjectScan = metadata.sourceKind === "project_scan";
|
|
1642
|
-
if (localSkillDir && localSkillDir.startsWith(managedRoot)) {
|
|
1643
|
-
return {
|
|
1644
|
-
editable: true,
|
|
1645
|
-
editableReason: null,
|
|
1646
|
-
sourceLabel: "Rudder workspace",
|
|
1647
|
-
sourceBadge: "rudder",
|
|
1648
|
-
sourcePath: managedRoot,
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
return {
|
|
1652
|
-
editable: true,
|
|
1653
|
-
editableReason: null,
|
|
1654
|
-
sourceLabel: isProjectScan
|
|
1655
|
-
? [projectName, workspaceName].filter((value) => Boolean(value)).join(" / ")
|
|
1656
|
-
|| skill.sourceLocator
|
|
1657
|
-
: skill.sourceLocator,
|
|
1658
|
-
sourceBadge: "local",
|
|
1659
|
-
sourcePath: null,
|
|
1660
|
-
};
|
|
1661
|
-
}
|
|
1662
|
-
return {
|
|
1663
|
-
editable: false,
|
|
1664
|
-
editableReason: "This skill source is read-only.",
|
|
1665
|
-
sourceLabel: skill.sourceLocator,
|
|
1666
|
-
sourceBadge: "catalog",
|
|
1667
|
-
sourcePath: null,
|
|
1668
|
-
};
|
|
1669
|
-
}
|
|
1670
|
-
function enrichSkill(skill, attachedAgentCount, usedByAgents = []) {
|
|
1671
|
-
const source = deriveSkillSourceInfo(skill);
|
|
1672
|
-
return {
|
|
1673
|
-
...skill,
|
|
1674
|
-
attachedAgentCount,
|
|
1675
|
-
usedByAgents,
|
|
1676
|
-
...source,
|
|
1677
|
-
workspaceEditPath: resolveWorkspaceEditPath(skill.orgId, normalizeSkillDirectory(skill)),
|
|
1678
|
-
};
|
|
1679
|
-
}
|
|
1680
|
-
function toCompanySkillListItem(skill, attachedAgentCount) {
|
|
1681
|
-
const source = deriveSkillSourceInfo(skill);
|
|
1682
|
-
return {
|
|
1683
|
-
id: skill.id,
|
|
1684
|
-
orgId: skill.orgId,
|
|
1685
|
-
key: skill.key,
|
|
1686
|
-
slug: skill.slug,
|
|
1687
|
-
name: skill.name,
|
|
1688
|
-
description: skill.description,
|
|
1689
|
-
sourceType: skill.sourceType,
|
|
1690
|
-
sourceLocator: skill.sourceLocator,
|
|
1691
|
-
sourceRef: skill.sourceRef,
|
|
1692
|
-
trustLevel: skill.trustLevel,
|
|
1693
|
-
compatibility: skill.compatibility,
|
|
1694
|
-
fileInventory: skill.fileInventory,
|
|
1695
|
-
createdAt: skill.createdAt,
|
|
1696
|
-
updatedAt: skill.updatedAt,
|
|
1697
|
-
attachedAgentCount,
|
|
1698
|
-
editable: source.editable,
|
|
1699
|
-
editableReason: source.editableReason,
|
|
1700
|
-
sourceLabel: source.sourceLabel,
|
|
1701
|
-
sourceBadge: source.sourceBadge,
|
|
1702
|
-
sourcePath: source.sourcePath,
|
|
1703
|
-
workspaceEditPath: resolveWorkspaceEditPath(skill.orgId, normalizeSkillDirectory(skill)),
|
|
1704
|
-
};
|
|
1705
|
-
}
|
|
1706
|
-
function compareOrganizationSkillListItems(left, right) {
|
|
1707
|
-
const leftBundledSlug = getBundledRudderSkillSlug(left.key);
|
|
1708
|
-
const rightBundledSlug = getBundledRudderSkillSlug(right.key);
|
|
1709
|
-
if (leftBundledSlug && rightBundledSlug) {
|
|
1710
|
-
const leftIndex = RUDDER_BUNDLED_SKILL_SLUGS.findIndex((slug) => slug === leftBundledSlug);
|
|
1711
|
-
const rightIndex = RUDDER_BUNDLED_SKILL_SLUGS.findIndex((slug) => slug === rightBundledSlug);
|
|
1712
|
-
if (leftIndex !== rightIndex)
|
|
1713
|
-
return leftIndex - rightIndex;
|
|
1714
|
-
}
|
|
1715
|
-
else if (leftBundledSlug) {
|
|
1716
|
-
return -1;
|
|
1717
|
-
}
|
|
1718
|
-
else if (rightBundledSlug) {
|
|
1719
|
-
return 1;
|
|
1720
|
-
}
|
|
1721
|
-
const byName = left.name.localeCompare(right.name, undefined, { sensitivity: "base" });
|
|
1722
|
-
if (byName !== 0)
|
|
1723
|
-
return byName;
|
|
1724
|
-
return left.key.localeCompare(right.key, undefined, { sensitivity: "base" });
|
|
1725
|
-
}
|
|
12
|
+
import { ADAPTER_SKILL_HOME_DEFINITIONS, CANONICAL_BUNDLED_SKILL_KEYS, COMMUNITY_PRESET_SKILLS, COMMUNITY_PRESET_SKILL_SLUGS, applyDesiredSelectionsToCatalog, arraysEqual, asString, buildAdapterSelectionKey, buildAgentPrivateSkillEntry, buildAgentSelectionKey, buildBundledSelectionKey, buildDraftSkillMarkdown, buildGlobalSelectionKey, buildOrganizationSelectionKey, buildSkillRuntimeName, compareOrganizationSkillListItems, deriveCanonicalSkillKey, deriveSkillSourceInfo, enrichSkill, findMissingLocalSkillIds, getRequiredBundledSkillKeys, getSkillMeta, inferLanguageFromPath, isBundledRudderSkillKey, isBundledRudderSourceKind, isMarkdownPath, isPlainRecord, listStaleBundledSkillIds, listStaleCommunityPresetSkillIds, normalizeGitHubSkillDirectory, normalizePackageFileMap, normalizePortablePath, normalizeSelectionRef, normalizeSkillDescription, normalizeSkillDirectory, normalizeSkillSlug, parseSelectionKey, readDiscoveredSkillEntries, resolveConfiguredHomeDir, resolveLocalSkillFilePath, resolveManagedSkillsRoot, resolveRequestedSkillKeysOrThrow, resolveWorkspaceEditPath, serializeFileInventory, skillInventoryRefreshPromises, sortUniqueSelectionRefs, statPath, toCompanySkill, toCompanySkillListItem, uniqueImportedSkillKey, uniqueSkillSlug, } from "./organization-skills.catalog.js";
|
|
13
|
+
import { fetchText, parseFrontmatterMarkdown, parseSkillImportSourceInput, readCommunityPresetFallbackImport, readInlineSkillImports, readLocalSkillImportFromDirectory, readLocalSkillImports, readUrlSkillImports, resolveBundledSkillsRoot, resolveCommunityPresetSkillsRoot, resolveGitHubCommitSha, resolveRawGitHubUrl, } from "./organization-skills.sources.js";
|
|
14
|
+
import { createOrganizationSkillScanHandlers } from "./organization-skills.scans.js";
|
|
1726
15
|
export function organizationSkillService(db) {
|
|
1727
16
|
const agents = agentService(db);
|
|
1728
17
|
const enabledSkills = agentEnabledSkillsService(db);
|
|
1729
18
|
const projects = projectService(db);
|
|
19
|
+
const { scanProjectWorkspaces, scanLocalSkillRoots } = createOrganizationSkillScanHandlers({
|
|
20
|
+
ensureSkillInventoryCurrent,
|
|
21
|
+
listFull,
|
|
22
|
+
projects,
|
|
23
|
+
upsertImportedSkills,
|
|
24
|
+
});
|
|
1730
25
|
async function getAgentWorkspaceRow(orgId, agentId) {
|
|
1731
26
|
const row = await db
|
|
1732
27
|
.select({
|
|
@@ -2564,294 +859,6 @@ export function organizationSkillService(db) {
|
|
|
2564
859
|
const imported = await upsertImportedSkills(orgId, [matching]);
|
|
2565
860
|
return imported[0] ?? null;
|
|
2566
861
|
}
|
|
2567
|
-
async function scanProjectWorkspaces(orgId, input = {}) {
|
|
2568
|
-
await ensureSkillInventoryCurrent(orgId);
|
|
2569
|
-
const projectRows = input.projectIds?.length
|
|
2570
|
-
? await projects.listByIds(orgId, input.projectIds)
|
|
2571
|
-
: await projects.list(orgId);
|
|
2572
|
-
const workspaceFilter = new Set(input.workspaceIds ?? []);
|
|
2573
|
-
const skipped = [];
|
|
2574
|
-
const conflicts = [];
|
|
2575
|
-
const warnings = [];
|
|
2576
|
-
const imported = [];
|
|
2577
|
-
const updated = [];
|
|
2578
|
-
const availableSkills = await listFull(orgId);
|
|
2579
|
-
const acceptedSkills = [...availableSkills];
|
|
2580
|
-
const acceptedByKey = new Map(acceptedSkills.map((skill) => [skill.key, skill]));
|
|
2581
|
-
const scanTargets = [];
|
|
2582
|
-
const scannedProjectIds = new Set();
|
|
2583
|
-
let discovered = 0;
|
|
2584
|
-
const trackWarning = (message) => {
|
|
2585
|
-
warnings.push(message);
|
|
2586
|
-
return message;
|
|
2587
|
-
};
|
|
2588
|
-
const upsertAcceptedSkill = (skill) => {
|
|
2589
|
-
const nextIndex = acceptedSkills.findIndex((entry) => entry.id === skill.id || entry.key === skill.key);
|
|
2590
|
-
if (nextIndex >= 0)
|
|
2591
|
-
acceptedSkills[nextIndex] = skill;
|
|
2592
|
-
else
|
|
2593
|
-
acceptedSkills.push(skill);
|
|
2594
|
-
acceptedByKey.set(skill.key, skill);
|
|
2595
|
-
};
|
|
2596
|
-
for (const project of projectRows) {
|
|
2597
|
-
for (const workspace of project.workspaces) {
|
|
2598
|
-
if (workspaceFilter.size > 0 && !workspaceFilter.has(workspace.id))
|
|
2599
|
-
continue;
|
|
2600
|
-
const workspaceCwd = asString(workspace.cwd);
|
|
2601
|
-
if (!workspaceCwd) {
|
|
2602
|
-
skipped.push({
|
|
2603
|
-
projectId: project.id,
|
|
2604
|
-
projectName: project.name,
|
|
2605
|
-
workspaceId: workspace.id,
|
|
2606
|
-
workspaceName: workspace.name,
|
|
2607
|
-
path: null,
|
|
2608
|
-
reason: trackWarning(`Skipped ${project.name} / ${workspace.name}: no local workspace path is configured.`),
|
|
2609
|
-
});
|
|
2610
|
-
continue;
|
|
2611
|
-
}
|
|
2612
|
-
const workspaceStat = await statPath(workspaceCwd);
|
|
2613
|
-
if (!workspaceStat?.isDirectory()) {
|
|
2614
|
-
skipped.push({
|
|
2615
|
-
projectId: project.id,
|
|
2616
|
-
projectName: project.name,
|
|
2617
|
-
workspaceId: workspace.id,
|
|
2618
|
-
workspaceName: workspace.name,
|
|
2619
|
-
path: workspaceCwd,
|
|
2620
|
-
reason: trackWarning(`Skipped ${project.name} / ${workspace.name}: local workspace path is not available at ${workspaceCwd}.`),
|
|
2621
|
-
});
|
|
2622
|
-
continue;
|
|
2623
|
-
}
|
|
2624
|
-
scanTargets.push({
|
|
2625
|
-
projectId: project.id,
|
|
2626
|
-
projectName: project.name,
|
|
2627
|
-
workspaceId: workspace.id,
|
|
2628
|
-
workspaceName: workspace.name,
|
|
2629
|
-
workspaceCwd,
|
|
2630
|
-
});
|
|
2631
|
-
}
|
|
2632
|
-
}
|
|
2633
|
-
for (const target of scanTargets) {
|
|
2634
|
-
scannedProjectIds.add(target.projectId);
|
|
2635
|
-
const directories = await discoverProjectWorkspaceSkillDirectories(target);
|
|
2636
|
-
for (const directory of directories) {
|
|
2637
|
-
discovered += 1;
|
|
2638
|
-
let nextSkill;
|
|
2639
|
-
try {
|
|
2640
|
-
nextSkill = await readLocalSkillImportFromDirectory(orgId, directory.skillDir, {
|
|
2641
|
-
inventoryMode: directory.inventoryMode,
|
|
2642
|
-
metadata: {
|
|
2643
|
-
sourceKind: "project_scan",
|
|
2644
|
-
projectId: target.projectId,
|
|
2645
|
-
projectName: target.projectName,
|
|
2646
|
-
workspaceId: target.workspaceId,
|
|
2647
|
-
workspaceName: target.workspaceName,
|
|
2648
|
-
workspaceCwd: target.workspaceCwd,
|
|
2649
|
-
},
|
|
2650
|
-
});
|
|
2651
|
-
}
|
|
2652
|
-
catch (error) {
|
|
2653
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2654
|
-
skipped.push({
|
|
2655
|
-
projectId: target.projectId,
|
|
2656
|
-
projectName: target.projectName,
|
|
2657
|
-
workspaceId: target.workspaceId,
|
|
2658
|
-
workspaceName: target.workspaceName,
|
|
2659
|
-
path: directory.skillDir,
|
|
2660
|
-
reason: trackWarning(`Skipped ${directory.skillDir}: ${message}`),
|
|
2661
|
-
});
|
|
2662
|
-
continue;
|
|
2663
|
-
}
|
|
2664
|
-
const normalizedSourceDir = normalizeSourceLocatorDirectory(nextSkill.sourceLocator);
|
|
2665
|
-
const existingByKey = acceptedByKey.get(nextSkill.key) ?? null;
|
|
2666
|
-
if (existingByKey) {
|
|
2667
|
-
const existingSourceDir = normalizeSkillDirectory(existingByKey);
|
|
2668
|
-
if (existingByKey.sourceType !== "local_path"
|
|
2669
|
-
|| !existingSourceDir
|
|
2670
|
-
|| !normalizedSourceDir
|
|
2671
|
-
|| existingSourceDir !== normalizedSourceDir) {
|
|
2672
|
-
conflicts.push({
|
|
2673
|
-
slug: nextSkill.slug,
|
|
2674
|
-
key: nextSkill.key,
|
|
2675
|
-
projectId: target.projectId,
|
|
2676
|
-
projectName: target.projectName,
|
|
2677
|
-
workspaceId: target.workspaceId,
|
|
2678
|
-
workspaceName: target.workspaceName,
|
|
2679
|
-
path: directory.skillDir,
|
|
2680
|
-
existingSkillId: existingByKey.id,
|
|
2681
|
-
existingSkillKey: existingByKey.key,
|
|
2682
|
-
existingSourceLocator: existingByKey.sourceLocator,
|
|
2683
|
-
reason: `Skill key ${nextSkill.key} already points at ${existingByKey.sourceLocator ?? "another source"}.`,
|
|
2684
|
-
});
|
|
2685
|
-
continue;
|
|
2686
|
-
}
|
|
2687
|
-
const persisted = (await upsertImportedSkills(orgId, [nextSkill]))[0];
|
|
2688
|
-
if (!persisted)
|
|
2689
|
-
continue;
|
|
2690
|
-
updated.push(persisted);
|
|
2691
|
-
upsertAcceptedSkill(persisted);
|
|
2692
|
-
continue;
|
|
2693
|
-
}
|
|
2694
|
-
const slugConflict = acceptedSkills.find((skill) => {
|
|
2695
|
-
if (skill.slug !== nextSkill.slug)
|
|
2696
|
-
return false;
|
|
2697
|
-
return normalizeSkillDirectory(skill) !== normalizedSourceDir;
|
|
2698
|
-
});
|
|
2699
|
-
if (slugConflict) {
|
|
2700
|
-
conflicts.push({
|
|
2701
|
-
slug: nextSkill.slug,
|
|
2702
|
-
key: nextSkill.key,
|
|
2703
|
-
projectId: target.projectId,
|
|
2704
|
-
projectName: target.projectName,
|
|
2705
|
-
workspaceId: target.workspaceId,
|
|
2706
|
-
workspaceName: target.workspaceName,
|
|
2707
|
-
path: directory.skillDir,
|
|
2708
|
-
existingSkillId: slugConflict.id,
|
|
2709
|
-
existingSkillKey: slugConflict.key,
|
|
2710
|
-
existingSourceLocator: slugConflict.sourceLocator,
|
|
2711
|
-
reason: `Slug ${nextSkill.slug} is already in use by ${slugConflict.sourceLocator ?? slugConflict.key}.`,
|
|
2712
|
-
});
|
|
2713
|
-
continue;
|
|
2714
|
-
}
|
|
2715
|
-
const persisted = (await upsertImportedSkills(orgId, [nextSkill]))[0];
|
|
2716
|
-
if (!persisted)
|
|
2717
|
-
continue;
|
|
2718
|
-
imported.push(persisted);
|
|
2719
|
-
upsertAcceptedSkill(persisted);
|
|
2720
|
-
}
|
|
2721
|
-
}
|
|
2722
|
-
return {
|
|
2723
|
-
scannedProjects: scannedProjectIds.size,
|
|
2724
|
-
scannedWorkspaces: scanTargets.length,
|
|
2725
|
-
discovered,
|
|
2726
|
-
imported,
|
|
2727
|
-
updated,
|
|
2728
|
-
skipped,
|
|
2729
|
-
conflicts,
|
|
2730
|
-
warnings,
|
|
2731
|
-
};
|
|
2732
|
-
}
|
|
2733
|
-
async function scanLocalSkillRoots(orgId, input = {}) {
|
|
2734
|
-
await ensureSkillInventoryCurrent(orgId);
|
|
2735
|
-
const requestedRoots = input.roots?.length
|
|
2736
|
-
? input.roots
|
|
2737
|
-
: [path.join(os.homedir(), ".agents")];
|
|
2738
|
-
const roots = Array.from(new Set(requestedRoots
|
|
2739
|
-
.map((root) => root.trim())
|
|
2740
|
-
.filter(Boolean)
|
|
2741
|
-
.map((root) => path.resolve(root))));
|
|
2742
|
-
const skipped = [];
|
|
2743
|
-
const conflicts = [];
|
|
2744
|
-
const warnings = [];
|
|
2745
|
-
const imported = [];
|
|
2746
|
-
const updated = [];
|
|
2747
|
-
const availableSkills = await listFull(orgId);
|
|
2748
|
-
const acceptedSkills = [...availableSkills];
|
|
2749
|
-
const acceptedByKey = new Map(acceptedSkills.map((skill) => [skill.key, skill]));
|
|
2750
|
-
let discovered = 0;
|
|
2751
|
-
const trackWarning = (message) => {
|
|
2752
|
-
warnings.push(message);
|
|
2753
|
-
return message;
|
|
2754
|
-
};
|
|
2755
|
-
const upsertAcceptedSkill = (skill) => {
|
|
2756
|
-
const nextIndex = acceptedSkills.findIndex((entry) => entry.id === skill.id || entry.key === skill.key);
|
|
2757
|
-
if (nextIndex >= 0)
|
|
2758
|
-
acceptedSkills[nextIndex] = skill;
|
|
2759
|
-
else
|
|
2760
|
-
acceptedSkills.push(skill);
|
|
2761
|
-
acceptedByKey.set(skill.key, skill);
|
|
2762
|
-
};
|
|
2763
|
-
for (const root of roots) {
|
|
2764
|
-
const rootStat = await statPath(root);
|
|
2765
|
-
if (!rootStat?.isDirectory()) {
|
|
2766
|
-
skipped.push({
|
|
2767
|
-
root,
|
|
2768
|
-
path: null,
|
|
2769
|
-
reason: trackWarning(`Skipped ${root}: local skill root is not available.`),
|
|
2770
|
-
});
|
|
2771
|
-
continue;
|
|
2772
|
-
}
|
|
2773
|
-
let discoveredSkills;
|
|
2774
|
-
try {
|
|
2775
|
-
discoveredSkills = await readLocalSkillImports(orgId, root);
|
|
2776
|
-
}
|
|
2777
|
-
catch (error) {
|
|
2778
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2779
|
-
skipped.push({
|
|
2780
|
-
root,
|
|
2781
|
-
path: root,
|
|
2782
|
-
reason: trackWarning(`Skipped ${root}: ${message}`),
|
|
2783
|
-
});
|
|
2784
|
-
continue;
|
|
2785
|
-
}
|
|
2786
|
-
discovered += discoveredSkills.length;
|
|
2787
|
-
for (const nextSkill of discoveredSkills) {
|
|
2788
|
-
nextSkill.metadata = {
|
|
2789
|
-
...(nextSkill.metadata ?? {}),
|
|
2790
|
-
sourceKind: "local_scan",
|
|
2791
|
-
sourceRoot: root,
|
|
2792
|
-
};
|
|
2793
|
-
const normalizedSourceDir = normalizeSourceLocatorDirectory(nextSkill.sourceLocator);
|
|
2794
|
-
const existingByKey = acceptedByKey.get(nextSkill.key) ?? null;
|
|
2795
|
-
if (existingByKey) {
|
|
2796
|
-
const existingSourceDir = normalizeSkillDirectory(existingByKey);
|
|
2797
|
-
if (existingByKey.sourceType !== "local_path"
|
|
2798
|
-
|| !existingSourceDir
|
|
2799
|
-
|| !normalizedSourceDir
|
|
2800
|
-
|| existingSourceDir !== normalizedSourceDir) {
|
|
2801
|
-
conflicts.push({
|
|
2802
|
-
root,
|
|
2803
|
-
slug: nextSkill.slug,
|
|
2804
|
-
key: nextSkill.key,
|
|
2805
|
-
path: nextSkill.sourceLocator ?? root,
|
|
2806
|
-
existingSkillId: existingByKey.id,
|
|
2807
|
-
existingSkillKey: existingByKey.key,
|
|
2808
|
-
existingSourceLocator: existingByKey.sourceLocator,
|
|
2809
|
-
reason: `Skill key ${nextSkill.key} already points at ${existingByKey.sourceLocator ?? "another source"}.`,
|
|
2810
|
-
});
|
|
2811
|
-
continue;
|
|
2812
|
-
}
|
|
2813
|
-
const persisted = (await upsertImportedSkills(orgId, [nextSkill]))[0];
|
|
2814
|
-
if (!persisted)
|
|
2815
|
-
continue;
|
|
2816
|
-
updated.push(persisted);
|
|
2817
|
-
upsertAcceptedSkill(persisted);
|
|
2818
|
-
continue;
|
|
2819
|
-
}
|
|
2820
|
-
const slugConflict = acceptedSkills.find((skill) => {
|
|
2821
|
-
if (skill.slug !== nextSkill.slug)
|
|
2822
|
-
return false;
|
|
2823
|
-
return normalizeSkillDirectory(skill) !== normalizedSourceDir;
|
|
2824
|
-
});
|
|
2825
|
-
if (slugConflict) {
|
|
2826
|
-
conflicts.push({
|
|
2827
|
-
root,
|
|
2828
|
-
slug: nextSkill.slug,
|
|
2829
|
-
key: nextSkill.key,
|
|
2830
|
-
path: nextSkill.sourceLocator ?? root,
|
|
2831
|
-
existingSkillId: slugConflict.id,
|
|
2832
|
-
existingSkillKey: slugConflict.key,
|
|
2833
|
-
existingSourceLocator: slugConflict.sourceLocator,
|
|
2834
|
-
reason: `Slug ${nextSkill.slug} is already in use by ${slugConflict.sourceLocator ?? slugConflict.key}.`,
|
|
2835
|
-
});
|
|
2836
|
-
continue;
|
|
2837
|
-
}
|
|
2838
|
-
const persisted = (await upsertImportedSkills(orgId, [nextSkill]))[0];
|
|
2839
|
-
if (!persisted)
|
|
2840
|
-
continue;
|
|
2841
|
-
imported.push(persisted);
|
|
2842
|
-
upsertAcceptedSkill(persisted);
|
|
2843
|
-
}
|
|
2844
|
-
}
|
|
2845
|
-
return {
|
|
2846
|
-
scannedRoots: roots.length,
|
|
2847
|
-
discovered,
|
|
2848
|
-
imported,
|
|
2849
|
-
updated,
|
|
2850
|
-
skipped,
|
|
2851
|
-
conflicts,
|
|
2852
|
-
warnings,
|
|
2853
|
-
};
|
|
2854
|
-
}
|
|
2855
862
|
async function materializeCatalogSkillFiles(orgId, skill, normalizedFiles) {
|
|
2856
863
|
const packageDir = skill.packageDir ? normalizePortablePath(skill.packageDir) : null;
|
|
2857
864
|
if (!packageDir)
|
|
@@ -3092,7 +1099,6 @@ export function organizationSkillService(db) {
|
|
|
3092
1099
|
? `Skill ${parsed.requestedSkillSlug} was not found in the provided source.`
|
|
3093
1100
|
: "No skills were found in the provided source.");
|
|
3094
1101
|
}
|
|
3095
|
-
// Override sourceType/sourceLocator for skills imported via skills.sh
|
|
3096
1102
|
if (parsed.originalSkillsShUrl) {
|
|
3097
1103
|
for (const skill of filteredSkills) {
|
|
3098
1104
|
skill.sourceType = "skills_sh";
|
|
@@ -3115,13 +1121,10 @@ export function organizationSkillService(db) {
|
|
|
3115
1121
|
if (!row)
|
|
3116
1122
|
return null;
|
|
3117
1123
|
const skill = toCompanySkill(row);
|
|
3118
|
-
// Remove from any agent enabled skills that reference this skill
|
|
3119
1124
|
await enabledSkills.removeSkillKeys(orgId, [skill.key]);
|
|
3120
|
-
// Delete DB row
|
|
3121
1125
|
await db
|
|
3122
1126
|
.delete(organizationSkills)
|
|
3123
1127
|
.where(eq(organizationSkills.id, skillId));
|
|
3124
|
-
// Clean up materialized runtime files
|
|
3125
1128
|
await fs.rm(resolveRuntimeSkillMaterializedPath(orgId, skill), { recursive: true, force: true });
|
|
3126
1129
|
return skill;
|
|
3127
1130
|
}
|