@skaile/workspaces 5.15.0 → 5.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/{asset-feeds-LIDC7547.js → asset-feeds-BELBSJEE.js} +18 -18
  3. package/dist/{asset-feeds-LIDC7547.js.map → asset-feeds-BELBSJEE.js.map} +1 -1
  4. package/dist/asset-manager/catalog-deployer.js +7 -7
  5. package/dist/asset-manager/index.js +13 -13
  6. package/dist/asset-manager/installer.js +9 -9
  7. package/dist/asset-manager/renderers.js +8 -8
  8. package/dist/asset-manager/src/index.d.ts +105 -6
  9. package/dist/asset-manager/src/index.d.ts.map +1 -1
  10. package/dist/asset-manager/src/installer.d.ts +11 -2
  11. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  12. package/dist/asset-manager/src/mcp-config.d.ts.map +1 -1
  13. package/dist/bridge/drivers/claude-sdk.js +150 -19
  14. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  15. package/dist/bridge/drivers/codex.js +15 -128
  16. package/dist/bridge/drivers/codex.js.map +1 -1
  17. package/dist/bridge/drivers/continue.js +10 -10
  18. package/dist/bridge/drivers/echo.js +2 -2
  19. package/dist/bridge/drivers/gemini.js +7 -7
  20. package/dist/bridge/drivers/goose.js +10 -10
  21. package/dist/bridge/drivers/omp.js +61 -5
  22. package/dist/bridge/drivers/omp.js.map +1 -1
  23. package/dist/bridge/drivers/opencode.js +10 -10
  24. package/dist/bridge/drivers/qwen.js +7 -7
  25. package/dist/bridge/index.js +13 -12
  26. package/dist/bridge/src/approval-card.d.ts +24 -0
  27. package/dist/bridge/src/approval-card.d.ts.map +1 -0
  28. package/dist/bridge/src/driver-model-check.d.ts +21 -0
  29. package/dist/bridge/src/driver-model-check.d.ts.map +1 -0
  30. package/dist/bridge/src/drivers/claude-sdk.d.ts +22 -0
  31. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  32. package/dist/bridge/src/drivers/omp-models-yml.d.ts.map +1 -1
  33. package/dist/bridge/src/drivers/omp.d.ts.map +1 -1
  34. package/dist/bridge/src/index.d.ts +2 -0
  35. package/dist/bridge/src/index.d.ts.map +1 -1
  36. package/dist/bridge/src/types.d.ts +23 -0
  37. package/dist/bridge/src/types.d.ts.map +1 -1
  38. package/dist/{chunk-YUURCZWY.js → chunk-2SKPPIV7.js} +5 -5
  39. package/dist/{chunk-YUURCZWY.js.map → chunk-2SKPPIV7.js.map} +1 -1
  40. package/dist/{chunk-PK5XQBOC.js → chunk-46IRZJV4.js} +4 -4
  41. package/dist/{chunk-PK5XQBOC.js.map → chunk-46IRZJV4.js.map} +1 -1
  42. package/dist/{chunk-AXNX2SKZ.js → chunk-5CE6J4DB.js} +46 -67
  43. package/dist/chunk-5CE6J4DB.js.map +1 -0
  44. package/dist/{chunk-54TVXZP3.js → chunk-5DCOG5B5.js} +3 -3
  45. package/dist/{chunk-54TVXZP3.js.map → chunk-5DCOG5B5.js.map} +1 -1
  46. package/dist/{chunk-X7DIT65W.js → chunk-5RDZ5JHH.js} +4 -4
  47. package/dist/{chunk-X7DIT65W.js.map → chunk-5RDZ5JHH.js.map} +1 -1
  48. package/dist/{chunk-BV7NLPI6.js → chunk-6GSE63K2.js} +4 -4
  49. package/dist/{chunk-BV7NLPI6.js.map → chunk-6GSE63K2.js.map} +1 -1
  50. package/dist/{chunk-GQ5OYFVD.js → chunk-6Q7NKTDG.js} +47 -47
  51. package/dist/chunk-6Q7NKTDG.js.map +1 -0
  52. package/dist/chunk-6RTEUWXX.js +125 -0
  53. package/dist/chunk-6RTEUWXX.js.map +1 -0
  54. package/dist/{chunk-RTYDYSUV.js → chunk-72KZJE3G.js} +82 -22
  55. package/dist/chunk-72KZJE3G.js.map +1 -0
  56. package/dist/{chunk-SAKVAG23.js → chunk-A26MUUN7.js} +7 -7
  57. package/dist/{chunk-SAKVAG23.js.map → chunk-A26MUUN7.js.map} +1 -1
  58. package/dist/{chunk-KUB4HFGK.js → chunk-A7AZ3RL5.js} +6 -6
  59. package/dist/{chunk-KUB4HFGK.js.map → chunk-A7AZ3RL5.js.map} +1 -1
  60. package/dist/{chunk-LEOXPIC6.js → chunk-AP24HESN.js} +3 -3
  61. package/dist/{chunk-LEOXPIC6.js.map → chunk-AP24HESN.js.map} +1 -1
  62. package/dist/{chunk-SRKU7QUG.js → chunk-CQFVKN6S.js} +230 -62
  63. package/dist/chunk-CQFVKN6S.js.map +1 -0
  64. package/dist/{chunk-OOQ5Q75W.js → chunk-D3PU3C6F.js} +41 -16
  65. package/dist/chunk-D3PU3C6F.js.map +1 -0
  66. package/dist/{chunk-WXKNM2JJ.js → chunk-D5REDKVU.js} +3 -3
  67. package/dist/{chunk-WXKNM2JJ.js.map → chunk-D5REDKVU.js.map} +1 -1
  68. package/dist/{chunk-XX4C72CC.js → chunk-DADAZLFW.js} +648 -76
  69. package/dist/chunk-DADAZLFW.js.map +1 -0
  70. package/dist/{chunk-AVUJBSFA.js → chunk-DQOAPR4M.js} +2 -2
  71. package/dist/{chunk-AVUJBSFA.js.map → chunk-DQOAPR4M.js.map} +1 -1
  72. package/dist/{chunk-SCUEPCSJ.js → chunk-E634MBP6.js} +12 -9
  73. package/dist/chunk-E634MBP6.js.map +1 -0
  74. package/dist/{chunk-NKDSU4Q7.js → chunk-ES6ARJPQ.js} +21 -7
  75. package/dist/chunk-ES6ARJPQ.js.map +1 -0
  76. package/dist/{chunk-WWUWMOX2.js → chunk-FSLVMZXR.js} +37 -17
  77. package/dist/chunk-FSLVMZXR.js.map +1 -0
  78. package/dist/{chunk-MKTJZLQM.js → chunk-FSSRBWYL.js} +4 -4
  79. package/dist/{chunk-MKTJZLQM.js.map → chunk-FSSRBWYL.js.map} +1 -1
  80. package/dist/{chunk-VY6NTFE7.js → chunk-FT4ZHLYR.js} +3 -2
  81. package/dist/chunk-FT4ZHLYR.js.map +1 -0
  82. package/dist/chunk-FWAJUWXQ.js +1016 -0
  83. package/dist/chunk-FWAJUWXQ.js.map +1 -0
  84. package/dist/{chunk-ANSZULXH.js → chunk-H7R7R5BN.js} +21 -6
  85. package/dist/chunk-H7R7R5BN.js.map +1 -0
  86. package/dist/{chunk-4AKR4XPQ.js → chunk-HG52NGAV.js} +3 -3
  87. package/dist/chunk-HG52NGAV.js.map +1 -0
  88. package/dist/{plugin-store-BIITCNOZ.js → chunk-HUKUYWBJ.js} +32 -23
  89. package/dist/chunk-HUKUYWBJ.js.map +1 -0
  90. package/dist/{chunk-D2MZZ2KB.js → chunk-ISSYT5AZ.js} +3 -3
  91. package/dist/{chunk-D2MZZ2KB.js.map → chunk-ISSYT5AZ.js.map} +1 -1
  92. package/dist/{chunk-2LTF2ZHH.js → chunk-J4RRH2NP.js} +4 -4
  93. package/dist/{chunk-2LTF2ZHH.js.map → chunk-J4RRH2NP.js.map} +1 -1
  94. package/dist/{chunk-NOF73BUH.js → chunk-L6UMSKDF.js} +10 -2
  95. package/dist/chunk-L6UMSKDF.js.map +1 -0
  96. package/dist/{chunk-TCWECSEB.js → chunk-NSNM2ZXF.js} +28 -4
  97. package/dist/chunk-NSNM2ZXF.js.map +1 -0
  98. package/dist/{chunk-QR7W5JV4.js → chunk-OHNNCXMB.js} +114 -81
  99. package/dist/chunk-OHNNCXMB.js.map +1 -0
  100. package/dist/{chunk-DP6YRAPZ.js → chunk-OJHPG6HK.js} +57 -13
  101. package/dist/chunk-OJHPG6HK.js.map +1 -0
  102. package/dist/{chunk-SZPE55SB.js → chunk-PGZDSDOB.js} +3 -3
  103. package/dist/{chunk-SZPE55SB.js.map → chunk-PGZDSDOB.js.map} +1 -1
  104. package/dist/{chunk-4QINFQ27.js → chunk-PIC23DDE.js} +59 -17
  105. package/dist/chunk-PIC23DDE.js.map +1 -0
  106. package/dist/{chunk-GLMKS6V6.js → chunk-RTNWU2UD.js} +4 -4
  107. package/dist/{chunk-GLMKS6V6.js.map → chunk-RTNWU2UD.js.map} +1 -1
  108. package/dist/{chunk-YL23KWBX.js → chunk-SAGBVBY2.js} +5 -5
  109. package/dist/{chunk-YL23KWBX.js.map → chunk-SAGBVBY2.js.map} +1 -1
  110. package/dist/{chunk-JHP6MIW2.js → chunk-SMUIYYCH.js} +70 -34
  111. package/dist/chunk-SMUIYYCH.js.map +1 -0
  112. package/dist/{chunk-N36S36OZ.js → chunk-SPV2CXJ3.js} +3 -3
  113. package/dist/{chunk-N36S36OZ.js.map → chunk-SPV2CXJ3.js.map} +1 -1
  114. package/dist/{chunk-KRRHADE7.js → chunk-SXWGLW23.js} +4 -4
  115. package/dist/{chunk-KRRHADE7.js.map → chunk-SXWGLW23.js.map} +1 -1
  116. package/dist/{chunk-74THIMZV.js → chunk-TADJCATD.js} +3 -3
  117. package/dist/{chunk-74THIMZV.js.map → chunk-TADJCATD.js.map} +1 -1
  118. package/dist/{chunk-PQF5QHVS.js → chunk-TKI7YDWE.js} +19 -5
  119. package/dist/chunk-TKI7YDWE.js.map +1 -0
  120. package/dist/{chunk-O673TBWJ.js → chunk-TXGVG7VH.js} +3 -3
  121. package/dist/{chunk-O673TBWJ.js.map → chunk-TXGVG7VH.js.map} +1 -1
  122. package/dist/{chunk-PIDMOE7R.js → chunk-VJ2DZLDM.js} +62 -6
  123. package/dist/chunk-VJ2DZLDM.js.map +1 -0
  124. package/dist/{chunk-PV47EEHD.js → chunk-WKEBNB5S.js} +4 -4
  125. package/dist/{chunk-PV47EEHD.js.map → chunk-WKEBNB5S.js.map} +1 -1
  126. package/dist/{chunk-ESXZJP4M.js → chunk-WXWQNJCT.js} +3 -3
  127. package/dist/{chunk-ESXZJP4M.js.map → chunk-WXWQNJCT.js.map} +1 -1
  128. package/dist/{chunk-DPCEQOIZ.js → chunk-XGR7IDTY.js} +3 -3
  129. package/dist/{chunk-DPCEQOIZ.js.map → chunk-XGR7IDTY.js.map} +1 -1
  130. package/dist/{chunk-I3JO3NIZ.js → chunk-Y25V2MW2.js} +4 -4
  131. package/dist/{chunk-I3JO3NIZ.js.map → chunk-Y25V2MW2.js.map} +1 -1
  132. package/dist/{chunk-MGM5EJQQ.js → chunk-YK3XLEQJ.js} +40 -26
  133. package/dist/chunk-YK3XLEQJ.js.map +1 -0
  134. package/dist/{chunk-SP5NGNBV.js → chunk-YW2BNDSE.js} +11 -4
  135. package/dist/chunk-YW2BNDSE.js.map +1 -0
  136. package/dist/{chunk-J76LDUDS.js → chunk-ZVZTCDUN.js} +26 -10
  137. package/dist/chunk-ZVZTCDUN.js.map +1 -0
  138. package/dist/cli/index.js +899 -1617
  139. package/dist/cli/index.js.map +1 -1
  140. package/dist/cli/src/agent-checks.d.ts +22 -10
  141. package/dist/cli/src/agent-checks.d.ts.map +1 -1
  142. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  143. package/dist/cli/src/commands/config.d.ts.map +1 -1
  144. package/dist/cli/src/commands/integration.d.ts.map +1 -1
  145. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  146. package/dist/cli/src/commands/library-index.d.ts +6 -0
  147. package/dist/cli/src/commands/library-index.d.ts.map +1 -1
  148. package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
  149. package/dist/cli/src/commands/outdated.d.ts.map +1 -1
  150. package/dist/cli/src/commands/plugin/codex.d.ts +10 -0
  151. package/dist/cli/src/commands/plugin/codex.d.ts.map +1 -1
  152. package/dist/cli/src/commands/plugin/types.d.ts +5 -2
  153. package/dist/cli/src/commands/plugin/types.d.ts.map +1 -1
  154. package/dist/cli/src/commands/preset-export.d.ts.map +1 -1
  155. package/dist/cli/src/commands/project.d.ts +6 -0
  156. package/dist/cli/src/commands/project.d.ts.map +1 -1
  157. package/dist/cli/src/commands/rebuild.d.ts.map +1 -1
  158. package/dist/cli/src/commands/run.d.ts.map +1 -1
  159. package/dist/cli/src/commands/serve-preflight.d.ts +13 -0
  160. package/dist/cli/src/commands/serve-preflight.d.ts.map +1 -1
  161. package/dist/cli/src/commands/serve.d.ts.map +1 -1
  162. package/dist/cli/src/commands/show.d.ts.map +1 -1
  163. package/dist/cli/src/commands/source.d.ts +2 -1
  164. package/dist/cli/src/commands/source.d.ts.map +1 -1
  165. package/dist/cli/src/commands/store.d.ts.map +1 -1
  166. package/dist/cli/src/commands/update.d.ts.map +1 -1
  167. package/dist/cli/src/commands/validate.d.ts +6 -1
  168. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  169. package/dist/cli/src/commands/verify.d.ts.map +1 -1
  170. package/dist/cli/src/helpers.d.ts +23 -0
  171. package/dist/cli/src/helpers.d.ts.map +1 -1
  172. package/dist/cli/src/plugin-store/index.d.ts +8 -0
  173. package/dist/cli/src/plugin-store/index.d.ts.map +1 -1
  174. package/dist/cli/src/plugin-store/load.d.ts +1 -0
  175. package/dist/cli/src/plugin-store/load.d.ts.map +1 -1
  176. package/dist/cli/src/skill-checks.d.ts +9 -21
  177. package/dist/cli/src/skill-checks.d.ts.map +1 -1
  178. package/dist/connectors/config.js +5 -5
  179. package/dist/connectors/index.js +16 -15
  180. package/dist/{connectors-JQR5RAMC.js → connectors-44RE2QEM.js} +19 -18
  181. package/dist/{connectors-JQR5RAMC.js.map → connectors-44RE2QEM.js.map} +1 -1
  182. package/dist/core/index.js +6 -6
  183. package/dist/core/manifest.js +2 -2
  184. package/dist/core/models.js +1 -1
  185. package/dist/core/publish-manifest.js +3 -3
  186. package/dist/core/ref.js +1 -1
  187. package/dist/core/runtime-assets.js +5 -5
  188. package/dist/core/src/conformance.d.ts.map +1 -1
  189. package/dist/core/src/driver-sdk.d.ts +12 -2
  190. package/dist/core/src/driver-sdk.d.ts.map +1 -1
  191. package/dist/core/src/index.d.ts +4 -4
  192. package/dist/core/src/index.d.ts.map +1 -1
  193. package/dist/core/src/lock.d.ts +7 -0
  194. package/dist/core/src/lock.d.ts.map +1 -1
  195. package/dist/core/src/manifest.d.ts +9 -0
  196. package/dist/core/src/manifest.d.ts.map +1 -1
  197. package/dist/core/src/models.d.ts.map +1 -1
  198. package/dist/core/src/publish-manifest.d.ts +5 -2
  199. package/dist/core/src/publish-manifest.d.ts.map +1 -1
  200. package/dist/core/src/repo-manager.d.ts +14 -0
  201. package/dist/core/src/repo-manager.d.ts.map +1 -1
  202. package/dist/core/workspace-config.js +4 -4
  203. package/dist/deploy/index.js +6 -6
  204. package/dist/discovery/index.js +5 -5
  205. package/dist/discovery/src/index.d.ts +2 -2
  206. package/dist/discovery/src/index.d.ts.map +1 -1
  207. package/dist/discovery/src/parse-manifest.d.ts +7 -0
  208. package/dist/discovery/src/parse-manifest.d.ts.map +1 -1
  209. package/dist/discovery/src/source-config.d.ts +6 -0
  210. package/dist/discovery/src/source-config.d.ts.map +1 -1
  211. package/dist/{ensure-sources-NAGBSMJE.js → ensure-sources-R6AHZ7RV.js} +16 -16
  212. package/dist/{ensure-sources-NAGBSMJE.js.map → ensure-sources-R6AHZ7RV.js.map} +1 -1
  213. package/dist/factory-assets/README.md +1 -1
  214. package/dist/factory-assets/connectors/box.js +16 -15
  215. package/dist/factory-assets/connectors/deploy.js +16 -15
  216. package/dist/factory-assets/connectors/devserver.js +16 -15
  217. package/dist/factory-assets/connectors/flow/adapter.js +16 -15
  218. package/dist/factory-assets/connectors/flow/engine.js +7 -7
  219. package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
  220. package/dist/factory-assets/connectors/flow/run-flow.js +18 -17
  221. package/dist/factory-assets/connectors/flow.js +16 -15
  222. package/dist/factory-assets/connectors/git/CONNECTOR.md +1 -0
  223. package/dist/factory-assets/connectors/git/lifecycle.d.ts.map +1 -1
  224. package/dist/factory-assets/connectors/git/repo.d.ts +17 -3
  225. package/dist/factory-assets/connectors/git/repo.d.ts.map +1 -1
  226. package/dist/factory-assets/connectors/git.js +16 -15
  227. package/dist/factory-assets/connectors/gmail.js +16 -15
  228. package/dist/factory-assets/connectors/googledrive.js +16 -15
  229. package/dist/factory-assets/connectors/local.js +16 -15
  230. package/dist/factory-assets/connectors/mattermost.js +16 -15
  231. package/dist/factory-assets/connectors/memory.js +16 -15
  232. package/dist/factory-assets/connectors/minio.js +16 -15
  233. package/dist/factory-assets/connectors/postgres.js +16 -15
  234. package/dist/factory-assets/connectors/s3.js +16 -15
  235. package/dist/factory-assets/connectors/sharepoint.js +16 -15
  236. package/dist/factory-assets/connectors/sqlite.js +16 -15
  237. package/dist/factory-assets/connectors/static-server.js +16 -15
  238. package/dist/factory-assets/connectors/tunnel.js +16 -15
  239. package/dist/factory-assets/connectors/webdav.js +16 -15
  240. package/dist/factory-assets/connectors/xstate-store.js +16 -15
  241. package/dist/factory-assets/connectors/xstate.js +16 -15
  242. package/dist/factory-assets/skills/authoring-skaile-assets/SKILL.md +12 -11
  243. package/dist/factory-assets/skills/authoring-skaile-manifests/SKILL.md +11 -7
  244. package/dist/factory-assets/skills/skailify/SKILL.md +3 -3
  245. package/dist/{flows-CYJI3URW.js → flows-YKC53LDP.js} +11 -11
  246. package/dist/{flows-CYJI3URW.js.map → flows-YKC53LDP.js.map} +1 -1
  247. package/dist/{helpers-XFHJDJOA.js → helpers-FNQL5PI7.js} +16 -16
  248. package/dist/{helpers-XFHJDJOA.js.map → helpers-FNQL5PI7.js.map} +1 -1
  249. package/dist/library/index.js +10 -10
  250. package/dist/library/install.js +4 -4
  251. package/dist/library/src/index.d.ts +1 -1
  252. package/dist/library/src/index.d.ts.map +1 -1
  253. package/dist/library/src/local/store-paths.d.ts +8 -0
  254. package/dist/library/src/local/store-paths.d.ts.map +1 -1
  255. package/dist/library/src/manifest-writer.d.ts +5 -1
  256. package/dist/library/src/manifest-writer.d.ts.map +1 -1
  257. package/dist/library/src/remote/remote-catalog-source.d.ts +3 -2
  258. package/dist/library/src/remote/remote-catalog-source.d.ts.map +1 -1
  259. package/dist/{library-publish-FC3ZABR2.js → library-publish-GHPVY42Y.js} +56 -48
  260. package/dist/library-publish-GHPVY42Y.js.map +1 -0
  261. package/dist/model-endpoint/index.js +19 -3
  262. package/dist/model-endpoint/src/index.d.ts +1 -1
  263. package/dist/model-endpoint/src/index.d.ts.map +1 -1
  264. package/dist/model-endpoint/src/resolve.d.ts +5 -0
  265. package/dist/model-endpoint/src/resolve.d.ts.map +1 -1
  266. package/dist/model-endpoint/src/server.d.ts +7 -0
  267. package/dist/model-endpoint/src/server.d.ts.map +1 -1
  268. package/dist/model-endpoint/src/types.d.ts +41 -7
  269. package/dist/model-endpoint/src/types.d.ts.map +1 -1
  270. package/dist/{open-library-5ET7SAJC.js → open-library-YSUGNP36.js} +19 -19
  271. package/dist/{open-library-5ET7SAJC.js.map → open-library-YSUGNP36.js.map} +1 -1
  272. package/dist/{open-registry-X6YQVP7V.js → open-registry-YPRQDUMK.js} +14 -14
  273. package/dist/{open-registry-X6YQVP7V.js.map → open-registry-YPRQDUMK.js.map} +1 -1
  274. package/dist/{paths-NXWIBZCX.js → paths-P55DTBEK.js} +9 -9
  275. package/dist/{paths-NXWIBZCX.js.map → paths-P55DTBEK.js.map} +1 -1
  276. package/dist/plugin-store-IPIBZRBX.js +23 -0
  277. package/dist/plugin-store-IPIBZRBX.js.map +1 -0
  278. package/dist/project-NDSLXERO.js +83 -0
  279. package/dist/{project-2UXHSVVA.js.map → project-NDSLXERO.js.map} +1 -1
  280. package/dist/resolver/index.js +7 -7
  281. package/dist/resolver/src/types.d.ts +1 -0
  282. package/dist/resolver/src/types.d.ts.map +1 -1
  283. package/dist/resolver/src/validator.d.ts +1 -1
  284. package/dist/runner/index.js +32 -31
  285. package/dist/runner/src/agent.d.ts +9 -1
  286. package/dist/runner/src/agent.d.ts.map +1 -1
  287. package/dist/runner/src/agents/in-process-host.d.ts.map +1 -1
  288. package/dist/runner/src/composition/content-source.d.ts.map +1 -1
  289. package/dist/runner/src/composition/index.d.ts +1 -1
  290. package/dist/runner/src/composition/index.d.ts.map +1 -1
  291. package/dist/runner/src/composition/resolve-instance.d.ts +20 -0
  292. package/dist/runner/src/composition/resolve-instance.d.ts.map +1 -1
  293. package/dist/runner/src/model-endpoint-discovery.d.ts.map +1 -1
  294. package/dist/runner/src/resource-handler.d.ts.map +1 -1
  295. package/dist/runner/src/runner-capabilities.d.ts +9 -3
  296. package/dist/runner/src/runner-capabilities.d.ts.map +1 -1
  297. package/dist/runner/src/serve.d.ts +10 -0
  298. package/dist/runner/src/serve.d.ts.map +1 -1
  299. package/dist/runner/src/session-builder.d.ts +20 -1
  300. package/dist/runner/src/session-builder.d.ts.map +1 -1
  301. package/dist/runner/src/session-capability-wiring.d.ts +5 -0
  302. package/dist/runner/src/session-capability-wiring.d.ts.map +1 -1
  303. package/dist/sdk/asset-manager.js +13 -13
  304. package/dist/sdk/bridge.js +13 -12
  305. package/dist/sdk/core.js +6 -6
  306. package/dist/sdk/flow.js +7 -7
  307. package/dist/sdk/index.js +32 -31
  308. package/dist/sdk/index.js.map +1 -1
  309. package/dist/sdk/resolver.js +7 -7
  310. package/dist/sdk/runner.js +32 -31
  311. package/dist/sdk/transport/ws/server.js +1 -1
  312. package/dist/sdk/transport/ws.js +1 -1
  313. package/dist/sdk/transport.js +1 -1
  314. package/dist/{serve-preflight-GD7IQFA3.js → serve-preflight-ON2LS3SD.js} +15 -9
  315. package/dist/serve-preflight-ON2LS3SD.js.map +1 -0
  316. package/dist/{setup-IKH4R7UN.js → setup-MFPEQS2I.js} +23 -22
  317. package/dist/setup-MFPEQS2I.js.map +1 -0
  318. package/dist/{skills-XVHOXOOH.js → skills-7G6QCDEM.js} +12 -12
  319. package/dist/{skills-XVHOXOOH.js.map → skills-7G6QCDEM.js.map} +1 -1
  320. package/dist/{source-M6IARYNO.js → source-4QVRD5NV.js} +42 -41
  321. package/dist/{source-M6IARYNO.js.map → source-4QVRD5NV.js.map} +1 -1
  322. package/dist/{source-manifest-publish-UP2IS2QW.js → source-manifest-publish-ZIO24GUM.js} +44 -43
  323. package/dist/source-manifest-publish-ZIO24GUM.js.map +1 -0
  324. package/dist/transport/index.js +1 -1
  325. package/dist/transport/src/ws/server.d.ts +9 -0
  326. package/dist/transport/src/ws/server.d.ts.map +1 -1
  327. package/dist/transport/ws/server.js +1 -1
  328. package/dist/transport/ws.js +1 -1
  329. package/dist/tui/index.js +107 -41
  330. package/dist/tui/index.js.map +1 -1
  331. package/dist/tui/src/repl-session.d.ts +7 -0
  332. package/dist/tui/src/repl-session.d.ts.map +1 -1
  333. package/dist/tui/src/repl.d.ts +16 -0
  334. package/dist/tui/src/repl.d.ts.map +1 -1
  335. package/dist/types/manifests.js +1 -1
  336. package/dist/types/src/manifests/index.d.ts +1 -0
  337. package/dist/types/src/manifests/index.d.ts.map +1 -1
  338. package/dist/types/src/manifests/scaffold-body.d.ts +8 -0
  339. package/dist/types/src/manifests/scaffold-body.d.ts.map +1 -0
  340. package/dist/workspace-plugin/adapters/mcp.js +23 -22
  341. package/dist/workspace-plugin/adapters/omp.js +14 -14
  342. package/dist/workspace-plugin/index.js +1 -1
  343. package/package.json +1 -1
  344. package/dist/chunk-4AKR4XPQ.js.map +0 -1
  345. package/dist/chunk-4QINFQ27.js.map +0 -1
  346. package/dist/chunk-ANSZULXH.js.map +0 -1
  347. package/dist/chunk-AXNX2SKZ.js.map +0 -1
  348. package/dist/chunk-DP6YRAPZ.js.map +0 -1
  349. package/dist/chunk-GQ5OYFVD.js.map +0 -1
  350. package/dist/chunk-J76LDUDS.js.map +0 -1
  351. package/dist/chunk-JHP6MIW2.js.map +0 -1
  352. package/dist/chunk-MGM5EJQQ.js.map +0 -1
  353. package/dist/chunk-NKDSU4Q7.js.map +0 -1
  354. package/dist/chunk-NOF73BUH.js.map +0 -1
  355. package/dist/chunk-OOQ5Q75W.js.map +0 -1
  356. package/dist/chunk-PIDMOE7R.js.map +0 -1
  357. package/dist/chunk-PQF5QHVS.js.map +0 -1
  358. package/dist/chunk-QR7W5JV4.js.map +0 -1
  359. package/dist/chunk-RTYDYSUV.js.map +0 -1
  360. package/dist/chunk-SCUEPCSJ.js.map +0 -1
  361. package/dist/chunk-SP5NGNBV.js.map +0 -1
  362. package/dist/chunk-SRKU7QUG.js.map +0 -1
  363. package/dist/chunk-TCWECSEB.js.map +0 -1
  364. package/dist/chunk-VY6NTFE7.js.map +0 -1
  365. package/dist/chunk-WWUWMOX2.js.map +0 -1
  366. package/dist/chunk-XX4C72CC.js.map +0 -1
  367. package/dist/library-publish-FC3ZABR2.js.map +0 -1
  368. package/dist/plugin-store-BIITCNOZ.js.map +0 -1
  369. package/dist/project-2UXHSVVA.js +0 -82
  370. package/dist/serve-preflight-GD7IQFA3.js.map +0 -1
  371. package/dist/setup-IKH4R7UN.js.map +0 -1
  372. package/dist/source-manifest-publish-UP2IS2QW.js.map +0 -1
