@skaile/workspaces 0.22.0-beta.0 → 0.22.0-beta.2

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 (306) hide show
  1. package/CHANGELOG.md +221 -0
  2. package/dist/{asset-feeds-PJDJ3QYI.js → asset-feeds-2M6UKEJ7.js} +13 -14
  3. package/dist/asset-feeds-2M6UKEJ7.js.map +1 -0
  4. package/dist/asset-manager/catalog-deployer.d.ts +2 -0
  5. package/dist/asset-manager/contrib.d.ts +2 -0
  6. package/dist/asset-manager/fragments.d.ts +2 -0
  7. package/dist/asset-manager/history.d.ts +2 -0
  8. package/dist/asset-manager/index.d.ts +2 -0
  9. package/dist/asset-manager/index.js +7 -7
  10. package/dist/asset-manager/installer.d.ts +2 -0
  11. package/dist/asset-manager/installer.js +6 -6
  12. package/dist/asset-manager/renderers.d.ts +2 -0
  13. package/dist/asset-manager/src/index.d.ts +18 -7
  14. package/dist/asset-manager/src/index.d.ts.map +1 -1
  15. package/dist/asset-manager/src/installer.d.ts +3 -3
  16. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  17. package/dist/base-assets/connectors/deploy.d.ts +2 -0
  18. package/dist/base-assets/connectors/deploy.js +8 -8
  19. package/dist/base-assets/connectors/devserver.d.ts +2 -0
  20. package/dist/base-assets/connectors/devserver.js +8 -8
  21. package/dist/base-assets/connectors/flow/adapter.js +8 -8
  22. package/dist/base-assets/connectors/flow/engine.d.ts +2 -0
  23. package/dist/base-assets/connectors/flow/run-flow.js +9 -9
  24. package/dist/base-assets/connectors/flow.d.ts +2 -0
  25. package/dist/base-assets/connectors/flow.js +8 -8
  26. package/dist/base-assets/connectors/git/driver.d.ts.map +1 -1
  27. package/dist/base-assets/connectors/git.d.ts +2 -0
  28. package/dist/base-assets/connectors/git.js +8 -8
  29. package/dist/base-assets/connectors/gmail.d.ts +2 -0
  30. package/dist/base-assets/connectors/gmail.js +8 -8
  31. package/dist/base-assets/connectors/googledrive.d.ts +2 -0
  32. package/dist/base-assets/connectors/googledrive.js +8 -8
  33. package/dist/base-assets/connectors/local.d.ts +2 -0
  34. package/dist/base-assets/connectors/local.js +8 -8
  35. package/dist/base-assets/connectors/mattermost.d.ts +2 -0
  36. package/dist/base-assets/connectors/mattermost.js +8 -8
  37. package/dist/base-assets/connectors/memory.d.ts +2 -0
  38. package/dist/base-assets/connectors/memory.js +8 -8
  39. package/dist/base-assets/connectors/minio.d.ts +2 -0
  40. package/dist/base-assets/connectors/minio.js +8 -8
  41. package/dist/base-assets/connectors/postgres.d.ts +2 -0
  42. package/dist/base-assets/connectors/postgres.js +8 -8
  43. package/dist/base-assets/connectors/s3.d.ts +2 -0
  44. package/dist/base-assets/connectors/s3.js +8 -8
  45. package/dist/base-assets/connectors/sharepoint.d.ts +2 -0
  46. package/dist/base-assets/connectors/sharepoint.js +8 -8
  47. package/dist/base-assets/connectors/sqlite.d.ts +2 -0
  48. package/dist/base-assets/connectors/sqlite.js +8 -8
  49. package/dist/base-assets/connectors/static-server.d.ts +2 -0
  50. package/dist/base-assets/connectors/static-server.js +8 -8
  51. package/dist/base-assets/connectors/tunnel.d.ts +2 -0
  52. package/dist/base-assets/connectors/tunnel.js +8 -8
  53. package/dist/base-assets/connectors/webdav.d.ts +2 -0
  54. package/dist/base-assets/connectors/webdav.js +8 -8
  55. package/dist/base-assets/connectors/xstate-store.d.ts +2 -0
  56. package/dist/base-assets/connectors/xstate-store.js +8 -8
  57. package/dist/base-assets/connectors/xstate.d.ts +2 -0
  58. package/dist/base-assets/connectors/xstate.js +8 -8
  59. package/dist/bridge/drivers/claude-sdk.d.ts +2 -0
  60. package/dist/bridge/drivers/claude-sdk.js +2 -2
  61. package/dist/bridge/drivers/codex.d.ts +2 -0
  62. package/dist/bridge/drivers/codex.js +2 -2
  63. package/dist/bridge/drivers/echo.d.ts +2 -0
  64. package/dist/bridge/drivers/echo.js +2 -2
  65. package/dist/bridge/drivers/omp.d.ts +2 -0
  66. package/dist/bridge/drivers/omp.js +2 -2
  67. package/dist/bridge/index.d.ts +2 -0
  68. package/dist/bridge/index.js +3 -3
  69. package/dist/bridge/src/registry.d.ts +4 -2
  70. package/dist/bridge/src/registry.d.ts.map +1 -1
  71. package/dist/chunk-32NA4TVC.js +30 -0
  72. package/dist/chunk-32NA4TVC.js.map +1 -0
  73. package/dist/{chunk-CSDQBWE6.js → chunk-3KLWGHDE.js} +5 -5
  74. package/dist/{chunk-CSDQBWE6.js.map → chunk-3KLWGHDE.js.map} +1 -1
  75. package/dist/{chunk-UZRY5UI2.js → chunk-6E6PKKAD.js} +68 -3
  76. package/dist/chunk-6E6PKKAD.js.map +1 -0
  77. package/dist/{chunk-G6GKWGOW.js → chunk-6SA2SIOU.js} +26 -10
  78. package/dist/chunk-6SA2SIOU.js.map +1 -0
  79. package/dist/{chunk-IGQEXBBG.js → chunk-6VTG73UY.js} +13 -9
  80. package/dist/chunk-6VTG73UY.js.map +1 -0
  81. package/dist/{chunk-TTY56FQQ.js → chunk-74GTZ4TJ.js} +17 -5
  82. package/dist/chunk-74GTZ4TJ.js.map +1 -0
  83. package/dist/chunk-7QBNJTTQ.js +3 -0
  84. package/dist/{chunk-W2O5LWYU.js.map → chunk-7QBNJTTQ.js.map} +1 -1
  85. package/dist/{chunk-SL6JVGRD.js → chunk-CEUHU3C4.js} +3 -3
  86. package/dist/{chunk-SL6JVGRD.js.map → chunk-CEUHU3C4.js.map} +1 -1
  87. package/dist/{chunk-J2FCO6TM.js → chunk-FIHVQFXB.js} +2 -2
  88. package/dist/{chunk-J2FCO6TM.js.map → chunk-FIHVQFXB.js.map} +1 -1
  89. package/dist/{chunk-F3MGZ5E6.js → chunk-FVZLCBSX.js} +3 -3
  90. package/dist/{chunk-F3MGZ5E6.js.map → chunk-FVZLCBSX.js.map} +1 -1
  91. package/dist/{chunk-7PTP3SQJ.js → chunk-GTS2FODO.js} +32 -7
  92. package/dist/chunk-GTS2FODO.js.map +1 -0
  93. package/dist/{chunk-KA46DUM4.js → chunk-I5SGBFMM.js} +51 -3
  94. package/dist/chunk-I5SGBFMM.js.map +1 -0
  95. package/dist/{chunk-MO4JPTRD.js → chunk-LDLZFYLR.js} +5 -5
  96. package/dist/{chunk-MO4JPTRD.js.map → chunk-LDLZFYLR.js.map} +1 -1
  97. package/dist/{chunk-TKOLD2O7.js → chunk-LDYPQVRU.js} +516 -145
  98. package/dist/chunk-LDYPQVRU.js.map +1 -0
  99. package/dist/{chunk-GKM6MDUC.js → chunk-M5JDVO6D.js} +3 -3
  100. package/dist/{chunk-GKM6MDUC.js.map → chunk-M5JDVO6D.js.map} +1 -1
  101. package/dist/{chunk-XHFMUGDD.js → chunk-M5TE6YI5.js} +3 -3
  102. package/dist/{chunk-XHFMUGDD.js.map → chunk-M5TE6YI5.js.map} +1 -1
  103. package/dist/{chunk-NGC7ZQI4.js → chunk-NICAMYPV.js} +39 -45
  104. package/dist/chunk-NICAMYPV.js.map +1 -0
  105. package/dist/{chunk-WIR34WMU.js → chunk-NQL3T75I.js} +24 -59
  106. package/dist/chunk-NQL3T75I.js.map +1 -0
  107. package/dist/{chunk-RENHNO4J.js → chunk-P4FYHEHW.js} +206 -137
  108. package/dist/chunk-P4FYHEHW.js.map +1 -0
  109. package/dist/{chunk-2DNSSQ22.js → chunk-TWQPDBHB.js} +270 -173
  110. package/dist/chunk-TWQPDBHB.js.map +1 -0
  111. package/dist/{chunk-UZVHJ7LX.js → chunk-UBLTUFFI.js} +4 -4
  112. package/dist/{chunk-UZVHJ7LX.js.map → chunk-UBLTUFFI.js.map} +1 -1
  113. package/dist/{chunk-X5Y4EGZB.js → chunk-VUCPJBAG.js} +43 -10
  114. package/dist/chunk-VUCPJBAG.js.map +1 -0
  115. package/dist/{chunk-PBWMV5GM.js → chunk-WQ7DE5UC.js} +18 -4
  116. package/dist/chunk-WQ7DE5UC.js.map +1 -0
  117. package/dist/cli/index.d.ts +2 -0
  118. package/dist/cli/index.js +258 -262
  119. package/dist/cli/index.js.map +1 -1
  120. package/dist/cli/src/commands/deploy.d.ts.map +1 -1
  121. package/dist/cli/src/commands/manage.d.ts +22 -31
  122. package/dist/cli/src/commands/manage.d.ts.map +1 -1
  123. package/dist/cli/src/commands/npx.d.ts +5 -3
  124. package/dist/cli/src/commands/npx.d.ts.map +1 -1
  125. package/dist/cli/src/commands/project.d.ts.map +1 -1
  126. package/dist/cli/src/commands/source.d.ts +7 -0
  127. package/dist/cli/src/commands/source.d.ts.map +1 -1
  128. package/dist/cli/src/commands/update.d.ts.map +1 -1
  129. package/dist/cli/src/ensure-sources.d.ts.map +1 -1
  130. package/dist/client/index.d.ts +2 -0
  131. package/dist/connectors/config.d.ts +2 -0
  132. package/dist/connectors/config.js +6 -6
  133. package/dist/connectors/index.d.ts +2 -0
  134. package/dist/connectors/index.js +8 -8
  135. package/dist/connectors/rclone-config.d.ts +2 -0
  136. package/dist/connectors/rclone.d.ts +2 -0
  137. package/dist/connectors-shared/index.d.ts +2 -0
  138. package/dist/core/discovery.d.ts +2 -0
  139. package/dist/core/driver-targets.d.ts +2 -0
  140. package/dist/core/framework.d.ts +2 -0
  141. package/dist/core/index.d.ts +2 -0
  142. package/dist/core/index.js +5 -5
  143. package/dist/core/logging.d.ts +2 -0
  144. package/dist/core/manifest.d.ts +2 -0
  145. package/dist/core/manifest.js +2 -2
  146. package/dist/core/models.d.ts +2 -0
  147. package/dist/core/models.js +1 -1
  148. package/dist/core/runtime-assets.d.ts +2 -0
  149. package/dist/core/runtime-assets.js +4 -4
  150. package/dist/core/src/index.d.ts +7 -5
  151. package/dist/core/src/index.d.ts.map +1 -1
  152. package/dist/core/src/lock.d.ts +47 -26
  153. package/dist/core/src/lock.d.ts.map +1 -1
  154. package/dist/core/src/manifest.d.ts.map +1 -1
  155. package/dist/core/src/models.d.ts +61 -49
  156. package/dist/core/src/models.d.ts.map +1 -1
  157. package/dist/core/src/repo-manager.d.ts +79 -36
  158. package/dist/core/src/repo-manager.d.ts.map +1 -1
  159. package/dist/core/src/runtime-assets.d.ts.map +1 -1
  160. package/dist/core/src/walker.d.ts +52 -0
  161. package/dist/core/src/walker.d.ts.map +1 -0
  162. package/dist/core/src/workspace-config.d.ts +160 -45
  163. package/dist/core/src/workspace-config.d.ts.map +1 -1
  164. package/dist/core/src/workspace-yaml-editor.d.ts +33 -16
  165. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  166. package/dist/core/store.d.ts +2 -0
  167. package/dist/core/workspace-config.d.ts +2 -0
  168. package/dist/core/workspace-config.js +3 -3
  169. package/dist/deploy/index.d.ts +2 -0
  170. package/dist/deploy/index.js +167 -52
  171. package/dist/deploy/index.js.map +1 -1
  172. package/dist/deploy/src/index.d.ts +4 -3
  173. package/dist/deploy/src/index.d.ts.map +1 -1
  174. package/dist/deploy/src/targets/container-runtime.d.ts +1 -0
  175. package/dist/deploy/src/targets/container-runtime.d.ts.map +1 -1
  176. package/dist/deploy/src/targets/docker.d.ts +1 -0
  177. package/dist/deploy/src/targets/docker.d.ts.map +1 -1
  178. package/dist/deploy/src/targets/local.d.ts.map +1 -1
  179. package/dist/deploy/src/targets/nix.d.ts +36 -0
  180. package/dist/deploy/src/targets/nix.d.ts.map +1 -0
  181. package/dist/deploy/src/targets/podman.d.ts +1 -0
  182. package/dist/deploy/src/targets/podman.d.ts.map +1 -1
  183. package/dist/deploy/src/targets/process-handle.d.ts +34 -0
  184. package/dist/deploy/src/targets/process-handle.d.ts.map +1 -0
  185. package/dist/discovery/index.d.ts +2 -0
  186. package/dist/discovery/index.js +3 -3
  187. package/dist/discovery/src/source-config.d.ts +2 -2
  188. package/dist/{ensure-sources-COGVKY44.js → ensure-sources-ALTI5PXR.js} +20 -16
  189. package/dist/ensure-sources-ALTI5PXR.js.map +1 -0
  190. package/dist/library/index.d.ts +2 -0
  191. package/dist/library/index.js +4 -4
  192. package/dist/library/src/remote/remote-catalog-source.d.ts +17 -0
  193. package/dist/library/src/remote/remote-catalog-source.d.ts.map +1 -1
  194. package/dist/open-library-EEGG6RDN.js +13 -0
  195. package/dist/{open-library-DWAQFUSQ.js.map → open-library-EEGG6RDN.js.map} +1 -1
  196. package/dist/plugin-registry/index.d.ts +2 -0
  197. package/dist/plugin-registry/index.js +1 -1
  198. package/dist/plugin-registry/src/context.d.ts +30 -1
  199. package/dist/plugin-registry/src/context.d.ts.map +1 -1
  200. package/dist/plugin-registry/src/deploy-handle.d.ts +17 -1
  201. package/dist/plugin-registry/src/deploy-handle.d.ts.map +1 -1
  202. package/dist/plugin-registry/src/deploy-helpers.d.ts +69 -0
  203. package/dist/plugin-registry/src/deploy-helpers.d.ts.map +1 -0
  204. package/dist/plugin-registry/src/index.d.ts +6 -4
  205. package/dist/plugin-registry/src/index.d.ts.map +1 -1
  206. package/dist/plugin-registry/src/internal.d.ts.map +1 -1
  207. package/dist/plugin-registry/src/registry.d.ts +1 -0
  208. package/dist/plugin-registry/src/registry.d.ts.map +1 -1
  209. package/dist/plugin-registry/src/targets.d.ts +4 -0
  210. package/dist/plugin-registry/src/targets.d.ts.map +1 -1
  211. package/dist/{plugin-store-6OENKNFW.js → plugin-store-G277ZX3B.js} +8 -8
  212. package/dist/{plugin-store-6OENKNFW.js.map → plugin-store-G277ZX3B.js.map} +1 -1
  213. package/dist/plugins/index.d.ts +2 -0
  214. package/dist/plugins/src/catalog-source.d.ts +5 -0
  215. package/dist/plugins/src/catalog-source.d.ts.map +1 -1
  216. package/dist/resolver/index.d.ts +2 -0
  217. package/dist/runner/index.d.ts +2 -0
  218. package/dist/runner/index.js +17 -16
  219. package/dist/runner/prompt-assembly.d.ts +2 -0
  220. package/dist/runner/src/resources.d.ts.map +1 -1
  221. package/dist/runner/src/serve.d.ts +7 -0
  222. package/dist/runner/src/serve.d.ts.map +1 -1
  223. package/dist/sdk/asset-manager.d.ts +2 -0
  224. package/dist/sdk/asset-manager.js +7 -7
  225. package/dist/sdk/bridge.d.ts +2 -0
  226. package/dist/sdk/bridge.js +3 -3
  227. package/dist/sdk/client.d.ts +2 -0
  228. package/dist/sdk/core.d.ts +2 -0
  229. package/dist/sdk/core.js +5 -5
  230. package/dist/sdk/flow.d.ts +2 -0
  231. package/dist/sdk/index.d.ts +2 -0
  232. package/dist/sdk/index.js +53 -19
  233. package/dist/sdk/index.js.map +1 -1
  234. package/dist/sdk/resolver.d.ts +2 -0
  235. package/dist/sdk/runner.d.ts +2 -0
  236. package/dist/sdk/runner.js +17 -16
  237. package/dist/sdk/session.d.ts +2 -0
  238. package/dist/sdk/src/local-runtime.d.ts +8 -0
  239. package/dist/sdk/src/local-runtime.d.ts.map +1 -1
  240. package/dist/sdk/src/transport.d.ts +7 -1
  241. package/dist/sdk/src/transport.d.ts.map +1 -1
  242. package/dist/sdk/store.d.ts +2 -0
  243. package/dist/sdk/telemetry.d.ts +2 -0
  244. package/dist/sdk/transport/ws/client.d.ts +2 -0
  245. package/dist/sdk/transport/ws/client.js +2 -1
  246. package/dist/sdk/transport/ws/server.d.ts +2 -0
  247. package/dist/sdk/transport/ws/server.js +2 -1
  248. package/dist/sdk/transport/ws.d.ts +2 -0
  249. package/dist/sdk/transport/ws.js +4 -3
  250. package/dist/sdk/transport.d.ts +2 -0
  251. package/dist/sdk/transport.js +4 -3
  252. package/dist/sdk/types.d.ts +2 -0
  253. package/dist/secrets/index.d.ts +2 -0
  254. package/dist/session/index.d.ts +2 -0
  255. package/dist/{setup-ACMP3QZC.js → setup-REX4I5NE.js} +10 -10
  256. package/dist/{setup-ACMP3QZC.js.map → setup-REX4I5NE.js.map} +1 -1
  257. package/dist/store/index.d.ts +2 -0
  258. package/dist/store/react.d.ts +2 -0
  259. package/dist/store/vue.d.ts +2 -0
  260. package/dist/store-client-IX3Y67NK.js +14 -0
  261. package/dist/{store-client-ZSLNOOQG.js.map → store-client-IX3Y67NK.js.map} +1 -1
  262. package/dist/telemetry/index.d.ts +2 -0
  263. package/dist/transport/index.d.ts +2 -0
  264. package/dist/transport/index.js +4 -3
  265. package/dist/transport/src/ws/auth.d.ts +34 -0
  266. package/dist/transport/src/ws/auth.d.ts.map +1 -0
  267. package/dist/transport/src/ws/client.d.ts +4 -0
  268. package/dist/transport/src/ws/client.d.ts.map +1 -1
  269. package/dist/transport/src/ws/index.d.ts +3 -2
  270. package/dist/transport/src/ws/index.d.ts.map +1 -1
  271. package/dist/transport/src/ws/server.d.ts +5 -0
  272. package/dist/transport/src/ws/server.d.ts.map +1 -1
  273. package/dist/transport/ws/client.d.ts +2 -0
  274. package/dist/transport/ws/client.js +2 -1
  275. package/dist/transport/ws/server.d.ts +2 -0
  276. package/dist/transport/ws/server.js +2 -1
  277. package/dist/transport/ws.d.ts +2 -0
  278. package/dist/transport/ws.js +4 -3
  279. package/dist/tui/index.d.ts +2 -0
  280. package/dist/tui/index.js +17 -16
  281. package/dist/tui/index.js.map +1 -1
  282. package/dist/types/index.d.ts +2 -0
  283. package/dist/types/manifests.d.ts +2 -0
  284. package/dist/workspace-plugin/adapters/mcp.d.ts +2 -0
  285. package/dist/workspace-plugin/adapters/omp.d.ts +2 -0
  286. package/dist/workspace-plugin/index.d.ts +2 -0
  287. package/dist/workspace-plugin/index.js +1 -1
  288. package/package.json +4 -2
  289. package/dist/asset-feeds-PJDJ3QYI.js.map +0 -1
  290. package/dist/chunk-2DNSSQ22.js.map +0 -1
  291. package/dist/chunk-7PTP3SQJ.js.map +0 -1
  292. package/dist/chunk-G6GKWGOW.js.map +0 -1
  293. package/dist/chunk-IGQEXBBG.js.map +0 -1
  294. package/dist/chunk-KA46DUM4.js.map +0 -1
  295. package/dist/chunk-NGC7ZQI4.js.map +0 -1
  296. package/dist/chunk-PBWMV5GM.js.map +0 -1
  297. package/dist/chunk-RENHNO4J.js.map +0 -1
  298. package/dist/chunk-TKOLD2O7.js.map +0 -1
  299. package/dist/chunk-TTY56FQQ.js.map +0 -1
  300. package/dist/chunk-UZRY5UI2.js.map +0 -1
  301. package/dist/chunk-W2O5LWYU.js +0 -3
  302. package/dist/chunk-WIR34WMU.js.map +0 -1
  303. package/dist/chunk-X5Y4EGZB.js.map +0 -1
  304. package/dist/ensure-sources-COGVKY44.js.map +0 -1
  305. package/dist/open-library-DWAQFUSQ.js +0 -13
  306. package/dist/store-client-ZSLNOOQG.js +0 -14
