@xopcai/xopc 0.0.127 → 0.0.129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CgGgO4OV.js → agent-avatar-dicebear-DHzfg5D-.js} +1 -1
  4. package/dist/gateway/static/root/assets/agents-CMxKKFQD.js +2 -0
  5. package/dist/gateway/static/root/assets/app-management-settings-panel-ptk9c5BN.js +1 -0
  6. package/dist/gateway/static/root/assets/{appearance-settings-BnHQW7Hv.js → appearance-settings-bYeEj3IW.js} +1 -1
  7. package/dist/gateway/static/root/assets/apps-page-DdZvDf5B.js +1 -0
  8. package/dist/gateway/static/root/assets/{archive-plugin-5NXT7Zqv.js → archive-plugin-Dy6lsTHw.js} +1 -1
  9. package/dist/gateway/static/root/assets/{attachment-load-DghwhYe0.js → attachment-load-qOtBYR6Y.js} +1 -1
  10. package/dist/gateway/static/root/assets/{automation-suggestion-card-DYmESgwl.js → automation-suggestion-card-BFH-RfaV.js} +1 -1
  11. package/dist/gateway/static/root/assets/automations-page-B8JTjkCr.js +4 -0
  12. package/dist/gateway/static/root/assets/{binary-plugins-DSbPKdWX.js → binary-plugins-DJvi_thc.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-CoZ-_np-.js → block-editor-DlMnizlt.js} +4 -4
  14. package/dist/gateway/static/root/assets/browser-settings-page-slHLiZgi.js +8 -0
  15. package/dist/gateway/static/root/assets/capability-presets-api-D4q7vHVJ.js +1 -0
  16. package/dist/gateway/static/root/assets/capability-presets-settings-panel-Dng8Grpv.js +1 -0
  17. package/dist/gateway/static/root/assets/channel-recipient-api-BrW5Kcgc.js +1 -0
  18. package/dist/gateway/static/root/assets/channels-settings-KIsISXw0.js +1 -0
  19. package/dist/gateway/static/root/assets/{code-editor-gbRgS14t.js → code-editor--tMO38Tr.js} +1 -1
  20. package/dist/gateway/static/root/assets/connectors-page-C5fx4Qs9.js +2 -0
  21. package/dist/gateway/static/root/assets/desktop-pet-Jmdn6q47.js +1 -0
  22. package/dist/gateway/static/root/assets/{desktop-pet-settings-npJ0Oh5p.js → desktop-pet-settings-B-u63N4e.js} +1 -1
  23. package/dist/gateway/static/root/assets/{directory-picker-path-field-BR2ItBuf.js → directory-picker-path-field-8p1ehIHn.js} +1 -1
  24. package/dist/gateway/static/root/assets/dreaming-settings-CCkKP-I4.js +3 -0
  25. package/dist/gateway/static/root/assets/{extension-debug-page-Tl0f1rNv.js → extension-debug-page-DJ1ABK95.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-Ck7QuwE3.js → extension-page-CP9P2lGM.js} +1 -1
  27. package/dist/gateway/static/root/assets/extension-settings-page-DnhU-tx0.js +1 -0
  28. package/dist/gateway/static/root/assets/fetch-BMjlW0g1.js +1 -0
  29. package/dist/gateway/static/root/assets/gateway-config-api-CjxDn46s.js +1 -0
  30. package/dist/gateway/static/root/assets/gateway-settings-TING7224.js +1 -0
  31. package/dist/gateway/static/root/assets/{gateway-startup-retry-Yh8h3hkj.js → gateway-startup-retry-CY5W2Pp4.js} +1 -1
  32. package/dist/gateway/static/root/assets/goal-create-dialog-2lPbGfp3.js +1 -0
  33. package/dist/gateway/static/root/assets/goal-detail-page-DHWVRafk.js +4 -0
  34. package/dist/gateway/static/root/assets/goals-config-api-BqhuxG8P.js +1 -0
  35. package/dist/gateway/static/root/assets/goals-page-CMk-9ZYN.js +2 -0
  36. package/dist/gateway/static/root/assets/goals-settings-BHzOAU_l.js +1 -0
  37. package/dist/gateway/static/root/assets/heartbeat-settings-BQIm0P3V.js +1 -0
  38. package/dist/gateway/static/root/assets/{html-workspace-editor-DV90ddkE.js → html-workspace-editor-COqmtESs.js} +1 -1
  39. package/dist/gateway/static/root/assets/index-C4k6QSXw.js +257 -0
  40. package/dist/gateway/static/root/assets/index-EKmpPvZj.css +1 -0
  41. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CfQrt_Rb.js → keyboard-shortcuts-settings-BrENZzM7.js} +1 -1
  42. package/dist/gateway/static/root/assets/logs-page-BiL302Ve.js +2 -0
  43. package/dist/gateway/static/root/assets/{markdown-editor-BFOjBBla.js → markdown-editor-jD1LPiP-.js} +2 -2
  44. package/dist/gateway/static/root/assets/{markdown-split-BwiMdCf_.js → markdown-split-BYF4ReGM.js} +1 -1
  45. package/dist/gateway/static/root/assets/{media-plugins-DhoizyZv.js → media-plugins-CpQDz-QJ.js} +1 -1
  46. package/dist/gateway/static/root/assets/messages-YzjnURPJ.js +3 -0
  47. package/dist/gateway/static/root/assets/models-hub-panel-CFPpl3Cq.js +2 -0
  48. package/dist/gateway/static/root/assets/notes-page-BurgcdVZ.js +1 -0
  49. package/dist/gateway/static/root/assets/notes-workbench-BAELRM_O.js +2 -0
  50. package/dist/gateway/static/root/assets/{page-tabs-DDC2t4Z8.js → page-tabs-RnYYOIWO.js} +1 -1
  51. package/dist/gateway/static/root/assets/{preview-open-alternatives-BTmk-XDM.js → preview-open-alternatives-CTRt06Ri.js} +1 -1
  52. package/dist/gateway/static/root/assets/project-detail-page-iXX1Ceo2.js +1 -0
  53. package/dist/gateway/static/root/assets/projects-page-GDVTFAY7.js +1 -0
  54. package/dist/gateway/static/root/assets/remote-access-hub-BUrbUnwv.js +1 -0
  55. package/dist/gateway/static/root/assets/{save-bar-controls-DqU-yKIh.js → save-bar-controls-c4pZeikq.js} +1 -1
  56. package/dist/gateway/static/root/assets/{schedule-field-CUttkoSB.js → schedule-field-Cb6R7iuP.js} +1 -1
  57. package/dist/gateway/static/root/assets/{schema-form-CVSQA9nI.js → schema-form-BqET_hEP.js} +1 -1
  58. package/dist/gateway/static/root/assets/sessions-page-CiOreA2k.js +1 -0
  59. package/dist/gateway/static/root/assets/settings-advanced-gate-C-qvptlu.js +1 -0
  60. package/dist/gateway/static/root/assets/{settings-collapsible-section-CK7ogS-P.js → settings-collapsible-section-DuOvqNsI.js} +1 -1
  61. package/dist/gateway/static/root/assets/{settings-form-section-Bugp38Fa.js → settings-form-section-DptSsbWd.js} +1 -1
  62. package/dist/gateway/static/root/assets/settings-loading-skeleton-BlVfBCb9.js +1 -0
  63. package/dist/gateway/static/root/assets/{settings-page-layout-DBRWwdhh.js → settings-page-layout-DVt69dJJ.js} +1 -1
  64. package/dist/gateway/static/root/assets/settings-page-xQx2Pkg5.js +1 -0
  65. package/dist/gateway/static/root/assets/setup-status-panel-DWB8VdLK.js +1 -0
  66. package/dist/gateway/static/root/assets/share-preview-page-8m_ACH0z.js +2 -0
  67. package/dist/gateway/static/root/assets/shares-settings-CgxfhNxI.js +1 -0
  68. package/dist/gateway/static/root/assets/skill-reload-api-BBFUOxmf.js +1 -0
  69. package/dist/gateway/static/root/assets/skills-page-bo695odu.js +2 -0
  70. package/dist/gateway/static/root/assets/{system-settings-panel-DJ6YuLqR.js → system-settings-panel-CW22OkZ4.js} +1 -1
  71. package/dist/gateway/static/root/assets/{typed-models-lib-B0T7N8Km.js → typed-models-lib-BqpGAKxV.js} +1 -1
  72. package/dist/gateway/static/root/assets/url-DFGNSBNT.js +1 -0
  73. package/dist/gateway/static/root/assets/use-channel-catalog-CPdJ0o0i.js +1 -0
  74. package/dist/gateway/static/root/assets/user-profile-settings-panel-CERVLuaW.js +1 -0
  75. package/dist/gateway/static/root/assets/{utils-CehXcWAY.js → utils-BnmysurA.js} +1 -1
  76. package/dist/gateway/static/root/assets/vendor-codemirror-C1WKjCk5.js +45 -0
  77. package/dist/gateway/static/root/assets/voice-api-key-field-Jr3jvPcB.js +1 -0
  78. package/dist/gateway/static/root/assets/work-item-detail-page-B-8rex4u.js +1 -0
  79. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-c4jeaohQ.js → workflow-run-setup-panel-Ci9GSPnS.js} +2 -2
  80. package/dist/gateway/static/root/assets/{workflow-start-dialog-ENRHjoqa.js → workflow-start-dialog-BhDj8pqH.js} +1 -1
  81. package/dist/gateway/static/root/assets/workflows-page-Brb3UGvs.js +18 -0
  82. package/dist/gateway/static/root/index.html +7 -6
  83. package/dist/package.js +1 -1
  84. package/dist/src/agent/agent-manager.d.ts +3 -0
  85. package/dist/src/agent/agent-manager.js +14 -6
  86. package/dist/src/agent/agent-manager.js.map +1 -1
  87. package/dist/src/agent/capabilities/definitions.d.ts +14 -0
  88. package/dist/src/agent/capabilities/definitions.js +19 -0
  89. package/dist/src/agent/capabilities/definitions.js.map +1 -1
  90. package/dist/src/agent/goals/post-turn.js +23 -2
  91. package/dist/src/agent/goals/post-turn.js.map +1 -1
  92. package/dist/src/agent/messaging/command-handler.d.ts +4 -0
  93. package/dist/src/agent/messaging/command-handler.js +6 -0
  94. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  95. package/dist/src/agent/service.d.ts +2 -0
  96. package/dist/src/agent/service.js +23 -0
  97. package/dist/src/agent/service.js.map +1 -1
  98. package/dist/src/agent/skills/hub-lock.d.ts +5 -5
  99. package/dist/src/agent/skills/hub-lock.js +12 -12
  100. package/dist/src/agent/skills/hub-lock.js.map +1 -1
  101. package/dist/src/agent/skills/hub-pull.d.ts +15 -0
  102. package/dist/src/agent/skills/hub-pull.js +299 -24
  103. package/dist/src/agent/skills/hub-pull.js.map +1 -1
  104. package/dist/src/agent/skills/index.js +4 -40
  105. package/dist/src/agent/skills/index.js.map +1 -1
  106. package/dist/src/agent/skills/install-target.d.ts +3 -0
  107. package/dist/src/agent/skills/install-target.js +11 -0
  108. package/dist/src/agent/skills/install-target.js.map +1 -0
  109. package/dist/src/agent/skills/managed-store.d.ts +5 -3
  110. package/dist/src/agent/skills/managed-store.js +10 -9
  111. package/dist/src/agent/skills/managed-store.js.map +1 -1
  112. package/dist/src/agent/skills/marketplace/adapters/store/adapter.js +5 -3
  113. package/dist/src/agent/skills/marketplace/adapters/store/adapter.js.map +1 -1
  114. package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.d.ts +21 -1
  115. package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js +62 -6
  116. package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js.map +1 -1
  117. package/dist/src/agent/skills/marketplace/resolve-adapter.js +1 -1
  118. package/dist/src/agent/skills/marketplace/resolve-adapter.js.map +1 -1
  119. package/dist/src/agent/skills/skill-manage-ops.js +2 -2
  120. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  121. package/dist/src/agent/skills/skill-manager.js +1 -6
  122. package/dist/src/agent/skills/skill-manager.js.map +1 -1
  123. package/dist/src/agent/skills/workspace-skills-dir.d.ts +1 -0
  124. package/dist/src/agent/skills/workspace-skills-dir.js +5 -1
  125. package/dist/src/agent/skills/workspace-skills-dir.js.map +1 -1
  126. package/dist/src/agent/tools/automation-tool.d.ts +6 -6
  127. package/dist/src/agent/tools/browser/tool/schemas.d.ts +20 -20
  128. package/dist/src/agent/tools/composio-tool.d.ts +3 -3
  129. package/dist/src/agent/tools/desktop-pet-tool.d.ts +7 -7
  130. package/dist/src/agent/tools/factory.d.ts +3 -0
  131. package/dist/src/agent/tools/factory.js +6 -1
  132. package/dist/src/agent/tools/factory.js.map +1 -1
  133. package/dist/src/agent/tools/goal-tool.d.ts +16 -16
  134. package/dist/src/agent/tools/index.d.ts +1 -0
  135. package/dist/src/agent/tools/index.js +2 -1
  136. package/dist/src/agent/tools/skill-install-tool.d.ts +28 -0
  137. package/dist/src/agent/tools/skill-install-tool.js +81 -0
  138. package/dist/src/agent/tools/skill-install-tool.js.map +1 -0
  139. package/dist/src/agent/tools/skill-manage-tool.js +3 -6
  140. package/dist/src/agent/tools/skill-manage-tool.js.map +1 -1
  141. package/dist/src/agent/tools/tool-manual-tool.d.ts +2 -2
  142. package/dist/src/agent/tools/xopc-use-tool.d.ts +5 -5
  143. package/dist/src/automations/domain/types.d.ts +1 -0
  144. package/dist/src/automations/draft/automation-draft-service.js +1 -1
  145. package/dist/src/automations/service/automation-service.js +9 -0
  146. package/dist/src/automations/service/automation-service.js.map +1 -1
  147. package/dist/src/browser/schemas.d.ts +52 -52
  148. package/dist/src/capabilities/store-connector.d.ts +17 -0
  149. package/dist/src/capabilities/store-connector.js +191 -0
  150. package/dist/src/capabilities/store-connector.js.map +1 -0
  151. package/dist/src/chat-commands/builtins/skill.d.ts +1 -0
  152. package/dist/src/chat-commands/builtins/skill.js +132 -0
  153. package/dist/src/chat-commands/builtins/skill.js.map +1 -0
  154. package/dist/src/chat-commands/context.d.ts +3 -0
  155. package/dist/src/chat-commands/context.js +2 -0
  156. package/dist/src/chat-commands/context.js.map +1 -1
  157. package/dist/src/chat-commands/index.d.ts +1 -0
  158. package/dist/src/chat-commands/index.js +3 -1
  159. package/dist/src/chat-commands/index.js.map +1 -1
  160. package/dist/src/chat-commands/types.d.ts +17 -0
  161. package/dist/src/cli/command-loaders.js +1 -0
  162. package/dist/src/cli/command-loaders.js.map +1 -1
  163. package/dist/src/cli/command-manifest.js +4 -0
  164. package/dist/src/cli/command-manifest.js.map +1 -1
  165. package/dist/src/cli/commands/agent.js +2 -2
  166. package/dist/src/cli/commands/connectors.d.ts +4 -0
  167. package/dist/src/cli/commands/connectors.js +96 -0
  168. package/dist/src/cli/commands/connectors.js.map +1 -0
  169. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  170. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  171. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  172. package/dist/src/cli/commands/models.js +1 -1
  173. package/dist/src/cli/commands/onboard.js +1 -1
  174. package/dist/src/cli/commands/profile.js +1 -1
  175. package/dist/src/cli/commands/skills.js +1 -1
  176. package/dist/src/cli/commands/tunnel.js +1 -1
  177. package/dist/src/cli/commands/update.js +1 -1
  178. package/dist/src/cli/extension-cli-register.js +1 -1
  179. package/dist/src/config/agent-profile.js +1 -1
  180. package/dist/src/config/index.js +1 -1
  181. package/dist/src/config/schema.js +2 -2
  182. package/dist/src/config/schema.js.map +1 -1
  183. package/dist/src/connectors/health.js +1 -1
  184. package/dist/src/connectors/install.js +3 -1
  185. package/dist/src/connectors/install.js.map +1 -1
  186. package/dist/src/connectors/materialize.js +2 -0
  187. package/dist/src/connectors/materialize.js.map +1 -1
  188. package/dist/src/connectors/types.d.ts +19 -1
  189. package/dist/src/extensions/install.js +1 -1
  190. package/dist/src/extensions/types/manifest.d.ts +1 -1
  191. package/dist/src/extensions/update.js +5 -3
  192. package/dist/src/extensions/update.js.map +1 -1
  193. package/dist/src/gateway/agents-admin.js +1 -1
  194. package/dist/src/gateway/chat-stream/mapper.js +5 -1
  195. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  196. package/dist/src/gateway/chat-stream/protocol.d.ts +2 -0
  197. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  198. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  199. package/dist/src/gateway/hono/middleware/auth.d.ts +2 -2
  200. package/dist/src/gateway/hono/middleware/logger.d.ts +1 -1
  201. package/dist/src/gateway/hono/middleware/route-errors.d.ts +1 -1
  202. package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +2 -2
  203. package/dist/src/gateway/hono/routes/capabilities.d.ts +3 -0
  204. package/dist/src/gateway/hono/routes/capabilities.js +79 -0
  205. package/dist/src/gateway/hono/routes/capabilities.js.map +1 -0
  206. package/dist/src/gateway/hono/routes/commands-skills.js +81 -3
  207. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  208. package/dist/src/gateway/hono/routes/connectors.js +2 -2
  209. package/dist/src/gateway/hono/routes/goals.js +212 -3
  210. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  211. package/dist/src/gateway/hono/routes/index.js +2 -0
  212. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  213. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  214. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  215. package/dist/src/gateway/hono/routes/mcp.js +1 -1
  216. package/dist/src/gateway/hono/routes/mobile.d.ts +3 -0
  217. package/dist/src/gateway/hono/routes/mobile.js +113 -0
  218. package/dist/src/gateway/hono/routes/mobile.js.map +1 -0
  219. package/dist/src/gateway/hono/routes/projects.js +17 -0
  220. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  221. package/dist/src/gateway/hono/routes/workspace.js +4 -1
  222. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  223. package/dist/src/gateway/hono/sse.d.ts +3 -3
  224. package/dist/src/gateway/run-loop.js +1 -1
  225. package/dist/src/gateway/service/marketplace-service.d.ts +23 -1
  226. package/dist/src/gateway/service/marketplace-service.js +57 -6
  227. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  228. package/dist/src/gateway/service.d.ts +2 -0
  229. package/dist/src/gateway/service.js +12 -2
  230. package/dist/src/gateway/service.js.map +1 -1
  231. package/dist/src/goals/goal-contract-draft.d.ts +15 -0
  232. package/dist/src/goals/goal-contract-draft.js +114 -0
  233. package/dist/src/goals/goal-contract-draft.js.map +1 -0
  234. package/dist/src/goals/goal-evidence-review.d.ts +15 -0
  235. package/dist/src/goals/goal-evidence-review.js +91 -0
  236. package/dist/src/goals/goal-evidence-review.js.map +1 -0
  237. package/dist/src/goals/goal-runner.js +3 -1
  238. package/dist/src/goals/goal-runner.js.map +1 -1
  239. package/dist/src/goals/goal-service.d.ts +17 -1
  240. package/dist/src/goals/goal-service.js +44 -3
  241. package/dist/src/goals/goal-service.js.map +1 -1
  242. package/dist/src/goals/goal-store.d.ts +18 -1
  243. package/dist/src/goals/goal-store.js +190 -1
  244. package/dist/src/goals/goal-store.js.map +1 -1
  245. package/dist/src/goals/index.d.ts +4 -0
  246. package/dist/src/goals/index.js +3 -1
  247. package/dist/src/goals/types.d.ts +42 -0
  248. package/dist/src/mcp/channel-bridge.js +1 -1
  249. package/dist/src/mobile/notification-service.d.ts +8 -0
  250. package/dist/src/mobile/notification-service.js +139 -0
  251. package/dist/src/mobile/notification-service.js.map +1 -0
  252. package/dist/src/mobile/notification-store.d.ts +28 -0
  253. package/dist/src/mobile/notification-store.js +134 -0
  254. package/dist/src/mobile/notification-store.js.map +1 -0
  255. package/dist/src/mobile/notification-types.d.ts +37 -0
  256. package/dist/src/mobile/notification-types.js +11 -0
  257. package/dist/src/mobile/notification-types.js.map +1 -0
  258. package/dist/src/storage/sqlite/migrations/026_goal_contracts.sql +10 -0
  259. package/dist/src/storage/sqlite/migrations/027_mobile_notifications.sql +43 -0
  260. package/dist/src/storage/sqlite/migrations/028_goal_evidence_requirements.sql +47 -0
  261. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  262. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  263. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  264. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  265. package/dist/src/storage/sqlite/schema.js +1 -1
  266. package/dist/src/storage/sqlite/schema.sql +44 -0
  267. package/dist/src/tui/tui-startup-resources.js +1 -1
  268. package/package.json +1 -1
  269. package/skills/THIRD_PARTY_NOTICES.md +16 -0
  270. package/skills/creative/algorithmic-art/SKILL.md +37 -0
  271. package/skills/creative/algorithmic-art/templates/generator-template.js +27 -0
  272. package/skills/creative/algorithmic-art/templates/viewer.html +27 -0
  273. package/skills/documents/doc-coauthoring/SKILL.md +40 -0
  274. package/skills/documents/doc-coauthoring/references/reader-review.md +9 -0
  275. package/skills/documents/docx/SKILL.md +40 -0
  276. package/skills/documents/docx/references/editing-checklist.md +8 -0
  277. package/skills/documents/docx/scripts/inspect_ooxml.py +36 -0
  278. package/skills/documents/docx/scripts/render_docx.py +33 -0
  279. package/skills/documents/pdf/SKILL.md +47 -0
  280. package/skills/documents/pdf/references/operations.md +16 -0
  281. package/skills/documents/pdf/scripts/inspect_pdf.py +41 -0
  282. package/skills/documents/pdf/scripts/render_pdf.py +32 -0
  283. package/skills/documents/pptx/SKILL.md +41 -0
  284. package/skills/documents/pptx/references/qa-checklist.md +6 -0
  285. package/skills/documents/pptx/scripts/inspect_ooxml.py +34 -0
  286. package/skills/documents/pptx/scripts/render_pptx.py +33 -0
  287. package/skills/engineering/define-goal/SKILL.md +41 -0
  288. package/skills/engineering/define-goal/references/objective-rubric.md +12 -0
  289. package/dist/gateway/static/root/assets/agents-k59l7NMb.js +0 -2
  290. package/dist/gateway/static/root/assets/app-management-settings-panel-DeQsTsyy.js +0 -1
  291. package/dist/gateway/static/root/assets/apps-page-BG9b4abG.js +0 -1
  292. package/dist/gateway/static/root/assets/automations-page-De8mVutL.js +0 -4
  293. package/dist/gateway/static/root/assets/browser-settings-page-C_ZRa4LF.js +0 -8
  294. package/dist/gateway/static/root/assets/capability-presets-api-hny224j-.js +0 -1
  295. package/dist/gateway/static/root/assets/capability-presets-settings-panel-Drc0qLsw.js +0 -1
  296. package/dist/gateway/static/root/assets/channel-recipient-api-DAlOHxKJ.js +0 -1
  297. package/dist/gateway/static/root/assets/channels-settings-CA20zBV9.js +0 -1
  298. package/dist/gateway/static/root/assets/connectors-page-qMyMB5te.js +0 -2
  299. package/dist/gateway/static/root/assets/desktop-pet-XG6Vb_fZ.js +0 -1
  300. package/dist/gateway/static/root/assets/dreaming-settings-CkUZ997f.js +0 -3
  301. package/dist/gateway/static/root/assets/extension-settings-page-3RMhQtZ3.js +0 -1
  302. package/dist/gateway/static/root/assets/fetch-pg2ErdAk.js +0 -1
  303. package/dist/gateway/static/root/assets/gateway-config-api-DbG97ia2.js +0 -1
  304. package/dist/gateway/static/root/assets/gateway-settings-B8cyqKk-.js +0 -1
  305. package/dist/gateway/static/root/assets/goal-create-dialog-DmeW7Zz3.js +0 -1
  306. package/dist/gateway/static/root/assets/goal-detail-page-DrTyGbVD.js +0 -1
  307. package/dist/gateway/static/root/assets/goals-config-api-CxCWGIBB.js +0 -1
  308. package/dist/gateway/static/root/assets/goals-page-DV1bJhIh.js +0 -2
  309. package/dist/gateway/static/root/assets/goals-settings-9DrfOkP5.js +0 -1
  310. package/dist/gateway/static/root/assets/heartbeat-settings-KaDpZrv0.js +0 -1
  311. package/dist/gateway/static/root/assets/index-C2F0Clvm.js +0 -256
  312. package/dist/gateway/static/root/assets/index-COQSX5d_.css +0 -1
  313. package/dist/gateway/static/root/assets/logs-page-C9dpiNTF.js +0 -2
  314. package/dist/gateway/static/root/assets/messages-DmnbiNxU.js +0 -3
  315. package/dist/gateway/static/root/assets/models-hub-panel-zdCdmBmH.js +0 -2
  316. package/dist/gateway/static/root/assets/notes-page-MpGeFfPt.js +0 -1
  317. package/dist/gateway/static/root/assets/notes-workbench-DYCM3yd2.js +0 -2
  318. package/dist/gateway/static/root/assets/project-detail-page-Cu0ElRnS.js +0 -1
  319. package/dist/gateway/static/root/assets/projects-page-DakNaUjZ.js +0 -1
  320. package/dist/gateway/static/root/assets/remote-access-hub-DYKjt7L9.js +0 -1
  321. package/dist/gateway/static/root/assets/sessions-page-CcGHDsz1.js +0 -1
  322. package/dist/gateway/static/root/assets/settings-advanced-gate-CuFdeKNu.js +0 -1
  323. package/dist/gateway/static/root/assets/settings-page-0AJoUvxi.js +0 -1
  324. package/dist/gateway/static/root/assets/setup-status-panel-D6a_hEk7.js +0 -1
  325. package/dist/gateway/static/root/assets/share-preview-page-BtsuETKF.js +0 -2
  326. package/dist/gateway/static/root/assets/shares-settings-CcEFT7S9.js +0 -1
  327. package/dist/gateway/static/root/assets/skill-reload-api-BcN_RuTD.js +0 -1
  328. package/dist/gateway/static/root/assets/skills-page-BbRZZZA5.js +0 -2
  329. package/dist/gateway/static/root/assets/use-channel-catalog-C6vrxdzZ.js +0 -1
  330. package/dist/gateway/static/root/assets/user-profile-settings-panel-DZ0nKMgj.js +0 -1
  331. package/dist/gateway/static/root/assets/vendor-codemirror-BvNgyE1Q.js +0 -45
  332. package/dist/gateway/static/root/assets/voice-api-key-field-2QKflCx8.js +0 -1
  333. package/dist/gateway/static/root/assets/work-item-detail-page-DfJnuWq1.js +0 -1
  334. package/dist/gateway/static/root/assets/workflows-page-BJwwFTm_.js +0 -18