@@ -1,23 +1,6 @@
1
1
  import { pluginRegistry } from './chunk-6E6PKKAD.js';
2
- import { readPluginsLockSlice } from './chunk-4QINFQ27.js';
3
- import './chunk-4GY63JU6.js';
4
- import './chunk-X7DIT65W.js';
5
- import './chunk-D54T2F54.js';
6
- import './chunk-5BEFNLOR.js';
7
- import './chunk-A2ULN3KX.js';
8
- import './chunk-J76LDUDS.js';
9
- import './chunk-L5VSIPQD.js';
10
- import './chunk-IG5LZ3WU.js';
11
- import './chunk-ANSZULXH.js';
12
- import './chunk-RVRB27AN.js';
13
- import './chunk-SP5NGNBV.js';
14
- import './chunk-VY6NTFE7.js';
15
- import './chunk-ICSCSM5D.js';
16
- import './chunk-DKXNKFKP.js';
17
- import './chunk-MJHSLODD.js';
18
- import './chunk-C3CAXQMA.js';
19
- import './chunk-BOFL7GIF.js';
20
- import './chunk-NSBPE2FW.js';
2
+ import { readPluginsLockSlice } from './chunk-PIC23DDE.js';
3
+ import pc from 'picocolors';
21
4
  import { pathToFileURL } from 'url';
22
5
  import { existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from 'fs';
23
6
  import { join, resolve } from 'path';
@@ -107,7 +90,7 @@ async function loadPlugins(projectDir, plugins, registry = pluginRegistry, opts
107
90
  loaded.push(spec);
108
91
  } catch (err) {
109
92
  const message = err instanceof Error ? err.message : String(err);
110
- failed.push({ spec, error: `failed to load plugin "${spec}": ${message}` });
93
+ failed.push({ spec, error: `failed to load plugin "${spec}": ${message}`, reason: message });
111
94
  opts.log?.warn(`plugin load failed: ${spec}`, { error: message });
112
95
  }
113
96
  }
@@ -221,7 +204,33 @@ async function ensurePluginsLoaded(projectDir, plugins, registry = pluginRegistr
221
204
  await reconcilePlugins(projectDir, plugins, opts);
222
205
  return loadPlugins(projectDir, plugins, registry, { log: opts.log });
223
206
  }
207
+ function warnPluginFailed(spec, reason) {
208
+ let name = spec;
209
+ try {
210
+ name = specName(spec);
211
+ } catch {
212
+ }
213
+ process.stderr.write(` ${pc.yellow("!")} plugin ${name} failed to load: ${reason}
214
+ `);
215
+ }
216
+ async function loadDeclaredPlugins(projectDir) {
217
+ let plugins;
218
+ try {
219
+ const { resolveSkWorkspaceConfig } = await import('./core/index.js');
220
+ plugins = resolveSkWorkspaceConfig(projectDir).plugins;
221
+ } catch {
222
+ return;
223
+ }
224
+ if (!plugins || plugins.length === 0) return;
225
+ try {
226
+ const result = await ensurePluginsLoaded(projectDir, plugins, pluginRegistry);
227
+ for (const f of result.failed) warnPluginFailed(f.spec, f.reason);
228
+ } catch (err) {
229
+ const reason = err instanceof Error ? err.message : String(err);
230
+ for (const spec of plugins) warnPluginFailed(spec, reason);
231
+ }
232
+ }
224
233
 