@@ -1,10 +1,13 @@
1
- import { scanDirectory, fromMcpServerMd } from './chunk-WIR34WMU.js';
2
- import { parseAssetRef } from './chunk-X5Y4EGZB.js';
3
- import { mkdirSync, existsSync, readFileSync, writeFileSync, lstatSync, symlinkSync, rmSync, readdirSync } from 'fs';
1
+ import { scanDirectory, parseFrontmatter, fromMcpServerMd } from './chunk-NQL3T75I.js';
2
+ import { parseAssetRef } from './chunk-VUCPJBAG.js';
3
+ import { mkdirSync, existsSync, readFileSync, writeFileSync, lstatSync, symlinkSync, rmSync, readdirSync, statSync } from 'fs';
4
4
  import { homedir } from 'os';
5
- import { join, resolve, parse as parse$1, dirname } from 'path';
5
+ import { join, resolve, relative, parse as parse$1, dirname } from 'path';
6
6
  import { parse, stringify } from 'yaml';
7
7
  import { spawnSync } from 'child_process';
8
+ import semver from 'semver';
9
+ import { createHash } from 'crypto';
10
+ import fg from 'fast-glob';
8
11
 
9
12
  function getGlobalCacheDir() {
10
13
  return process.env.SKAILE_CACHE_DIR ?? join(homedir(), ".skaile", "repos");
@@ -117,9 +120,14 @@ function ensureRepo(decl, name, reposDir, opts) {
117
120
  if (decl.path) {
118
121
  const projectDir = opts?.projectDir ?? resolve(reposDir, "..");
119
122
  const resolved = resolve(projectDir, decl.path);
120
- if (!existsSync(resolved)) {
123
+ if (existsSync(resolved)) return resolved;
124
+ if (!decl.url) {
121
125
  throw new Error(`Local repository path not found: ${decl.path} (resolved: ${resolved})`);
122
126
  }
127
+ if (!cloneRepo(decl.url, decl.branch ?? "main", resolved)) {
128
+ throw new Error(`Failed to clone ${decl.url} into ${resolved}`);
129
+ }
130
+ if (opts?.pin) checkoutPin(resolved, opts.pin);
123
131
  return resolved;
124
132
  }
125
133
  if (decl.url) {
@@ -157,13 +165,25 @@ function ensureRepo(decl, name, reposDir, opts) {
157
165
  }
158
166
  throw new Error(`Repository "${name}" has neither url nor path`);
159
167
  }
168
+ function repoSlugFromGitRemote(rootDir) {
169
+ const r = spawnSync("git", ["-C", rootDir, "config", "--get", "remote.origin.url"], {
170
+ encoding: "utf8",
171
+ stdio: "pipe",
172
+ env: GIT_ENV
173
+ });
174
+ if (r.status !== 0) return void 0;
175
+ const m = /github\.com[/:]([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(r.stdout.trim());
176
+ return m ? `${m[1]}/${m[2]}` : void 0;
177
+ }
160
178
  function scanRepo(repoDir, repoName) {
161
- return scanDirectory(repoDir, repoName);
179
+ const repo = repoSlugFromGitRemote(repoDir);
180
+ const entries = scanDirectory(repoDir, repoName);
181
+ return repo ? entries.map((e) => ({ ...e, repo })) : entries;
162
182
  }
163
183
  function resolveAsset(ref, repositories, reposDir, opts) {
164
184
  let repoNames;
165
- if (ref.repository) {
166
- repoNames = [ref.repository];
185
+ if (ref.publisher) {
186
+ repoNames = [ref.publisher];
167
187
  } else if (opts?.preferRepo && opts.preferRepo in repositories) {
168
188
  const rest = Object.keys(repositories).filter((n) => n !== opts.preferRepo);
169
189
  repoNames = [opts.preferRepo, ...rest];
@@ -199,67 +219,149 @@ function resolveRepoDir(decl, name, reposDir, projectDir) {
199
219
  if (existsSync(join(globalDest, ".git"))) return globalDest;
200
220
  return null;
201
221
  }
202
- function resolveAll(deps, repositories, reposDir, opts) {
222
+ var CanonicalRefConflictError = class extends Error {
223
+ constructor(ref, candidates, depChain) {
224
+ super(
225
+ [
226
+ `error: divergent sha256 for ${ref}`,
227
+ "",
228
+ " pulled in via:",
229
+ ...depChain.map((s, i) => ` ${" ".repeat(i * 2)}${s}`),
230
+ "",
231
+ " candidates:",
232
+ ...candidates.map(
233
+ (c) => ` ${c.sourceUrl} @ ${c.commit.slice(0, 8)}
234
+ sha256: ${c.sha256}`
235
+ ),
236
+ "",
237
+ "resolve by:",
238
+ " 1. removing one source from skaile.yaml,",
239
+ " 2. configuring a store and using its canonical digest, or",
240
+ " 3. adding to overrides: (with a non-empty reason:)"
241
+ ].join("\n")
242
+ );
243
+ this.ref = ref;
244
+ this.candidates = candidates;
245
+ this.depChain = depChain;
246
+ this.name = "CanonicalRefConflictError";
247
+ }
248
+ ref;
249
+ candidates;
250
+ depChain;
251
+ };
252
+ var SHA_PIN_RE = /^[0-9a-f]{40}$/i;
253
+ async function resolveAll(deps, opts) {
203
254
  const resolved = [];
204
255
  const seen = /* @__PURE__ */ new Set();
205
256
  const missing = [];
206
257
  const resolvedBy = /* @__PURE__ */ new Map();
207
- function visit(refStr, parent, parentRepo) {
258
+ const overridesApplied = /* @__PURE__ */ new Set();
259
+ const overridesByRef = new Map(opts.overrides.map((o) => [o.ref, o]));
260
+ async function visit(refStr, parent, depChain) {
208
261
  const ref = parseAssetRef(refStr);
209
- const key = `${ref.kind}:${ref.name}`;
262
+ if (!ref.publisher) throw new Error(`dep "${refStr}" missing publisher`);
263
+ const key = `${ref.publisher}/${ref.kind}:${ref.name}`;
210
264
  if (seen.has(key)) return;
211
265
  seen.add(key);
212
- const preferRepo = ref.repository ?? parentRepo;
213
- const entry = resolveAsset(ref, repositories, reposDir, {
214
- ...opts,
215
- preferRepo
216
- });
217
- if (!entry) {
266
+ const sourceCandidates = opts.provenanceIndex.get(key) ?? [];
267
+ const storeCandidates = [];
268
+ if (opts.stores.length > 0 && opts.storeFetcher) {
269
+ const candidateVersions = enumerateVersionsForPin(
270
+ ref.pin,
271
+ sourceCandidates.map((c) => c.version)
272
+ );
273
+ for (const store of opts.stores) {
274
+ for (const v of candidateVersions) {
275
+ if (!v) continue;
276
+ const m = await opts.storeFetcher.getInstallManifest(
277
+ store.url,
278
+ `${ref.publisher}/${ref.kind}:${ref.name}@${v}`
279
+ );
280
+ if (m) {
281
+ storeCandidates.push({
282
+ publisher: ref.publisher,
283
+ kind: ref.kind,
284
+ name: ref.name,
285
+ version: v,
286
+ sourceUrl: m.sourceUrl,
287
+ commit: m.commit,
288
+ files: m.files,
289
+ sha256: m.sha256
290
+ });
291
+ }
292
+ }
293
+ }
294
+ }
295
+ const all = [...sourceCandidates, ...storeCandidates];
296
+ if (all.length === 0) {
218
297
  missing.push(refStr);
219
298
  return;
220
299
  }
221
- resolvedBy.set(key, parent);
222
- const entryRepo = entry.repository;
223
- for (const req of entry.requires) {
224
- visit(`${req.kind}:${req.name}`, key, entryRepo);
300
+ const filtered = all.filter((c) => matchPin(ref.pin, c.version));
301
+ if (filtered.length === 0) {
302
+ missing.push(refStr);
303
+ return;
225
304
  }
226
- for (const dep of entry.dependencies) {
227
- visit(dep, key, entryRepo);
305
+ const versions = Array.from(new Set(filtered.map((c) => c.version)));
306
+ const best = versions.sort((a, b) => semver.rcompare(coerceVersion(a), coerceVersion(b)))[0];
307
+ const finalists = filtered.filter((c) => c.version === best);
308
+ const canonicalRef = `${ref.publisher}/${ref.kind}:${ref.name}@${best}`;
309
+ const uniqueShas = Array.from(new Set(finalists.map((c) => c.sha256)));
310
+ let chosen;
311
+ if (uniqueShas.length > 1) {
312
+ const override = overridesByRef.get(canonicalRef);
313
+ if (!override) {
314
+ throw new CanonicalRefConflictError(canonicalRef, finalists, [...depChain, refStr]);
315
+ }
316
+ const pick = finalists.find((c) => c.sourceUrl === override.source);
317
+ if (!pick) {
318
+ throw new Error(
319
+ `override for ${canonicalRef} points at ${override.source}, which is not among the resolved candidates`
320
+ );
321
+ }
322
+ chosen = pick;
323
+ overridesApplied.add(canonicalRef);
324
+ } else {
325
+ chosen = [...finalists].sort((a, b) => a.sourceUrl.localeCompare(b.sourceUrl))[0];
326
+ }
327
+ if (opts.storeFetcher && opts.stores.length > 0 && sourceCandidates.some((c) => c.version === best) && storeCandidates.some((c) => c.version === best)) {
328
+ for (const store of opts.stores) {
329
+ const d = await opts.storeFetcher.getCanonicalDigest(store.url, canonicalRef);
330
+ if (d && d.sha256 !== chosen.sha256) {
331
+ throw new CanonicalRefConflictError(
332
+ canonicalRef,
333
+ [...finalists, { ...chosen, sourceUrl: store.url, sha256: d.sha256 }],
334
+ [...depChain, refStr]
335
+ );
336
+ }
337
+ }
228
338
  }
229
- resolved.push(entry);
339
+ resolved.push(chosen);
340
+ resolvedBy.set(key, parent);
230
341
  }
231
342
  for (const dep of deps) {
232
- visit(dep, "direct");
343
+ await visit(dep, "direct", []);
233
344
  }
234
- const collisions = detectCollisions(resolved, repositories, reposDir, opts?.projectDir);
235
- return { resolved, missing, resolvedBy, collisions };
345
+ return { resolved, missing, resolvedBy, overridesApplied };
236
346
  }
237
- function detectCollisions(resolved, repositories, reposDir, projectDir) {
238
- const repoNames = Object.keys(repositories);
239
- if (repoNames.length < 2) return [];
240
- const repoEntries = /* @__PURE__ */ new Map();
241
- for (const repoName of repoNames) {
242
- const decl = repositories[repoName];
243
- if (!decl) continue;
244
- const repoDir = resolveRepoDir(decl, repoName, reposDir, projectDir);
245
- if (!repoDir || !existsSync(repoDir)) continue;
246
- repoEntries.set(repoName, scanRepo(repoDir, repoName));
247
- }
248
- const collisions = [];
249
- for (const entry of resolved) {
250
- const key = `${entry.kind}:${entry.name}`;
251
- const shadowedIn = [];
252
- for (const [repoName, entries] of repoEntries) {
253
- if (repoName === entry.repository) continue;
254
- if (entries.some((e) => e.kind === entry.kind && e.name === entry.name)) {
255
- shadowedIn.push(repoName);
256
- }
257
- }
258
- if (shadowedIn.length > 0 && entry.repository) {
259
- collisions.push({ key, resolvedFrom: entry.repository, shadowedIn });
260
- }
347
+ function coerceVersion(v) {
348
+ return semver.valid(v) ? v : semver.coerce(v)?.version ?? "0.0.0";
349
+ }
350
+ function matchPin(pin, version) {
351
+ if (!pin) return true;
352
+ if (SHA_PIN_RE.test(pin)) {
353
+ return version === `0.0.0-sha.${pin.slice(0, 7)}`;
261
354
  }
262
- return collisions;
355
+ if (version.startsWith("0.0.0-sha.")) {
356
+ return version === pin;
357
+ }
358
+ if (!semver.valid(version)) return version === pin;
359
+ return semver.satisfies(version, pin, { includePrerelease: false });
360
+ }
361
+ function enumerateVersionsForPin(pin, sourceVersionsHint) {
362
+ if (!pin) return sourceVersionsHint.length > 0 ? sourceVersionsHint : [];
363
+ if (semver.valid(pin)) return [pin];
364
+ return sourceVersionsHint;
263
365
  }
264
366
  function checkRepoStatus(decl, name, reposDir, projectDir) {
265
367
  if (projectDir) {
@@ -320,8 +422,185 @@ function checkRepoStatus(decl, name, reposDir, projectDir) {
320
422
  base.upToDate = base.behind === 0;
321
423
  return base;
322
424
  }
425
+ function fileSha256(p) {
426
+ return createHash("sha256").update(readFileSync(p)).digest("hex");
427
+ }
428
+ function compositeSha256(files) {
429
+ const lines = files.slice().sort((a, b) => a.path.localeCompare(b.path)).map((f) => `${f.path}:${f.sha256}
430
+ `).join("");
431
+ return createHash("sha256").update(lines).digest("hex");
432
+ }
433
+ function publisherFromGithubUrl(url) {
434
+ const m = url.match(/github\.com[/:]([^/]+)\/[^/]+/);
435
+ return m?.[1];
436
+ }
437
+ function syntheticVersion(commit) {
438
+ return `0.0.0-sha.${commit.slice(0, 7)}`;
439
+ }
440
+ var KIND_DIRS = [
441
+ { dir: "skills", kind: "skill", mdName: "SKILL.md" },
442
+ { dir: "agents", kind: "agent", mdName: "AGENT.md" },
443
+ { dir: "bundles", kind: "bundle", mdName: "BUNDLE.md" },
444
+ { dir: "mcp-servers", kind: "mcp-server", mdName: "MCP.md" },
445
+ { dir: "connectors", kind: "connector", mdName: "CONNECTOR.md" },
446
+ { dir: "prompts", kind: "prompt", mdName: "PROMPT.md" },
447
+ { dir: "contracts", kind: "contract", mdName: "CONTRACT.md" }
448
+ ];
449
+ var MD_FILE_BY_KIND = Object.fromEntries(
450
+ KIND_DIRS.map((k) => [k.kind, k.mdName])
451
+ );
452
+ function buildProvenanceIndex(clones, _opts) {
453
+ const index = /* @__PURE__ */ new Map();
454
+ for (const clone of clones) walkOne(clone, index);
455
+ return index;
456
+ }
457
+ function walkOne(clone, index) {
458
+ const yamlPath = join(clone.localPath, "skaile.yaml");
459
+ if (existsSync(yamlPath)) {
460
+ walkWithManifest(clone, yamlPath, index);
461
+ } else {
462
+ walkFilenameConvention(clone, index);
463
+ }
464
+ }
465
+ function walkWithManifest(clone, yamlPath, index) {
466
+ const raw = parse(readFileSync(yamlPath, "utf8")) ?? {};
467
+ const publisher = typeof raw.publisher === "string" && raw.publisher.length > 0 ? raw.publisher : publisherFromGithubUrl(clone.sourceUrl);
468
+ if (!publisher) {
469
+ throw new Error(
470
+ `${clone.sourceUrl}: publisher must be declared in skaile.yaml (non-GitHub source URL \u2014 auto-derivation not available).`
471
+ );
472
+ }
473
+ const sourceVersion = (typeof raw.version === "string" ? raw.version : void 0) ?? (clone.tag ? clone.tag.replace(/^v/, "") : void 0) ?? syntheticVersion(clone.commit);
474
+ const assets = Array.isArray(raw.assets) ? raw.assets : [];
475
+ if (assets.length === 0) {
476
+ walkFilenameConvention(clone, index, publisher, sourceVersion);
477
+ return;
478
+ }
479
+ for (const a of assets) {
480
+ const assetPublisher = a.publisher ?? publisher;
481
+ const version = a.version ?? sourceVersion;
482
+ const files = expandAssetFiles(clone.localPath, a);
483
+ verifyNameMatch(clone.localPath, files, a.name, a.kind);
484
+ const metadata = readMetadata(clone.localPath, files, a.kind);
485
+ const candidate = {
486
+ publisher: assetPublisher,
487
+ kind: a.kind,
488
+ name: a.name,
489
+ version,
490
+ sourceUrl: clone.sourceUrl,
491
+ commit: clone.commit,
492
+ files,
493
+ sha256: compositeSha256(files),
494
+ metadata
495
+ };
496
+ push(index, `${assetPublisher}/${a.kind}:${a.name}`, candidate);
497
+ }
498
+ }
499
+ function expandAssetFiles(repoRoot, a) {
500
+ const out = [];
501
+ if (a.root) {
502
+ walkDirRecursive(join(repoRoot, a.root), repoRoot, out);
503
+ }
504
+ if (a.files) {
505
+ const matches = fg.sync(a.files, { cwd: repoRoot, onlyFiles: true, dot: false });
506
+ for (const rel of matches) {
507
+ out.push({ path: rel, sha256: fileSha256(join(repoRoot, rel)) });
508
+ }
509
+ }
510
+ const seen = /* @__PURE__ */ new Set();
511
+ return out.filter((f) => seen.has(f.path) ? false : (seen.add(f.path), true));
512
+ }
513
+ function walkDirRecursive(abs, root, out) {
514
+ if (!existsSync(abs)) return;
515
+ for (const entry of readdirSync(abs, { withFileTypes: true })) {
516
+ if (entry.name === ".git" || entry.name === "node_modules") continue;
517
+ const childAbs = join(abs, entry.name);
518
+ if (entry.isDirectory()) {
519
+ walkDirRecursive(childAbs, root, out);
520
+ } else if (entry.isFile()) {
521
+ out.push({ path: relative(root, childAbs), sha256: fileSha256(childAbs) });
522
+ }
523
+ }
524
+ }
525
+ function walkFilenameConvention(clone, index, publisherOverride, versionOverride) {
526
+ const publisher = publisherOverride ?? publisherFromGithubUrl(clone.sourceUrl);
527
+ if (!publisher) {
528
+ throw new Error(
529
+ `${clone.sourceUrl}: publisher must be declared in skaile.yaml (non-GitHub source URL).`
530
+ );
531
+ }
532
+ const version = versionOverride ?? (clone.tag ? clone.tag.replace(/^v/, "") : void 0) ?? syntheticVersion(clone.commit);
533
+ for (const { dir, kind, mdName } of KIND_DIRS) {
534
+ const base = join(clone.localPath, dir);
535
+ if (!existsSync(base) || !statSync(base).isDirectory()) continue;
536
+ for (const entry of readdirSync(base, { withFileTypes: true })) {
537
+ if (!entry.isDirectory()) continue;
538
+ const assetDir = join(base, entry.name);
539
+ if (!existsSync(join(assetDir, mdName))) continue;
540
+ const files = [];
541
+ walkDirRecursive(assetDir, clone.localPath, files);
542
+ const metadata = readMetadata(clone.localPath, files, kind);
543
+ const candidate = {
544
+ publisher,
545
+ kind,
546
+ name: entry.name,
547
+ version,
548
+ sourceUrl: clone.sourceUrl,
549
+ commit: clone.commit,
550
+ files,
551
+ sha256: compositeSha256(files),
552
+ metadata
553
+ };
554
+ push(index, `${publisher}/${kind}:${entry.name}`, candidate);
555
+ }
556
+ }
557
+ }
558
+ function verifyNameMatch(repoRoot, files, expectedName, kind) {
559
+ const mdName = MD_FILE_BY_KIND[kind];
560
+ if (!mdName) return;
561
+ const md = files.find((f) => f.path.endsWith(mdName));
562
+ if (!md) return;
563
+ const { data } = parseFrontmatter(readFileSync(join(repoRoot, md.path), "utf8"));
564
+ const declared = typeof data.name === "string" ? data.name : void 0;
565
+ if (declared && declared !== expectedName) {
566
+ throw new Error(
567
+ `name mismatch: ${mdName} says "${declared}", skaile.yaml says "${expectedName}" \u2014 index-time hard error.`
568
+ );
569
+ }
570
+ }
571
+ function readMetadata(repoRoot, files, kind) {
572
+ if (kind !== "mcp-server") return void 0;
573
+ const mcpMd = files.find((f) => f.path.endsWith("MCP.md"));
574
+ if (!mcpMd) return void 0;
575
+ const { data } = parseFrontmatter(readFileSync(join(repoRoot, mcpMd.path), "utf8"));
576
+ return data;
577
+ }
578
+ function push(index, key, candidate) {
579
+ const arr = index.get(key) ?? [];
580
+ arr.push(candidate);
581
+ index.set(key, arr);
582
+ }
323
583
 
324
584
  // core/src/workspace-config.ts
585
+ var DEFAULT_RECIPE_ATTR = "default";
586
+ function validateAssetRecipeFlake(flake) {
587
+ if (typeof flake !== "string" || flake.length === 0) {
588
+ throw new Error("AssetRecipe.flake must be a non-empty string");
589
+ }
590
+ if (flake.length > 500) {
591
+ throw new Error(`AssetRecipe.flake too long (${flake.length} chars, max 500)`);
592
+ }
593
+ if (flake === ".") return;
594
+ const ALLOWED_SCHEME = /^(github:|git\+https:\/\/|git\+ssh:\/\/|path:\/)/;
595
+ if (!ALLOWED_SCHEME.test(flake)) {
596
+ throw new Error(
597
+ `AssetRecipe.flake "${flake}" has an unsupported source. Allowed: "." or a flake URL (github:, git+https://, git+ssh://, path:/).`
598
+ );
599
+ }
600
+ if (/[\s`$;|&<>(){}#\n\r]/.test(flake)) {
601
+ throw new Error(`AssetRecipe.flake "${flake}" contains forbidden characters.`);
602
+ }
603
+ }
325
604
  function validateAssetRecipeAttr(attr) {
326
605
  if (typeof attr !== "string" || attr.length === 0) {
327
606
  throw new Error("AssetRecipe.attr must be a non-empty string");
@@ -401,13 +680,20 @@ function listSkWorkspaceConfigs(dir) {
401
680
  function resolveSkWorkspaceConfig(projectDir, opts) {
402
681
  const name = opts?.name ?? SK_WORKSPACE_DEFAULT_NAME;
403
682
  const configs = [];
683
+ const assertNoLegacyKey = (file) => {
684
+ const d = file?.diagnostics?.find((x) => x.code === "legacy_key_rejected");
685
+ if (d) throw new Error(d.message);
686
+ };
404
687
  const userConfig = loadSkWorkspaceConfig(join(homedir(), ".skaile"), name);
688
+ assertNoLegacyKey(userConfig);
405
689
  if (userConfig) configs.push(userConfig.config);
406
690
  if (opts?.appDir) {
407
691
  const appConfig = loadSkWorkspaceConfig(opts.appDir, name);
692
+ assertNoLegacyKey(appConfig);
408
693
  if (appConfig) configs.push(appConfig.config);
409
694
  }
410
695
  const projectConfig = loadSkWorkspaceConfig(projectDir, name);
696
+ assertNoLegacyKey(projectConfig);
411
697
  if (projectConfig) configs.push(projectConfig.config);
412
698
  if (configs.length > 0) return configs.reduce(mergeSkWorkspaceConfigs);
413
699
  return {};
@@ -443,10 +729,12 @@ function mergeSkWorkspaceConfigs(base, overlay) {
443
729
  },
444
730
  // startup: overlay wins (project-level only)
445
731
  startup: overlay.startup ?? base.startup,
446
- // ai_resources: dedupe by source name overlay entry wins for same name
447
- ai_resources: dedupeByKey(
448
- [...base.ai_resources ?? [], ...overlay.ai_resources ?? []],
449
- "name"
732
+ // publication half scalars: overlay wins when defined; assets: by kind+name
733
+ publisher: overlay.publisher ?? base.publisher,
734
+ version: overlay.version ?? base.version,
735
+ assets: dedupeByKey(
736
+ [...base.assets ?? [], ...overlay.assets ?? []],
737
+ (a) => `${a.kind}:${a.name}`
450
738
  ),
451
739
  // mounts: by id — overlay wins
452
740
  mounts: mergeById(base.mounts ?? [], overlay.mounts ?? []),
@@ -511,14 +799,14 @@ function mergeSkWorkspaceConfigs(base, overlay) {
511
799
  secrets: overlay.secrets ?? base.secrets,
512
800
  // telemetry: overlay wins entirely (raw pass-through)
513
801
  telemetry: overlay.telemetry ?? base.telemetry,
514
- // repositories: always empty in merged output; the schema no longer
515
- // accepts this key. The internal install pipeline builds its own map
516
- // from `ai_resources[]` entries.
517
- repositories: {},
518
802
  // dependencies: concatenate and dedupe
519
803
  dependencies: dedupe([...base.dependencies ?? [], ...overlay.dependencies ?? []]),
520
- // sources: dedupe by name — overlay entry wins for the same name
521
- sources: dedupeByKey([...base.sources ?? [], ...overlay.sources ?? []], "name"),
804
+ // sources: dedupe by url — overlay entry wins for the same url
805
+ sources: dedupeByKey([...base.sources ?? [], ...overlay.sources ?? []], "url"),
806
+ // stores: dedupe by url — overlay wins
807
+ stores: dedupeByKey([...base.stores ?? [], ...overlay.stores ?? []], "url"),
808
+ // overrides: dedupe by ref — overlay wins
809
+ overrides: dedupeByKey([...base.overrides ?? [], ...overlay.overrides ?? []], "ref"),
522
810
  // plugins: concatenate + dedupe (like dependencies)
523
811
  plugins: dedupe([...base.plugins ?? [], ...overlay.plugins ?? []]),
524
812
  // deploy: overlay wins entirely (scalar-like selection)
@@ -600,46 +888,75 @@ function normalizeConfigInternal(raw) {
600
888
  if (Array.isArray(raw.startup)) {
601
889
  config.startup = raw.startup;
602
890
  }
603
- let aiResRaw;
604
- let aiResKey;
605
- if (raw.ai_resources !== void 0) {
606
- aiResRaw = raw.ai_resources;
607
- aiResKey = "ai_resources";
608
- } else if (raw.aiResources !== void 0) {
609
- aiResRaw = raw.aiResources;
610
- aiResKey = "aiResources";
611
- }
612
- if (aiResKey === "aiResources") {
613
- diagnostics.push({
614
- code: "legacy_key_camelcase",
615
- severity: "warning",
616
- message: 'Non-canonical key "aiResources" \u2014 use "ai_resources".',
617
- path: "aiResources"
618
- });
891
+ if (raw.repositories !== void 0) {
892
+ throw new Error(
893
+ "skaile.yaml: unknown top-level key `repositories:`. The schema changed in @skaile/workspaces 4.x \u2014 see docs/concepts/manifest-schema.md, or ask your AI assistant to apply the `migrate-skaile-manifest` skill."
894
+ );
619
895
  }
620
- if (Array.isArray(aiResRaw)) {
621
- config.ai_resources = aiResRaw;
622
- } else if (aiResRaw && typeof aiResRaw === "object") {
623
- diagnostics.push({
624
- code: "legacy_ai_resources_object",
625
- severity: "warning",
626
- message: "Legacy ai_resources object form ({sources, requires}) \u2014 use an array of {name, path, dependencies}.",
627
- path: aiResKey
628
- });
629
- const old = aiResRaw;
630
- const sources = Array.isArray(old.sources) ? old.sources : [];
631
- const requires = Array.isArray(old.requires) ? old.requires.filter(Boolean) : [];
632
- const autoDeploy = Boolean(old.auto_deploy ?? false);
633
- config.ai_resources = sources.map((s, i) => {
634
- const entry = {
635
- name: String(s.name ?? ""),
636
- path: String(s.path ?? s.url ?? "")
637
- };
638
- if (s.branch) entry.branch = String(s.branch);
639
- if (autoDeploy) entry.auto_deploy = true;
640
- if (i === 0 && requires.length > 0) entry.dependencies = requires;
641
- return entry;
642
- });
896
+ if (raw.ai_resources !== void 0 || raw.aiResources !== void 0) {
897
+ throw new Error(
898
+ "skaile.yaml: unknown top-level key `ai_resources:`. The schema changed in @skaile/workspaces 4.x \u2014 see docs/concepts/manifest-schema.md, or ask your AI assistant to apply the `migrate-skaile-manifest` skill."
899
+ );
900
+ }
901
+ if (typeof raw.publisher === "string" && raw.publisher.length > 0) {
902
+ config.publisher = raw.publisher;
903
+ }
904
+ if (typeof raw.version === "string" && raw.version.length > 0) {
905
+ config.version = raw.version;
906
+ }
907
+ if (Array.isArray(raw.assets)) {
908
+ const entries = [];
909
+ for (const item of raw.assets) {
910
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
911
+ const a = item;
912
+ const kind = typeof a.kind === "string" ? a.kind : "";
913
+ const name = typeof a.name === "string" ? a.name : "";
914
+ if (!kind || !name) continue;
915
+ const entry = { kind, name };
916
+ if (typeof a.root === "string") entry.root = a.root;
917
+ if (Array.isArray(a.files)) {
918
+ entry.files = a.files.filter(
919
+ (f) => typeof f === "string"
920
+ );
921
+ }
922
+ if (typeof a.version === "string") entry.version = a.version;
923
+ if (typeof a.publisher === "string") entry.publisher = a.publisher;
924
+ entries.push(entry);
925
+ }
926
+ if (entries.length > 0) config.assets = entries;
927
+ }
928
+ if (Array.isArray(raw.stores)) {
929
+ const entries = [];
930
+ for (const item of raw.stores) {
931
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
932
+ const s = item;
933
+ const url = typeof s.url === "string" ? s.url : "";
934
+ if (!url) continue;
935
+ entries.push({ url });
936
+ }
937
+ if (entries.length > 0) config.stores = entries;
938
+ }
939
+ if (Array.isArray(raw.overrides)) {
940
+ const entries = [];
941
+ for (const item of raw.overrides) {
942
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
943
+ const o = item;
944
+ const ref = typeof o.ref === "string" ? o.ref : "";
945
+ const source = typeof o.source === "string" ? o.source : "";
946
+ if (!ref || !source) continue;
947
+ if (typeof o.reason !== "string") {
948
+ throw new Error(
949
+ `skaile.yaml: overrides[] entry for ${ref}: reason is required and must be a non-empty string.`
950
+ );
951
+ }
952
+ if (o.reason.trim().length === 0) {
953
+ throw new Error(
954
+ `skaile.yaml: overrides[] entry for ${ref}: reason must not be empty.`
955
+ );
956
+ }
957
+ entries.push({ ref, source, reason: o.reason });
958
+ }
959
+ if (entries.length > 0) config.overrides = entries;
643
960
  }
644
961
  if (Array.isArray(raw.mounts)) {
645
962
  config.mounts = raw.mounts;
@@ -678,13 +995,12 @@ function normalizeConfigInternal(raw) {
678
995
  if (Array.isArray(raw.sources)) {
679
996
  const entries = [];
680
997
  for (const item of raw.sources) {
681
- if (!item || typeof item !== "object") continue;
998
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
682
999
  const s = item;
683
- const name = typeof s.name === "string" ? s.name : "";
684
1000
  const url = typeof s.url === "string" ? s.url : "";
685
- if (!name || !url) continue;
686
- const entry = { name, url };
687
- if (typeof s.branch === "string" && s.branch.length > 0) entry.branch = s.branch;
1001
+ if (!url) continue;
1002
+ const entry = { url };
1003
+ if (typeof s.pin === "string" && s.pin.length > 0) entry.pin = s.pin;
688
1004
  entries.push(entry);
689
1005
  }
690
1006
  if (entries.length > 0) config.sources = entries;
@@ -722,7 +1038,21 @@ function decodeSkaileYaml(text) {
722
1038
  ]
723
1039
  };
724
1040
  }
725
- const result = normalizeConfigInternal(parsed);
1041
+ let result;
1042
+ try {
1043
+ result = normalizeConfigInternal(parsed);
1044
+ } catch (err) {
1045
+ return {
1046
+ config: {},
1047
+ diagnostics: [
1048
+ {
1049
+ code: "legacy_key_rejected",
1050
+ severity: "error",
1051
+ message: err instanceof Error ? err.message : String(err)
1052
+ }
1053
+ ]
1054
+ };
1055
+ }
726
1056
  validateConfigValues(result.config, result.diagnostics);
727
1057
  return result;
728
1058
  }
@@ -764,12 +1094,16 @@ function validateConfigValues(config, diagnostics) {
764
1094
  var CANONICAL_KEY_ORDER = [
765
1095
  "name",
766
1096
  "description",
1097
+ "publisher",
1098
+ "version",
1099
+ "assets",
767
1100
  "agent_config",
768
1101
  "dependencies",
769
1102
  "plugins",
770
1103
  "sources",
1104
+ "stores",
1105
+ "overrides",
771
1106
  "startup",
772
- "ai_resources",
773
1107
  "connectors",
774
1108
  "mcp_servers",
775
1109
  "deploy",
@@ -779,8 +1113,7 @@ var CANONICAL_KEY_ORDER = [
779
1113
  "secrets",
780
1114
  "telemetry",
781
1115
  "compaction",
782
- "patches",
783
- "repositories"
1116
+ "patches"
784
1117
  ];
785
1118
  var CONFIG_KEY_TO_YAML = {
786
1119
  agent_config: "agent-config"
@@ -814,10 +1147,11 @@ function dedupe(arr) {
814
1147
  return [...new Set(arr)];
815
1148
  }
816
1149
  function dedupeByKey(arr, key) {
1150
+ const keyOf = typeof key === "function" ? key : (item) => String(item[key] ?? "");
817
1151
  const seen = /* @__PURE__ */ new Set();
818
1152
  const result = [];
819
1153
  for (let i = arr.length - 1; i >= 0; i--) {
820
- const val = String(arr[i][key] ?? "");
1154
+ const val = keyOf(arr[i]);
821
1155
  if (!seen.has(val)) {
822
1156
  seen.add(val);
823
1157
  result.unshift(arr[i]);
@@ -854,13 +1188,22 @@ function mcpDeclFromCatalogEntry(entry) {
854
1188
  if (entry.description) decl.description = entry.description;
855
1189
  if (m.recipe && typeof m.recipe === "object" && !Array.isArray(m.recipe)) {
856
1190
  const r = m.recipe;
857
- if (typeof r.attr === "string") {
1191
+ const hasFlake = typeof r.flake === "string";
1192
+ const rawAttr = typeof r.attr === "string" ? r.attr : void 0;
1193
+ const effectiveAttr = rawAttr ?? (hasFlake ? DEFAULT_RECIPE_ATTR : void 0);
1194
+ if (effectiveAttr !== void 0) {
858
1195
  try {
859
- validateAssetRecipeAttr(r.attr);
860
- decl.recipe = { attr: r.attr };
1196
+ validateAssetRecipeAttr(effectiveAttr);
1197
+ if (hasFlake) {
1198
+ validateAssetRecipeFlake(r.flake);
1199
+ decl.recipe = { attr: effectiveAttr, flake: r.flake };
1200
+ if (typeof r.publisher === "string") decl.recipe.publisher = r.publisher;
1201
+ } else {
1202
+ decl.recipe = { attr: effectiveAttr };
1203
+ }
861
1204
  } catch (err) {
862
1205
  console.warn(
863
- `[workspace-config] mcpDeclFromCatalogEntry: invalid recipe.attr for entry "${entry.name}": ${err instanceof Error ? err.message : String(err)}`
1206
+ `[workspace-config] mcpDeclFromCatalogEntry: invalid recipe for entry "${entry.name}": ${err instanceof Error ? err.message : String(err)}`
864
1207
  );
865
1208
  }
866
1209
  }
@@ -932,33 +1275,49 @@ function loadMaterializedMcpDeclarations(projectDir) {
932
1275
  }
933
1276
  return result;
934
1277
  }
1278
+ function sourceSlug(url) {
1279
+ return url.replace(/\.git$/, "").split(/[/:]/).pop() ?? "source";
1280
+ }
1281
+ function buildSourceClones(sources) {
1282
+ const home = process.env.SKAILE_HOME ?? join(homedir(), ".skaile");
1283
+ const sourcesDir = join(home, "sources");
1284
+ const clones = [];
1285
+ for (const s of sources) {
1286
+ const localPath = join(sourcesDir, sourceSlug(s.url));
1287
+ if (!existsSync(localPath)) continue;
1288
+ const commit = getRepoCommit(localPath) ?? "0".repeat(40);
1289
+ clones.push({ localPath, sourceUrl: s.url, commit, tag: s.pin });
1290
+ }
1291
+ return clones;
1292
+ }
1293
+ function catalogEntryFromCandidate(c) {
1294
+ const md = c.metadata ?? {};
1295
+ return {
1296
+ name: c.name,
1297
+ kind: c.kind,
1298
+ description: typeof md.description === "string" ? md.description : "",
1299
+ source: c.files[0]?.path ?? "",
1300
+ publisher: c.publisher,
1301
+ version: c.version,
1302
+ requires: [],
1303
+ dependencies: [],
1304
+ metadata: c.metadata
1305
+ };
1306
+ }
935
1307
  function loadMcpServerDeclarations(projectDir) {
936
1308
  const config = resolveSkWorkspaceConfig(projectDir);
937
1309
  const explicit = config.mcp_servers ?? [];
938
1310
  const materialized = loadMaterializedMcpDeclarations(projectDir);
939
1311
  const mcpRefStrings = [];
940
1312
  for (const dep of config.dependencies ?? []) {
941
- const ref = parseAssetRef(dep);
942
- if (ref.kind === "mcp-server") mcpRefStrings.push(dep);
943
- }
944
- for (const res of config.ai_resources ?? []) {
945
- for (const dep of res.dependencies ?? []) {
1313
+ try {
946
1314
  const ref = parseAssetRef(dep);
947
1315
  if (ref.kind === "mcp-server") mcpRefStrings.push(dep);
1316
+ } catch {
948
1317
  }
949
1318
  }
950
- const repositories = config.repositories ?? {};
951
- const allRepos = { ...repositories };
952
- for (const res of config.ai_resources ?? []) {
953
- if (res.name && res.path && !(res.name in allRepos)) {
954
- allRepos[res.name] = {
955
- url: res.path.startsWith("/") ? void 0 : res.path,
956
- path: res.path.startsWith("/") ? res.path : void 0,
957
- branch: res.branch
958
- };
959
- }
960
- }
961
- const reposDir = getGlobalCacheDir();
1319
+ const sources = config.sources ?? [];
1320
+ const provenanceIndex = sources.length > 0 ? buildProvenanceIndex(buildSourceClones(sources)) : /* @__PURE__ */ new Map();
962
1321
  const byId = /* @__PURE__ */ new Map();
963
1322
  const order = [];
964
1323
  const upsert = (decl) => {
@@ -972,12 +1331,29 @@ function loadMcpServerDeclarations(projectDir) {
972
1331
  };
973
1332
  const seen = /* @__PURE__ */ new Set();
974
1333
  for (const refStr of mcpRefStrings) {
975
- const ref = parseAssetRef(refStr);
1334
+ let ref;
1335
+ try {
1336
+ ref = parseAssetRef(refStr);
1337
+ } catch {
1338
+ continue;
1339
+ }
976
1340
  if (seen.has(ref.name)) continue;
977
1341
  seen.add(ref.name);
978
- const entry = resolveAsset(ref, allRepos, reposDir, { projectDir });
979
- if (!entry) continue;
980
- const catalogDecl = mcpDeclFromCatalogEntry(entry);
1342
+ let candidates;
1343
+ if (ref.publisher) {
1344
+ candidates = provenanceIndex.get(`${ref.publisher}/${ref.kind}:${ref.name}`);
1345
+ }
1346
+ if (!candidates) {
1347
+ for (const [key, arr] of provenanceIndex) {
1348
+ if (key.endsWith(`/${ref.kind}:${ref.name}`)) {
1349
+ candidates = arr;
1350
+ break;
1351
+ }
1352
+ }
1353
+ }
1354
+ const candidate = candidates?.[0];
1355
+ if (!candidate) continue;
1356
+ const catalogDecl = mcpDeclFromCatalogEntry(catalogEntryFromCandidate(candidate));
981
1357
  if (!catalogDecl) continue;
982
1358
  upsert(catalogDecl);
983
1359
  }
@@ -1011,16 +1387,11 @@ function resolveAgentDir(projectDir) {
1011
1387
  return join(aiAssetsDir, relPath);
1012
1388
  }
1013
1389
  if (definition.startsWith("agent:")) {
1014
- const ref = parseAssetRef(definition);
1015
- const repositories = config.repositories ?? {};
1016
- const reposDir = getGlobalCacheDir();
1017
- const entry = resolveAsset(ref, repositories, reposDir, { projectDir });
1018
- if (!entry) return void 0;
1019
- return dirname(entry.source);
1390
+ return void 0;
1020
1391
  }
1021
1392
  return resolve(projectDir, definition);
1022
1393
  }
1023
1394
 
1024
- export { COMPACTION_DEFAULTS, SKAILE_YAML_DEFAULT, SKAILE_YAML_SUFFIX, SK_WORKSPACE_DEFAULT_NAME, SK_WORKSPACE_SUFFIX, checkRepoStatus, checkoutPin, cloneRepo, decodeSkaileYaml, encodeSkaileYaml, ensureRepo, findWorkspaceRoot, getGlobalCacheDir, getRepoCommit, isWorkspaceConfigFilename, linkRepo, listSkWorkspaceConfigs, loadMcpServerDeclarations, loadSkWorkspaceConfig, mergeSkWorkspaceConfigs, normalizeConfig, pullRepo, readLinks, resolveAgentDir, resolveAll, resolveAsset, resolveSkWorkspaceConfig, saveSkWorkspaceConfig, scanRepo, unlinkRepo, validateAssetRecipeAttr, workspaceConfigFilename, workspaceNameFromFilename, writeLinks };
1025
- //# sourceMappingURL=chunk-TKOLD2O7.js.map
1026
- //# sourceMappingURL=chunk-TKOLD2O7.js.map
1395
+ export { COMPACTION_DEFAULTS, CanonicalRefConflictError, DEFAULT_RECIPE_ATTR, SKAILE_YAML_DEFAULT, SKAILE_YAML_SUFFIX, SK_WORKSPACE_DEFAULT_NAME, SK_WORKSPACE_SUFFIX, buildProvenanceIndex, checkRepoStatus, checkoutPin, cloneRepo, decodeSkaileYaml, encodeSkaileYaml, ensureRepo, findWorkspaceRoot, getGlobalCacheDir, getRepoCommit, isWorkspaceConfigFilename, linkRepo, listSkWorkspaceConfigs, loadMcpServerDeclarations, loadSkWorkspaceConfig, mergeSkWorkspaceConfigs, normalizeConfig, pullRepo, readLinks, resolveAgentDir, resolveAll, resolveAsset, resolveSkWorkspaceConfig, saveSkWorkspaceConfig, scanRepo, unlinkRepo, validateAssetRecipeAttr, validateAssetRecipeFlake, workspaceConfigFilename, workspaceNameFromFilename, writeLinks };
1396
+ //# sourceMappingURL=chunk-LDYPQVRU.js.map
1397
+ //# sourceMappingURL=chunk-LDYPQVRU.js.map