@@ -184,60 +184,24 @@ function loadSkills(options) {
184
184
  if (builtinDir) {
185
185
  const discovered = discoverSkills(builtinDir, "builtin");
186
186
  diagnostics.push(...discovered.diagnostics);
187
- for (const skill of discovered.skills) {
188
- const existing = skillMap.get(skill.name);
189
- if (existing) diagnostics.push({
190
- type: "collision",
191
- skillName: skill.name,
192
- message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
193
- path: skill.filePath
194
- });
195
- else skillMap.set(skill.name, skill);
196
- }
187
+ for (const skill of discovered.skills) if (!skillMap.has(skill.name)) skillMap.set(skill.name, skill);
197
188
  }
198
189
  const globalDirs = [options.globalDir, join(resolveStateDir(), "skills")].filter((d) => !!d && existsSync(d));
199
190
  for (const dir of globalDirs) {
200
191
  const discovered = discoverSkills(dir, "global");
201
192
  diagnostics.push(...discovered.diagnostics);
202
- for (const skill of discovered.skills) {
203
- const existing = skillMap.get(skill.name);
204
- if (existing) diagnostics.push({
205
- type: "collision",
206
- skillName: skill.name,
207
- message: `Skill "${skill.name}" collision: ${skill.source} overrides ${existing.source}`,
208
- path: skill.filePath
209
- });
210
- skillMap.set(skill.name, skill);
211
- }
193
+ for (const skill of discovered.skills) skillMap.set(skill.name, skill);
212
194
  }
213
195
  if (workspaceDir) {
214
196
  const discovered = discoverSkills(resolveWorkspaceSkillsDir(workspaceDir), "workspace");
215
197
  diagnostics.push(...discovered.diagnostics);
216
198
  const workspaceSkills = discovered.skills;
217
- for (const skill of workspaceSkills) {
218
- const existing = skillMap.get(skill.name);
219
- if (existing) diagnostics.push({
220
- type: "collision",
221
- skillName: skill.name,
222
- message: `Skill "${skill.name}" collision: ${skill.source} overrides ${existing.source}`,
223
- path: skill.filePath
224
- });
225
- skillMap.set(skill.name, skill);
226
- }
199
+ for (const skill of workspaceSkills) skillMap.set(skill.name, skill);
227
200
  }
228
201
  for (const extraDir of extraDirs) if (existsSync(extraDir)) {
229
202
  const discovered = discoverSkills(extraDir, "global");
230
203
  diagnostics.push(...discovered.diagnostics);
231
- for (const skill of discovered.skills) {
232
- const existing = skillMap.get(skill.name);
233
- if (existing) diagnostics.push({
234
- type: "collision",
235
- skillName: skill.name,
236
- message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
237
- path: skill.filePath
238
- });
239
- else skillMap.set(skill.name, skill);
240
- }
204
+ for (const skill of discovered.skills) if (!skillMap.has(skill.name)) skillMap.set(skill.name, skill);
241
205
  }
242
206
  const skillsConfig = createSkillConfigManager(resolveStateDir()).load();
243
207
  const merged = Array.from(skillMap.values());
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/agent/skills/index.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'fs';\nimport { basename, dirname, join, relative, sep } from 'path';\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { resolveStateDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { createSkillConfigManager, isSkillEnabled } from './config.js';\nimport { formatSkillsForPrompt } from './format-skills-prompt.js';\nimport { parseRequiredEnvVarNames } from './required-env-vars.js';\nimport { parseSkillMetadata } from './parse-skill-metadata.js';\nimport { parseSkillToolConditions } from './skill-tool-gating.js';\nimport { resolveWorkspaceSkillsDir } from './workspace-skills-dir.js';\nimport type { Skill, SkillDiagnostic, LoadSkillsResult, SkillConfig, SkillsConfig } from './types.js';\n\nconst log = createLogger('SkillLoader');\n\nconst IGNORE_FILES = ['.gitignore', '.ignore', '.fdignore'];\n\nfunction toPosixPath(p: string): string {\n return p.split(sep).join('/');\n}\n\nfunction prefixIgnorePattern(line: string, prefix: string): string | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n if (trimmed.startsWith('#') && !trimmed.startsWith('\\\\#')) return null;\n\n let pattern = line;\n let negated = false;\n\n if (pattern.startsWith('!')) {\n negated = true;\n pattern = pattern.slice(1);\n } else if (pattern.startsWith('\\\\!')) {\n pattern = pattern.slice(1);\n }\n\n if (pattern.startsWith('/')) {\n pattern = pattern.slice(1);\n }\n\n const prefixed = prefix ? `${prefix}${pattern}` : pattern;\n return negated ? `!${prefixed}` : prefixed;\n}\n\nfunction loadIgnoreRules(dir: string, rootDir: string): Set<string> {\n const ignoredPaths = new Set<string>();\n const relativeDir = relative(rootDir, dir);\n const prefix = relativeDir ? `${toPosixPath(relativeDir)}/` : '';\n\n for (const filename of IGNORE_FILES) {\n const ignorePath = join(dir, filename);\n if (!existsSync(ignorePath)) continue;\n\n try {\n const content = readFileSync(ignorePath, 'utf-8');\n for (const line of content.split(/\\r?\\n/)) {\n const pattern = prefixIgnorePattern(line, prefix);\n if (pattern) {\n const fullPattern = pattern.startsWith('!')\n ? `!${prefix}${pattern.slice(1)}`\n : `${prefix}${pattern}`;\n ignoredPaths.add(fullPattern);\n }\n }\n } catch {}\n }\n\n return ignoredPaths;\n}\n\nfunction shouldIgnore(path: string, ignoredPaths: Set<string>): boolean {\n for (const pattern of ignoredPaths) {\n if (pattern.startsWith('!')) {\n const positive = pattern.slice(1);\n if (path === positive || path.startsWith(`${positive}/`)) {\n return false;\n }\n } else {\n if (path === pattern || path.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction discoverSkills(\n dir: string,\n source: 'builtin' | 'workspace' | 'global',\n): { skills: Skill[]; diagnostics: SkillDiagnostic[] } {\n const skills: Skill[] = [];\n const diagnostics: SkillDiagnostic[] = [];\n if (!existsSync(dir)) return { skills, diagnostics };\n\n function scan(currentDir: string, currentIgnoredPaths: Set<string>) {\n try {\n const entries = readdirSync(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;\n\n const fullPath = join(currentDir, entry.name);\n const relPath = toPosixPath(relative(dir, fullPath));\n\n if (shouldIgnore(relPath, currentIgnoredPaths)) continue;\n\n if (entry.isDirectory()) {\n const skillMdPath = join(fullPath, 'SKILL.md');\n const skillRelPath = `${relPath}/`;\n\n const subIgnoredPaths = new Set(currentIgnoredPaths);\n const subIgnoreFile = join(fullPath, '.gitignore');\n if (existsSync(subIgnoreFile)) {\n const subRules = loadIgnoreRules(fullPath, dir);\n for (const rule of subRules) {\n subIgnoredPaths.add(`${skillRelPath}${rule}`);\n }\n }\n\n if (existsSync(skillMdPath) && !shouldIgnore(skillRelPath, currentIgnoredPaths)) {\n const { skill, diagnostic } = loadSkillFromFile(skillMdPath, source, dir);\n if (diagnostic) diagnostics.push(diagnostic);\n if (skill) skills.push(skill);\n }\n\n scan(fullPath, subIgnoredPaths);\n }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n diagnostics.push({\n type: 'error',\n message: `Failed to scan skills directory: ${message}`,\n path: currentDir,\n });\n }\n }\n\n scan(dir, loadIgnoreRules(dir, dir));\n return { skills, diagnostics };\n}\n\nfunction deriveDescriptionFromMarkdown(content: string): string | undefined {\n let fallbackHeading = '';\n\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line) continue;\n\n const heading = line.match(/^#{1,6}\\s+(.+)$/);\n if (heading) {\n fallbackHeading ||= heading[1]?.trim() ?? '';\n continue;\n }\n\n return line\n .replace(/\\[([^\\]]+)\\]\\([^)]*\\)/g, '$1')\n .replace(/[*_`~]/g, '')\n .trim() || undefined;\n }\n\n return fallbackHeading || undefined;\n}\n\nfunction loadSkillFromFile(\n filePath: string,\n source: 'builtin' | 'workspace' | 'global',\n rootDir?: string,\n): { skill: Skill | null; diagnostic?: SkillDiagnostic } {\n try {\n const rawContent = readFileSync(filePath, 'utf-8');\n const { frontmatter, content } = parseFrontmatter(rawContent);\n const skillDir = dirname(filePath);\n const parentDirName = basename(skillDir);\n\n const name = (frontmatter.name as string | undefined) || parentDirName;\n const description =\n (frontmatter.description as string | undefined)?.trim() || deriveDescriptionFromMarkdown(content);\n if (!description?.trim()) {\n return {\n skill: null,\n diagnostic: {\n type: 'error',\n skillName: name,\n message: `Skill \"${name}\" is missing a description`,\n path: filePath,\n },\n };\n }\n\n // Derive category from directory path: .xopc/skills/creative/algorithmic-art → 'creative'\n // Only assign a category when the skill is nested at least two levels below rootDir.\n let category: string | undefined;\n if (rootDir) {\n const relDir = toPosixPath(relative(rootDir, skillDir));\n const segments = relDir.split('/');\n if (segments.length > 1) {\n category = segments[0];\n }\n }\n\n const metadata = parseSkillMetadata(frontmatter);\n metadata.name ||= name;\n metadata.description ||= description.trim();\n const toolConditions = parseSkillToolConditions(frontmatter);\n const requiredEnvVarNames = parseRequiredEnvVarNames(frontmatter);\n\n return {\n skill: {\n name,\n description: description.trim(),\n category,\n filePath,\n baseDir: skillDir,\n source,\n disableModelInvocation: frontmatter['disable-model-invocation'] === true,\n metadata,\n toolConditions,\n requiredEnvVarNames: requiredEnvVarNames.length > 0 ? requiredEnvVarNames : undefined,\n content,\n },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n skill: null,\n diagnostic: {\n type: 'error',\n message: `Failed to load skill file: ${message}`,\n path: filePath,\n },\n };\n }\n}\n\nexport function loadSkills(options: {\n workspaceDir?: string;\n globalDir?: string;\n builtinDir?: string;\n extraDirs?: string[];\n}): LoadSkillsResult {\n const { workspaceDir, builtinDir, extraDirs = [] } = options;\n\n const skillMap = new Map<string, Skill>();\n const diagnostics: SkillDiagnostic[] = [];\n\n if (builtinDir) {\n const discovered = discoverSkills(builtinDir, 'builtin');\n diagnostics.push(...discovered.diagnostics);\n for (const skill of discovered.skills) {\n const existing = skillMap.get(skill.name);\n if (existing) {\n diagnostics.push({\n type: 'collision',\n skillName: skill.name,\n message: `Skill \"${skill.name}\" collision: ${existing.source} overrides ${skill.source}`,\n path: skill.filePath,\n });\n } else {\n skillMap.set(skill.name, skill);\n }\n }\n }\n\n const globalDirs = [\n options.globalDir,\n join(resolveStateDir(), 'skills'),\n ].filter((d): d is string => !!d && existsSync(d));\n\n for (const dir of globalDirs) {\n const discovered = discoverSkills(dir, 'global');\n diagnostics.push(...discovered.diagnostics);\n for (const skill of discovered.skills) {\n const existing = skillMap.get(skill.name);\n if (existing) {\n diagnostics.push({\n type: 'collision',\n skillName: skill.name,\n message: `Skill \"${skill.name}\" collision: ${skill.source} overrides ${existing.source}`,\n path: skill.filePath,\n });\n }\n // Global must win over bundled when names match (Workspace > Global > Bundled).\n skillMap.set(skill.name, skill);\n }\n }\n\n if (workspaceDir) {\n const discovered = discoverSkills(resolveWorkspaceSkillsDir(workspaceDir), 'workspace');\n diagnostics.push(...discovered.diagnostics);\n const workspaceSkills = discovered.skills;\n for (const skill of workspaceSkills) {\n const existing = skillMap.get(skill.name);\n if (existing) {\n diagnostics.push({\n type: 'collision',\n skillName: skill.name,\n message: `Skill \"${skill.name}\" collision: ${skill.source} overrides ${existing.source}`,\n path: skill.filePath,\n });\n }\n skillMap.set(skill.name, skill);\n }\n }\n\n // Scan extra directories\n for (const extraDir of extraDirs) {\n if (existsSync(extraDir)) {\n const discovered = discoverSkills(extraDir, 'global');\n diagnostics.push(...discovered.diagnostics);\n for (const skill of discovered.skills) {\n const existing = skillMap.get(skill.name);\n if (existing) {\n diagnostics.push({\n type: 'collision',\n skillName: skill.name,\n message: `Skill \"${skill.name}\" collision: ${existing.source} overrides ${skill.source}`,\n path: skill.filePath,\n });\n } else {\n skillMap.set(skill.name, skill);\n }\n }\n }\n }\n\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n const merged = Array.from(skillMap.values());\n\n return {\n skills: merged,\n prompt: formatSkillsForPrompt(merged, skillsConfig),\n diagnostics,\n };\n}\n\nexport interface SkillLoader {\n init: (workspace: string, builtin: string | null) => LoadSkillsResult;\n load: () => LoadSkillsResult;\n reload: () => LoadSkillsResult;\n /** Recompute `<available_skills>` from disk-loaded skills and current ~/.xopc/skills.json (no filesystem rescan). */\n refreshPromptFromConfig: () => void;\n getSkills: () => Skill[];\n getPrompt: () => string;\n getDiagnostics: () => SkillDiagnostic[];\n getLastLoadTime: () => number;\n getSkillByName: (name: string) => Skill | undefined;\n getEnabledSkills: (config?: Record<string, SkillConfig>) => Skill[];\n}\n\nexport function createSkillLoader(): SkillLoader {\n let cachedSkills: Skill[] = [];\n let cachedPrompt: string = '';\n let cachedDiagnostics: SkillDiagnostic[] = [];\n let lastLoadTime = 0;\n let workspaceDir: string | undefined;\n let builtinDir: string | undefined;\n let extraDirs: string[] = [];\n\n function updateCache(result: LoadSkillsResult): LoadSkillsResult {\n cachedSkills = result.skills;\n cachedPrompt = result.prompt;\n cachedDiagnostics = result.diagnostics;\n lastLoadTime = Date.now();\n return result;\n }\n\n return {\n init: (workspace: string, builtin: string | null) => {\n workspaceDir = workspace;\n builtinDir = builtin || undefined;\n return updateCache(loadSkills({ workspaceDir, builtinDir, extraDirs }));\n },\n \n load: () => {\n return updateCache(loadSkills({ workspaceDir, builtinDir, extraDirs }));\n },\n \n reload: () => {\n log.info('Reloading skills');\n return updateCache(loadSkills({ workspaceDir, builtinDir, extraDirs }));\n },\n\n refreshPromptFromConfig: () => {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n cachedPrompt = formatSkillsForPrompt(cachedSkills, skillsConfig);\n lastLoadTime = Date.now();\n },\n\n getSkills: () => cachedSkills,\n getPrompt: () => cachedPrompt,\n getDiagnostics: () => cachedDiagnostics,\n getLastLoadTime: () => lastLoadTime,\n \n getSkillByName: (name: string) => {\n return cachedSkills.find(s => s.name === name);\n },\n \n getEnabledSkills: (entries?: Record<string, SkillConfig>) => {\n const skillsConfig: SkillsConfig =\n entries !== undefined ? { entries } : createSkillConfigManager(resolveStateDir()).load();\n return cachedSkills.filter(\n (skill) => !skill.disableModelInvocation && isSkillEnabled(skill, skillsConfig),\n );\n },\n };\n}\n\n// `SkillManager` + sibling types removed from this barrel — `skill-manager.ts`\n// imports `createSkillLoader` from THIS file, so re-exporting it back here\n// formed a circular cycle. External callers should import directly from\n// `./skill-manager.js`.\n\n// Re-export types for convenience\nexport type { \n Skill, \n SkillMetadata, \n SkillConfig,\n SkillInstallSpec,\n SkillInstallResult,\n SkillInstallRequest,\n LoadSkillsResult,\n // Note: SkillDiagnostic is re-exported from skill-manager.ts above\n SkillSnapshot,\n} from './types.js';\n\n// Re-export installer\nexport { \n installSkill, \n findInstallSpec,\n hasBinary,\n getDefaultInstallerPreferences,\n type InstallerPreferences,\n type InstallContext,\n} from './installer.js';\n\n// Re-export scanner\nexport {\n scanSkillDirectory,\n formatScanSummary,\n collectSkillInstallWarnings,\n type ScanSummary,\n type SecurityFinding,\n type Severity,\n} from './scanner.js';\n\n// Re-export config manager\nexport {\n resolveSkillConfig,\n applySkillEnvOverrides,\n getSkillEnvironment,\n createSkillConfigManager,\n isSkillEnabled,\n validateSkillConfig,\n type SkillConfigFile,\n} from './config.js';\n\n// `watcher.ts` re-exports removed — it imports `SkillLoader` from THIS file, so\n// re-exporting back here formed a circular cycle. External callers should\n// import directly from `./watcher.js`.\n\n// Re-export test framework\nexport {\n SkillTestFramework,\n SkillTestRunner,\n formatTestResults,\n formatTestResultsJson,\n formatTestResultsTap,\n type TestResult,\n type TestStatus,\n type SkillTestReport,\n type TestOptions,\n type TestRunnerOptions,\n} from './test-framework.js';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/agent/skills/index.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'fs';\nimport { basename, dirname, join, relative, sep } from 'path';\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { resolveStateDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { createSkillConfigManager, isSkillEnabled } from './config.js';\nimport { formatSkillsForPrompt } from './format-skills-prompt.js';\nimport { parseRequiredEnvVarNames } from './required-env-vars.js';\nimport { parseSkillMetadata } from './parse-skill-metadata.js';\nimport { parseSkillToolConditions } from './skill-tool-gating.js';\nimport { resolveWorkspaceSkillsDir } from './workspace-skills-dir.js';\nimport type { Skill, SkillDiagnostic, LoadSkillsResult, SkillConfig, SkillsConfig } from './types.js';\n\nconst log = createLogger('SkillLoader');\n\nconst IGNORE_FILES = ['.gitignore', '.ignore', '.fdignore'];\n\nfunction toPosixPath(p: string): string {\n return p.split(sep).join('/');\n}\n\nfunction prefixIgnorePattern(line: string, prefix: string): string | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n if (trimmed.startsWith('#') && !trimmed.startsWith('\\\\#')) return null;\n\n let pattern = line;\n let negated = false;\n\n if (pattern.startsWith('!')) {\n negated = true;\n pattern = pattern.slice(1);\n } else if (pattern.startsWith('\\\\!')) {\n pattern = pattern.slice(1);\n }\n\n if (pattern.startsWith('/')) {\n pattern = pattern.slice(1);\n }\n\n const prefixed = prefix ? `${prefix}${pattern}` : pattern;\n return negated ? `!${prefixed}` : prefixed;\n}\n\nfunction loadIgnoreRules(dir: string, rootDir: string): Set<string> {\n const ignoredPaths = new Set<string>();\n const relativeDir = relative(rootDir, dir);\n const prefix = relativeDir ? `${toPosixPath(relativeDir)}/` : '';\n\n for (const filename of IGNORE_FILES) {\n const ignorePath = join(dir, filename);\n if (!existsSync(ignorePath)) continue;\n\n try {\n const content = readFileSync(ignorePath, 'utf-8');\n for (const line of content.split(/\\r?\\n/)) {\n const pattern = prefixIgnorePattern(line, prefix);\n if (pattern) {\n const fullPattern = pattern.startsWith('!')\n ? `!${prefix}${pattern.slice(1)}`\n : `${prefix}${pattern}`;\n ignoredPaths.add(fullPattern);\n }\n }\n } catch {}\n }\n\n return ignoredPaths;\n}\n\nfunction shouldIgnore(path: string, ignoredPaths: Set<string>): boolean {\n for (const pattern of ignoredPaths) {\n if (pattern.startsWith('!')) {\n const positive = pattern.slice(1);\n if (path === positive || path.startsWith(`${positive}/`)) {\n return false;\n }\n } else {\n if (path === pattern || path.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction discoverSkills(\n dir: string,\n source: 'builtin' | 'workspace' | 'global',\n): { skills: Skill[]; diagnostics: SkillDiagnostic[] } {\n const skills: Skill[] = [];\n const diagnostics: SkillDiagnostic[] = [];\n if (!existsSync(dir)) return { skills, diagnostics };\n\n function scan(currentDir: string, currentIgnoredPaths: Set<string>) {\n try {\n const entries = readdirSync(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;\n\n const fullPath = join(currentDir, entry.name);\n const relPath = toPosixPath(relative(dir, fullPath));\n\n if (shouldIgnore(relPath, currentIgnoredPaths)) continue;\n\n if (entry.isDirectory()) {\n const skillMdPath = join(fullPath, 'SKILL.md');\n const skillRelPath = `${relPath}/`;\n\n const subIgnoredPaths = new Set(currentIgnoredPaths);\n const subIgnoreFile = join(fullPath, '.gitignore');\n if (existsSync(subIgnoreFile)) {\n const subRules = loadIgnoreRules(fullPath, dir);\n for (const rule of subRules) {\n subIgnoredPaths.add(`${skillRelPath}${rule}`);\n }\n }\n\n if (existsSync(skillMdPath) && !shouldIgnore(skillRelPath, currentIgnoredPaths)) {\n const { skill, diagnostic } = loadSkillFromFile(skillMdPath, source, dir);\n if (diagnostic) diagnostics.push(diagnostic);\n if (skill) skills.push(skill);\n }\n\n scan(fullPath, subIgnoredPaths);\n }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n diagnostics.push({\n type: 'error',\n message: `Failed to scan skills directory: ${message}`,\n path: currentDir,\n });\n }\n }\n\n scan(dir, loadIgnoreRules(dir, dir));\n return { skills, diagnostics };\n}\n\nfunction deriveDescriptionFromMarkdown(content: string): string | undefined {\n let fallbackHeading = '';\n\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line) continue;\n\n const heading = line.match(/^#{1,6}\\s+(.+)$/);\n if (heading) {\n fallbackHeading ||= heading[1]?.trim() ?? '';\n continue;\n }\n\n return line\n .replace(/\\[([^\\]]+)\\]\\([^)]*\\)/g, '$1')\n .replace(/[*_`~]/g, '')\n .trim() || undefined;\n }\n\n return fallbackHeading || undefined;\n}\n\nfunction loadSkillFromFile(\n filePath: string,\n source: 'builtin' | 'workspace' | 'global',\n rootDir?: string,\n): { skill: Skill | null; diagnostic?: SkillDiagnostic } {\n try {\n const rawContent = readFileSync(filePath, 'utf-8');\n const { frontmatter, content } = parseFrontmatter(rawContent);\n const skillDir = dirname(filePath);\n const parentDirName = basename(skillDir);\n\n const name = (frontmatter.name as string | undefined) || parentDirName;\n const description =\n (frontmatter.description as string | undefined)?.trim() || deriveDescriptionFromMarkdown(content);\n if (!description?.trim()) {\n return {\n skill: null,\n diagnostic: {\n type: 'error',\n skillName: name,\n message: `Skill \"${name}\" is missing a description`,\n path: filePath,\n },\n };\n }\n\n // Derive category from directory path: .xopc/skills/creative/algorithmic-art → 'creative'\n // Only assign a category when the skill is nested at least two levels below rootDir.\n let category: string | undefined;\n if (rootDir) {\n const relDir = toPosixPath(relative(rootDir, skillDir));\n const segments = relDir.split('/');\n if (segments.length > 1) {\n category = segments[0];\n }\n }\n\n const metadata = parseSkillMetadata(frontmatter);\n metadata.name ||= name;\n metadata.description ||= description.trim();\n const toolConditions = parseSkillToolConditions(frontmatter);\n const requiredEnvVarNames = parseRequiredEnvVarNames(frontmatter);\n\n return {\n skill: {\n name,\n description: description.trim(),\n category,\n filePath,\n baseDir: skillDir,\n source,\n disableModelInvocation: frontmatter['disable-model-invocation'] === true,\n metadata,\n toolConditions,\n requiredEnvVarNames: requiredEnvVarNames.length > 0 ? requiredEnvVarNames : undefined,\n content,\n },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n skill: null,\n diagnostic: {\n type: 'error',\n message: `Failed to load skill file: ${message}`,\n path: filePath,\n },\n };\n }\n}\n\nexport function loadSkills(options: {\n workspaceDir?: string;\n globalDir?: string;\n builtinDir?: string;\n extraDirs?: string[];\n}): LoadSkillsResult {\n const { workspaceDir, builtinDir, extraDirs = [] } = options;\n\n const skillMap = new Map<string, Skill>();\n const diagnostics: SkillDiagnostic[] = [];\n\n if (builtinDir) {\n const discovered = discoverSkills(builtinDir, 'builtin');\n diagnostics.push(...discovered.diagnostics);\n for (const skill of discovered.skills) {\n if (!skillMap.has(skill.name)) {\n skillMap.set(skill.name, skill);\n }\n }\n }\n\n const globalDirs = [\n options.globalDir,\n join(resolveStateDir(), 'skills'),\n ].filter((d): d is string => !!d && existsSync(d));\n\n for (const dir of globalDirs) {\n const discovered = discoverSkills(dir, 'global');\n diagnostics.push(...discovered.diagnostics);\n for (const skill of discovered.skills) {\n // Global must win over bundled when names match (Workspace > Global > Bundled).\n skillMap.set(skill.name, skill);\n }\n }\n\n if (workspaceDir) {\n const discovered = discoverSkills(resolveWorkspaceSkillsDir(workspaceDir), 'workspace');\n diagnostics.push(...discovered.diagnostics);\n const workspaceSkills = discovered.skills;\n for (const skill of workspaceSkills) {\n skillMap.set(skill.name, skill);\n }\n }\n\n // Scan extra directories\n for (const extraDir of extraDirs) {\n if (existsSync(extraDir)) {\n const discovered = discoverSkills(extraDir, 'global');\n diagnostics.push(...discovered.diagnostics);\n for (const skill of discovered.skills) {\n if (!skillMap.has(skill.name)) {\n skillMap.set(skill.name, skill);\n }\n }\n }\n }\n\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n const merged = Array.from(skillMap.values());\n\n return {\n skills: merged,\n prompt: formatSkillsForPrompt(merged, skillsConfig),\n diagnostics,\n };\n}\n\nexport interface SkillLoader {\n init: (workspace: string, builtin: string | null) => LoadSkillsResult;\n load: () => LoadSkillsResult;\n reload: () => LoadSkillsResult;\n /** Recompute `<available_skills>` from disk-loaded skills and current ~/.xopc/skills.json (no filesystem rescan). */\n refreshPromptFromConfig: () => void;\n getSkills: () => Skill[];\n getPrompt: () => string;\n getDiagnostics: () => SkillDiagnostic[];\n getLastLoadTime: () => number;\n getSkillByName: (name: string) => Skill | undefined;\n getEnabledSkills: (config?: Record<string, SkillConfig>) => Skill[];\n}\n\nexport function createSkillLoader(): SkillLoader {\n let cachedSkills: Skill[] = [];\n let cachedPrompt: string = '';\n let cachedDiagnostics: SkillDiagnostic[] = [];\n let lastLoadTime = 0;\n let workspaceDir: string | undefined;\n let builtinDir: string | undefined;\n let extraDirs: string[] = [];\n\n function updateCache(result: LoadSkillsResult): LoadSkillsResult {\n cachedSkills = result.skills;\n cachedPrompt = result.prompt;\n cachedDiagnostics = result.diagnostics;\n lastLoadTime = Date.now();\n return result;\n }\n\n return {\n init: (workspace: string, builtin: string | null) => {\n workspaceDir = workspace;\n builtinDir = builtin || undefined;\n return updateCache(loadSkills({ workspaceDir, builtinDir, extraDirs }));\n },\n \n load: () => {\n return updateCache(loadSkills({ workspaceDir, builtinDir, extraDirs }));\n },\n \n reload: () => {\n log.info('Reloading skills');\n return updateCache(loadSkills({ workspaceDir, builtinDir, extraDirs }));\n },\n\n refreshPromptFromConfig: () => {\n const skillsConfig = createSkillConfigManager(resolveStateDir()).load();\n cachedPrompt = formatSkillsForPrompt(cachedSkills, skillsConfig);\n lastLoadTime = Date.now();\n },\n\n getSkills: () => cachedSkills,\n getPrompt: () => cachedPrompt,\n getDiagnostics: () => cachedDiagnostics,\n getLastLoadTime: () => lastLoadTime,\n \n getSkillByName: (name: string) => {\n return cachedSkills.find(s => s.name === name);\n },\n \n getEnabledSkills: (entries?: Record<string, SkillConfig>) => {\n const skillsConfig: SkillsConfig =\n entries !== undefined ? { entries } : createSkillConfigManager(resolveStateDir()).load();\n return cachedSkills.filter(\n (skill) => !skill.disableModelInvocation && isSkillEnabled(skill, skillsConfig),\n );\n },\n };\n}\n\n// `SkillManager` + sibling types removed from this barrel — `skill-manager.ts`\n// imports `createSkillLoader` from THIS file, so re-exporting it back here\n// formed a circular cycle. External callers should import directly from\n// `./skill-manager.js`.\n\n// Re-export types for convenience\nexport type { \n Skill, \n SkillMetadata, \n SkillConfig,\n SkillInstallSpec,\n SkillInstallResult,\n SkillInstallRequest,\n LoadSkillsResult,\n // Note: SkillDiagnostic is re-exported from skill-manager.ts above\n SkillSnapshot,\n} from './types.js';\n\n// Re-export installer\nexport { \n installSkill, \n findInstallSpec,\n hasBinary,\n getDefaultInstallerPreferences,\n type InstallerPreferences,\n type InstallContext,\n} from './installer.js';\n\n// Re-export scanner\nexport {\n scanSkillDirectory,\n formatScanSummary,\n collectSkillInstallWarnings,\n type ScanSummary,\n type SecurityFinding,\n type Severity,\n} from './scanner.js';\n\n// Re-export config manager\nexport {\n resolveSkillConfig,\n applySkillEnvOverrides,\n getSkillEnvironment,\n createSkillConfigManager,\n isSkillEnabled,\n validateSkillConfig,\n type SkillConfigFile,\n} from './config.js';\n\n// `watcher.ts` re-exports removed — it imports `SkillLoader` from THIS file, so\n// re-exporting back here formed a circular cycle. External callers should\n// import directly from `./watcher.js`.\n\n// Re-export test framework\nexport {\n SkillTestFramework,\n SkillTestRunner,\n formatTestResults,\n formatTestResultsJson,\n formatTestResultsTap,\n type TestResult,\n type TestStatus,\n type SkillTestReport,\n type TestOptions,\n type TestRunnerOptions,\n} from './test-framework.js';\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export type SkillInstallTarget = 'workspace' | 'global';
2
+ export declare function normalizeSkillInstallTarget(value: unknown): SkillInstallTarget;
3
+ export declare function isSkillInstallTarget(value: unknown): value is SkillInstallTarget;
@@ -0,0 +1,11 @@
1
+ //#region src/agent/skills/install-target.ts
2
+ function normalizeSkillInstallTarget(value) {
3
+ return value === "global" ? "global" : "workspace";
4
+ }
5
+ function isSkillInstallTarget(value) {
6
+ return value === "workspace" || value === "global";
7
+ }
8
+ //#endregion
9
+ export { isSkillInstallTarget, normalizeSkillInstallTarget };
10
+
11
+ //# sourceMappingURL=install-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-target.js","names":[],"sources":["../../../../src/agent/skills/install-target.ts"],"sourcesContent":["export type SkillInstallTarget = 'workspace' | 'global';\n\nexport function normalizeSkillInstallTarget(value: unknown): SkillInstallTarget {\n return value === 'global' ? 'global' : 'workspace';\n}\n\nexport function isSkillInstallTarget(value: unknown): value is SkillInstallTarget {\n return value === 'workspace' || value === 'global';\n}\n"],"mappings":";AAEA,SAAgB,4BAA4B,OAAoC;AAC9E,QAAO,UAAU,WAAW,WAAW;;AAGzC,SAAgB,qBAAqB,OAA6C;AAChF,QAAO,UAAU,eAAe,UAAU"}
@@ -19,13 +19,13 @@ export interface ManagedSkillListItem {
19
19
  hub?: SkillHubLockEntry;
20
20
  }
21
21
  export declare function listManagedSkillDirs(): ManagedSkillListItem[];
22
- export declare function deleteManagedSkill(skillId: string): void;
22
+ export declare function deleteManagedSkill(skillId: string, rootDir?: string): void;
23
23
  export declare function getManagedSkillDir(skillId: string): string;
24
- export declare function assertManagedSkillDestination(skillId: string): {
24
+ export declare function assertManagedSkillDestination(skillId: string, rootDir?: string): {
25
25
  root: string;
26
26
  destDir: string;
27
27
  };
28
- export declare function prepareManagedSkillTempDir(skillId: string): {
28
+ export declare function prepareManagedSkillTempDir(skillId: string, rootDir?: string): {
29
29
  root: string;
30
30
  destDir: string;
31
31
  tempDir: string;
@@ -34,6 +34,7 @@ export declare function promoteManagedSkillTempDir(params: {
34
34
  skillId: string;
35
35
  tempDir: string;
36
36
  overwrite?: boolean;
37
+ rootDir?: string;
37
38
  }): {
38
39
  skillId: string;
39
40
  path: string;
@@ -45,6 +46,7 @@ export declare function promoteManagedSkillTempDir(params: {
45
46
  export declare function installSkillFromZip(buffer: Buffer, options: {
46
47
  skillId?: string;
47
48
  overwrite?: boolean;
49
+ rootDir?: string;
48
50
  }): {
49
51
  skillId: string;
50
52
  path: string;
@@ -87,9 +87,9 @@ function listManagedSkillDirs() {
87
87
  out.sort((a, b) => a.id.localeCompare(b.id));
88
88
  return out;
89
89
  }
90
- function deleteManagedSkill(skillId) {
90
+ function deleteManagedSkill(skillId, rootDir = resolveSkillsDir()) {
91
91
  if (!isValidSkillId(skillId)) throw new Error("Invalid skill id");
92
- const root = resolveSkillsDir();
92
+ const root = resolve(rootDir);
93
93
  const dir = resolve(join(root, skillId));
94
94
  const rootResolved = resolve(root);
95
95
  if (!dir.startsWith(rootResolved + sep) && dir !== rootResolved) throw new Error("Invalid path");
@@ -112,9 +112,9 @@ function getManagedSkillDir(skillId) {
112
112
  if (!isValidSkillId(skillId)) throw new Error("Invalid skill id");
113
113
  return join(resolveSkillsDir(), skillId);
114
114
  }
115
- function assertManagedSkillDestination(skillId) {
115
+ function assertManagedSkillDestination(skillId, rootDir = resolveSkillsDir()) {
116
116
  if (!isValidSkillId(skillId)) throw new Error(`Invalid skill id "${skillId}" (letters, digits, ._-; max 63 chars after first)`);
117
- const root = resolveSkillsDir();
117
+ const root = resolve(rootDir);
118
118
  mkdirSync(root, { recursive: true });
119
119
  const destDir = join(root, skillId);
120
120
  const destResolved = resolve(destDir);
@@ -125,8 +125,8 @@ function assertManagedSkillDestination(skillId) {
125
125
  destDir
126
126
  };
127
127
  }
128
- function prepareManagedSkillTempDir(skillId) {
129
- const { root, destDir } = assertManagedSkillDestination(skillId);
128
+ function prepareManagedSkillTempDir(skillId, rootDir) {
129
+ const { root, destDir } = assertManagedSkillDestination(skillId, rootDir);
130
130
  const tempDir = createTransientManagedDir(root, TEMP_PREFIX, skillId);
131
131
  mkdirSync(tempDir, { recursive: true });
132
132
  return {
@@ -136,7 +136,7 @@ function prepareManagedSkillTempDir(skillId) {
136
136
  };
137
137
  }
138
138
  function promoteManagedSkillTempDir(params) {
139
- const { root, destDir } = assertManagedSkillDestination(params.skillId);
139
+ const { root, destDir } = assertManagedSkillDestination(params.skillId, params.rootDir);
140
140
  const tempResolved = resolve(params.tempDir);
141
141
  const rootResolved = resolve(root);
142
142
  if (!tempResolved.startsWith(rootResolved + sep)) throw new Error("Invalid temporary skill directory");
@@ -199,7 +199,7 @@ function installSkillFromZip(buffer, options) {
199
199
  }
200
200
  }
201
201
  if (!targetId || !isValidSkillId(targetId)) throw new Error("Could not determine skill id: use a folder named with the skill id, pass skillId, or set name: in SKILL.md (letters, digits, ._-)");
202
- const { destDir, tempDir } = prepareManagedSkillTempDir(targetId);
202
+ const { destDir, tempDir } = prepareManagedSkillTempDir(targetId, options.rootDir);
203
203
  const tempResolved = resolve(tempDir);
204
204
  try {
205
205
  if (existsSync(destDir) && !options.overwrite) throw new Error(`Skill "${targetId}" already exists. Pass overwrite to replace.`);
@@ -222,7 +222,8 @@ function installSkillFromZip(buffer, options) {
222
222
  return promoteManagedSkillTempDir({
223
223
  skillId: targetId,
224
224
  tempDir,
225
- overwrite: options.overwrite
225
+ overwrite: options.overwrite,
226
+ rootDir: options.rootDir
226
227
  });
227
228
  } catch (err) {
228
229
  rmSync(tempDir, {
@@ -1 +1 @@
1
- {"version":3,"file":"managed-store.js","names":[],"sources":["../../../../src/agent/skills/managed-store.ts"],"sourcesContent":["/**\n * Managed skills live under ~/.xopc/skills (see resolveSkillsDir).\n * Install/update/delete via zip or folder operations; used by gateway API.\n */\n\nimport AdmZip from 'adm-zip';\nimport {\n existsSync,\n mkdirSync,\n renameSync,\n readdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { dirname, join, resolve, sep } from 'node:path';\nimport { resolveSkillsDir } from '../../config/paths.js';\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { loadSkillsLock, type SkillHubLockEntry } from './hub-lock.js';\n\nexport const MAX_SKILL_ZIP_BYTES = 15 * 1024 * 1024;\n\nconst SKILL_ID_RE = /^[a-zA-Z0-9]([a-zA-Z0-9._-]{0,62})$/;\nconst TEMP_PREFIX = '.tmp-';\nconst TRASH_PREFIX = '.trash-';\n\nexport function isValidSkillId(id: string): boolean {\n return SKILL_ID_RE.test(id);\n}\n\nexport function isManagedSkillTransientDirName(name: string): boolean {\n return name.startsWith(TEMP_PREFIX) || name.startsWith(TRASH_PREFIX);\n}\n\n/** True when `baseDir` is the global managed skills root or a direct child folder. */\nexport function isUnderManagedSkillsDir(baseDir: string): boolean {\n const b = resolve(baseDir);\n const r = resolve(resolveSkillsDir());\n return b === r || b.startsWith(r + sep);\n}\n\nfunction isSafeZipPath(name: string): boolean {\n if (!name) return false;\n const normalized = name.replace(/\\\\/g, '/');\n if (normalized.includes('..')) return false;\n if (normalized.startsWith('/') || /^\\w:/.test(normalized)) return false;\n for (const p of normalized.split('/')) {\n if (p === '..') return false;\n }\n return true;\n}\n\n/** macOS/Windows noise; not part of the skill tree. */\nexport function isIgnorableZipEntry(name: string): boolean {\n const n = name.replace(/\\\\/g, '/');\n if (n.startsWith('__MACOSX/')) return true;\n const segments = n.split('/').filter(Boolean);\n for (const s of segments) {\n if (s === '.DS_Store' || s === 'Thumbs.db' || s === 'desktop.ini') return true;\n if (s.startsWith('._')) return true;\n }\n return false;\n}\n\nexport interface ManagedSkillListItem {\n id: string;\n name: string;\n description: string;\n path: string;\n /** Set when this folder was installed via `skills hub pull` / lock file. */\n hub?: SkillHubLockEntry;\n}\n\nfunction readSkillMdMeta(skillMdPath: string): { name: string; description: string } {\n try {\n const raw = readFileSync(skillMdPath, 'utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n return {\n name: (frontmatter.name as string) || '',\n description: (frontmatter.description as string)?.trim() || '',\n };\n } catch {\n return { name: '', description: '' };\n }\n}\n\nexport function listManagedSkillDirs(): ManagedSkillListItem[] {\n const root = resolveSkillsDir();\n mkdirSync(root, { recursive: true });\n const lock = loadSkillsLock();\n let dirNames: string[];\n try {\n dirNames = readdirSync(root, { withFileTypes: true })\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((e) => e.name);\n } catch {\n return [];\n }\n const out: ManagedSkillListItem[] = [];\n for (const id of dirNames) {\n if (!isValidSkillId(id)) continue;\n const skillMd = join(root, id, 'SKILL.md');\n if (!existsSync(skillMd)) continue;\n const meta = readSkillMdMeta(skillMd);\n const hub = lock.entries[id];\n out.push({\n id,\n name: meta.name || id,\n description: meta.description || '',\n path: join(root, id),\n ...(hub ? { hub } : {}),\n });\n }\n out.sort((a, b) => a.id.localeCompare(b.id));\n return out;\n}\n\nexport function deleteManagedSkill(skillId: string): void {\n if (!isValidSkillId(skillId)) {\n throw new Error('Invalid skill id');\n }\n const root = resolveSkillsDir();\n const dir = resolve(join(root, skillId));\n const rootResolved = resolve(root);\n if (!dir.startsWith(rootResolved + sep) && dir !== rootResolved) {\n throw new Error('Invalid path');\n }\n if (!existsSync(join(dir, 'SKILL.md'))) {\n throw new Error('Skill not found');\n }\n const trashDir = createTransientManagedDir(root, TRASH_PREFIX, skillId);\n renameSync(dir, trashDir);\n rmSync(trashDir, { recursive: true, force: true });\n}\n\nfunction createTransientManagedDir(root: string, prefix: string, skillId: string): string {\n for (let i = 0; i < 100; i += 1) {\n const suffix = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}-${i}`;\n const dir = join(root, `${prefix}${skillId}-${suffix}`);\n if (!existsSync(dir)) return dir;\n }\n throw new Error('Could not allocate temporary skill directory');\n}\n\nexport function getManagedSkillDir(skillId: string): string {\n if (!isValidSkillId(skillId)) {\n throw new Error('Invalid skill id');\n }\n return join(resolveSkillsDir(), skillId);\n}\n\nexport function assertManagedSkillDestination(skillId: string): { root: string; destDir: string } {\n if (!isValidSkillId(skillId)) {\n throw new Error(`Invalid skill id \"${skillId}\" (letters, digits, ._-; max 63 chars after first)`);\n }\n const root = resolveSkillsDir();\n mkdirSync(root, { recursive: true });\n const destDir = join(root, skillId);\n const destResolved = resolve(destDir);\n const rootResolved = resolve(root);\n if (!destResolved.startsWith(rootResolved + sep) && destResolved !== rootResolved) {\n throw new Error('Invalid destination path');\n }\n return { root, destDir };\n}\n\nexport function prepareManagedSkillTempDir(skillId: string): { root: string; destDir: string; tempDir: string } {\n const { root, destDir } = assertManagedSkillDestination(skillId);\n const tempDir = createTransientManagedDir(root, TEMP_PREFIX, skillId);\n mkdirSync(tempDir, { recursive: true });\n return { root, destDir, tempDir };\n}\n\nexport function promoteManagedSkillTempDir(params: {\n skillId: string;\n tempDir: string;\n overwrite?: boolean;\n}): { skillId: string; path: string } {\n const { root, destDir } = assertManagedSkillDestination(params.skillId);\n const tempResolved = resolve(params.tempDir);\n const rootResolved = resolve(root);\n if (!tempResolved.startsWith(rootResolved + sep)) {\n throw new Error('Invalid temporary skill directory');\n }\n if (!existsSync(join(params.tempDir, 'SKILL.md'))) {\n throw new Error('Installed tree is missing SKILL.md');\n }\n\n if (existsSync(destDir)) {\n if (!params.overwrite) {\n throw new Error(`Skill \"${params.skillId}\" already exists. Pass overwrite to replace.`);\n }\n const trashDir = createTransientManagedDir(root, TRASH_PREFIX, params.skillId);\n renameSync(destDir, trashDir);\n try {\n renameSync(params.tempDir, destDir);\n } catch (err) {\n renameSync(trashDir, destDir);\n throw err;\n }\n rmSync(trashDir, { recursive: true, force: true });\n } else {\n renameSync(params.tempDir, destDir);\n }\n\n return { skillId: params.skillId, path: destDir };\n}\n\nfunction inferStripPrefix(primary: string): string {\n const parts = primary.split('/').filter(Boolean);\n if (parts.length === 1 && parts[0].toLowerCase() === 'skill.md') {\n return '';\n }\n return primary.slice(0, -'SKILL.md'.length);\n}\n\n/**\n * Install or replace a skill from a zip buffer. Layout: either `SKILL.md` at archive root\n * or a single top-level folder containing `SKILL.md` (optionally nested one level).\n */\nexport function installSkillFromZip(\n buffer: Buffer,\n options: { skillId?: string; overwrite?: boolean },\n): { skillId: string; path: string } {\n if (buffer.length > MAX_SKILL_ZIP_BYTES) {\n throw new Error(`Zip exceeds maximum size (${MAX_SKILL_ZIP_BYTES} bytes)`);\n }\n\n const zip = new AdmZip(buffer);\n const entries = zip\n .getEntries()\n .filter((e) => !e.isDirectory && e.entryName && !isIgnorableZipEntry(e.entryName));\n\n const safeEntries = entries.filter((e) => isSafeZipPath(e.entryName));\n if (safeEntries.length === 0) {\n throw new Error('Zip is empty or invalid');\n }\n\n const names = safeEntries.map((e) => e.entryName.replace(/\\\\/g, '/'));\n const skillMdPaths = names.filter((n) => /(^|\\/)SKILL\\.md$/i.test(n));\n if (skillMdPaths.length === 0) {\n throw new Error('Zip must contain at least one SKILL.md');\n }\n\n const shallow = skillMdPaths.filter((p) => p.split('/').filter(Boolean).length <= 2);\n if (shallow.length === 0) {\n throw new Error(\n 'SKILL.md is nested too deeply; use a zip with SKILL.md at archive root or one folder (e.g. my-skill/SKILL.md)',\n );\n }\n\n shallow.sort((a, b) => a.length - b.length);\n const stripPrefix = inferStripPrefix(shallow[0]);\n\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n const outside = names.filter((n) => !n.startsWith(prefixNorm) && !isIgnorableZipEntry(n));\n if (outside.length > 0) {\n throw new Error(\n `Invalid zip: expected all skill files under \"${prefixNorm.replace(/\\/$/, '')}/\", but found \"${outside[0]}\". ` +\n 'Put SKILL.md and assets in one folder, or zip that folder only.',\n );\n }\n }\n\n let targetId = options.skillId?.trim();\n if (!targetId) {\n if (stripPrefix) {\n const first = stripPrefix.replace(/\\/$/, '').split('/')[0];\n targetId = first || '';\n }\n if (!targetId) {\n const skillName = shallow.find((n) => /(^|\\/)SKILL\\.md$/i.test(n)) || '';\n const entry = zip.getEntry(skillName);\n if (!entry) throw new Error('SKILL.md missing');\n const raw = entry.getData().toString('utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n targetId = String(frontmatter.name || '').trim();\n }\n }\n\n if (!targetId || !isValidSkillId(targetId)) {\n throw new Error(\n 'Could not determine skill id: use a folder named with the skill id, pass skillId, or set name: in SKILL.md (letters, digits, ._-)',\n );\n }\n\n const { destDir, tempDir } = prepareManagedSkillTempDir(targetId);\n const tempResolved = resolve(tempDir);\n try {\n if (existsSync(destDir) && !options.overwrite) {\n throw new Error(`Skill \"${targetId}\" already exists. Pass overwrite to replace.`);\n }\n\n for (const e of safeEntries) {\n const norm = e.entryName.replace(/\\\\/g, '/');\n let rel: string;\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n if (!norm.startsWith(prefixNorm)) continue;\n rel = norm.slice(prefixNorm.length).replace(/^\\//, '');\n } else {\n rel = norm;\n }\n if (!rel || rel.includes('..')) continue;\n\n const targetPath = join(tempDir, rel);\n const resolvedTarget = resolve(targetPath);\n if (!resolvedTarget.startsWith(tempResolved + sep) && resolvedTarget !== tempResolved) {\n continue;\n }\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, e.getData());\n }\n\n if (!existsSync(join(tempDir, 'SKILL.md'))) {\n throw new Error('Extracted content is missing SKILL.md');\n }\n\n return promoteManagedSkillTempDir({\n skillId: targetId,\n tempDir,\n overwrite: options.overwrite,\n });\n } catch (err) {\n rmSync(tempDir, { recursive: true, force: true });\n throw err;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"managed-store.js","names":[],"sources":["../../../../src/agent/skills/managed-store.ts"],"sourcesContent":["/**\n * Managed skills live under ~/.xopc/skills (see resolveSkillsDir).\n * Install/update/delete via zip or folder operations; used by gateway API.\n */\n\nimport AdmZip from 'adm-zip';\nimport {\n existsSync,\n mkdirSync,\n renameSync,\n readdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { dirname, join, resolve, sep } from 'node:path';\nimport { resolveSkillsDir } from '../../config/paths.js';\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { loadSkillsLock, type SkillHubLockEntry } from './hub-lock.js';\n\nexport const MAX_SKILL_ZIP_BYTES = 15 * 1024 * 1024;\n\nconst SKILL_ID_RE = /^[a-zA-Z0-9]([a-zA-Z0-9._-]{0,62})$/;\nconst TEMP_PREFIX = '.tmp-';\nconst TRASH_PREFIX = '.trash-';\n\nexport function isValidSkillId(id: string): boolean {\n return SKILL_ID_RE.test(id);\n}\n\nexport function isManagedSkillTransientDirName(name: string): boolean {\n return name.startsWith(TEMP_PREFIX) || name.startsWith(TRASH_PREFIX);\n}\n\n/** True when `baseDir` is the global managed skills root or a direct child folder. */\nexport function isUnderManagedSkillsDir(baseDir: string): boolean {\n const b = resolve(baseDir);\n const r = resolve(resolveSkillsDir());\n return b === r || b.startsWith(r + sep);\n}\n\nfunction isSafeZipPath(name: string): boolean {\n if (!name) return false;\n const normalized = name.replace(/\\\\/g, '/');\n if (normalized.includes('..')) return false;\n if (normalized.startsWith('/') || /^\\w:/.test(normalized)) return false;\n for (const p of normalized.split('/')) {\n if (p === '..') return false;\n }\n return true;\n}\n\n/** macOS/Windows noise; not part of the skill tree. */\nexport function isIgnorableZipEntry(name: string): boolean {\n const n = name.replace(/\\\\/g, '/');\n if (n.startsWith('__MACOSX/')) return true;\n const segments = n.split('/').filter(Boolean);\n for (const s of segments) {\n if (s === '.DS_Store' || s === 'Thumbs.db' || s === 'desktop.ini') return true;\n if (s.startsWith('._')) return true;\n }\n return false;\n}\n\nexport interface ManagedSkillListItem {\n id: string;\n name: string;\n description: string;\n path: string;\n /** Set when this folder was installed via `skills hub pull` / lock file. */\n hub?: SkillHubLockEntry;\n}\n\nfunction readSkillMdMeta(skillMdPath: string): { name: string; description: string } {\n try {\n const raw = readFileSync(skillMdPath, 'utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n return {\n name: (frontmatter.name as string) || '',\n description: (frontmatter.description as string)?.trim() || '',\n };\n } catch {\n return { name: '', description: '' };\n }\n}\n\nexport function listManagedSkillDirs(): ManagedSkillListItem[] {\n const root = resolveSkillsDir();\n mkdirSync(root, { recursive: true });\n const lock = loadSkillsLock();\n let dirNames: string[];\n try {\n dirNames = readdirSync(root, { withFileTypes: true })\n .filter((e) => e.isDirectory() && !e.name.startsWith('.'))\n .map((e) => e.name);\n } catch {\n return [];\n }\n const out: ManagedSkillListItem[] = [];\n for (const id of dirNames) {\n if (!isValidSkillId(id)) continue;\n const skillMd = join(root, id, 'SKILL.md');\n if (!existsSync(skillMd)) continue;\n const meta = readSkillMdMeta(skillMd);\n const hub = lock.entries[id];\n out.push({\n id,\n name: meta.name || id,\n description: meta.description || '',\n path: join(root, id),\n ...(hub ? { hub } : {}),\n });\n }\n out.sort((a, b) => a.id.localeCompare(b.id));\n return out;\n}\n\nexport function deleteManagedSkill(skillId: string, rootDir = resolveSkillsDir()): void {\n if (!isValidSkillId(skillId)) {\n throw new Error('Invalid skill id');\n }\n const root = resolve(rootDir);\n const dir = resolve(join(root, skillId));\n const rootResolved = resolve(root);\n if (!dir.startsWith(rootResolved + sep) && dir !== rootResolved) {\n throw new Error('Invalid path');\n }\n if (!existsSync(join(dir, 'SKILL.md'))) {\n throw new Error('Skill not found');\n }\n const trashDir = createTransientManagedDir(root, TRASH_PREFIX, skillId);\n renameSync(dir, trashDir);\n rmSync(trashDir, { recursive: true, force: true });\n}\n\nfunction createTransientManagedDir(root: string, prefix: string, skillId: string): string {\n for (let i = 0; i < 100; i += 1) {\n const suffix = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}-${i}`;\n const dir = join(root, `${prefix}${skillId}-${suffix}`);\n if (!existsSync(dir)) return dir;\n }\n throw new Error('Could not allocate temporary skill directory');\n}\n\nexport function getManagedSkillDir(skillId: string): string {\n if (!isValidSkillId(skillId)) {\n throw new Error('Invalid skill id');\n }\n return join(resolveSkillsDir(), skillId);\n}\n\nexport function assertManagedSkillDestination(\n skillId: string,\n rootDir = resolveSkillsDir(),\n): { root: string; destDir: string } {\n if (!isValidSkillId(skillId)) {\n throw new Error(`Invalid skill id \"${skillId}\" (letters, digits, ._-; max 63 chars after first)`);\n }\n const root = resolve(rootDir);\n mkdirSync(root, { recursive: true });\n const destDir = join(root, skillId);\n const destResolved = resolve(destDir);\n const rootResolved = resolve(root);\n if (!destResolved.startsWith(rootResolved + sep) && destResolved !== rootResolved) {\n throw new Error('Invalid destination path');\n }\n return { root, destDir };\n}\n\nexport function prepareManagedSkillTempDir(\n skillId: string,\n rootDir?: string,\n): { root: string; destDir: string; tempDir: string } {\n const { root, destDir } = assertManagedSkillDestination(skillId, rootDir);\n const tempDir = createTransientManagedDir(root, TEMP_PREFIX, skillId);\n mkdirSync(tempDir, { recursive: true });\n return { root, destDir, tempDir };\n}\n\nexport function promoteManagedSkillTempDir(params: {\n skillId: string;\n tempDir: string;\n overwrite?: boolean;\n rootDir?: string;\n}): { skillId: string; path: string } {\n const { root, destDir } = assertManagedSkillDestination(params.skillId, params.rootDir);\n const tempResolved = resolve(params.tempDir);\n const rootResolved = resolve(root);\n if (!tempResolved.startsWith(rootResolved + sep)) {\n throw new Error('Invalid temporary skill directory');\n }\n if (!existsSync(join(params.tempDir, 'SKILL.md'))) {\n throw new Error('Installed tree is missing SKILL.md');\n }\n\n if (existsSync(destDir)) {\n if (!params.overwrite) {\n throw new Error(`Skill \"${params.skillId}\" already exists. Pass overwrite to replace.`);\n }\n const trashDir = createTransientManagedDir(root, TRASH_PREFIX, params.skillId);\n renameSync(destDir, trashDir);\n try {\n renameSync(params.tempDir, destDir);\n } catch (err) {\n renameSync(trashDir, destDir);\n throw err;\n }\n rmSync(trashDir, { recursive: true, force: true });\n } else {\n renameSync(params.tempDir, destDir);\n }\n\n return { skillId: params.skillId, path: destDir };\n}\n\nfunction inferStripPrefix(primary: string): string {\n const parts = primary.split('/').filter(Boolean);\n if (parts.length === 1 && parts[0].toLowerCase() === 'skill.md') {\n return '';\n }\n return primary.slice(0, -'SKILL.md'.length);\n}\n\n/**\n * Install or replace a skill from a zip buffer. Layout: either `SKILL.md` at archive root\n * or a single top-level folder containing `SKILL.md` (optionally nested one level).\n */\nexport function installSkillFromZip(\n buffer: Buffer,\n options: { skillId?: string; overwrite?: boolean; rootDir?: string },\n): { skillId: string; path: string } {\n if (buffer.length > MAX_SKILL_ZIP_BYTES) {\n throw new Error(`Zip exceeds maximum size (${MAX_SKILL_ZIP_BYTES} bytes)`);\n }\n\n const zip = new AdmZip(buffer);\n const entries = zip\n .getEntries()\n .filter((e) => !e.isDirectory && e.entryName && !isIgnorableZipEntry(e.entryName));\n\n const safeEntries = entries.filter((e) => isSafeZipPath(e.entryName));\n if (safeEntries.length === 0) {\n throw new Error('Zip is empty or invalid');\n }\n\n const names = safeEntries.map((e) => e.entryName.replace(/\\\\/g, '/'));\n const skillMdPaths = names.filter((n) => /(^|\\/)SKILL\\.md$/i.test(n));\n if (skillMdPaths.length === 0) {\n throw new Error('Zip must contain at least one SKILL.md');\n }\n\n const shallow = skillMdPaths.filter((p) => p.split('/').filter(Boolean).length <= 2);\n if (shallow.length === 0) {\n throw new Error(\n 'SKILL.md is nested too deeply; use a zip with SKILL.md at archive root or one folder (e.g. my-skill/SKILL.md)',\n );\n }\n\n shallow.sort((a, b) => a.length - b.length);\n const stripPrefix = inferStripPrefix(shallow[0]);\n\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n const outside = names.filter((n) => !n.startsWith(prefixNorm) && !isIgnorableZipEntry(n));\n if (outside.length > 0) {\n throw new Error(\n `Invalid zip: expected all skill files under \"${prefixNorm.replace(/\\/$/, '')}/\", but found \"${outside[0]}\". ` +\n 'Put SKILL.md and assets in one folder, or zip that folder only.',\n );\n }\n }\n\n let targetId = options.skillId?.trim();\n if (!targetId) {\n if (stripPrefix) {\n const first = stripPrefix.replace(/\\/$/, '').split('/')[0];\n targetId = first || '';\n }\n if (!targetId) {\n const skillName = shallow.find((n) => /(^|\\/)SKILL\\.md$/i.test(n)) || '';\n const entry = zip.getEntry(skillName);\n if (!entry) throw new Error('SKILL.md missing');\n const raw = entry.getData().toString('utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n targetId = String(frontmatter.name || '').trim();\n }\n }\n\n if (!targetId || !isValidSkillId(targetId)) {\n throw new Error(\n 'Could not determine skill id: use a folder named with the skill id, pass skillId, or set name: in SKILL.md (letters, digits, ._-)',\n );\n }\n\n const { destDir, tempDir } = prepareManagedSkillTempDir(targetId, options.rootDir);\n const tempResolved = resolve(tempDir);\n try {\n if (existsSync(destDir) && !options.overwrite) {\n throw new Error(`Skill \"${targetId}\" already exists. Pass overwrite to replace.`);\n }\n\n for (const e of safeEntries) {\n const norm = e.entryName.replace(/\\\\/g, '/');\n let rel: string;\n if (stripPrefix) {\n const prefixNorm = stripPrefix.replace(/\\\\/g, '/');\n if (!norm.startsWith(prefixNorm)) continue;\n rel = norm.slice(prefixNorm.length).replace(/^\\//, '');\n } else {\n rel = norm;\n }\n if (!rel || rel.includes('..')) continue;\n\n const targetPath = join(tempDir, rel);\n const resolvedTarget = resolve(targetPath);\n if (!resolvedTarget.startsWith(tempResolved + sep) && resolvedTarget !== tempResolved) {\n continue;\n }\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileSync(targetPath, e.getData());\n }\n\n if (!existsSync(join(tempDir, 'SKILL.md'))) {\n throw new Error('Extracted content is missing SKILL.md');\n }\n\n return promoteManagedSkillTempDir({\n skillId: targetId,\n tempDir,\n overwrite: options.overwrite,\n rootDir: options.rootDir,\n });\n } catch (err) {\n rmSync(tempDir, { recursive: true, force: true });\n throw err;\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { registerMarketplaceAdapter } from "../../registry.js";
2
- import { downloadSkillZipBuffer, fetchMarketplacePackageDetail, listSkillPackages, resolveSkillZipDownloadUrl, resolveSkillsStoreBaseUrl, skillIdForMarketplaceInstall } from "./store-api-client.js";
2
+ import { downloadSkillZipBuffer, fetchMarketplacePackageDetail, listSkillPackages, resolveSkillZipDownloadUrl, resolveSkillsStoreBaseUrl, skillIdForMarketplaceInstall, verifyStoreArtifactSha256 } from "./store-api-client.js";
3
3
  import { buildSkillMarkdownPreviewFromRaw } from "../../../skill-markdown-preview-from-raw.js";
4
4
  //#region src/agent/skills/marketplace/adapters/store/adapter.ts
5
5
  const storeMarketplaceAdapter = {
@@ -31,9 +31,11 @@ const storeMarketplaceAdapter = {
31
31
  },
32
32
  async downloadPackage(config, packageName, version) {
33
33
  const base = resolveSkillsStoreBaseUrl(config);
34
- const { downloadUrl, version: resolvedVersion } = await resolveSkillZipDownloadUrl(base, packageName, version);
34
+ const { downloadUrl, version: resolvedVersion, sha256 } = await resolveSkillZipDownloadUrl(base, packageName, version);
35
+ const buffer = await downloadSkillZipBuffer(base, downloadUrl);
36
+ verifyStoreArtifactSha256(buffer, sha256);
35
37
  return {
36
- buffer: await downloadSkillZipBuffer(base, downloadUrl),
38
+ buffer,
37
39
  skillId: skillIdForMarketplaceInstall(packageName) ?? packageName,
38
40
  version: resolvedVersion
39
41
  };
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","names":[],"sources":["../../../../../../../src/agent/skills/marketplace/adapters/store/adapter.ts"],"sourcesContent":["import { buildSkillMarkdownPreviewFromRaw } from '../../../skill-markdown-preview-from-raw.js';\nimport {\n downloadSkillZipBuffer,\n fetchMarketplacePackageDetail,\n listSkillPackages,\n resolveSkillZipDownloadUrl,\n resolveSkillsStoreBaseUrl,\n skillIdForMarketplaceInstall,\n} from './store-api-client.js';\n\nimport type { SkillsMarketplaceAdapter } from '../../adapter.types.js';\nimport { registerMarketplaceAdapter } from '../../registry.js';\n\nexport const storeMarketplaceAdapter: SkillsMarketplaceAdapter = {\n id: 'store',\n\n async listCategories(_config) {\n return [];\n },\n\n async listPackages(config, params) {\n const base = resolveSkillsStoreBaseUrl(config);\n const response = await listSkillPackages(base, params);\n return {\n items: response.items,\n meta: response.meta,\n provider: 'store',\n };\n },\n\n async getPackageDetail(config, packageName) {\n const base = resolveSkillsStoreBaseUrl(config);\n const detail = await fetchMarketplacePackageDetail(base, packageName);\n const readme = detail.readme?.trim();\n if (!readme) {\n throw new Error(`Store skill package [${packageName}] did not include readme`);\n }\n return {\n ...detail,\n readme,\n provider: 'store',\n skillDocPreview: buildSkillMarkdownPreviewFromRaw(readme, {\n name: detail.name,\n description: detail.description,\n }),\n };\n },\n\n async downloadPackage(config, packageName, version) {\n const base = resolveSkillsStoreBaseUrl(config);\n const { downloadUrl, version: resolvedVersion } = await resolveSkillZipDownloadUrl(\n base,\n packageName,\n version,\n );\n const buffer = await downloadSkillZipBuffer(base, downloadUrl);\n return {\n buffer,\n skillId: skillIdForMarketplaceInstall(packageName) ?? packageName,\n version: resolvedVersion,\n };\n },\n};\n\n// Self-register into the dynamic marketplace adapter registry.\nregisterMarketplaceAdapter({\n adapter: storeMarketplaceAdapter,\n displayName: 'XOPC Store',\n});\n"],"mappings":";;;;AAaA,MAAa,0BAAoD;CAC/D,IAAI;CAEJ,MAAM,eAAe,SAAS;AAC5B,SAAO,EAAE;;CAGX,MAAM,aAAa,QAAQ,QAAQ;EAEjC,MAAM,WAAW,MAAM,kBADV,0BAA0B,OACM,EAAE,OAAO;AACtD,SAAO;GACL,OAAO,SAAS;GAChB,MAAM,SAAS;GACf,UAAU;GACX;;CAGH,MAAM,iBAAiB,QAAQ,aAAa;EAE1C,MAAM,SAAS,MAAM,8BADR,0BAA0B,OACgB,EAAE,YAAY;EACrE,MAAM,SAAS,OAAO,QAAQ,MAAM;AACpC,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,wBAAwB,YAAY,0BAA0B;AAEhF,SAAO;GACL,GAAG;GACH;GACA,UAAU;GACV,iBAAiB,iCAAiC,QAAQ;IACxD,MAAM,OAAO;IACb,aAAa,OAAO;IACrB,CAAC;GACH;;CAGH,MAAM,gBAAgB,QAAQ,aAAa,SAAS;EAClD,MAAM,OAAO,0BAA0B,OAAO;EAC9C,MAAM,EAAE,aAAa,SAAS,oBAAoB,MAAM,2BACtD,MACA,aACA,QACD;AAED,SAAO;GACL,QAAA,MAFmB,uBAAuB,MAAM,YAAY;GAG5D,SAAS,6BAA6B,YAAY,IAAI;GACtD,SAAS;GACV;;CAEJ;AAGD,2BAA2B;CACzB,SAAS;CACT,aAAa;CACd,CAAC"}
1
+ {"version":3,"file":"adapter.js","names":[],"sources":["../../../../../../../src/agent/skills/marketplace/adapters/store/adapter.ts"],"sourcesContent":["import { buildSkillMarkdownPreviewFromRaw } from '../../../skill-markdown-preview-from-raw.js';\nimport {\n downloadSkillZipBuffer,\n fetchMarketplacePackageDetail,\n listSkillPackages,\n resolveSkillZipDownloadUrl,\n resolveSkillsStoreBaseUrl,\n skillIdForMarketplaceInstall,\n verifyStoreArtifactSha256,\n} from './store-api-client.js';\n\nimport type { SkillsMarketplaceAdapter } from '../../adapter.types.js';\nimport { registerMarketplaceAdapter } from '../../registry.js';\n\nexport const storeMarketplaceAdapter: SkillsMarketplaceAdapter = {\n id: 'store',\n\n async listCategories(_config) {\n return [];\n },\n\n async listPackages(config, params) {\n const base = resolveSkillsStoreBaseUrl(config);\n const response = await listSkillPackages(base, params);\n return {\n items: response.items,\n meta: response.meta,\n provider: 'store',\n };\n },\n\n async getPackageDetail(config, packageName) {\n const base = resolveSkillsStoreBaseUrl(config);\n const detail = await fetchMarketplacePackageDetail(base, packageName);\n const readme = detail.readme?.trim();\n if (!readme) {\n throw new Error(`Store skill package [${packageName}] did not include readme`);\n }\n return {\n ...detail,\n readme,\n provider: 'store',\n skillDocPreview: buildSkillMarkdownPreviewFromRaw(readme, {\n name: detail.name,\n description: detail.description,\n }),\n };\n },\n\n async downloadPackage(config, packageName, version) {\n const base = resolveSkillsStoreBaseUrl(config);\n const { downloadUrl, version: resolvedVersion, sha256 } = await resolveSkillZipDownloadUrl(\n base,\n packageName,\n version,\n );\n const buffer = await downloadSkillZipBuffer(base, downloadUrl);\n verifyStoreArtifactSha256(buffer, sha256);\n return {\n buffer,\n skillId: skillIdForMarketplaceInstall(packageName) ?? packageName,\n version: resolvedVersion,\n };\n },\n};\n\n// Self-register into the dynamic marketplace adapter registry.\nregisterMarketplaceAdapter({\n adapter: storeMarketplaceAdapter,\n displayName: 'XOPC Store',\n});\n"],"mappings":";;;;AAcA,MAAa,0BAAoD;CAC/D,IAAI;CAEJ,MAAM,eAAe,SAAS;AAC5B,SAAO,EAAE;;CAGX,MAAM,aAAa,QAAQ,QAAQ;EAEjC,MAAM,WAAW,MAAM,kBADV,0BAA0B,OACM,EAAE,OAAO;AACtD,SAAO;GACL,OAAO,SAAS;GAChB,MAAM,SAAS;GACf,UAAU;GACX;;CAGH,MAAM,iBAAiB,QAAQ,aAAa;EAE1C,MAAM,SAAS,MAAM,8BADR,0BAA0B,OACgB,EAAE,YAAY;EACrE,MAAM,SAAS,OAAO,QAAQ,MAAM;AACpC,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,wBAAwB,YAAY,0BAA0B;AAEhF,SAAO;GACL,GAAG;GACH;GACA,UAAU;GACV,iBAAiB,iCAAiC,QAAQ;IACxD,MAAM,OAAO;IACb,aAAa,OAAO;IACrB,CAAC;GACH;;CAGH,MAAM,gBAAgB,QAAQ,aAAa,SAAS;EAClD,MAAM,OAAO,0BAA0B,OAAO;EAC9C,MAAM,EAAE,aAAa,SAAS,iBAAiB,WAAW,MAAM,2BAC9D,MACA,aACA,QACD;EACD,MAAM,SAAS,MAAM,uBAAuB,MAAM,YAAY;AAC9D,4BAA0B,QAAQ,OAAO;AACzC,SAAO;GACL;GACA,SAAS,6BAA6B,YAAY,IAAI;GACtD,SAAS;GACV;;CAEJ;AAGD,2BAA2B;CACzB,SAAS;CACT,aAAa;CACd,CAAC"}
@@ -70,6 +70,8 @@ export declare function getStoreOrigin(baseUrl: string): string;
70
70
  */
71
71
  export declare function assertDownloadUrlAllowed(downloadUrl: string, storeBaseUrl: string): URL;
72
72
  export declare function listSkillPackages(storeBaseUrl: string, params: SkillsStoreListParams): Promise<SkillsStoreListResponse>;
73
+ /** Public connector catalog exposed by xopc-store. */
74
+ export declare function listConnectorPackages(storeBaseUrl: string, params: SkillsStoreListParams): Promise<SkillsStoreListResponse>;
73
75
  /** GET /api/v1/packages/:name — published skill package (public). */
74
76
  export interface MarketplacePackageDetail {
75
77
  id: string;
@@ -88,12 +90,28 @@ export interface MarketplacePackageDetail {
88
90
  publishedAt: string;
89
91
  };
90
92
  }
93
+ export interface StoreConnectorPackageDetail {
94
+ id: string;
95
+ name: string;
96
+ type: 'connector';
97
+ description: string;
98
+ latestVersion: {
99
+ version: string;
100
+ manifest: unknown;
101
+ downloadUrl: string;
102
+ sha256: string | null;
103
+ };
104
+ }
105
+ export declare function fetchStoreConnectorPackageDetail(storeBaseUrl: string, packageName: string, version?: string): Promise<StoreConnectorPackageDetail>;
91
106
  export declare function fetchMarketplacePackageDetail(storeBaseUrl: string, packageName: string): Promise<MarketplacePackageDetail>;
92
107
  export declare function resolveSkillZipDownloadUrl(storeBaseUrl: string, packageName: string, version?: string): Promise<{
93
108
  downloadUrl: string;
94
109
  version: string;
110
+ sha256?: string;
95
111
  }>;
96
- export declare function downloadSkillZipBuffer(storeBaseUrl: string, downloadUrl: string): Promise<Buffer>;
112
+ /** Verify Store-provided immutable artifact digest before any local installation. */
113
+ export declare function verifyStoreArtifactSha256(buffer: Buffer, expected: string | null | undefined): void;
114
+ export declare function downloadSkillZipBuffer(storeBaseUrl: string, downloadUrl: string, expectedSha256?: string): Promise<Buffer>;
97
115
  /** GET /api/v1/packages/:name — subset used for extension installs. */
98
116
  export interface StorePublishedPackageHead {
99
117
  name: string;
@@ -110,8 +128,10 @@ export declare function resolveExtensionZipDownloadUrl(storeBaseUrl: string, pac
110
128
  downloadUrl: string;
111
129
  version: string;
112
130
  integrity?: string;
131
+ sha256?: string;
113
132
  }>;
114
133
  export declare function downloadExtensionStoreZipBuffer(storeBaseUrl: string, downloadUrl: string): Promise<Buffer>;
134
+ export declare function downloadConnectorStoreZipBuffer(storeBaseUrl: string, downloadUrl: string): Promise<Buffer>;
115
135
  export declare function skillIdForMarketplaceInstall(packageName: string): string | undefined;
116
136
  /**
117
137
  * Unified marketplace list response (adapter fills `provider`).
@@ -1,6 +1,13 @@
1
1
  import { MAX_SKILL_ZIP_BYTES, isValidSkillId } from "../../../managed-store.js";
2
2
  import { MAX_EXTENSION_STORE_ZIP_BYTES } from "../../../../../extensions/store-zip-limits.js";
3
+ import { createHash } from "node:crypto";
3
4
  //#region src/agent/skills/marketplace/adapters/store/store-api-client.ts
5
+ /**
6
+ * HTTP client for the public xopc-store-compatible REST API (packages list, detail, zip download).
7
+ * Used by the store marketplace adapter and SSRF-safe download helpers.
8
+ *
9
+ * Facade: {@link ../../../skills-marketplace.js}.
10
+ */
4
11
  const DEFAULT_STORE_BASE = "https://store.xopc.ai";
5
12
  function resolveSkillsStoreBaseUrl(config) {
6
13
  const env = process.env.XOPC_SKILLS_STORE_URL?.trim();
@@ -87,6 +94,34 @@ async function listSkillPackages(storeBaseUrl, params) {
87
94
  if (params.category?.trim()) sp.set("category", params.category.trim());
88
95
  return fetchJson(`${base}/api/v1/packages?${sp.toString()}`);
89
96
  }
97
+ /** Public connector catalog exposed by xopc-store. */
98
+ async function listConnectorPackages(storeBaseUrl, params) {
99
+ const base = normalizeBaseUrl(storeBaseUrl);
100
+ const sp = new URLSearchParams();
101
+ sp.set("type", "connector");
102
+ if (params.q?.trim()) sp.set("q", params.q.trim());
103
+ if (params.page != null) sp.set("page", String(params.page));
104
+ if (params.pageSize != null) sp.set("pageSize", String(params.pageSize));
105
+ if (params.sort) sp.set("sort", params.sort);
106
+ if (params.category?.trim()) sp.set("category", params.category.trim());
107
+ return fetchJson(`${base}/api/v1/packages?${sp.toString()}`);
108
+ }
109
+ async function fetchStoreConnectorPackageDetail(storeBaseUrl, packageName, version) {
110
+ const base = normalizeBaseUrl(storeBaseUrl);
111
+ const enc = encodeURIComponent(packageName.trim());
112
+ const pkg = await fetchJson(`${base}/api/v1/packages/${enc}`);
113
+ if (pkg.type !== "connector") throw new Error(`Package "${packageName}" has type "${pkg.type}" (expected connector).`);
114
+ if (!version?.trim() || version.trim() === pkg.latestVersion.version) {
115
+ assertDownloadUrlAllowed(pkg.latestVersion.downloadUrl, base);
116
+ return pkg;
117
+ }
118
+ const detail = await fetchJson(`${base}/api/v1/packages/${enc}/versions/${encodeURIComponent(version.trim())}`);
119
+ assertDownloadUrlAllowed(detail.downloadUrl, base);
120
+ return {
121
+ ...pkg,
122
+ latestVersion: detail
123
+ };
124
+ }
90
125
  async function fetchMarketplacePackageDetail(storeBaseUrl, packageName) {
91
126
  return fetchJson(`${normalizeBaseUrl(storeBaseUrl)}/api/v1/packages/${encodeURIComponent(packageName.trim())}`);
92
127
  }
@@ -99,7 +134,8 @@ async function resolveSkillZipDownloadUrl(storeBaseUrl, packageName, version) {
99
134
  assertDownloadUrlAllowed(detail.downloadUrl, base);
100
135
  return {
101
136
  downloadUrl: detail.downloadUrl,
102
- version: detail.version
137
+ version: detail.version,
138
+ ...detail.sha256 ? { sha256: detail.sha256 } : {}
103
139
  };
104
140
  }
105
141
  const pkg = await fetchJson(`${base}/api/v1/packages/${enc}`);
@@ -107,10 +143,16 @@ async function resolveSkillZipDownloadUrl(storeBaseUrl, packageName, version) {
107
143
  assertDownloadUrlAllowed(pkg.latestVersion.downloadUrl, base);
108
144
  return {
109
145
  downloadUrl: pkg.latestVersion.downloadUrl,
110
- version: pkg.latestVersion.version
146
+ version: pkg.latestVersion.version,
147
+ ...pkg.latestVersion.sha256 ? { sha256: pkg.latestVersion.sha256 } : {}
111
148
  };
112
149
  }
113
- async function downloadSkillZipBuffer(storeBaseUrl, downloadUrl) {
150
+ /** Verify Store-provided immutable artifact digest before any local installation. */
151
+ function verifyStoreArtifactSha256(buffer, expected) {
152
+ if (!expected || !/^[a-f0-9]{64}$/i.test(expected)) throw new Error("Store version is missing a valid SHA-256 checksum");
153
+ if (createHash("sha256").update(buffer).digest("hex") !== expected.toLowerCase()) throw new Error("Store artifact checksum verification failed");
154
+ }
155
+ async function downloadSkillZipBuffer(storeBaseUrl, downloadUrl, expectedSha256) {
114
156
  assertDownloadUrlAllowed(downloadUrl, normalizeBaseUrl(storeBaseUrl));
115
157
  const res = await fetch(downloadUrl, { redirect: "follow" });
116
158
  if (!res.ok) throw new Error(`Failed to download skill archive (${res.status})`);
@@ -122,6 +164,7 @@ async function downloadSkillZipBuffer(storeBaseUrl, downloadUrl) {
122
164
  const ab = await res.arrayBuffer();
123
165
  const buf = Buffer.from(ab);
124
166
  if (buf.length > 15728640) throw new Error(`Zip exceeds maximum size (${MAX_SKILL_ZIP_BYTES} bytes)`);
167
+ if (expectedSha256 !== void 0) verifyStoreArtifactSha256(buf, expectedSha256);
125
168
  return buf;
126
169
  }
127
170
  async function resolveExtensionZipDownloadUrl(storeBaseUrl, packageName, version) {
@@ -136,7 +179,8 @@ async function resolveExtensionZipDownloadUrl(storeBaseUrl, packageName, version
136
179
  return {
137
180
  downloadUrl: detail.downloadUrl,
138
181
  version: detail.version,
139
- integrity: detail.integrity ?? detail.checksum ?? detail.sha256
182
+ integrity: detail.integrity ?? detail.checksum ?? detail.sha256,
183
+ ...detail.sha256 ? { sha256: detail.sha256 } : {}
140
184
  };
141
185
  }
142
186
  const lv = meta.latestVersion;
@@ -145,7 +189,8 @@ async function resolveExtensionZipDownloadUrl(storeBaseUrl, packageName, version
145
189
  return {
146
190
  downloadUrl: lv.downloadUrl,
147
191
  version: lv.version,
148
- integrity: lv.integrity ?? lv.checksum ?? lv.sha256
192
+ integrity: lv.integrity ?? lv.checksum ?? lv.sha256,
193
+ ...lv.sha256 ? { sha256: lv.sha256 } : {}
149
194
  };
150
195
  }
151
196
  async function downloadExtensionStoreZipBuffer(storeBaseUrl, downloadUrl) {
@@ -162,12 +207,23 @@ async function downloadExtensionStoreZipBuffer(storeBaseUrl, downloadUrl) {
162
207
  if (buf.length > 10485760) throw new Error(`Zip exceeds maximum size (${MAX_EXTENSION_STORE_ZIP_BYTES} bytes)`);
163
208
  return buf;
164
209
  }
210
+ async function downloadConnectorStoreZipBuffer(storeBaseUrl, downloadUrl) {
211
+ assertDownloadUrlAllowed(downloadUrl, normalizeBaseUrl(storeBaseUrl));
212
+ const res = await fetch(downloadUrl, { redirect: "error" });
213
+ if (!res.ok) throw new Error(`Failed to download connector archive (${res.status})`);
214
+ const maxBytes = 1024 * 1024;
215
+ const len = res.headers.get("content-length");
216
+ if (len && Number(len) > maxBytes) throw new Error(`Connector archive exceeds maximum size (${maxBytes} bytes)`);
217
+ const buf = Buffer.from(await res.arrayBuffer());
218
+ if (buf.length > maxBytes) throw new Error(`Connector archive exceeds maximum size (${maxBytes} bytes)`);
219
+ return buf;
220
+ }
165
221
  function skillIdForMarketplaceInstall(packageName) {
166
222
  const trimmed = packageName.trim();
167
223
  if (!trimmed) return void 0;
168
224
  return isValidSkillId(trimmed) ? trimmed : void 0;
169
225
  }
170
226
  //#endregion
171
- export { MAX_EXTENSION_STORE_ZIP_BYTES, assertDownloadUrlAllowed, downloadExtensionStoreZipBuffer, downloadSkillZipBuffer, fetchMarketplacePackageDetail, getStoreOrigin, listSkillPackages, resolveExtensionZipDownloadUrl, resolveExtensionsStoreBaseUrl, resolveSkillZipDownloadUrl, resolveSkillsStoreBaseUrl, skillIdForMarketplaceInstall };
227
+ export { MAX_EXTENSION_STORE_ZIP_BYTES, assertDownloadUrlAllowed, downloadConnectorStoreZipBuffer, downloadExtensionStoreZipBuffer, downloadSkillZipBuffer, fetchMarketplacePackageDetail, fetchStoreConnectorPackageDetail, getStoreOrigin, listConnectorPackages, listSkillPackages, resolveExtensionZipDownloadUrl, resolveExtensionsStoreBaseUrl, resolveSkillZipDownloadUrl, resolveSkillsStoreBaseUrl, skillIdForMarketplaceInstall, verifyStoreArtifactSha256 };
172
228
 
173
229
  //# sourceMappingURL=store-api-client.js.map