225
- export { ensurePluginsLoaded, loadPlugins, reconcilePlugins, rollbackPluginStore, specName };
226
- //# sourceMappingURL=plugin-store-BIITCNOZ.js.map
227
- //# sourceMappingURL=plugin-store-BIITCNOZ.js.map
234
+ export { ensurePluginsLoaded, loadDeclaredPlugins, loadPlugins, reconcilePlugins, rollbackPluginStore, specName };
235
+ //# sourceMappingURL=chunk-HUKUYWBJ.js.map
236
+ //# sourceMappingURL=chunk-HUKUYWBJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../cli/src/plugin-store/paths.ts","../cli/src/plugin-store/spec.ts","../cli/src/plugin-store/load.ts","../cli/src/plugin-store/reconcile.ts","../cli/src/plugin-store/index.ts"],"names":["existsSync","readFileSync","join"],"mappings":"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","file":"chunk-HUKUYWBJ.js","sourcesContent":["/**\n * Pure path helpers for the project-local plugin store.\n *\n * The store is a self-contained npm workspace owned by the CLI under\n * `<projectDir>/.skaile/plugins/`. Only `resolvePluginEntry` reads the filesystem.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\n/** `<projectDir>/.skaile/plugins` — the store root. */\nexport function pluginsDir(projectDir: string): string {\n return join(projectDir, \".skaile\", \"plugins\");\n}\n\n/** `<store>/package.json` — generated, never user-edited. */\nexport function pluginsPkgJson(projectDir: string): string {\n return join(pluginsDir(projectDir), \"package.json\");\n}\n\n/** `<store>/node_modules` — where reconciled plugin packages land. */\nexport function nodeModulesDir(projectDir: string): string {\n return join(pluginsDir(projectDir), \"node_modules\");\n}\n\n/** `<store>/.reconcile-hash` — SHA of plugins-list + lock slice; skip reinstall when unchanged. */\nexport function reconcileHashFile(projectDir: string): string {\n return join(pluginsDir(projectDir), \".reconcile-hash\");\n}\n\n/** Pick the file an `exports` target points at, preferring the ESM-friendly conditions. */\nfunction exportsTarget(target: unknown): string | undefined {\n if (typeof target === \"string\") return target;\n if (Array.isArray(target)) {\n for (const t of target) {\n const hit = exportsTarget(t);\n if (hit) return hit;\n }\n return undefined;\n }\n if (target && typeof target === \"object\") {\n const map = target as Record<string, unknown>;\n for (const cond of [\"import\", \"node\", \"default\", \"require\"]) {\n if (cond in map) {\n const hit = exportsTarget(map[cond]);\n if (hit) return hit;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Absolute path to a plugin package's entry file under the store's\n * `node_modules`, resolved from its `package.json` (`exports[\".\"]`, then\n * `module`, then `main`, then `index.js`). Node's ESM loader rejects directory\n * imports, so the entry must be a file; `import()` of the result works inside\n * compiled binaries via `pathToFileURL`.\n *\n * @throws When the package is not installed or declares no resolvable entry.\n */\nexport function resolvePluginEntry(projectDir: string, pkgName: string): string {\n const pkgDir = join(nodeModulesDir(projectDir), pkgName);\n const pkgJsonPath = join(pkgDir, \"package.json\");\n if (!existsSync(pkgJsonPath)) {\n throw new Error(`package \"${pkgName}\" is not installed in ${nodeModulesDir(projectDir)}`);\n }\n const pkg = JSON.parse(readFileSync(pkgJsonPath, \"utf8\")) as {\n exports?: unknown;\n module?: string;\n main?: string;\n };\n let rel: string | undefined;\n if (pkg.exports !== undefined) {\n const root =\n typeof pkg.exports === \"object\" && pkg.exports !== null && !Array.isArray(pkg.exports)\n ? ((pkg.exports as Record<string, unknown>)[\".\"] ?? pkg.exports)\n : pkg.exports;\n rel = exportsTarget(root);\n }\n rel ??= pkg.module ?? pkg.main ?? \"index.js\";\n const entry = resolve(pkgDir, rel);\n if (!existsSync(entry)) {\n throw new Error(`package \"${pkgName}\" entry ${rel} does not exist`);\n }\n return entry;\n}\n","/**\n * Parse npm package specifiers from the `plugins:` list into name + range.\n *\n * The leading `@` of a scope is NOT a separator: only the last `@` at a\n * non-zero index splits the package name from its semver range.\n * \"@skaile/provider-fly@^0.1.0\" → { name: \"@skaile/provider-fly\", range: \"^0.1.0\" }\n * \"@skaile/connector-redis\" → { name: \"@skaile/connector-redis\", range: \"*\" }\n * \"lodash@^4\" → { name: \"lodash\", range: \"^4\" }\n */\n\n/** A parsed plugin specifier: package name plus a semver range (`\"*\"` when omitted). */\nexport interface PluginSpec {\n name: string;\n range: string;\n}\n\n/**\n * Parse a single plugin specifier into `{ name, range }`.\n *\n * @param spec - An npm specifier, e.g. `\"@skaile/provider-fly@^0.1.0\"` or `\"lodash\"`.\n * @returns The package name and semver range (range defaults to `\"*\"`).\n * @throws Error when the specifier is empty.\n */\nexport function parseSpec(spec: string): PluginSpec {\n const trimmed = spec.trim();\n if (!trimmed) throw new Error(\"parseSpec: empty specifier\");\n\n // Find the version-separating `@` — the last one that is not the scope `@`\n // at index 0. A bare `@scope/name` has its only `@` at index 0 (no range).\n const at = trimmed.lastIndexOf(\"@\");\n if (at <= 0) {\n return { name: trimmed, range: \"*\" };\n }\n const name = trimmed.slice(0, at);\n const range = trimmed.slice(at + 1);\n return { name, range: range.length > 0 ? range : \"*\" };\n}\n\n/** Convenience: just the package name of a specifier. */\nexport function specName(spec: string): string {\n return parseSpec(spec).name;\n}\n","/**\n * Runtime plugin loader: dynamic-import each reconciled plugin and let it\n * self-register into the `pluginRegistry`.\n *\n * Each plugin specifier resolves to an absolute path under the store's\n * `node_modules`; we import it via a `file://` URL so the import works inside\n * `bun --compile` binaries (which bundle only the literal-string switches, but\n * can still execute a `file://` import against the unpacked store on disk).\n * Failures are caught per-plugin so one broken plugin never aborts the rest.\n */\n\nimport { pathToFileURL } from \"node:url\";\nimport { type PluginRegistry, pluginRegistry } from \"@skaile/workspaces/plugin-registry\";\nimport type { Logger } from \"@skaile/workspaces/types\";\nimport { resolvePluginEntry } from \"./paths.js\";\nimport { parseSpec } from \"./spec.js\";\n\n/** Options for {@link loadPlugins}. */\nexport interface LoadOptions {\n log?: Logger;\n}\n\n/** Result of a load pass: which specs registered and which failed. */\nexport interface LoadResult {\n loaded: string[];\n failed: { spec: string; error: string; reason: string }[];\n}\n\n/** The self-registration entrypoint a plugin module is expected to export. */\ntype PluginModule = { register?: (registry: PluginRegistry) => void };\n\n/**\n * Load and register each plugin in `plugins` into `registry`.\n *\n * @param projectDir - Project root holding `.skaile/plugins/node_modules`.\n * @param plugins - The `skaile.yaml` `plugins:` list (npm specifiers).\n * @param registry - Registry to register into. Defaults to the process-wide `pluginRegistry`.\n * @param opts - Optional logger.\n * @returns Loaded specs + per-plugin failures (never throws).\n */\nexport async function loadPlugins(\n projectDir: string,\n plugins: string[],\n registry: PluginRegistry = pluginRegistry,\n opts: LoadOptions = {},\n): Promise<LoadResult> {\n const loaded: string[] = [];\n const failed: { spec: string; error: string; reason: string }[] = [];\n\n for (const spec of plugins) {\n try {\n const { name } = parseSpec(spec);\n const entry = resolvePluginEntry(projectDir, name);\n const mod = (await import(pathToFileURL(entry).href)) as PluginModule;\n if (typeof mod.register !== \"function\") {\n throw new Error(`plugin \"${name}\" does not export a register() function`);\n }\n mod.register(registry);\n loaded.push(spec);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n failed.push({ spec, error: `failed to load plugin \"${spec}\": ${message}`, reason: message });\n opts.log?.warn(`plugin load failed: ${spec}`, { error: message });\n }\n }\n\n return { loaded, failed };\n}\n","/**\n * Reconcile the project-local plugin store (`<projectDir>/.skaile/plugins/`).\n *\n * The reconciler is hash-skipped: it computes a SHA-256 over the sorted plugin\n * list plus the lock file's `plugins` slice and compares it to the stored\n * `.reconcile-hash`. On a match it returns with zero further I/O; otherwise it\n * regenerates the store's `package.json` and runs `bun install` against it.\n *\n * The install step is injectable (`InstallFn`) so tests stay hermetic — they\n * pass a fake installer that records calls and writes a stub `node_modules`.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readPluginsLockSlice } from \"@skaile/workspaces/core\";\nimport { nodeModulesDir, pluginsDir, pluginsPkgJson, reconcileHashFile } from \"./paths.js\";\nimport { parseSpec } from \"./spec.js\";\n\n/** How `InstallFn` may treat the store's lock: `frozen` forbids lock changes. */\nexport interface InstallOptions {\n /**\n * `false` when the spec list changed since the last `package.json`, so the\n * lock has to change too. Defaults to `true` (frozen when a lock exists).\n */\n frozen?: boolean;\n}\n\n/**\n * Installs the store's dependencies. Returns `ok: false` (never throws) on a\n * failed install so the reconciler can surface the captured `output`.\n */\nexport type InstallFn = (\n pluginsDir: string,\n opts?: InstallOptions,\n) => Promise<{ ok: boolean; output: string }>;\n\n/** Options for {@link reconcilePlugins}. */\nexport interface ReconcileOptions {\n /** Injectable installer. Defaults to a real `bun install` of the store. */\n runInstall?: InstallFn;\n /**\n * Lock slice to fold into the reconcile-hash. Defaults to\n * `readPluginsLockSlice(projectDir)` so a lock edit invalidates the hash.\n */\n lockSlice?: unknown;\n}\n\n/** Result of a reconcile pass. */\nexport interface ReconcileResult {\n changed: boolean;\n reason: \"no-plugins\" | \"up-to-date\" | \"reconciled\";\n}\n\n/** Run a command to completion, capturing stdout+stderr. `started: false` when it cannot spawn. */\nasync function runCapture(\n args: string[],\n cwd: string,\n): Promise<{ started: boolean; code: number; output: string }> {\n const { portableSpawn } = await import(\"@skaile/workspaces/core\");\n const proc = portableSpawn(args, { cwd });\n const chunks: string[] = [];\n const decoder = new TextDecoder();\n const pump = async (stream: ReadableStream<Uint8Array>) => {\n const reader = stream.getReader();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) chunks.push(decoder.decode(value, { stream: true }));\n }\n };\n await Promise.all([pump(proc.stdout), pump(proc.stderr)]);\n const code = await proc.exited;\n // `portableSpawn` reports a spawn error (ENOENT) as exit -1 with no pid.\n return { started: proc.pid !== -1, code, output: chunks.join(\"\") };\n}\n\n/**\n * The default real installer: `bun install` inside the store directory, or\n * `npm install` when `bun` is not on PATH. Frozen only when a lock exists and\n * the spec list did not change (`opts.frozen !== false`): a changed list has to\n * rewrite the lock, which a frozen install refuses.\n */\nexport const defaultInstall: InstallFn = async (dir, opts = {}) => {\n const hasBunLock = existsSync(`${dir}/bun.lock`) || existsSync(`${dir}/bun.lockb`);\n const hasNpmLock = existsSync(`${dir}/package-lock.json`);\n const frozen = opts.frozen !== false;\n\n const bunArgs =\n frozen && (hasBunLock || hasNpmLock)\n ? [\"bun\", \"install\", \"--frozen-lockfile\"]\n : [\"bun\", \"install\"];\n const withBun = await runCapture(bunArgs, dir);\n if (withBun.started) return { ok: withBun.code === 0, output: withBun.output };\n\n const npmArgs =\n frozen && hasNpmLock\n ? [\"npm\", \"ci\", \"--ignore-scripts\", \"--no-audit\", \"--no-fund\"]\n : [\"npm\", \"install\", \"--ignore-scripts\", \"--no-audit\", \"--no-fund\"];\n const withNpm = await runCapture(npmArgs, dir);\n if (withNpm.started) return { ok: withNpm.code === 0, output: withNpm.output };\n return {\n ok: false,\n output:\n \"neither bun nor npm found on PATH; plugin install needs one of them (https://bun.sh or https://nodejs.org)\",\n };\n};\n\n/**\n * Generate the store's `package.json` from the plugin specs. Returns the\n * previous file content (`undefined` when there was none) and whether the\n * dependency set changed.\n */\nfunction writePackageJson(\n projectDir: string,\n plugins: string[],\n): { previous: string | undefined; depsChanged: boolean } {\n const dependencies: Record<string, string> = {};\n for (const spec of plugins) {\n const { name, range } = parseSpec(spec);\n dependencies[name] = range;\n }\n const pkg = {\n name: \"skaile-project-plugins\",\n private: true,\n dependencies,\n };\n const file = pluginsPkgJson(projectDir);\n const previous = existsSync(file) ? readFileSync(file, \"utf8\") : undefined;\n let before: Record<string, string> | undefined;\n try {\n before = previous === undefined ? undefined : JSON.parse(previous).dependencies;\n } catch {\n before = undefined;\n }\n const sortedJson = (d: Record<string, string> | undefined) =>\n JSON.stringify(Object.entries(d ?? {}).sort(([a], [b]) => a.localeCompare(b)));\n const depsChanged = before === undefined || sortedJson(before) !== sortedJson(dependencies);\n mkdirSync(pluginsDir(projectDir), { recursive: true });\n writeFileSync(file, `${JSON.stringify(pkg, null, 2)}\\n`);\n return { previous, depsChanged };\n}\n\n/**\n * Reconcile the plugin store for a project.\n *\n * @param projectDir - Project root containing `skaile.yaml` / `skaile.lock.yaml`.\n * @param plugins - The `skaile.yaml` `plugins:` list (npm specifiers).\n * @param opts - Optional injectable installer + lock slice override.\n * @returns Whether the store changed and why.\n */\nexport async function reconcilePlugins(\n projectDir: string,\n plugins: string[],\n opts: ReconcileOptions = {},\n): Promise<ReconcileResult> {\n if (plugins.length === 0) {\n return { changed: false, reason: \"no-plugins\" };\n }\n\n const lockSlice = opts.lockSlice ?? readPluginsLockSlice(projectDir);\n const wantHash = createHash(\"sha256\")\n .update(JSON.stringify({ plugins: [...plugins].sort(), lock: lockSlice }))\n .digest(\"hex\");\n\n const hashFile = reconcileHashFile(projectDir);\n if (existsSync(hashFile)) {\n const have = readFileSync(hashFile, \"utf8\").trim();\n if (have === wantHash) {\n return { changed: false, reason: \"up-to-date\" };\n }\n }\n\n const { previous, depsChanged } = writePackageJson(projectDir, plugins);\n\n const install = opts.runInstall ?? defaultInstall;\n const result = await install(pluginsDir(projectDir), { frozen: !depsChanged });\n if (!result.ok) {\n // A failed install must not leave the spec behind: the next reconcile\n // would compare against it and treat the store as already changed.\n if (previous === undefined) rmSync(pluginsPkgJson(projectDir), { force: true });\n else writeFileSync(pluginsPkgJson(projectDir), previous);\n throw new Error(`plugin install failed:\\n${result.output}`);\n }\n\n writeFileSync(hashFile, wantHash);\n return { changed: true, reason: \"reconciled\" };\n}\n\n/**\n * Put the store back to `previousPlugins` after `plugin install` failed past\n * the reconcile (the plugin installed but does not load). Best effort: a\n * failing reinstall is swallowed, the store is then rebuilt by the next\n * reconcile because the hash no longer matches the declared list.\n *\n * With no previous plugins every generated file of the store goes away\n * (`package.json`, hash, lock file and `node_modules`), so the store keeps\n * neither the rejected spec nor its installed package.\n */\nexport async function rollbackPluginStore(\n projectDir: string,\n previousPlugins: string[],\n opts: ReconcileOptions = {},\n): Promise<void> {\n if (previousPlugins.length === 0) {\n const dir = pluginsDir(projectDir);\n rmSync(pluginsPkgJson(projectDir), { force: true });\n rmSync(reconcileHashFile(projectDir), { force: true });\n rmSync(nodeModulesDir(projectDir), { recursive: true, force: true });\n for (const lock of [\"bun.lock\", \"bun.lockb\", \"package-lock.json\"]) {\n rmSync(join(dir, lock), { force: true });\n }\n return;\n }\n try {\n await reconcilePlugins(projectDir, previousPlugins, opts);\n } catch {\n // the next reconcile retries\n }\n}\n","/**\n * The project-local plugin store: reconcile (`<projectDir>/.skaile/plugins/`)\n * then load the reconciled plugins into the `pluginRegistry`.\n *\n * `ensurePluginsLoaded` is the single entrypoint commands wire in. It is a\n * guarded no-op when `plugins` is empty/undefined — zero overhead for the\n * common case where no project declares `plugins:` in `skaile.yaml`.\n */\n\nimport { type PluginRegistry, pluginRegistry } from \"@skaile/workspaces/plugin-registry\";\nimport type { Logger } from \"@skaile/workspaces/types\";\nimport pc from \"picocolors\";\nimport { type LoadResult, loadPlugins } from \"./load.js\";\nimport { type ReconcileOptions, reconcilePlugins } from \"./reconcile.js\";\nimport { specName } from \"./spec.js\";\n\nexport type { LoadResult } from \"./load.js\";\nexport { loadPlugins } from \"./load.js\";\nexport type { ReconcileOptions } from \"./reconcile.js\";\nexport { reconcilePlugins, rollbackPluginStore } from \"./reconcile.js\";\nexport { specName } from \"./spec.js\";\n\n/** Options for {@link ensurePluginsLoaded}. */\nexport interface EnsurePluginsOptions extends ReconcileOptions {\n log?: Logger;\n}\n\n/**\n * Reconcile the plugin store then load each plugin into `registry`.\n *\n * No-op (returns immediately) when `plugins` is empty/undefined, so the common\n * no-plugins path costs nothing.\n *\n * @param projectDir - Project root.\n * @param plugins - The `skaile.yaml` `plugins:` list (may be undefined).\n * @param registry - Registry to register into. Defaults to `pluginRegistry`.\n * @param opts - Injectable installer / lock slice + optional logger.\n * @returns The load result, or an empty result when there are no plugins.\n */\nexport async function ensurePluginsLoaded(\n projectDir: string,\n plugins: string[] | undefined,\n registry: PluginRegistry = pluginRegistry,\n opts: EnsurePluginsOptions = {},\n): Promise<LoadResult> {\n if (!plugins || plugins.length === 0) {\n return { loaded: [], failed: [] };\n }\n await reconcilePlugins(projectDir, plugins, opts);\n return loadPlugins(projectDir, plugins, registry, { log: opts.log });\n}\n\n/** One `! plugin <name> failed to load: <reason>` line on stderr (stdout carries a run's answer). */\nfunction warnPluginFailed(spec: string, reason: string): void {\n let name = spec;\n try {\n name = specName(spec);\n } catch {\n /* a spec that does not parse is named as written */\n }\n process.stderr.write(` ${pc.yellow(\"!\")} plugin ${name} failed to load: ${reason}\\n`);\n}\n\n/**\n * Reconcile + load the `plugins:` of the project's `skaile.yaml` into the process registry, so a\n * plugin's driver is known to `run`, `repl` and `config set driver`. A plugin that fails to\n * reconcile or load is skipped (its driver stays unknown, the command goes on) and named in one\n * `! plugin <name> failed to load: <reason>` line on stderr. An unreadable config is not this\n * function's to report: the command reads it again and says so.\n */\nexport async function loadDeclaredPlugins(projectDir: string): Promise<void> {\n let plugins: string[] | undefined;\n try {\n const { resolveSkWorkspaceConfig } = await import(\"@skaile/workspaces/core\");\n plugins = resolveSkWorkspaceConfig(projectDir).plugins;\n } catch {\n return;\n }\n if (!plugins || plugins.length === 0) return;\n try {\n const result = await ensurePluginsLoaded(projectDir, plugins, pluginRegistry);\n for (const f of result.failed) warnPluginFailed(f.spec, f.reason);\n } catch (err) {\n // The store could not be reconciled: no plugin was loaded.\n const reason = err instanceof Error ? err.message : String(err);\n for (const spec of plugins) warnPluginFailed(spec, reason);\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { getStoreConfig, getStoreToken, storeFetch } from './chunk-I3JO3NIZ.js';
1
+ import { getStoreConfig, getStoreToken, storeFetch } from './chunk-Y25V2MW2.js';
2
2
 
3
3
  // cli/src/store-publisher.ts
4
4
  function makeHttpPublisherClient(config = getStoreConfig(), timeoutMs = 2e4) {
@@ -25,5 +25,5 @@ function makeHttpPublisherClient(config = getStoreConfig(), timeoutMs = 2e4) {
25
25
  }
26
26
 
27
27
  export { makeHttpPublisherClient };
28
- //# sourceMappingURL=chunk-D2MZZ2KB.js.map
29
- //# sourceMappingURL=chunk-D2MZZ2KB.js.map
28
+ //# sourceMappingURL=chunk-ISSYT5AZ.js.map
29
+ //# sourceMappingURL=chunk-ISSYT5AZ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../cli/src/store-publisher.ts"],"names":[],"mappings":";;;AA+FO,SAAS,uBAAA,CACd,MAAA,GAAsB,cAAA,EAAe,EACrC,YAAY,GAAA,EACK;AAGjB,EAAA,MAAM,IAAA,GAAO,OACX,IAAA,EACA,IAAA,KACe;AACf,IAAA,MAAM,GAAA,GAAM,MAAM,aAAA,EAAc;AAChC,IAAA,OAAO,UAAA,CAAc,EAAE,GAAG,MAAA,EAAQ,GAAA,EAAI,EAAG,IAAA,EAAM,EAAE,SAAA,EAAW,GAAG,IAAA,EAAM,CAAA;AAAA,EACvE,CAAA;AACA,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,MAAM,IAAA,CAAK,YAAY,CAAA;AAAA,IAC3B,UAAA,EAAY,CAAC,IAAA,KAAS,IAAA,CAAK,mBAAmB,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,IACtE,WAAA,EAAa,MAAM,IAAA,CAAK,iBAAiB,CAAA;AAAA,IACzC,UAAA,EAAY,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,gBAAA,EAAmB,EAAE,CAAA,KAAA,CAAA,EAAS,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,IACnF,QAAQ,CAAC,KAAA,KAAU,IAAA,CAAK,CAAA,aAAA,EAAgB,KAAK,CAAA,CAAE,CAAA;AAAA,IAC/C,kBAAkB,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,gBAAA,EAAmB,EAAE,CAAA,WAAA,CAAa,CAAA;AAAA,IACjE,cAAc,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,eAAA,EAAkB,EAAE,CAAA,QAAA,CAAU,CAAA;AAAA,IACzD,YAAA,EAAc,CAAC,EAAA,EAAI,IAAA,EAAM,WACvB,IAAA,CAAK,CAAA,eAAA,EAAkB,EAAE,CAAA,QAAA,CAAA,EAAY;AAAA,MACnC,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA;AAAA,MACA,GAAI,SAAS,EAAE,MAAA,EAAQ,EAAE,MAAA,EAAQ,GAAA,EAAI,EAAE,GAAI;AAAC,KAC7C,CAAA;AAAA,IACH,cAAA,EAAgB,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,eAAA,EAAkB,EAAE,CAAA,UAAA,CAAA,EAAc,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,IAC3F,qBAAA,EAAuB,CAAC,EAAA,KACtB,IAAA,CAAK,sBAAsB,kBAAA,CAAmB,EAAE,CAAC,CAAA,aAAA,CAAe;AAAA,GACpE;AACF","file":"chunk-D2MZZ2KB.js","sourcesContent":["/**\n * Typed REST client for the AI Asset Store **publisher** surface — the\n * curator-publish flow (`skaile source manifest publish` / `yank`). Thin\n * wrappers over {@link storeFetch}; every method maps 1:1 to an `/api/v1/…`\n * endpoint. The {@link PublisherClient} interface is injectable so the\n * orchestration in `commands/source-manifest-publish.ts` can be unit-tested\n * against a fake without a live store.\n *\n * @docLink cli/dev-guide#store-publisher\n */\n\nimport { getStoreConfig, getStoreToken, type StoreConfig, storeFetch } from \"./store-client.ts\";\n\n/**\n * The publisher REST surface used by the curator-publish flow. Kept minimal —\n * only the endpoints `runPublish` / `runYank` actually call. Response shapes\n * are narrowed to the fields the orchestration reads (the server returns more).\n */\nexport interface PublisherClient {\n /**\n * Identity of the caller. `username` is the Keycloak login (personal-tier\n * namespace); `githubLogin` is the verified GitHub handle when connected —\n * the namespace to publish under when it differs from the login.\n */\n me(): Promise<{ username: string; githubLogin?: string | null }>;\n /** Register (or re-register) a source; returns the created/existing row id. */\n sourcesAdd(body: {\n url: string;\n ref: string;\n sidecarUrl?: string;\n sidecarRef?: string;\n /**\n * Target publisher namespace (the manifest's `publisher`). When it differs\n * from the caller's login username, the store resolves the CLAIMED Publisher\n * for it (the store must already have the claim). Omitted → the store\n * defaults to `@<username>` (personal).\n */\n namespace?: string;\n }): Promise<{ id: string }>;\n /** List sources — used to recover an id when `sourcesAdd` rejects a dup url. */\n sourcesList(): Promise<Array<{ id: string; url: string }>>;\n /** Kick off a source sync; returns the async job id to poll. */\n sourceSync(id: string): Promise<{ jobId: string }>;\n /** Poll a job's terminal status + per-item results. */\n jobGet(\n jobId: string,\n ): Promise<{ status: string; items?: Array<{ label: string; status: string; error?: string }> }>;\n /** Discovered publishable candidates for a synced source. */\n sourceCandidates(id: string): Promise<\n Array<{\n id: string;\n name: string;\n version: string;\n kind: string;\n published: boolean;\n assetRoot: string;\n }>\n >;\n /** Per-asset preview — the discovered file set + resolved candidate metadata. */\n assetPreview(id: string): Promise<{\n discoveredFiles: string[];\n candidate: { name: string; version: string; kind: string; publisherNamespace: string };\n }>;\n /**\n * Publish one asset. `dryRun` returns the would-be `{ manifest }` without\n * launching a job; the normal path returns `{ jobId }`.\n */\n assetPublish(\n id: string,\n body: { ref: string; files: string[] },\n dryRun?: boolean,\n ): Promise<{ jobId?: string; manifest?: unknown }>;\n /** Retract a published asset. Response body is ignored. */\n assetUnpublish(id: string): Promise<unknown>;\n /**\n * Read-only \"can I publish under `@<ns>` right now, and if not, what's the\n * path?\" probe. Backs the `publish --dry-run` namespace status line; its\n * `message` is store-authored and meant to be surfaced verbatim.\n */\n namespaceAvailability(ns: string): Promise<{\n namespace: string;\n status: \"owned-by-you\" | \"claimable-by-you\" | \"reserved\" | \"available\";\n claimMethod?: string;\n message: string;\n }>;\n}\n\n/**\n * Construct the real HTTP-backed {@link PublisherClient}. Every call carries a\n * longer timeout than the default store client because publish/sync trigger\n * server-side clone work.\n *\n * @param config - Store connection config; defaults to {@link getStoreConfig}.\n * @param timeoutMs - Per-request abort timeout (default 20s).\n */\nexport function makeHttpPublisherClient(\n config: StoreConfig = getStoreConfig(),\n timeoutMs = 20_000,\n): PublisherClient {\n // Acquire a fresh Keycloak token per call (device-flow session in\n // ~/.skaile/auth.json, refreshed on demand) rather than a static config.jwt.\n const call = async <T>(\n path: string,\n opts?: { method?: string; body?: unknown; params?: Record<string, string> },\n ): Promise<T> => {\n const jwt = await getStoreToken();\n return storeFetch<T>({ ...config, jwt }, path, { timeoutMs, ...opts });\n };\n return {\n me: () => call(\"/api/v1/me\"),\n sourcesAdd: (body) => call(\"/api/v1/sources\", { method: \"POST\", body }),\n sourcesList: () => call(\"/api/v1/sources\"),\n sourceSync: (id) => call(`/api/v1/sources/${id}/sync`, { method: \"POST\", body: {} }),\n jobGet: (jobId) => call(`/api/v1/jobs/${jobId}`),\n sourceCandidates: (id) => call(`/api/v1/sources/${id}/candidates`),\n assetPreview: (id) => call(`/api/v1/assets/${id}/preview`),\n assetPublish: (id, body, dryRun) =>\n call(`/api/v1/assets/${id}/publish`, {\n method: \"POST\",\n body,\n ...(dryRun ? { params: { dryRun: \"1\" } } : {}),\n }),\n assetUnpublish: (id) => call(`/api/v1/assets/${id}/unpublish`, { method: \"POST\", body: {} }),\n namespaceAvailability: (ns) =>\n call(`/api/v1/namespaces/${encodeURIComponent(ns)}/availability`),\n };\n}\n"]}
1
+ {"version":3,"sources":["../cli/src/store-publisher.ts"],"names":[],"mappings":";;;AA+FO,SAAS,uBAAA,CACd,MAAA,GAAsB,cAAA,EAAe,EACrC,YAAY,GAAA,EACK;AAGjB,EAAA,MAAM,IAAA,GAAO,OACX,IAAA,EACA,IAAA,KACe;AACf,IAAA,MAAM,GAAA,GAAM,MAAM,aAAA,EAAc;AAChC,IAAA,OAAO,UAAA,CAAc,EAAE,GAAG,MAAA,EAAQ,GAAA,EAAI,EAAG,IAAA,EAAM,EAAE,SAAA,EAAW,GAAG,IAAA,EAAM,CAAA;AAAA,EACvE,CAAA;AACA,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,MAAM,IAAA,CAAK,YAAY,CAAA;AAAA,IAC3B,UAAA,EAAY,CAAC,IAAA,KAAS,IAAA,CAAK,mBAAmB,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,IACtE,WAAA,EAAa,MAAM,IAAA,CAAK,iBAAiB,CAAA;AAAA,IACzC,UAAA,EAAY,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,gBAAA,EAAmB,EAAE,CAAA,KAAA,CAAA,EAAS,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,IACnF,QAAQ,CAAC,KAAA,KAAU,IAAA,CAAK,CAAA,aAAA,EAAgB,KAAK,CAAA,CAAE,CAAA;AAAA,IAC/C,kBAAkB,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,gBAAA,EAAmB,EAAE,CAAA,WAAA,CAAa,CAAA;AAAA,IACjE,cAAc,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,eAAA,EAAkB,EAAE,CAAA,QAAA,CAAU,CAAA;AAAA,IACzD,YAAA,EAAc,CAAC,EAAA,EAAI,IAAA,EAAM,WACvB,IAAA,CAAK,CAAA,eAAA,EAAkB,EAAE,CAAA,QAAA,CAAA,EAAY;AAAA,MACnC,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA;AAAA,MACA,GAAI,SAAS,EAAE,MAAA,EAAQ,EAAE,MAAA,EAAQ,GAAA,EAAI,EAAE,GAAI;AAAC,KAC7C,CAAA;AAAA,IACH,cAAA,EAAgB,CAAC,EAAA,KAAO,IAAA,CAAK,CAAA,eAAA,EAAkB,EAAE,CAAA,UAAA,CAAA,EAAc,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,IAC3F,qBAAA,EAAuB,CAAC,EAAA,KACtB,IAAA,CAAK,sBAAsB,kBAAA,CAAmB,EAAE,CAAC,CAAA,aAAA,CAAe;AAAA,GACpE;AACF","file":"chunk-ISSYT5AZ.js","sourcesContent":["/**\n * Typed REST client for the AI Asset Store **publisher** surface — the\n * curator-publish flow (`skaile source manifest publish` / `yank`). Thin\n * wrappers over {@link storeFetch}; every method maps 1:1 to an `/api/v1/…`\n * endpoint. The {@link PublisherClient} interface is injectable so the\n * orchestration in `commands/source-manifest-publish.ts` can be unit-tested\n * against a fake without a live store.\n *\n * @docLink cli/dev-guide#store-publisher\n */\n\nimport { getStoreConfig, getStoreToken, type StoreConfig, storeFetch } from \"./store-client.ts\";\n\n/**\n * The publisher REST surface used by the curator-publish flow. Kept minimal —\n * only the endpoints `runPublish` / `runYank` actually call. Response shapes\n * are narrowed to the fields the orchestration reads (the server returns more).\n */\nexport interface PublisherClient {\n /**\n * Identity of the caller. `username` is the Keycloak login (personal-tier\n * namespace); `githubLogin` is the verified GitHub handle when connected —\n * the namespace to publish under when it differs from the login.\n */\n me(): Promise<{ username: string; githubLogin?: string | null }>;\n /** Register (or re-register) a source; returns the created/existing row id. */\n sourcesAdd(body: {\n url: string;\n ref: string;\n sidecarUrl?: string;\n sidecarRef?: string;\n /**\n * Target publisher namespace (the manifest's `publisher`). When it differs\n * from the caller's login username, the store resolves the CLAIMED Publisher\n * for it (the store must already have the claim). Omitted → the store\n * defaults to `@<username>` (personal).\n */\n namespace?: string;\n }): Promise<{ id: string }>;\n /** List sources — used to recover an id when `sourcesAdd` rejects a dup url. */\n sourcesList(): Promise<Array<{ id: string; url: string }>>;\n /** Kick off a source sync; returns the async job id to poll. */\n sourceSync(id: string): Promise<{ jobId: string }>;\n /** Poll a job's terminal status + per-item results. */\n jobGet(\n jobId: string,\n ): Promise<{ status: string; items?: Array<{ label: string; status: string; error?: string }> }>;\n /** Discovered publishable candidates for a synced source. */\n sourceCandidates(id: string): Promise<\n Array<{\n id: string;\n name: string;\n version: string;\n kind: string;\n published: boolean;\n assetRoot: string;\n }>\n >;\n /** Per-asset preview — the discovered file set + resolved candidate metadata. */\n assetPreview(id: string): Promise<{\n discoveredFiles: string[];\n candidate: { name: string; version: string; kind: string; publisherNamespace: string };\n }>;\n /**\n * Publish one asset. `dryRun` returns the would-be `{ manifest }` without\n * launching a job; the normal path returns `{ jobId }`.\n */\n assetPublish(\n id: string,\n body: { ref: string; files: string[] },\n dryRun?: boolean,\n ): Promise<{ jobId?: string; manifest?: unknown }>;\n /** Retract a published asset. Response body is ignored. */\n assetUnpublish(id: string): Promise<unknown>;\n /**\n * Read-only \"can I publish under `@<ns>` right now, and if not, what's the\n * path?\" probe. Backs the `publish --dry-run` namespace status line; its\n * `message` is store-authored and meant to be surfaced verbatim.\n */\n namespaceAvailability(ns: string): Promise<{\n namespace: string;\n status: \"owned-by-you\" | \"claimable-by-you\" | \"reserved\" | \"available\";\n claimMethod?: string;\n message: string;\n }>;\n}\n\n/**\n * Construct the real HTTP-backed {@link PublisherClient}. Every call carries a\n * longer timeout than the default store client because publish/sync trigger\n * server-side clone work.\n *\n * @param config - Store connection config; defaults to {@link getStoreConfig}.\n * @param timeoutMs - Per-request abort timeout (default 20s).\n */\nexport function makeHttpPublisherClient(\n config: StoreConfig = getStoreConfig(),\n timeoutMs = 20_000,\n): PublisherClient {\n // Acquire a fresh Keycloak token per call (device-flow session in\n // ~/.skaile/auth.json, refreshed on demand) rather than a static config.jwt.\n const call = async <T>(\n path: string,\n opts?: { method?: string; body?: unknown; params?: Record<string, string> },\n ): Promise<T> => {\n const jwt = await getStoreToken();\n return storeFetch<T>({ ...config, jwt }, path, { timeoutMs, ...opts });\n };\n return {\n me: () => call(\"/api/v1/me\"),\n sourcesAdd: (body) => call(\"/api/v1/sources\", { method: \"POST\", body }),\n sourcesList: () => call(\"/api/v1/sources\"),\n sourceSync: (id) => call(`/api/v1/sources/${id}/sync`, { method: \"POST\", body: {} }),\n jobGet: (jobId) => call(`/api/v1/jobs/${jobId}`),\n sourceCandidates: (id) => call(`/api/v1/sources/${id}/candidates`),\n assetPreview: (id) => call(`/api/v1/assets/${id}/preview`),\n assetPublish: (id, body, dryRun) =>\n call(`/api/v1/assets/${id}/publish`, {\n method: \"POST\",\n body,\n ...(dryRun ? { params: { dryRun: \"1\" } } : {}),\n }),\n assetUnpublish: (id) => call(`/api/v1/assets/${id}/unpublish`, { method: \"POST\", body: {} }),\n namespaceAvailability: (ns) =>\n call(`/api/v1/namespaces/${encodeURIComponent(ns)}/availability`),\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { logErr } from './chunk-PIDMOE7R.js';
2
- import { getKindRegistry } from './chunk-YUURCZWY.js';
1
+ import { logErr } from './chunk-VJ2DZLDM.js';
2
+ import { getKindRegistry } from './chunk-2SKPPIV7.js';
3
3
  import { userConfigPath } from './chunk-L5VSIPQD.js';
4
4
 
5
5
  // cli/src/open-library.ts
@@ -90,5 +90,5 @@ async function openLibraryManager() {
90
90
  }
91
91
 
92
92
  export { LocalCatalogDisabledError, openCatalogSource, openLibrary, openLibraryManager, resolveCatalogSource };
93
- //# sourceMappingURL=chunk-2LTF2ZHH.js.map
94
- //# sourceMappingURL=chunk-2LTF2ZHH.js.map
93
+ //# sourceMappingURL=chunk-J4RRH2NP.js.map
94
+ //# sourceMappingURL=chunk-J4RRH2NP.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../cli/src/open-library.ts"],"names":["source"],"mappings":"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","file":"chunk-2LTF2ZHH.js","sourcesContent":["/**\n * open-library.ts — shared helpers to open the LocalIndex and the configured\n * Catalog source.\n *\n * Lazy-loads the `@skaile/workspaces/library` subpath and returns the\n * LocalIndex instance (backed by `@libsql/client`; runs on Node and Bun).\n * The catalog-source helper reads\n * `~/.skaile/config.yaml` (and project-level overlay if `projectDir` is given)\n * to decide between {@link RemoteCatalogSource} (default — points at\n * `https://skaile.store`) and a {@link LocalCatalogSource} bound to the most\n * recent local library registered via `skaile source add`.\n */\n\nimport { userConfigPath } from \"@skaile/workspaces/core\";\nimport { logErr } from \"./helpers.ts\";\nimport { getKindRegistry } from \"./open-registry.ts\";\n\n/** Thrown by {@link openCatalogSource} when `catalog.url` is the `local` sentinel. */\nexport class LocalCatalogDisabledError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LocalCatalogDisabledError\";\n }\n}\n\n/**\n * Open the LocalIndex with the full kind registry. Exits with code 1\n * if the library directory cannot be created.\n *\n * @returns A `LocalIndex` instance ready for use.\n * @docLink cli/dev-guide#open-library\n */\nexport async function openLibrary() {\n try {\n const { LocalIndex } = await import(\"@skaile/workspaces/library\");\n return new LocalIndex({ kindRegistry: getKindRegistry() });\n } catch (err) {\n logErr(`Could not open LocalIndex: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n}\n\n/**\n * Resolve the configured **remote** Catalog source for the CLI.\n *\n * Reads `~/.skaile/config.yaml` (plus the optional project-level overlay) and\n * returns a {@link RemoteCatalogSource} pointing at `catalog.url` (default:\n * `https://skaile.store`). The `cache_ttl: 0` config flag flips the source\n * into air-gapped mode: network reads are disabled, only `skaile update`\n * performs refreshes.\n *\n * **Local-mode rejection.** If the resolved config has `catalog.url: local`\n * this helper throws — callers wanting the dispatched local-or-remote source\n * must use {@link resolveCatalogSource}. Remote-only consumers like\n * `skaile update --catalog-only` rely on this strict behaviour.\n *\n * @param opts - Project directory (overlays project config) and explicit overrides.\n * @returns A configured {@link RemoteCatalogSource}.\n * @throws When `catalog.url` is the `local` sentinel.\n * @docLink cli/dev-guide#open-library\n */\nexport async function openCatalogSource(opts?: {\n projectDir?: string;\n baseUrlOverride?: string;\n /** Override the user-config path for tests. */\n userConfigFile?: string;\n}): Promise<\n | import(\"@skaile/workspaces/library\").RemoteCatalogSource\n | import(\"@skaile/workspaces/library\").RestCatalogSource\n> {\n const { resolveConfig, RemoteCatalogSource, RestCatalogSource, isLocalCatalogUrl } = await import(\n \"@skaile/workspaces/library\"\n );\n const cfg = resolveConfig({\n projectDir: opts?.projectDir,\n userConfigFile: opts?.userConfigFile,\n });\n const baseUrl = opts?.baseUrlOverride ?? cfg.catalog.url;\n if (isLocalCatalogUrl(baseUrl)) {\n throw new LocalCatalogDisabledError(\n \"catalog.url is set to 'local' — remote catalog is disabled. \" +\n \"Use `skaile library init <name> --path <dir>` for a local folder, \" +\n `or set \\`catalog.url: https://skaile.store\\` in ${userConfigPath()}.`,\n );\n }\n const cacheTtlMs = cfg.catalog.cache_ttl * 1000;\n if (cfg.catalog.framing === \"rest\") {\n return new RestCatalogSource({ baseUrl, cacheTtlMs });\n }\n return new RemoteCatalogSource({ baseUrl, cacheTtlMs });\n}\n\n/**\n * Resolved catalog source plus a close handle for releasing any underlying\n * resources (e.g. the `LocalIndex` SQLite connection in local mode).\n *\n * Callers MUST invoke `close()` when done — typically in a `finally` block.\n * For remote-mode sources `close()` is a no-op, but the contract is uniform\n * so consumers don't have to branch.\n *\n * @docLink cli/dev-guide#open-library\n */\nexport interface ResolvedCatalogSource {\n /** The {@link ICatalogSource} ready for use. */\n source: import(\"@skaile/workspaces/library\").ICatalogSource;\n /** Release any underlying resources (SQLite handle in local mode). */\n close(): void;\n}\n\n/**\n * Resolve the configured Catalog source — local or remote — based on\n * `~/.skaile/config.yaml`.\n *\n * Dispatch:\n * - `catalog.url: local` → opens {@link LocalIndex}, picks the most recently\n * registered local library whose `path` still exists on disk, and returns a\n * {@link LocalCatalogSource} bound to that library. Throws if no usable\n * local library is registered.\n * - any URL → returns {@link RemoteCatalogSource} (same as\n * {@link openCatalogSource}).\n *\n * The returned `close()` releases the underlying `LocalIndex` SQLite handle\n * in local mode (no-op in remote mode). Callers MUST invoke it — typically in\n * a `finally` block — to satisfy `library/CLAUDE.md` § \"Notes for Consumers\".\n *\n * @param opts - Project directory (overlays project config) and explicit overrides.\n * @returns A `ResolvedCatalogSource` carrying the source and a close handle.\n * @throws When `local` is configured but no usable local library is registered.\n * @docLink cli/dev-guide#open-library\n */\nexport async function resolveCatalogSource(opts?: {\n projectDir?: string;\n baseUrlOverride?: string;\n /** Override the user-config path for tests. */\n userConfigFile?: string;\n}): Promise<ResolvedCatalogSource> {\n const {\n resolveConfig,\n RemoteCatalogSource,\n RestCatalogSource,\n LocalCatalogSource,\n isLocalCatalogUrl,\n libraryDirMissing,\n missingLibraryDirMessage,\n } = await import(\"@skaile/workspaces/library\");\n const cfg = resolveConfig({\n projectDir: opts?.projectDir,\n userConfigFile: opts?.userConfigFile,\n });\n const baseUrl = opts?.baseUrlOverride ?? cfg.catalog.url;\n\n if (isLocalCatalogUrl(baseUrl)) {\n const fs = await import(\"node:fs\");\n const library = await openLibrary();\n try {\n const sources = await library.listSources();\n type LibraryRow = (typeof sources)[number];\n // Type-narrow: `path` is required for local libraries after the filter.\n const usable = sources\n .filter(\n (s): s is LibraryRow & { path: string } =>\n s.backend === \"local\" && typeof s.path === \"string\" && s.path.length > 0,\n )\n .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n\n // A registered library whose directory is gone has one wording everywhere.\n const gone = sources.filter(libraryDirMissing);\n if (usable.length === 0) {\n if (gone.length > 0) throw new Error(gone.map(missingLibraryDirMessage).join(\"\\n\"));\n throw new Error(\n \"catalog.url is set to 'local' but no local library is registered. \" +\n \"Run `skaile library init <name> --path <dir>` first (a git repo: `skaile source add <git-url>`).\",\n );\n }\n\n // Prefer the most recently added library whose path still exists.\n const present = usable.find((s) => fs.existsSync(s.path));\n if (!present) {\n throw new Error(gone.map(missingLibraryDirMessage).join(\"\\n\"));\n }\n\n const source = new LocalCatalogSource(library, present.id, present.path, getKindRegistry());\n return { source, close: () => library.close() };\n } catch (err) {\n // Release the handle on any failure during dispatch — otherwise the\n // SQLite WAL state leaks for the lifetime of the process.\n library.close();\n throw err;\n }\n }\n\n const cacheTtlMs = cfg.catalog.cache_ttl * 1000;\n const source =\n cfg.catalog.framing === \"rest\"\n ? new RestCatalogSource({ baseUrl, cacheTtlMs })\n : new RemoteCatalogSource({ baseUrl, cacheTtlMs });\n return { source, close: () => {} };\n}\n\n/**\n * Open the LibraryManager bound to the active LocalIndex.\n * Caller owns lifetime — must call `close()` (returned helper closes the\n * underlying LocalIndex).\n *\n * @docLink cli/dev-guide#open-library\n */\nexport async function openLibraryManager(): Promise<{\n manager: import(\"@skaile/workspaces/library\").LibraryManager;\n library: import(\"@skaile/workspaces/library\").LocalIndex;\n close: () => void;\n}> {\n const { LibraryManager } = await import(\"@skaile/workspaces/library\");\n const library = await openLibrary();\n const manager = new LibraryManager(library);\n return { manager, library, close: () => library.close() };\n}\n"]}
1
+ {"version":3,"sources":["../cli/src/open-library.ts"],"names":["source"],"mappings":"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","file":"chunk-J4RRH2NP.js","sourcesContent":["/**\n * open-library.ts — shared helpers to open the LocalIndex and the configured\n * Catalog source.\n *\n * Lazy-loads the `@skaile/workspaces/library` subpath and returns the\n * LocalIndex instance (backed by `@libsql/client`; runs on Node and Bun).\n * The catalog-source helper reads\n * `~/.skaile/config.yaml` (and project-level overlay if `projectDir` is given)\n * to decide between {@link RemoteCatalogSource} (default — points at\n * `https://skaile.store`) and a {@link LocalCatalogSource} bound to the most\n * recent local library registered via `skaile source add`.\n */\n\nimport { userConfigPath } from \"@skaile/workspaces/core\";\nimport { logErr } from \"./helpers.ts\";\nimport { getKindRegistry } from \"./open-registry.ts\";\n\n/** Thrown by {@link openCatalogSource} when `catalog.url` is the `local` sentinel. */\nexport class LocalCatalogDisabledError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LocalCatalogDisabledError\";\n }\n}\n\n/**\n * Open the LocalIndex with the full kind registry. Exits with code 1\n * if the library directory cannot be created.\n *\n * @returns A `LocalIndex` instance ready for use.\n * @docLink cli/dev-guide#open-library\n */\nexport async function openLibrary() {\n try {\n const { LocalIndex } = await import(\"@skaile/workspaces/library\");\n return new LocalIndex({ kindRegistry: getKindRegistry() });\n } catch (err) {\n logErr(`Could not open LocalIndex: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n}\n\n/**\n * Resolve the configured **remote** Catalog source for the CLI.\n *\n * Reads `~/.skaile/config.yaml` (plus the optional project-level overlay) and\n * returns a {@link RemoteCatalogSource} pointing at `catalog.url` (default:\n * `https://skaile.store`). The `cache_ttl: 0` config flag flips the source\n * into air-gapped mode: network reads are disabled, only `skaile update`\n * performs refreshes.\n *\n * **Local-mode rejection.** If the resolved config has `catalog.url: local`\n * this helper throws — callers wanting the dispatched local-or-remote source\n * must use {@link resolveCatalogSource}. Remote-only consumers like\n * `skaile update --catalog-only` rely on this strict behaviour.\n *\n * @param opts - Project directory (overlays project config) and explicit overrides.\n * @returns A configured {@link RemoteCatalogSource}.\n * @throws When `catalog.url` is the `local` sentinel.\n * @docLink cli/dev-guide#open-library\n */\nexport async function openCatalogSource(opts?: {\n projectDir?: string;\n baseUrlOverride?: string;\n /** Override the user-config path for tests. */\n userConfigFile?: string;\n}): Promise<\n | import(\"@skaile/workspaces/library\").RemoteCatalogSource\n | import(\"@skaile/workspaces/library\").RestCatalogSource\n> {\n const { resolveConfig, RemoteCatalogSource, RestCatalogSource, isLocalCatalogUrl } = await import(\n \"@skaile/workspaces/library\"\n );\n const cfg = resolveConfig({\n projectDir: opts?.projectDir,\n userConfigFile: opts?.userConfigFile,\n });\n const baseUrl = opts?.baseUrlOverride ?? cfg.catalog.url;\n if (isLocalCatalogUrl(baseUrl)) {\n throw new LocalCatalogDisabledError(\n \"catalog.url is set to 'local' — remote catalog is disabled. \" +\n \"Use `skaile library init <name> --path <dir>` for a local folder, \" +\n `or set \\`catalog.url: https://skaile.store\\` in ${userConfigPath()}.`,\n );\n }\n const cacheTtlMs = cfg.catalog.cache_ttl * 1000;\n if (cfg.catalog.framing === \"rest\") {\n return new RestCatalogSource({ baseUrl, cacheTtlMs });\n }\n return new RemoteCatalogSource({ baseUrl, cacheTtlMs });\n}\n\n/**\n * Resolved catalog source plus a close handle for releasing any underlying\n * resources (e.g. the `LocalIndex` SQLite connection in local mode).\n *\n * Callers MUST invoke `close()` when done — typically in a `finally` block.\n * For remote-mode sources `close()` is a no-op, but the contract is uniform\n * so consumers don't have to branch.\n *\n * @docLink cli/dev-guide#open-library\n */\nexport interface ResolvedCatalogSource {\n /** The {@link ICatalogSource} ready for use. */\n source: import(\"@skaile/workspaces/library\").ICatalogSource;\n /** Release any underlying resources (SQLite handle in local mode). */\n close(): void;\n}\n\n/**\n * Resolve the configured Catalog source — local or remote — based on\n * `~/.skaile/config.yaml`.\n *\n * Dispatch:\n * - `catalog.url: local` → opens {@link LocalIndex}, picks the most recently\n * registered local library whose `path` still exists on disk, and returns a\n * {@link LocalCatalogSource} bound to that library. Throws if no usable\n * local library is registered.\n * - any URL → returns {@link RemoteCatalogSource} (same as\n * {@link openCatalogSource}).\n *\n * The returned `close()` releases the underlying `LocalIndex` SQLite handle\n * in local mode (no-op in remote mode). Callers MUST invoke it — typically in\n * a `finally` block — to satisfy `library/CLAUDE.md` § \"Notes for Consumers\".\n *\n * @param opts - Project directory (overlays project config) and explicit overrides.\n * @returns A `ResolvedCatalogSource` carrying the source and a close handle.\n * @throws When `local` is configured but no usable local library is registered.\n * @docLink cli/dev-guide#open-library\n */\nexport async function resolveCatalogSource(opts?: {\n projectDir?: string;\n baseUrlOverride?: string;\n /** Override the user-config path for tests. */\n userConfigFile?: string;\n}): Promise<ResolvedCatalogSource> {\n const {\n resolveConfig,\n RemoteCatalogSource,\n RestCatalogSource,\n LocalCatalogSource,\n isLocalCatalogUrl,\n libraryDirMissing,\n missingLibraryDirMessage,\n } = await import(\"@skaile/workspaces/library\");\n const cfg = resolveConfig({\n projectDir: opts?.projectDir,\n userConfigFile: opts?.userConfigFile,\n });\n const baseUrl = opts?.baseUrlOverride ?? cfg.catalog.url;\n\n if (isLocalCatalogUrl(baseUrl)) {\n const fs = await import(\"node:fs\");\n const library = await openLibrary();\n try {\n const sources = await library.listSources();\n type LibraryRow = (typeof sources)[number];\n // Type-narrow: `path` is required for local libraries after the filter.\n const usable = sources\n .filter(\n (s): s is LibraryRow & { path: string } =>\n s.backend === \"local\" && typeof s.path === \"string\" && s.path.length > 0,\n )\n .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n\n // A registered library whose directory is gone has one wording everywhere.\n const gone = sources.filter(libraryDirMissing);\n if (usable.length === 0) {\n if (gone.length > 0) throw new Error(gone.map(missingLibraryDirMessage).join(\"\\n\"));\n throw new Error(\n \"catalog.url is set to 'local' but no local library is registered. \" +\n \"Run `skaile library init <name> --path <dir>` first (a git repo: `skaile source add <git-url>`).\",\n );\n }\n\n // Prefer the most recently added library whose path still exists.\n const present = usable.find((s) => fs.existsSync(s.path));\n if (!present) {\n throw new Error(gone.map(missingLibraryDirMessage).join(\"\\n\"));\n }\n\n const source = new LocalCatalogSource(library, present.id, present.path, getKindRegistry());\n return { source, close: () => library.close() };\n } catch (err) {\n // Release the handle on any failure during dispatch — otherwise the\n // SQLite WAL state leaks for the lifetime of the process.\n library.close();\n throw err;\n }\n }\n\n const cacheTtlMs = cfg.catalog.cache_ttl * 1000;\n const source =\n cfg.catalog.framing === \"rest\"\n ? new RestCatalogSource({ baseUrl, cacheTtlMs })\n : new RemoteCatalogSource({ baseUrl, cacheTtlMs });\n return { source, close: () => {} };\n}\n\n/**\n * Open the LibraryManager bound to the active LocalIndex.\n * Caller owns lifetime — must call `close()` (returned helper closes the\n * underlying LocalIndex).\n *\n * @docLink cli/dev-guide#open-library\n */\nexport async function openLibraryManager(): Promise<{\n manager: import(\"@skaile/workspaces/library\").LibraryManager;\n library: import(\"@skaile/workspaces/library\").LocalIndex;\n close: () => void;\n}> {\n const { LibraryManager } = await import(\"@skaile/workspaces/library\");\n const library = await openLibrary();\n const manager = new LibraryManager(library);\n return { manager, library, close: () => library.close() };\n}\n"]}
@@ -43,6 +43,7 @@ var WebSocketServerTransport = class _WebSocketServerTransport {
43
43
  authToken;
44
44
  heartbeatMs;
45
45
  maxMissedPongs;
46
+ onListening;
46
47
  commandHandlers = /* @__PURE__ */ new Set();
47
48
  connectHandlers = /* @__PURE__ */ new Set();
48
49
  disconnectHandlers = /* @__PURE__ */ new Set();
@@ -51,6 +52,7 @@ var WebSocketServerTransport = class _WebSocketServerTransport {
51
52
  this.log = options.onLog ?? console.log;
52
53
  this.host = options.host;
53
54
  this.authToken = options.authToken;
55
+ this.onListening = options.onListening;
54
56
  this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
55
57
  this.maxMissedPongs = options.maxMissedPongs ?? DEFAULT_MAX_MISSED_PONGS;
56
58
  }
@@ -128,6 +130,12 @@ var WebSocketServerTransport = class _WebSocketServerTransport {
128
130
  this.log(
129
131
  `[ws-server] listening on port ${boundPort} (${bind}; ${this.authToken ? "token required" : "no authentication"})`
130
132
  );
133
+ const urlHost = !this.host || this.host === "0.0.0.0" || this.host === "::" ? "127.0.0.1" : this.host.includes(":") && !this.host.startsWith("[") ? `[${this.host}]` : this.host;
134
+ this.onListening?.({
135
+ url: `ws://${urlHost}:${boundPort}`,
136
+ port: boundPort,
137
+ tokenRequired: this.authToken !== void 0
138
+ });
131
139
  }
132
140
  handleConnection(ws) {
133
141
  if (this.activeClient) {
@@ -276,5 +284,5 @@ var WebSocketServerTransport = class _WebSocketServerTransport {
276
284
  };
277
285
 
278
286
  export { WebSocketServerTransport, needsWildcardProbe };
279
- //# sourceMappingURL=chunk-NOF73BUH.js.map
280
- //# sourceMappingURL=chunk-NOF73BUH.js.map
287
+ //# sourceMappingURL=chunk-L6UMSKDF.js.map
288
+ //# sourceMappingURL=chunk-L6UMSKDF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/src/ws/server.ts"],"names":["createProbeServer","createServer"],"mappings":"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","file":"chunk-L6UMSKDF.js","sourcesContent":["/**\n * WebSocketServerTransport — `ws`-based WebSocket server.\n *\n * Accepts one client at a time. Implements ServerTransport from @skaile/workspaces/types.\n * Built on the `ws` package + `node:http`, both of which run under Node and Bun.\n */\n\nimport { createServer, type Server as HttpServer } from \"node:http\";\nimport { createServer as createProbeServer } from \"node:net\";\nimport type { AgentCommand, AgentEvent, ServerTransport } from \"@skaile/workspaces/types\";\nimport { type WebSocket, WebSocketServer } from \"ws\";\nimport { decodeBearerFromHeader, tokensEqual } from \"./auth.js\";\n\n/**\n * Options for {@link WebSocketServerTransport}.\n *\n * @param port - TCP port to bind to; `0` picks a free port (read it back via `boundPort`).\n * @param onLog - Optional callback for internal log lines (client connect/disconnect events).\n * @param host - Address to bind. Default: every interface (Node's unspecified address).\n * `127.0.0.1` accepts local clients only.\n * @param authToken - When set, every upgrade must present this token via the\n * `skaile-bearer.<base64url>` subprotocol or the handshake is rejected with\n * HTTP 401. When unset, no auth is enforced (back-compatible default).\n * @param onListening - Called once the socket is bound, with the address a client\n * connects to (`url`) and whether it needs a token. For a plain-text ready line\n * (the `onLog` record carries the port only inside a log sentence).\n * @param heartbeatMs - Interval in milliseconds between pings to the active\n * client. Default: 30 000.\n * @param maxMissedPongs - Heartbeat ticks the client may go without a pong (or\n * any inbound frame) before its socket is terminated, so a client lost behind a\n * dead path (no FIN) fires `onDisconnect` instead of swallowing every event\n * sent to it. Default: 2, i.e. detection within 3 x `heartbeatMs`. `0` turns\n * the heartbeat off. Every WebSocket client answers pings on its own.\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport interface WebSocketServerOptions {\n port: number;\n host?: string;\n onLog?: (line: string) => void;\n authToken?: string;\n onListening?: (info: { url: string; port: number; tokenRequired: boolean }) => void;\n heartbeatMs?: number;\n maxMissedPongs?: number;\n}\n\nconst DEFAULT_HEARTBEAT_MS = 30_000;\nconst DEFAULT_MAX_MISSED_PONGS = 2;\n\n/**\n * Shape-validate a `session_init` frame at ingress before forwarding to the\n * runner. Protocol v3 requires the deterministic bootstrap envelope to carry\n * an identity tuple, a semver-shaped `protocolVersion`, a resolved config\n * object, and a `credentials` map keyed by `mounts` / `connectors`. Frames\n * that fail the guard are rejected on the wire so the runner never sees a\n * malformed boot envelope.\n *\n * Returns `null` when the frame is well-formed, or a human-readable reason\n * when it should be rejected.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"SessionInitCommand schema\".\n *\n * @internal\n */\nfunction validateSessionInitShape(cmd: Record<string, unknown>): string | null {\n if (cmd.type !== \"session_init\") return \"type must be 'session_init'\";\n\n const identity = cmd.identity as Record<string, unknown> | undefined;\n if (!identity || typeof identity !== \"object\" || Array.isArray(identity)) {\n return \"identity must be an object\";\n }\n if (typeof identity.containerId !== \"string\") return \"identity.containerId must be a string\";\n if (typeof identity.sessionId !== \"string\") return \"identity.sessionId must be a string\";\n if (typeof identity.projectId !== \"string\") return \"identity.projectId must be a string\";\n\n const protocolVersion = cmd.protocolVersion as Record<string, unknown> | undefined;\n if (!protocolVersion || typeof protocolVersion !== \"object\" || Array.isArray(protocolVersion)) {\n return \"protocolVersion must be an object\";\n }\n if (typeof protocolVersion.major !== \"number\") return \"protocolVersion.major must be a number\";\n if (typeof protocolVersion.minor !== \"number\") return \"protocolVersion.minor must be a number\";\n if (typeof protocolVersion.patch !== \"number\") return \"protocolVersion.patch must be a number\";\n\n if (\n !cmd.resolvedConfig ||\n typeof cmd.resolvedConfig !== \"object\" ||\n Array.isArray(cmd.resolvedConfig)\n ) {\n return \"resolvedConfig must be an object\";\n }\n\n if (!cmd.credentials || typeof cmd.credentials !== \"object\" || Array.isArray(cmd.credentials)) {\n return \"credentials must be an object\";\n }\n\n return null;\n}\n\n/**\n * Whether a bind to `host:port` must first probe the wildcard address (p842). Only macOS lets a\n * specific-address bind coexist with a wildcard listener on the same port, so only there does the\n * real bind miss it. On Linux the real bind already fails with EADDRINUSE, and a wildcard probe\n * would wrongly refuse a port that another process holds on a different specific address.\n * A wildcard host or an ephemeral port needs no probe: the real bind covers them.\n */\nexport function needsWildcardProbe(\n host: string | undefined,\n port: number,\n platform: NodeJS.Platform,\n): boolean {\n if (!host || port === 0 || platform !== \"darwin\") return false;\n return ![\"0.0.0.0\", \"::\", \"::0\", \"0:0:0:0:0:0:0:0\"].includes(host);\n}\n\n/**\n * `ServerTransport` implementation backed by the `ws` package on top of a\n * `node:http` server.\n *\n * Enforces a single-client policy: if a new connection arrives while a client is\n * already connected, the stale connection is closed with code 4001 and replaced.\n * Incoming frames are parsed as JSON `AgentCommand` messages and fanned out to\n * registered `onCommand` handlers; outgoing `AgentEvent` objects are JSON-serialised\n * before sending. Call `listen()` to start accepting connections and `close()` to\n * shut down cleanly — `close()` terminates the active socket immediately rather\n * than keeping the event loop alive.\n *\n * @example\n * ```ts\n * const server = new WebSocketServerTransport({ port: 8080 });\n * await server.listen();\n * server.onCommand((cmd) => console.log(cmd));\n * server.send({ type: 'agent_end' });\n * await server.close();\n * ```\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport class WebSocketServerTransport implements ServerTransport {\n private httpServer: HttpServer | null = null;\n private wss: WebSocketServer | null = null;\n private activeClient: WebSocket | null = null;\n private readonly port: number;\n private readonly log: (line: string) => void;\n private readonly host?: string;\n private readonly authToken?: string;\n private readonly heartbeatMs: number;\n private readonly maxMissedPongs: number;\n\n private readonly onListening: WebSocketServerOptions[\"onListening\"];\n private readonly commandHandlers = new Set<(command: AgentCommand) => void>();\n private readonly connectHandlers = new Set<() => void>();\n private readonly disconnectHandlers = new Set<() => void>();\n\n constructor(options: WebSocketServerOptions) {\n this.port = options.port;\n this.log = options.onLog ?? console.log;\n this.host = options.host;\n this.authToken = options.authToken;\n this.onListening = options.onListening;\n this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n this.maxMissedPongs = options.maxMissedPongs ?? DEFAULT_MAX_MISSED_PONGS;\n }\n\n /**\n * Whether another process holds `port` on every interface. A specific-address\n * bind coexists with a wildcard listener on some platforms (macOS), so the\n * bind alone would succeed while the other process keeps answering other\n * clients (p842). Only EADDRINUSE counts; any other probe error (no wildcard\n * address, no permission) leaves the decision to the real bind.\n */\n private static holdsWildcard(port: number): Promise<boolean> {\n const { promise, resolve } = Promise.withResolvers<boolean>();\n const probe = createProbeServer();\n probe.once(\"error\", (err: NodeJS.ErrnoException) => resolve(err.code === \"EADDRINUSE\"));\n probe.listen(port, () => probe.close(() => resolve(false)));\n return promise;\n }\n\n /** Start the server and accept connections. */\n async listen(): Promise<void> {\n const httpServer = createServer((_req, res) => {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"skaile agent server\");\n });\n // When an auth token is configured, reject the upgrade at handshake time\n // (HTTP 401) unless the client presents the matching bearer subprotocol.\n // Rejecting here — before `connection` — keeps unauthenticated sockets from\n // ever reaching the command handlers.\n const expected = this.authToken;\n const wss = new WebSocketServer({\n server: httpServer,\n verifyClient: expected\n ? (info, cb) => {\n const presented = decodeBearerFromHeader(info.req.headers[\"sec-websocket-protocol\"]);\n if (presented !== null && tokensEqual(presented, expected)) {\n cb(true);\n } else {\n this.log(\"[ws-server] rejecting unauthenticated upgrade\");\n cb(false, 401, \"Unauthorized\");\n }\n }\n : undefined,\n });\n wss.on(\"connection\", (ws) => this.handleConnection(ws));\n // `ws` re-emits the http server's errors on `wss`; unhandled during the bind,\n // that throws before the reject below runs and `listen()` hangs. Scoped to the\n // bind so a post-startup server error still surfaces loudly.\n const swallowBindError = () => {};\n wss.on(\"error\", swallowBindError);\n this.httpServer = httpServer;\n this.wss = wss;\n\n try {\n if (\n needsWildcardProbe(this.host, this.port, process.platform) &&\n (await WebSocketServerTransport.holdsWildcard(this.port))\n )\n throw Object.assign(new Error(\"wildcard probe\"), { code: \"EADDRINUSE\" });\n await new Promise<void>((resolve, reject) => {\n httpServer.once(\"error\", reject);\n const onBound = () => {\n httpServer.off(\"error\", reject);\n resolve();\n };\n if (this.host) httpServer.listen(this.port, this.host, onBound);\n else httpServer.listen(this.port, onBound);\n });\n } catch (err) {\n wss.close();\n this.wss = null;\n this.httpServer = null;\n if (err instanceof Error && \"code\" in err && err.code === \"EADDRINUSE\") {\n const where = this.host ? `${this.host}:${this.port}` : `port ${this.port}`;\n throw Object.assign(\n new Error(\n `${where} is already in use: another process listens there. Pick another port.`,\n ),\n { code: \"EADDRINUSE\" },\n );\n }\n throw err;\n } finally {\n wss.off(\"error\", swallowBindError);\n }\n\n const addr = httpServer.address();\n const boundPort = typeof addr === \"object\" && addr ? addr.port : this.port;\n const bind = this.host ?? \"all interfaces\";\n this.log(\n `[ws-server] listening on port ${boundPort} (${bind}; ${this.authToken ? \"token required\" : \"no authentication\"})`,\n );\n // A wildcard bind is reachable on loopback; that is the URL worth printing.\n const urlHost =\n !this.host || this.host === \"0.0.0.0\" || this.host === \"::\"\n ? \"127.0.0.1\"\n : this.host.includes(\":\") && !this.host.startsWith(\"[\")\n ? `[${this.host}]`\n : this.host;\n this.onListening?.({\n url: `ws://${urlHost}:${boundPort}`,\n port: boundPort,\n tokenRequired: this.authToken !== undefined,\n });\n }\n\n private handleConnection(ws: WebSocket): void {\n if (this.activeClient) {\n // Close the stale connection instead of rejecting the new one.\n // This handles the race where waitForWsReady's probe hasn't\n // fully disconnected before the real gateway client connects.\n this.log(\"[ws-server] replacing previous connection\");\n try {\n this.activeClient.close(4001, \"Replaced by new connection\");\n } catch {\n // already closed\n }\n }\n this.log(\"[ws-server] client connected\");\n this.activeClient = ws;\n for (const handler of this.connectHandlers) {\n handler();\n }\n\n const stopHeartbeat = this.startHeartbeat(ws);\n\n ws.on(\"message\", (data) => {\n try {\n const parsed = JSON.parse(String(data)) as Record<string, unknown>;\n // Protocol v3: shape-validate `session_init` at ingress so the\n // runner never receives a malformed boot envelope. Other command\n // shapes pass through unchanged — invalid `AgentCommand`s surface\n // through the existing per-command handlers in serve.ts.\n if (parsed && typeof parsed === \"object\" && parsed.type === \"session_init\") {\n const reason = validateSessionInitShape(parsed);\n if (reason !== null) {\n this.log(`[ws-server] rejecting invalid session_init: ${reason}`);\n this.sendRaw({\n type: \"error\",\n message: `Invalid session_init: ${reason}`,\n fatal: false,\n });\n return;\n }\n }\n const cmd = parsed as unknown as AgentCommand;\n for (const handler of this.commandHandlers) {\n handler(cmd);\n }\n } catch (err) {\n this.sendRaw({\n type: \"error\",\n message: `Invalid message: ${err instanceof Error ? err.message : String(err)}`,\n fatal: false,\n });\n }\n });\n\n ws.on(\"close\", () => {\n stopHeartbeat();\n this.log(\"[ws-server] client disconnected\");\n if (this.activeClient === ws) {\n this.activeClient = null;\n for (const handler of this.disconnectHandlers) {\n handler();\n }\n }\n });\n }\n\n /**\n * Ping `ws` every {@link heartbeatMs}; terminate it after\n * {@link maxMissedPongs} unanswered ticks. `terminate()` makes `ws` emit\n * `close`, which runs the normal disconnect path. Returns the stop function.\n */\n private startHeartbeat(ws: WebSocket): () => void {\n if (this.maxMissedPongs <= 0) return () => {};\n let missed = 0;\n const markAlive = () => {\n missed = 0;\n };\n ws.on(\"pong\", markAlive);\n ws.on(\"message\", markAlive);\n const timer = setInterval(() => {\n if (ws.readyState !== ws.OPEN) return;\n if (missed >= this.maxMissedPongs) {\n this.log(\n `[ws-server] no pong for ${missed} heartbeats of ${this.heartbeatMs}ms; terminating client`,\n );\n clearInterval(timer);\n ws.terminate();\n return;\n }\n missed++;\n try {\n ws.ping();\n } catch {\n // socket closing underneath us; the close event cleans up\n }\n }, this.heartbeatMs);\n timer.unref?.();\n return () => clearInterval(timer);\n }\n\n /**\n * Stop the server.\n *\n * Terminates the active socket immediately — otherwise a still-connected\n * client would keep the event loop alive and block process shutdown.\n */\n async close(): Promise<void> {\n try {\n this.activeClient?.terminate();\n } catch {\n // already closed\n }\n this.activeClient = null;\n this.wss?.close();\n this.wss = null;\n const httpServer = this.httpServer;\n this.httpServer = null;\n if (httpServer) {\n httpServer.closeAllConnections();\n await new Promise<void>((resolve) => httpServer.close(() => resolve()));\n }\n }\n\n send(event: AgentEvent): void {\n this.sendRaw(event);\n }\n\n onCommand(handler: (command: AgentCommand) => void): () => void {\n this.commandHandlers.add(handler);\n return () => {\n this.commandHandlers.delete(handler);\n };\n }\n\n onConnect(handler: () => void): () => void {\n this.connectHandlers.add(handler);\n return () => {\n this.connectHandlers.delete(handler);\n };\n }\n\n onDisconnect(handler: () => void): () => void {\n this.disconnectHandlers.add(handler);\n return () => {\n this.disconnectHandlers.delete(handler);\n };\n }\n\n get connected(): boolean {\n return this.activeClient !== null;\n }\n\n /** The port actually bound by `listen()` (resolves `port: 0`); `undefined` when not listening. */\n get boundPort(): number | undefined {\n const addr = this.httpServer?.address();\n return typeof addr === \"object\" && addr ? addr.port : undefined;\n }\n\n private sendRaw(data: unknown): void {\n try {\n const client = this.activeClient;\n if (client && client.readyState === 1) {\n client.send(JSON.stringify(data));\n }\n } catch {\n // Silently ignore send failures\n }\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { resolveAgentExec } from './chunk-YROR332M.js';
2
- import { tryWithFsDeadline, fsDeadlineMs, withFsDeadline } from './chunk-SP5NGNBV.js';
2
+ import { tryWithFsDeadline, fsDeadlineMs, withFsDeadline } from './chunk-YW2BNDSE.js';
3
3
  import { entryTransport, normalizeAnthropicBaseUrl, resolveEntryBaseUrl } from './chunk-MJHSLODD.js';
4
4
  import { createWriteStream, createReadStream } from 'fs';
5
5
  import { unlink, rename, readdir, stat } from 'fs/promises';
@@ -8,6 +8,30 @@ import { pipeline } from 'stream/promises';
8
8
  import { spawn } from 'child_process';
9
9
  import { homedir } from 'os';
10
10
 
11
+ // bridge/src/approval-card.ts
12
+ var APPROVE_ONCE = "Approve once";
13
+ var DENY = "Deny";
14
+ function approvalCardOf(event) {
15
+ if (event.type !== "message_start" || !Array.isArray(event.message.content)) return void 0;
16
+ for (const block of event.message.content) {
17
+ if (block.type !== "tool_use" || block.name !== "AskUserQuestion") continue;
18
+ const input = block.input;
19
+ const approval = input?.approval;
20
+ const questions = input?.questions;
21
+ const first = Array.isArray(questions) ? questions[0] : void 0;
22
+ if (!approval || typeof first?.question !== "string" || !Array.isArray(approval.details) || !approval.details.every((d) => typeof d === "string")) {
23
+ continue;
24
+ }
25
+ return {
26
+ question: first.question,
27
+ ...typeof input?.requestId === "string" ? { requestId: input.requestId } : {},
28
+ kind: approval.kind,
29
+ details: approval.details
30
+ };
31
+ }
32
+ return void 0;
33
+ }
34
+
11
35
  // bridge/src/cloud.ts
12
36
  var SCRUBBED_ENV_KEYS = ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL"];
13
37
  var ANTHROPIC_DEFAULT_BASE_URL = "https://api.anthropic.com";
@@ -487,6 +511,6 @@ async function probeResumeTranscript(sessionId, opts) {
487
511
  }
488
512
  }
489
513
 
490
- export { ANTHROPIC_DEFAULT_BASE_URL, agentTranscriptStore, buildClaudeEndpointEnv, claudeEndpointMode, localTranscriptStore, probeResumeTranscript, resolveClaudeConfigDir, scrubPoisonedTranscript, transcriptStoreForRuntime };
491
- //# sourceMappingURL=chunk-TCWECSEB.js.map
492
- //# sourceMappingURL=chunk-TCWECSEB.js.map
514
+ export { ANTHROPIC_DEFAULT_BASE_URL, APPROVE_ONCE, DENY, agentTranscriptStore, approvalCardOf, buildClaudeEndpointEnv, claudeEndpointMode, localTranscriptStore, probeResumeTranscript, resolveClaudeConfigDir, scrubPoisonedTranscript, transcriptStoreForRuntime };
515
+ //# sourceMappingURL=chunk-NSNM2ZXF.js.map
516
+ //# sourceMappingURL=chunk-NSNM2ZXF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../bridge/src/approval-card.ts","../bridge/src/cloud.ts","../bridge/src/drivers/scrub-transcript.ts","../bridge/src/agent-transcript.ts","../bridge/src/drivers/resume-probe.ts"],"names":["join"],"mappings":"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","file":"chunk-NSNM2ZXF.js","sourcesContent":["/**\n * The tool-approval card a driver raises for `permissionMode: \"interactive\"`: a `message_start`\n * carrying an `AskUserQuestion` tool call whose input has an `approval` description. The\n * normalizer turns it into a `question` event with `approval`; a host that reads the driver's\n * events directly (the REPL, a single-shot run) recognises it with {@link approvalCardOf} and\n * answers with {@link APPROVE_ONCE} or {@link DENY} through `driver.answerQuestion`.\n */\nimport type { AgentEvent } from \"./types.js\";\n\n/** The one positive answer of an approval card. */\nexport const APPROVE_ONCE = \"Approve once\";\n/** The negative answer of an approval card. */\nexport const DENY = \"Deny\";\n\n/** What a host needs to ask and answer one approval card. */\nexport interface ApprovalCard {\n /** The question text; the key `answerQuestion` takes. */\n question: string;\n requestId?: string;\n kind: \"command\" | \"file\" | \"permissions\" | \"mcp\";\n /** Bounded plain text of what the tool call is about to do. */\n details: string[];\n}\n\n/** The approval card an event carries, else `undefined`. */\nexport function approvalCardOf(event: AgentEvent): ApprovalCard | undefined {\n if (event.type !== \"message_start\" || !Array.isArray(event.message.content)) return undefined;\n for (const block of event.message.content) {\n if (block.type !== \"tool_use\" || block.name !== \"AskUserQuestion\") continue;\n const input = block.input as Record<string, unknown> | undefined;\n const approval = input?.approval as { kind?: unknown; details?: unknown } | undefined;\n const questions = input?.questions;\n const first = Array.isArray(questions) ? (questions[0] as { question?: unknown }) : undefined;\n if (\n !approval ||\n typeof first?.question !== \"string\" ||\n !Array.isArray(approval.details) ||\n !approval.details.every((d) => typeof d === \"string\")\n ) {\n continue;\n }\n return {\n question: first.question,\n ...(typeof input?.requestId === \"string\" ? { requestId: input.requestId } : {}),\n kind: approval.kind as ApprovalCard[\"kind\"],\n details: approval.details as string[],\n };\n }\n return undefined;\n}\n","/**\n * AI endpoint transport for the `claude-sdk` driver — the Claude Agent SDK's\n * child-process environment.\n *\n * {@link buildClaudeEndpointEnv} takes an **AI provider registry entry** and\n * is what the driver calls, on every transport. It is plan §2.3's\n * `buildEndpointEnv` for the claude-sdk row (named for the driver because the\n * vars it emits — `CLAUDE_CODE_USE_*`, `ANTHROPIC_*` — are the Claude Agent\n * SDK's own contract, not a portable one).\n *\n * A registry entry says what the deleted `cloud` axis said — Amazon Bedrock,\n * Google Vertex AI, Azure AI Foundry, a generic gateway — with `surface` +\n * `transport` + `base_url` + `credential`, and says it for the direct\n * endpoint too, which the `cloud` axis could not address at all.\n *\n * Cross-repo contract: env key names and semantics here are load-bearing —\n * the platform resolves credential bundles against the same key set.\n */\n\nimport {\n type AIProviderEntry,\n type AIProviderLlmEntry,\n entryTransport,\n normalizeAnthropicBaseUrl,\n resolveEntryBaseUrl,\n} from \"@skaile/workspaces/core/provider-registry\";\n\n/**\n * Env vars scrubbed from the inherited environment before an endpoint env is\n * assembled. Ambient first-party credentials or a stale `CLAUDE_CODE_USE_*`\n * flag must never leak into a session whose entry points elsewhere (the\n * ambient carriers are restored explicitly when the entry has none of its\n * own).\n *\n * Deliberately NOT exhaustive: ambient transport-specific vars (AWS_*,\n * GOOGLE_*, ANTHROPIC_VERTEX_PROJECT_ID, ANTHROPIC_FOUNDRY_*) survive so a\n * standalone CLI inherits its own signing chain; the SDK ignores vars whose\n * transport flag is unset.\n */\nconst SCRUBBED_ENV_KEYS = [\"ANTHROPIC_API_KEY\", \"ANTHROPIC_AUTH_TOKEN\", \"ANTHROPIC_BASE_URL\"];\n\n/**\n * The Anthropic first-party endpoint. An entry that resolves to exactly this\n * host needs no `ANTHROPIC_BASE_URL`: the SDK already points there, and an\n * explicit value would make every default session look like a third-party\n * redirect to Claude Code's own telemetry and auth heuristics.\n */\nexport const ANTHROPIC_DEFAULT_BASE_URL = \"https://api.anthropic.com\";\n\n/**\n * Headers the SDK owns and an entry must not restate through\n * `ANTHROPIC_CUSTOM_HEADERS`: the protocol version it was built against, and\n * the two credential carriers (which are `credential.ref`'s job — a literal\n * secret in `headers` is a validation error anyway).\n */\nconst SDK_OWNED_HEADERS: Record<string, true> = {\n \"anthropic-version\": true,\n authorization: true,\n \"x-api-key\": true,\n};\n\n/**\n * Which of the Claude Agent SDK's four native transports an entry addresses.\n *\n * The SDK has no `transport` field: it switches on a `CLAUDE_CODE_USE_*` flag.\n * Bedrock and Vertex are their own wire surfaces, so they are recognized by\n * `surface` as well as by `transport` — a legacy-derived entry that had no\n * region (and therefore no `params`, and therefore no `transport`) is still a\n * Bedrock session, exactly as it was before the registry existed.\n */\nexport type ClaudeEndpointMode = \"anthropic\" | \"bedrock\" | \"vertex\" | \"foundry\";\n\n/** Classify a registry entry into its {@link ClaudeEndpointMode}. */\nexport function claudeEndpointMode(entry: AIProviderEntry): ClaudeEndpointMode {\n const surface = (entry as AIProviderLlmEntry).surface;\n const transport = entryTransport(entry);\n if (surface === \"bedrock-converse-stream\" || transport === \"bedrock\") return \"bedrock\";\n if (surface === \"google-vertex\" || transport === \"vertex\") return \"vertex\";\n if (transport === \"foundry\") return \"foundry\";\n return \"anthropic\";\n}\n\n/** Arguments for {@link buildClaudeEndpointEnv}. */\nexport interface ClaudeEndpointEnvInput {\n /** The selected registry entry — the whole endpoint definition. */\n entry: AIProviderEntry;\n /**\n * The entry's credential, already resolved from its `credential.ref`\n * (`resolveEntryCredential`). Absent for `kind: none`, for an ambient\n * `sigv4` / `google_adc` / `azure_entra` chain, for `oauth` (whose blob the\n * runner writes to the Claude credentials file, never to the env), and for a\n * reference that did not resolve.\n */\n credential?: string;\n /**\n * The driver's base environment — `process.env` with `undefined` dropped.\n * The endpoint vars are layered **on top** of it, never instead of it, so\n * `PATH`, `HOME` and `SKAILE_SESSION_ID` reach the child.\n */\n baseEnv: Record<string, string>;\n /** `AgentConfig.env` plus any credential material the session carried. */\n configEnv?: Record<string, string>;\n}\n\n/**\n * Assemble the Claude Agent SDK child env for one registry entry (plan §2.3).\n *\n * Pure (no external state read, no input mutated). Starts from `baseEnv`\n * merged with `configEnv`, scrubs the inherited Anthropic credential/routing\n * vars and every `CLAUDE_CODE_USE_*` flag, then writes exactly what the entry\n * describes: at most one transport flag, the endpoint, and the credential in\n * the carrier its `kind` names.\n *\n * Credential kinds (§1.4): `api_key_header` → `ANTHROPIC_API_KEY` (the SDK\n * sends it as `x-api-key`); `bearer` → `ANTHROPIC_AUTH_TOKEN` (sent as\n * `Authorization: Bearer`); `sigv4` / `google_adc` / `azure_entra` emit\n * nothing and let the AWS/Google/Azure chain sign — which is why the scrub\n * deliberately spares ambient `AWS_*` / `GOOGLE_*` values; `oauth` and `none`\n * emit nothing, and the pre-scrub ambient carriers are restored so an OAuth or\n * shell-configured session keeps the auth it had.\n *\n * Never returns `undefined` or empty values; absent settings are omitted.\n */\nexport function buildClaudeEndpointEnv(args: ClaudeEndpointEnvInput): Record<string, string> {\n const { entry, credential, baseEnv, configEnv } = args;\n const env: Record<string, string> = { ...baseEnv, ...(configEnv ?? {}) };\n\n // Captured before the scrub — an entry with no credential material of its\n // own falls back to these, the way the gateway branch always has.\n const ambient = { authToken: env.ANTHROPIC_AUTH_TOKEN, apiKey: env.ANTHROPIC_API_KEY };\n\n for (const key of Object.keys(env)) {\n if (SCRUBBED_ENV_KEYS.includes(key) || key.startsWith(\"CLAUDE_CODE_USE_\")) {\n delete env[key];\n }\n }\n\n const set = (key: string, value: string | undefined) => {\n // Falsy check is deliberate: an empty-string credential/setting is never\n // valid env — dropping it beats shipping a key the SDK treats as set.\n if (value) env[key] = value;\n };\n\n const kind = entry.credential?.kind ?? \"none\";\n const params = (entry as AIProviderLlmEntry).params as Record<string, string> | undefined;\n\n // Static headers ride as ANTHROPIC_CUSTOM_HEADERS (`Name: Value` per line),\n // the only channel the SDK offers. An entry with none leaves an ambient\n // value alone.\n const headers = Object.entries(entry.headers ?? {}).filter(\n ([name]) => !SDK_OWNED_HEADERS[name.toLowerCase()],\n );\n if (headers.length > 0) {\n env.ANTHROPIC_CUSTOM_HEADERS = headers.map(([name, value]) => `${name}: ${value}`).join(\"\\n\");\n }\n\n switch (claudeEndpointMode(entry)) {\n case \"bedrock\": {\n env.CLAUDE_CODE_USE_BEDROCK = \"1\";\n // The AWS credential bundle itself is `configEnv` material (AWS_*), and\n // ambient AWS_* survived the scrub, so a standalone CLI keeps its own.\n set(\"AWS_REGION\", params?.region);\n break;\n }\n case \"vertex\": {\n env.CLAUDE_CODE_USE_VERTEX = \"1\";\n // GOOGLE_APPLICATION_CREDENTIALS is a path the runner materialized; it\n // arrives through `configEnv` or ambient, never from `credential`.\n set(\"ANTHROPIC_VERTEX_PROJECT_ID\", params?.project);\n set(\"CLOUD_ML_REGION\", params?.location);\n break;\n }\n case \"foundry\": {\n env.CLAUDE_CODE_USE_FOUNDRY = \"1\";\n // §1.3: an explicit `base_url` wins over the resource-derived endpoint,\n // and the SDK has a separate var for each form.\n if (entry.base_url) set(\"ANTHROPIC_FOUNDRY_BASE_URL\", entry.base_url);\n else set(\"ANTHROPIC_FOUNDRY_RESOURCE\", params?.resource);\n set(\"ANTHROPIC_FOUNDRY_API_KEY\", credential);\n break;\n }\n case \"anthropic\": {\n const baseUrl = normalizeAnthropicBaseUrl(resolveEntryBaseUrl(entry));\n if (baseUrl && baseUrl.toLowerCase() !== ANTHROPIC_DEFAULT_BASE_URL) {\n env.ANTHROPIC_BASE_URL = baseUrl;\n }\n if (kind === \"bearer\") set(\"ANTHROPIC_AUTH_TOKEN\", credential);\n else if (kind === \"api_key_header\") set(\"ANTHROPIC_API_KEY\", credential);\n if (!credential) {\n // `oauth` / `none` / an unresolved ref: the ambient carriers are the\n // only auth this session has, and the scrub just removed them.\n if (ambient.authToken) env.ANTHROPIC_AUTH_TOKEN = ambient.authToken;\n else set(\"ANTHROPIC_API_KEY\", ambient.apiKey);\n }\n break;\n }\n }\n\n return env;\n}\n","/**\n * Poisoned-transcript repair for the Claude Agent SDK driver.\n *\n * The Anthropic Messages API can permanently reject a conversation once its\n * persisted history contains a malformed content block. Because the bad block\n * lives in the SDK's on-disk JSONL transcript, every replayed turn fails with\n * the same `400 invalid_request_error` and the session is bricked with no\n * in-band recovery.\n *\n * This module repairs the transcript in place, covering three known poison\n * classes:\n *\n * 1. **Image `media_type` mismatch / omission.** An image block whose declared\n * `media_type` does not match — or is missing for — the actual bytes:\n * `... The image was specified using the image/png media type, but the\n * image appears to be a image/jpeg image`. Produced by the Claude Code\n * `Read` tool's format fallback (anthropics/claude-code#55338, #30124,\n * #33179). Repaired by sniffing the base64 magic bytes and correcting\n * `media_type`.\n * 2. **Oversized image.** An image whose decoded payload exceeds the API's\n * 5 MB per-image limit (anthropics/claude-code#34566). Replaced with a\n * text stub — the byte budget cannot be recovered any other way.\n * 3. **`cache_control` on an empty text block.** A text block carrying a\n * `cache_control` marker with empty/whitespace `text`\n * (anthropics/claude-code#59626). The `cache_control` field is stripped.\n *\n * Genuinely unidentifiable image blocks are also replaced with a text stub.\n *\n * Recovering by content scan (rather than parsing the API's `messages.N`\n * index) is deliberate: the index addresses the assembled API request, not a\n * JSONL line, and reproducing the SDK's message-assembly is fragile. A full\n * scan is index-free and repairs every poisoned block in one pass.\n *\n * **Async and line-streaming since #454.** A Claude Code transcript is routinely\n * multi-megabyte (base64 images inline in `tool_result` blocks), and this runs on\n * a failed turn *and before every resume* — inside the single-threaded `skaile\n * serve` loop. The previous `readFileSync` + `writeFileSync` + `renameSync` held\n * the loop for the whole file and materialised it twice over as one string. Both\n * passes now stream line by line off a `createReadStream`, so the loop is yielded\n * between lines and peak memory is one JSONL line rather than the whole file, and\n * every I/O phase is deadline-bounded.\n *\n * Streaming was chosen over a worker thread: the work is I/O-bound rather than\n * CPU-bound (per-line `JSON.parse` on a yielded loop is not what stalls a turn),\n * and a worker would add a structured-clone of the same multi-MB payload plus a\n * thread lifecycle to a path that must never block a turn.\n *\n * @module\n */\n\nimport { createReadStream, createWriteStream } from \"node:fs\";\nimport { readdir, rename, stat, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { fsDeadlineMs, tryWithFsDeadline, withFsDeadline } from \"@skaile/workspaces/core\";\n\n/**\n * Minimal logger shape accepted by {@link scrubPoisonedTranscript}. Compatible\n * with the bridge `Logger`, but narrowed so the helper stays unit-testable\n * without a real logger instance.\n *\n * @category Transcript repair\n */\nexport interface ScrubLogger {\n info(message: string, data?: Record<string, unknown>): void;\n warn(message: string, data?: Record<string, unknown>): void;\n}\n\n/**\n * Options for {@link scrubPoisonedTranscript}.\n *\n * @category Transcript repair\n */\nexport interface ScrubTranscriptOptions {\n /**\n * Claude Code config directory — the parent of `projects/`. Resolve from\n * `CLAUDE_CONFIG_DIR`, falling back to `~/.claude`.\n */\n configDir: string;\n /** SDK session id whose transcript JSONL should be repaired. */\n sessionId: string;\n /** Optional logger for diagnostics. */\n log?: ScrubLogger;\n /**\n * Where the transcript is read and written. Defaults to this process's own\n * filesystem access ({@link localTranscriptStore}); with the agent uid split\n * the transcript belongs to the agent uid and must be reached through it.\n */\n store?: TranscriptStore;\n}\n\n/**\n * Filesystem access to Claude Code transcripts. The CLI creates them 0600 in\n * 0700 dirs, so when it runs as a different uid than the runner, only that uid\n * can locate, read or rewrite them (see `agent-transcript.ts`).\n *\n * @category Transcript repair\n */\nexport interface TranscriptStore {\n /** Path of `<sessionId>.jsonl` under `<configDir>/projects/`, or `null` when absent. */\n locate(configDir: string, sessionId: string, timeoutMs?: number): Promise<string | null>;\n /** The file's contents as UTF-8 chunks. */\n read(filePath: string): AsyncIterable<string>;\n /** Atomically replace the file with `chunks` via a `.scrub-tmp` sibling. */\n replace(filePath: string, chunks: AsyncIterable<string>): Promise<void>;\n /** Remove the sibling a failed or abandoned {@link replace} left (best effort). */\n discardTemp(filePath: string): Promise<void>;\n}\n\n/**\n * Outcome of a {@link scrubPoisonedTranscript} pass.\n *\n * @category Transcript repair\n */\nexport interface ScrubTranscriptResult {\n /** The located JSONL transcript path, or `null` when not found. */\n filePath: string | null;\n /** Image blocks whose `media_type` was corrected (or filled in) to match the bytes. */\n corrected: number;\n /**\n * Image blocks replaced with a text stub — either unidentifiable bytes or a\n * payload over the API's 5 MB per-image limit.\n */\n stubbed: number;\n /** Empty text blocks from which a rejected `cache_control` marker was stripped. */\n cacheStripped: number;\n /**\n * `true` when the transcript was located, contained at least one poisoned\n * block, and was rewritten. `false` means nothing needed repair (or the\n * file was not found) — the caller should not retry the resume.\n */\n changed: boolean;\n}\n\n/** Text inserted in place of an image block whose format cannot be identified. */\nconst STUB_TEXT = \"[image removed: unprocessable image data]\";\n\n/** Text inserted in place of an image block that exceeds the API size limit. */\nconst STUB_TEXT_OVERSIZED = \"[image removed: image too large]\";\n\n/**\n * The Anthropic Messages API rejects any single image whose decoded payload\n * exceeds 5 MB. An image at or under this is left alone; a larger one is\n * stubbed (its byte budget cannot be recovered).\n */\nconst MAX_IMAGE_BYTES = 5 * 1024 * 1024;\n\n/**\n * Identify an image's media type from the leading bytes of its base64 payload.\n *\n * Recognises the four formats the Anthropic API accepts. Returns `null` when\n * the magic bytes match no known format — the caller treats that as a\n * genuinely corrupt block and stubs it.\n *\n * @param base64 - Base64-encoded image data (only the prefix is inspected).\n * @returns An `image/*` media type, or `null` when unrecognised.\n * @category Transcript repair\n */\nexport function sniffImageMediaType(base64: string): string | null {\n let buf: Buffer;\n try {\n // 32 base64 chars decode to 24 bytes — enough for the WebP check at 12.\n buf = Buffer.from(base64.slice(0, 32), \"base64\");\n } catch {\n return null;\n }\n if (buf.length < 4) {\n return null;\n }\n // JPEG — SOI marker `FF D8`. Third byte varies (and may be corrupt), so a\n // two-byte check is intentional: it still matches malformed JPEGs, which is\n // exactly the poison case the API flags as \"appears to be image/jpeg\".\n if (buf[0] === 0xff && buf[1] === 0xd8) {\n return \"image/jpeg\";\n }\n // PNG — `89 50 4E 47`.\n if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {\n return \"image/png\";\n }\n // GIF — `47 49 46 38` (\"GIF8\").\n if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x38) {\n return \"image/gif\";\n }\n // WebP — `RIFF` at 0..3 and `WEBP` at 8..11.\n if (\n buf.length >= 12 &&\n buf[0] === 0x52 &&\n buf[1] === 0x49 &&\n buf[2] === 0x46 &&\n buf[3] === 0x46 &&\n buf[8] === 0x57 &&\n buf[9] === 0x45 &&\n buf[10] === 0x42 &&\n buf[11] === 0x50\n ) {\n return \"image/webp\";\n }\n return null;\n}\n\n/** A base64 image source on a content block (shape is SDK-defined). */\ninterface Base64ImageSource {\n type?: string;\n media_type?: string;\n data?: string;\n}\n\n/** Loosely-typed content block from a transcript message. */\ninterface ContentBlockLike {\n type?: string;\n source?: Base64ImageSource;\n content?: unknown;\n [k: string]: unknown;\n}\n\n/** Mutable counters threaded through one scrub pass. */\ninterface ScrubCounters {\n corrected: number;\n stubbed: number;\n cacheStripped: number;\n}\n\n/** Total repairs recorded in a counter set — used to detect per-line changes. */\nfunction totalChanges(c: ScrubCounters): number {\n return c.corrected + c.stubbed + c.cacheStripped;\n}\n\n/**\n * Repair a single content block. Returns the (possibly replaced) block.\n *\n * - A `tool_result` block recurses into its `content` array.\n * - A text block carrying a `cache_control` marker whose `text` is empty or\n * whitespace has the marker stripped (the API rejects `cache_control` on\n * empty text blocks).\n * - An image block whose decoded payload exceeds the API's 5 MB limit is\n * replaced with a text stub.\n * - An image block whose bytes match no known format is replaced with a text\n * stub (an image cannot simply be dropped — a `tool_result` must keep at\n * least one content entry, and a bare `image` block is replaced 1:1).\n * - An image block whose declared `media_type` mismatches (or is missing for)\n * the sniffed type is corrected in place.\n * - Every other block is returned untouched.\n */\nfunction scrubBlock(block: ContentBlockLike, counters: ScrubCounters): ContentBlockLike {\n if (block.type === \"tool_result\" && Array.isArray(block.content)) {\n return {\n ...block,\n content: (block.content as ContentBlockLike[]).map((inner) => scrubBlock(inner, counters)),\n };\n }\n\n // A `cache_control` marker on a text block whose text is empty or whitespace\n // is rejected by the API (\"cache_control cannot be set for empty text\n // blocks\"). Strip the marker — the empty block itself is harmless.\n if (block.type === \"text\" && block.cache_control != null) {\n const text = typeof block.text === \"string\" ? block.text : \"\";\n if (text.trim() === \"\") {\n counters.cacheStripped++;\n const clone = { ...block };\n delete clone.cache_control;\n return clone;\n }\n }\n\n if (\n block.type === \"image\" &&\n block.source?.type === \"base64\" &&\n typeof block.source.data === \"string\"\n ) {\n const data = block.source.data;\n // The API rejects any image whose decoded payload exceeds 5 MB. The byte\n // budget cannot be salvaged, so the whole block becomes a text stub.\n // base64 encodes 3 bytes per 4 chars, so decoded length ≈ len * 3 / 4.\n if (Math.floor((data.length * 3) / 4) > MAX_IMAGE_BYTES) {\n counters.stubbed++;\n return { type: \"text\", text: STUB_TEXT_OVERSIZED };\n }\n const sniffed = sniffImageMediaType(data);\n if (sniffed === null) {\n counters.stubbed++;\n return { type: \"text\", text: STUB_TEXT };\n }\n if (sniffed !== block.source.media_type) {\n counters.corrected++;\n return { ...block, source: { ...block.source, media_type: sniffed } };\n }\n }\n\n return block;\n}\n\n/**\n * Locate the JSONL transcript for `sessionId` under `<configDir>/projects/`.\n *\n * Claude Code encodes the project directory into the `projects/` subfolder\n * name; rather than reproduce that encoding, this scans every subfolder for\n * `<sessionId>.jsonl` (the session id is a UUID — globally unique).\n *\n * Async and deadline-bounded since #454. The `existsSync(projectsDir)` probe is\n * gone: `readdir` already fails on a missing directory, so the probe was a\n * second stat on the same path for no information.\n *\n * `timeoutMs` defaults to {@link fsDeadlineMs} so callers that only need the\n * path (the resume probe) can omit it.\n *\n * @category Transcript repair\n */\nexport async function locateTranscriptPath(\n configDir: string,\n sessionId: string,\n timeoutMs: number = fsDeadlineMs(),\n): Promise<string | null> {\n const projectsDir = join(configDir, \"projects\");\n const entries = await tryWithFsDeadline(\n `readdir ${projectsDir}`,\n () => readdir(projectsDir),\n null,\n timeoutMs,\n );\n if (!entries) return null;\n for (const entry of entries) {\n const candidate = join(projectsDir, entry, `${sessionId}.jsonl`);\n const found = await tryWithFsDeadline(\n `stat ${candidate}`,\n async () => (await stat(candidate)).isFile(),\n false,\n timeoutMs,\n );\n if (found) return candidate;\n }\n return null;\n}\n\n/**\n * Yield a file's contents line by line, reproducing `readFileSync(...).split(\"\\n\")`\n * exactly — including the final (possibly empty) segment, so a trailing newline\n * survives a rewrite byte for byte.\n *\n * Splits on `\\n` alone, so a line out of a CRLF transcript arrives with its\n * `\\r` still attached. That carrier byte is part of the line here, and\n * {@link repairLine} re-attaches it to a line it rewrites — the file keeps the\n * terminator style it arrived with. Deliberately not `node:readline`:\n * `crlfDelay` strips the `\\r` outright (rewriting a CRLF transcript to LF) and\n * the interface swallows the trailing-newline distinction, either of which\n * would make the \"clean transcript is left byte-for-byte untouched\" guarantee\n * false. Only one line is held in memory at a time, so a multi-MB transcript\n * never materialises as a single string.\n */\nasync function* streamLines(chunks: AsyncIterable<string>): AsyncGenerator<string> {\n let pending = \"\";\n for await (const chunk of chunks) {\n pending += chunk;\n let idx = pending.indexOf(\"\\n\");\n while (idx !== -1) {\n yield pending.slice(0, idx);\n pending = pending.slice(idx + 1);\n idx = pending.indexOf(\"\\n\");\n }\n }\n yield pending;\n}\n\n/**\n * The runner's own filesystem access: the default {@link TranscriptStore}.\n *\n * @category Transcript repair\n */\nexport const localTranscriptStore: TranscriptStore = {\n locate: (configDir, sessionId, timeoutMs) =>\n locateTranscriptPath(configDir, sessionId, timeoutMs),\n async *read(filePath) {\n const stream = createReadStream(filePath, { encoding: \"utf8\" });\n try {\n for await (const chunk of stream) yield chunk as string;\n } finally {\n stream.destroy();\n }\n },\n async replace(filePath, chunks) {\n const tmpPath = `${filePath}.scrub-tmp`;\n await pipeline(chunks, createWriteStream(tmpPath, { encoding: \"utf8\" }));\n await rename(tmpPath, filePath);\n },\n async discardTemp(filePath) {\n const tmpPath = `${filePath}.scrub-tmp`;\n await tryWithFsDeadline(`unlink ${tmpPath}`, () => unlink(tmpPath), undefined, fsDeadlineMs());\n },\n};\n\n/**\n * Repair one JSONL line, returning the (possibly rewritten) line and mutating\n * `counters`. A non-JSON, blank, or content-less line is returned untouched.\n *\n * The reader splits on `\\n` only, so a line from a CRLF transcript still carries\n * its trailing `\\r`. An untouched line is returned verbatim and keeps it; a\n * rewritten one is rebuilt by `JSON.stringify`, which would drop it — leaving a\n * repaired CRLF transcript with mixed line endings. So the terminator is split\n * off before the parse and re-appended to the rewritten line.\n */\nfunction repairLine(line: string, counters: ScrubCounters): string {\n if (line.trim() === \"\") return line;\n const eol = line.endsWith(\"\\r\") ? \"\\r\" : \"\";\n const body = eol === \"\" ? line : line.slice(0, -1);\n let entry: { message?: { content?: unknown } };\n try {\n entry = JSON.parse(body);\n } catch {\n return line; // Not JSON — leave untouched.\n }\n const content = entry.message?.content;\n if (!Array.isArray(content)) return line;\n const before = totalChanges(counters);\n const scrubbed = (content as ContentBlockLike[]).map((block) => scrubBlock(block, counters));\n if (totalChanges(counters) === before) return line; // Nothing changed on this line.\n return `${JSON.stringify({ ...entry, message: { ...entry.message, content: scrubbed } })}${eol}`;\n}\n\n/**\n * Scan a Claude Code SDK transcript for image content blocks whose declared\n * `media_type` does not match the actual bytes, repair them, and atomically\n * rewrite the file.\n *\n * Safe to call after a turn has failed: the SDK query is dead at that point,\n * and the next resume re-reads the transcript from disk.\n *\n * **Async since #454.** Two streaming passes rather than one whole-file read:\n * pass 1 counts repairs without writing anything, and pass 2 runs only when\n * pass 1 found something. The common case — the preventive scrub of a clean\n * transcript before every resume — is therefore one streaming read and zero\n * writes, and a clean transcript is still left byte-for-byte untouched.\n *\n * @param opts - Config directory, session id, and an optional logger.\n * @returns A {@link ScrubTranscriptResult}. When `changed` is `true` the\n * caller may retry the resume with the same session id.\n * @category Transcript repair\n */\nexport async function scrubPoisonedTranscript(\n opts: ScrubTranscriptOptions,\n): Promise<ScrubTranscriptResult> {\n const { configDir, sessionId, log } = opts;\n const store = opts.store ?? localTranscriptStore;\n const timeoutMs = fsDeadlineMs();\n const result: ScrubTranscriptResult = {\n filePath: null,\n corrected: 0,\n stubbed: 0,\n cacheStripped: 0,\n changed: false,\n };\n\n let filePath: string | null;\n try {\n filePath = await store.locate(configDir, sessionId, timeoutMs);\n } catch (err) {\n log?.warn(\"scrub-transcript: failed to locate transcript\", {\n configDir,\n sessionId,\n error: err instanceof Error ? err.message : String(err),\n });\n return result;\n }\n if (!filePath) {\n log?.warn(\"scrub-transcript: no transcript found\", { configDir, sessionId });\n return result;\n }\n result.filePath = filePath;\n\n // ── Pass 1: detect. Streams the transcript, counting repairs, writing nothing.\n const counters: ScrubCounters = { corrected: 0, stubbed: 0, cacheStripped: 0 };\n try {\n await withFsDeadline(\n `scan transcript ${filePath}`,\n async () => {\n for await (const line of streamLines(store.read(filePath))) repairLine(line, counters);\n },\n timeoutMs,\n );\n } catch (err) {\n log?.warn(\"scrub-transcript: failed to read transcript\", {\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n return result;\n }\n\n result.corrected = counters.corrected;\n result.stubbed = counters.stubbed;\n result.cacheStripped = counters.cacheStripped;\n\n if (totalChanges(counters) === 0) {\n return result;\n }\n\n // ── Pass 2: rewrite. Atomic: stream a sibling temp file, then rename over the\n // original. Line separators are re-emitted exactly as pass 1 split them, so a\n // trailing newline and any blank lines survive verbatim.\n const source = filePath;\n try {\n await withFsDeadline(\n `rewrite transcript ${source}`,\n async () => {\n const discard: ScrubCounters = { corrected: 0, stubbed: 0, cacheStripped: 0 };\n await store.replace(\n source,\n (async function* () {\n let first = true;\n for await (const line of streamLines(store.read(source))) {\n yield first ? repairLine(line, discard) : `\\n${repairLine(line, discard)}`;\n first = false;\n }\n })(),\n );\n },\n timeoutMs,\n );\n } catch (err) {\n log?.warn(\"scrub-transcript: failed to rewrite transcript\", {\n filePath,\n error: err instanceof Error ? err.message : String(err),\n });\n // Never leave a half-written `.scrub-tmp` in the SDK's projects dir, also\n // when the deadline abandoned the replace mid-write.\n await store.discardTemp(source).catch(() => {});\n return result;\n }\n\n result.changed = true;\n log?.info(\"scrub-transcript: repaired poisoned transcript\", {\n filePath,\n corrected: result.corrected,\n stubbed: result.stubbed,\n cacheStripped: result.cacheStripped,\n });\n return result;\n}\n","import { type ChildProcess, spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { fsDeadlineMs, withFsDeadline } from \"@skaile/workspaces/core\";\nimport { resolveAgentExec } from \"./agent-exec.js\";\nimport { localTranscriptStore, type TranscriptStore } from \"./drivers/scrub-transcript.js\";\n\n/**\n * Transcripts reached through the agent launch shim.\n *\n * With the uid split on, the Claude CLI runs as the agent uid and creates its\n * transcripts 0600 in 0700 dirs, so the runner can no longer list, read or\n * rewrite them itself. This store runs plain tools (`find`, `cat`, `sh`, `mv`)\n * as the agent uid through the shim instead. The child gets only `PATH` and\n * `LC_ALL`, never the runner's environment, and runs in `/` because the\n * runner's own cwd may not be enterable by the agent uid.\n */\nexport function agentTranscriptStore(shim: string): TranscriptStore {\n // A replace the scrub's deadline abandoned keeps running; discardTemp stops\n // it before removing its temp file.\n const inFlight = new Map<string, ChildProcess>();\n // Paths whose rewrite discardTemp gave up on: their rename must never run.\n const cancelled = new Set<string>();\n const track = (filePath: string, child: ChildProcess) => {\n inFlight.set(filePath, child);\n child.once(\"close\", () => {\n if (inFlight.get(filePath) === child) inFlight.delete(filePath);\n });\n };\n return {\n async locate(configDir, sessionId, timeoutMs = 10_000) {\n // A session id is a UUID; anything glob- or path-shaped is not ours.\n if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) return null;\n const projectsDir = join(configDir, \"projects\");\n // Markers on stdout, not exit codes, carry the answer: a shim or launcher\n // failure has exit codes of its own and must never read as \"not found\".\n const { stdout } = await collect(\n runAsAgent(shim, [\n \"sh\",\n \"-c\",\n '[ -d \"$1\" ] || { echo \"@@no-projects\"; exit 0; }; ' +\n 'find \"$1\" -mindepth 2 -maxdepth 2 -type f -name \"$2\" -print -quit; echo \"@@find:$?\"',\n \"sh\",\n projectsDir,\n `${sessionId}.jsonl`,\n ]),\n timeoutMs,\n );\n const lines = stdout.split(\"\\n\").map((l) => l.trim());\n // A missing projects/ is the same \"not found\" as a failed readdir locally.\n if (lines.includes(\"@@no-projects\")) return null;\n const status = lines.find((l) => l.startsWith(\"@@find:\"));\n if (status === undefined)\n throw new Error(\"locating a transcript as the agent uid failed (no answer from the shim)\");\n // A path find printed counts even if it later hit an unreadable sibling.\n const found = lines.find((l) => l.startsWith(\"/\"));\n if (found) return found;\n if (status !== \"@@find:0\")\n throw new Error(`locating a transcript as the agent uid failed (${status.slice(2)})`);\n return null;\n },\n\n async *read(filePath) {\n const child = runAsAgent(shim, [\"cat\", \"--\", filePath]);\n const exited = exitOf(child);\n exited.catch(() => {}); // awaited below; this only stops an unhandled rejection\n const stderr = drain(child);\n child.stdout?.setEncoding(\"utf8\");\n for await (const chunk of child.stdout as AsyncIterable<string>) yield chunk;\n const code = await exited;\n if (code !== 0)\n throw new Error(`reading ${filePath} as the agent uid failed: ${await stderr}`);\n },\n\n async replace(filePath, chunks) {\n const child = runAsAgent(shim, [\n \"sh\",\n \"-c\",\n // 0600 like the CLI's own files. Only writes the temp file: the rename\n // is a separate step below, run only after the input was fully sent,\n // so a killed or starved writer can never commit a partial transcript.\n 'umask 077; cat > \"$1.scrub-tmp\"',\n \"sh\",\n filePath,\n ]);\n cancelled.delete(filePath);\n track(filePath, child);\n const exited = exitOf(child);\n exited.catch(() => {}); // awaited below; this only stops an unhandled rejection\n const stderr = drain(child);\n const stdin = child.stdin;\n if (!stdin) throw new Error(\"agent shim has no stdin\");\n // A child that dies early surfaces through its exit code, not an EPIPE crash.\n stdin.on(\"error\", () => {});\n const gone = exited.then(\n () => undefined,\n () => undefined,\n );\n // Stop pulling input as soon as the child is gone (killed by discardTemp,\n // or failed), instead of waiting on a producer that may never resume.\n const input = chunks[Symbol.asyncIterator]();\n let drained = false;\n try {\n for (;;) {\n const next = await Promise.race([\n input.next(),\n gone.then(() => ({ dead: true as const })),\n ]);\n if (\"dead\" in next || stdin.destroyed) break;\n if (next.done) {\n drained = true;\n break;\n }\n if (!stdin.write(next.value)) {\n await Promise.race([new Promise((resolve) => stdin.once(\"drain\", resolve)), gone]);\n }\n }\n } finally {\n stdin.end();\n // Close the producer too, so the reader feeding it (a `cat` through\n // the shim) is torn down rather than left blocked on a full pipe.\n if (!drained) void input.return?.()?.catch(() => {});\n }\n const code = await exited;\n if (code !== 0 || !drained)\n throw new Error(`rewriting ${filePath} as the agent uid failed: ${await stderr}`);\n if (cancelled.has(filePath)) throw new Error(`rewrite of ${filePath} was abandoned`);\n const mover = runAsAgent(shim, [\"mv\", \"-f\", \"--\", `${filePath}.scrub-tmp`, filePath]);\n track(filePath, mover);\n const moved = await collect(mover, 10_000);\n if (moved.code !== 0) throw new Error(`renaming the rewritten ${filePath} failed`);\n },\n\n async discardTemp(filePath) {\n // Before anything else, so a writer that is just finishing never renames.\n cancelled.add(filePath);\n const pending = inFlight.get(filePath);\n if (pending && pending.exitCode === null && pending.signalCode === null) {\n // The writer cannot commit (the rename is a separate step), so this only\n // stops it; bounded, since a writer stuck on a hung mount may never die.\n const closed = new Promise((resolve) => pending.once(\"close\", resolve));\n pending.kill(\"SIGKILL\");\n await withFsDeadline(`stop the rewrite of ${filePath}`, () => closed, fsDeadlineMs());\n }\n await collect(runAsAgent(shim, [\"rm\", \"-f\", \"--\", `${filePath}.scrub-tmp`]), 10_000);\n },\n };\n}\n\n/**\n * The transcript store for this runner: through the agent shim when\n * `SKAILE_AGENT_EXEC` is set, the runner's own filesystem otherwise.\n */\nexport function transcriptStoreForRuntime(\n env: Record<string, string | undefined> = process.env,\n): TranscriptStore {\n const shim = resolveAgentExec(env);\n return shim ? agentTranscriptStore(shim) : localTranscriptStore;\n}\n\nfunction runAsAgent(shim: string, argv: string[]): ChildProcess {\n return spawn(shim, [\"--\", ...argv], {\n cwd: \"/\",\n env: { PATH: process.env.PATH ?? \"/usr/bin:/bin\", LC_ALL: \"C\" },\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n}\n\nfunction exitOf(child: ChildProcess): Promise<number> {\n return new Promise((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"close\", (code) => resolve(code ?? 1));\n });\n}\n\n/** Keep the last 1 KiB of stderr for the error message, and keep the pipe drained. */\nfunction drain(child: ChildProcess): Promise<string> {\n return new Promise((resolve) => {\n let tail = \"\";\n child.stderr?.setEncoding(\"utf8\");\n child.stderr?.on(\"data\", (c: string) => {\n tail = (tail + c).slice(-1024);\n });\n child.stderr?.once(\"close\", () => resolve(tail.trim()));\n });\n}\n\nasync function collect(\n child: ChildProcess,\n timeoutMs: number,\n): Promise<{ code: number; stdout: string }> {\n let stdout = \"\";\n child.stdout?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (c: string) => {\n stdout += c;\n });\n void drain(child);\n child.stdin?.end();\n let timer: NodeJS.Timeout | undefined;\n try {\n const code = await Promise.race([\n exitOf(child),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n child.kill(\"SIGKILL\");\n reject(new Error(`agent shim did not answer within ${timeoutMs} ms`));\n }, timeoutMs);\n }),\n ]);\n return { code, stdout };\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * Pre-first-turn resume probe for the Claude Agent SDK driver.\n *\n * A tier-1 native resume only hands its session id to the SDK inside the first\n * real turn, so the `jsonl_lost` outcome (\"No conversation found\") is invisible\n * until a turn is consumed. The runner shares the container filesystem with the\n * SDK, so a cheap `stat` of the on-disk JSONL transcript is an authoritative,\n * side-effect-free signal of whether the resume will find its history — no\n * probe query, no spurious model turn.\n *\n * @module\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { withFsDeadline } from \"@skaile/workspaces/core\";\nimport { locateTranscriptPath } from \"./scrub-transcript.js\";\n\n/**\n * Overall wall-clock budget for the probe. Deliberately a small fraction of the\n * platform's `session_init_ack` timeout (`DEFAULT_INIT_ACK_TIMEOUT_MS`, 15s):\n * the probe runs synchronously on the pre-ack path, so a wedged\n * `CLAUDE_CONFIG_DIR` mount must never push the ack past its deadline.\n */\nconst RESUME_PROBE_TIMEOUT_MS = 2_000;\n\n/**\n * Outcome of {@link probeResumeTranscript}: whether the SDK transcript for a\n * requested tier-1 resume exists on disk.\n *\n * @category Resume probe\n */\nexport type ResumeProbeOutcome = \"transcript_found\" | \"jsonl_lost\";\n\n/**\n * Resolve the Claude Code config directory — the parent of `projects/` — from\n * an explicit env map, the process environment, or the `~/.claude` default.\n *\n * The runner's probe and the claude-sdk driver share this one resolver, so they\n * agree as long as they see the same `CLAUDE_CONFIG_DIR`. Today they do:\n * `CLAUDE_CONFIG_DIR` is container-scoped and neither side threads a per-session\n * override through `config.env`, so both fall through to `process.env`. If a\n * future change ever set the driver's `config.env.CLAUDE_CONFIG_DIR` to a\n * divergent path, the reactive `resume_failed { jsonl_lost }` turn remains the\n * safety net.\n *\n * @category Resume probe\n */\nexport function resolveClaudeConfigDir(env?: Record<string, string | undefined>): string {\n return env?.CLAUDE_CONFIG_DIR || process.env.CLAUDE_CONFIG_DIR || join(homedir(), \".claude\");\n}\n\n/**\n * Probe whether the SDK JSONL transcript for `sessionId` exists on disk.\n *\n * Non-null locator result → `transcript_found`; null → `jsonl_lost`;\n * `undefined` → outcome unknown (the probe exceeded its budget or errored). The\n * caller leaves the ack field ABSENT on `undefined` so the reactive\n * `resume_failed { jsonl_lost }` turn stays the safety net — a timed-out probe\n * must never masquerade as a genuine `jsonl_lost` and force a needless downgrade.\n *\n * `opts.timeoutMs` overrides {@link RESUME_PROBE_TIMEOUT_MS}; `opts.locate`\n * overrides the transcript locator (used by tests / alternate stores).\n *\n * @category Resume probe\n */\nexport async function probeResumeTranscript(\n sessionId: string,\n opts?: {\n env?: Record<string, string | undefined>;\n timeoutMs?: number;\n locate?: (configDir: string, sessionId: string) => Promise<string | null>;\n },\n): Promise<ResumeProbeOutcome | undefined> {\n const configDir = resolveClaudeConfigDir(opts?.env);\n const timeoutMs = opts?.timeoutMs ?? RESUME_PROBE_TIMEOUT_MS;\n const locate = opts?.locate ?? locateTranscriptPath;\n try {\n // Bound the whole probe on wall-clock and THROW on expiry: the inner per-op\n // deadline swallows a timeout into a null path (a false `jsonl_lost`), so a\n // wedged mount must yield \"unknown\" here rather than a spurious downgrade.\n const filePath = await withFsDeadline(\n `resume-probe ${sessionId}`,\n () => locate(configDir, sessionId),\n timeoutMs,\n );\n return filePath ? \"transcript_found\" : \"jsonl_lost\";\n } catch {\n return undefined;\n }\n}\n"]}