xonecode 0.2.0 → 0.5.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 (406) hide show
  1. package/README.md +49 -7
  2. package/THIRD_PARTY_NOTICES.md +128 -0
  3. package/apps/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  4. package/apps/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  5. package/apps/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  6. package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  7. package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  8. package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  9. package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  10. package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  11. package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  12. package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  13. package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  14. package/apps/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  15. package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  16. package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  17. package/apps/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  18. package/apps/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  19. package/apps/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  20. package/apps/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  21. package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  22. package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  23. package/apps/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  24. package/apps/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  25. package/apps/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  26. package/apps/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  27. package/apps/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  28. package/apps/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  29. package/apps/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  30. package/apps/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  31. package/apps/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  32. package/apps/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  33. package/apps/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  34. package/apps/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  35. package/apps/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  36. package/apps/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  37. package/apps/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  38. package/apps/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  39. package/apps/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  40. package/apps/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  41. package/apps/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  42. package/apps/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  43. package/apps/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  44. package/apps/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  45. package/apps/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  46. package/apps/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  47. package/apps/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  48. package/apps/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  49. package/apps/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  50. package/apps/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  51. package/apps/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  52. package/apps/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  53. package/apps/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  54. package/apps/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  55. package/apps/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  56. package/apps/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  57. package/apps/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  58. package/apps/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  59. package/apps/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  60. package/apps/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  61. package/apps/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  62. package/apps/web/dist/assets/c-BIGW1oBm.js +1 -0
  63. package/apps/web/dist/assets/cpp-DddhVWhK.js +1 -0
  64. package/apps/web/dist/assets/csharp-DSvCPggb.js +1 -0
  65. package/apps/web/dist/assets/css-CLj8gQPS.js +1 -0
  66. package/apps/web/dist/assets/go-C27-OAKa.js +1 -0
  67. package/apps/web/dist/assets/html-CfGypltT.js +1 -0
  68. package/apps/web/dist/assets/index-BB3SamhR.css +1 -0
  69. package/apps/web/dist/assets/index-CFYAoHdw.js +477 -0
  70. package/apps/web/dist/assets/ini-BEwlwnbL.js +1 -0
  71. package/apps/web/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  72. package/apps/web/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  73. package/apps/web/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  74. package/apps/web/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  75. package/apps/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  76. package/apps/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  77. package/apps/web/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  78. package/apps/web/dist/assets/java-CylS5w8V.js +1 -0
  79. package/apps/web/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  80. package/apps/web/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  81. package/apps/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  82. package/apps/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  83. package/apps/web/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  84. package/apps/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
  85. package/apps/web/dist/assets/less-B1dDrJ26.js +1 -0
  86. package/apps/web/dist/assets/lua-BaeVxFsk.js +1 -0
  87. package/apps/web/dist/assets/markdown-Cvjx9yec.js +1 -0
  88. package/apps/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  89. package/apps/web/dist/assets/php-D_2--9WK.js +1 -0
  90. package/apps/web/dist/assets/python-B6aJPvgy.js +1 -0
  91. package/apps/web/dist/assets/ruby-DSHSDbk1.js +1 -0
  92. package/apps/web/dist/assets/rust-B1yitclQ.js +1 -0
  93. package/apps/web/dist/assets/scss-D5BDwBP9.js +1 -0
  94. package/apps/web/dist/assets/sql-CRqJ_cUM.js +1 -0
  95. package/apps/web/dist/assets/swift-C2oV4EkX.js +1 -0
  96. package/apps/web/dist/assets/toml-vGWfd6FD.js +1 -0
  97. package/apps/web/dist/assets/xml-sdJ4AIDG.js +1 -0
  98. package/apps/web/dist/assets/yaml-Buea-lGh.js +1 -0
  99. package/apps/web/dist/iconos/xonecode.png +0 -0
  100. package/apps/web/dist/index.html +20 -0
  101. package/dist/agent/agentesEnDisco.js +539 -0
  102. package/dist/agent/agentesEnDisco.js.map +1 -0
  103. package/dist/agent/arbolDeProyecto.js +245 -0
  104. package/dist/agent/arbolDeProyecto.js.map +1 -0
  105. package/dist/agent/artefactosEnDisco.js +139 -0
  106. package/dist/agent/artefactosEnDisco.js.map +1 -0
  107. package/dist/agent/aumentador.js +175 -0
  108. package/dist/agent/aumentador.js.map +1 -0
  109. package/dist/agent/authEnDisco.js +76 -1
  110. package/dist/agent/authEnDisco.js.map +1 -1
  111. package/dist/agent/busquedaRegex.js +92 -0
  112. package/dist/agent/busquedaRegex.js.map +1 -0
  113. package/dist/agent/catalogoModelos.js +345 -0
  114. package/dist/agent/catalogoModelos.js.map +1 -0
  115. package/dist/agent/checkpointer.js +127 -0
  116. package/dist/agent/checkpointer.js.map +1 -0
  117. package/dist/agent/cloudstudioClient.js +194 -0
  118. package/dist/agent/cloudstudioClient.js.map +1 -0
  119. package/dist/agent/cloudstudioMcp.js +722 -0
  120. package/dist/agent/cloudstudioMcp.js.map +1 -0
  121. package/dist/agent/configEnDisco.js +309 -11
  122. package/dist/agent/configEnDisco.js.map +1 -1
  123. package/dist/agent/consumoExterno.js +99 -0
  124. package/dist/agent/consumoExterno.js.map +1 -0
  125. package/dist/agent/descarga.js +149 -0
  126. package/dist/agent/descarga.js.map +1 -0
  127. package/dist/agent/diagnosticoDeTools.js +51 -0
  128. package/dist/agent/diagnosticoDeTools.js.map +1 -0
  129. package/dist/agent/dispositivosEnMaquina.js +596 -0
  130. package/dist/agent/dispositivosEnMaquina.js.map +1 -0
  131. package/dist/agent/escrituraDeCodex.js +134 -0
  132. package/dist/agent/escrituraDeCodex.js.map +1 -0
  133. package/dist/agent/escrituraDeOpencode.js +80 -0
  134. package/dist/agent/escrituraDeOpencode.js.map +1 -0
  135. package/dist/agent/escrituraExterna.js +742 -0
  136. package/dist/agent/escrituraExterna.js.map +1 -0
  137. package/dist/agent/gemini.js +81 -0
  138. package/dist/agent/gemini.js.map +1 -0
  139. package/dist/agent/git.js +83 -0
  140. package/dist/agent/git.js.map +1 -0
  141. package/dist/agent/gitSync.js +441 -0
  142. package/dist/agent/gitSync.js.map +1 -0
  143. package/dist/agent/hotswap.js +448 -0
  144. package/dist/agent/hotswap.js.map +1 -0
  145. package/dist/agent/instalacionEnMaquina.js +252 -0
  146. package/dist/agent/instalacionEnMaquina.js.map +1 -0
  147. package/dist/agent/instantanea.js +7 -10
  148. package/dist/agent/instantanea.js.map +1 -1
  149. package/dist/agent/interrupts.js +11 -2
  150. package/dist/agent/interrupts.js.map +1 -1
  151. package/dist/agent/juezDeTarea.js +345 -0
  152. package/dist/agent/juezDeTarea.js.map +1 -0
  153. package/dist/agent/lanzamientoEnMaquina.js +377 -0
  154. package/dist/agent/lanzamientoEnMaquina.js.map +1 -0
  155. package/dist/agent/manifiesto.js +59 -0
  156. package/dist/agent/manifiesto.js.map +1 -0
  157. package/dist/agent/memoriaDeProyecto.js +60 -0
  158. package/dist/agent/memoriaDeProyecto.js.map +1 -0
  159. package/dist/agent/modelos.js +105 -17
  160. package/dist/agent/modelos.js.map +1 -1
  161. package/dist/agent/modelosDeMotor.js +204 -0
  162. package/dist/agent/modelosDeMotor.js.map +1 -0
  163. package/dist/agent/paqueteDelProyecto.js +181 -0
  164. package/dist/agent/paqueteDelProyecto.js.map +1 -0
  165. package/dist/agent/perfiles.js +92 -31
  166. package/dist/agent/perfiles.js.map +1 -1
  167. package/dist/agent/persona.js +40 -0
  168. package/dist/agent/persona.js.map +1 -0
  169. package/dist/agent/procesosEnMaquina.js +204 -0
  170. package/dist/agent/procesosEnMaquina.js.map +1 -0
  171. package/dist/agent/proyecto.js +311 -13
  172. package/dist/agent/proyecto.js.map +1 -1
  173. package/dist/agent/puente.js +63 -6
  174. package/dist/agent/puente.js.map +1 -1
  175. package/dist/agent/resumenDeContexto.js +30 -0
  176. package/dist/agent/resumenDeContexto.js.map +1 -0
  177. package/dist/agent/resumenDeTool.js +64 -18
  178. package/dist/agent/resumenDeTool.js.map +1 -1
  179. package/dist/agent/sesionGit.js +481 -0
  180. package/dist/agent/sesionGit.js.map +1 -0
  181. package/dist/agent/settingsEnDisco.js +189 -0
  182. package/dist/agent/settingsEnDisco.js.map +1 -0
  183. package/dist/agent/subagenteCodex.js +368 -0
  184. package/dist/agent/subagenteCodex.js.map +1 -0
  185. package/dist/agent/subagenteExterno.js +423 -0
  186. package/dist/agent/subagenteExterno.js.map +1 -0
  187. package/dist/agent/subagenteOpencode.js +389 -0
  188. package/dist/agent/subagenteOpencode.js.map +1 -0
  189. package/dist/agent/subida.js +202 -0
  190. package/dist/agent/subida.js.map +1 -0
  191. package/dist/agent/tareasEnDisco.js +466 -0
  192. package/dist/agent/tareasEnDisco.js.map +1 -0
  193. package/dist/agent/turnoReal.js +663 -50
  194. package/dist/agent/turnoReal.js.map +1 -1
  195. package/dist/agent/versionEnDisco.js +48 -0
  196. package/dist/agent/versionEnDisco.js.map +1 -0
  197. package/dist/agent/xoneAgent.js +290 -49
  198. package/dist/agent/xoneAgent.js.map +1 -1
  199. package/dist/agent/zip.js +52 -0
  200. package/dist/agent/zip.js.map +1 -0
  201. package/dist/cli/acuseDeModelo.js +30 -0
  202. package/dist/cli/acuseDeModelo.js.map +1 -0
  203. package/dist/cli/aprobar.js +55 -2
  204. package/dist/cli/aprobar.js.map +1 -1
  205. package/dist/cli/config.js +9 -13
  206. package/dist/cli/config.js.map +1 -1
  207. package/dist/cli/consola.js +750 -19
  208. package/dist/cli/consola.js.map +1 -1
  209. package/dist/cli/main.js +740 -81
  210. package/dist/cli/main.js.map +1 -1
  211. package/dist/cli/markdown.js +188 -2
  212. package/dist/cli/markdown.js.map +1 -1
  213. package/dist/cli/run.js +32 -4
  214. package/dist/cli/run.js.map +1 -1
  215. package/dist/cli/stdio.js +46 -19
  216. package/dist/cli/stdio.js.map +1 -1
  217. package/dist/cli/tema.js +96 -8
  218. package/dist/cli/tema.js.map +1 -1
  219. package/dist/cli/tokens.js +29 -0
  220. package/dist/cli/tokens.js.map +1 -0
  221. package/dist/cli/tui/app.js +171 -0
  222. package/dist/cli/tui/app.js.map +1 -0
  223. package/dist/cli/tui/aprobarTui.js +80 -0
  224. package/dist/cli/tui/aprobarTui.js.map +1 -0
  225. package/dist/cli/tui/barra.js +37 -0
  226. package/dist/cli/tui/barra.js.map +1 -0
  227. package/dist/cli/tui/correrTui.js +478 -0
  228. package/dist/cli/tui/correrTui.js.map +1 -0
  229. package/dist/cli/tui/entrada.js +166 -0
  230. package/dist/cli/tui/entrada.js.map +1 -0
  231. package/dist/cli/tui/filas.js +24 -0
  232. package/dist/cli/tui/filas.js.map +1 -0
  233. package/dist/cli/tui/logo.js +49 -0
  234. package/dist/cli/tui/logo.js.map +1 -0
  235. package/dist/cli/tui/pielTui.js +14 -0
  236. package/dist/cli/tui/pielTui.js.map +1 -0
  237. package/dist/cli/tui/raton.js +126 -0
  238. package/dist/cli/tui/raton.js.map +1 -0
  239. package/dist/cli/tui/sidebar.js +86 -0
  240. package/dist/cli/tui/sidebar.js.map +1 -0
  241. package/dist/cli/tui/store.js +143 -0
  242. package/dist/cli/tui/store.js.map +1 -0
  243. package/dist/cli/tui/tarjeta.js +19 -0
  244. package/dist/cli/tui/tarjeta.js.map +1 -0
  245. package/dist/cli/tui/temaInk.js +114 -0
  246. package/dist/cli/tui/temaInk.js.map +1 -0
  247. package/dist/cli/tui/transcript.js +254 -0
  248. package/dist/cli/tui/transcript.js.map +1 -0
  249. package/dist/cli/wizardInicial.js +240 -0
  250. package/dist/cli/wizardInicial.js.map +1 -0
  251. package/dist/core/actos.js +113 -0
  252. package/dist/core/actos.js.map +1 -0
  253. package/dist/core/adjuntos.js +97 -0
  254. package/dist/core/adjuntos.js.map +1 -0
  255. package/dist/core/agentes.js +227 -0
  256. package/dist/core/agentes.js.map +1 -0
  257. package/dist/core/artefactos.js +149 -0
  258. package/dist/core/artefactos.js.map +1 -0
  259. package/dist/core/cloudstudio.js +7 -0
  260. package/dist/core/cloudstudio.js.map +1 -0
  261. package/dist/core/config.js +212 -5
  262. package/dist/core/config.js.map +1 -1
  263. package/dist/core/contextos.js +53 -1
  264. package/dist/core/contextos.js.map +1 -1
  265. package/dist/core/descargas.js +106 -0
  266. package/dist/core/descargas.js.map +1 -0
  267. package/dist/core/descriptoresDeApp.js +279 -0
  268. package/dist/core/descriptoresDeApp.js.map +1 -0
  269. package/dist/core/dispositivos.js +466 -0
  270. package/dist/core/dispositivos.js.map +1 -0
  271. package/dist/core/entrega.js +141 -0
  272. package/dist/core/entrega.js.map +1 -0
  273. package/dist/core/entrelazar.js +90 -0
  274. package/dist/core/entrelazar.js.map +1 -0
  275. package/dist/core/esqueleto.js +10 -0
  276. package/dist/core/esqueleto.js.map +1 -1
  277. package/dist/core/modelos.js +235 -3
  278. package/dist/core/modelos.js.map +1 -1
  279. package/dist/core/notify.js +27 -9
  280. package/dist/core/notify.js.map +1 -1
  281. package/dist/core/planDeSubida.js +127 -0
  282. package/dist/core/planDeSubida.js.map +1 -0
  283. package/dist/core/ports.js +189 -1
  284. package/dist/core/ports.js.map +1 -1
  285. package/dist/core/puedeLanzarse.js +239 -0
  286. package/dist/core/puedeLanzarse.js.map +1 -0
  287. package/dist/core/rutaVirtual.js +42 -0
  288. package/dist/core/rutaVirtual.js.map +1 -0
  289. package/dist/core/settings.js +246 -0
  290. package/dist/core/settings.js.map +1 -0
  291. package/dist/core/tareas.js +230 -0
  292. package/dist/core/tareas.js.map +1 -0
  293. package/dist/core/textos.js +36 -0
  294. package/dist/core/textos.js.map +1 -0
  295. package/dist/core/turno.js +70 -10
  296. package/dist/core/turno.js.map +1 -1
  297. package/dist/core/version.js +31 -0
  298. package/dist/core/version.js.map +1 -0
  299. package/dist/vendor/tokenTracking.js +22 -1
  300. package/dist/vendor/tokenTracking.js.map +1 -1
  301. package/dist/web/servidor/arranque.js +3964 -0
  302. package/dist/web/servidor/arranque.js.map +1 -0
  303. package/dist/web/servidor/consolaDeTarea.js +161 -0
  304. package/dist/web/servidor/consolaDeTarea.js.map +1 -0
  305. package/dist/web/servidor/consolaWeb.js +389 -0
  306. package/dist/web/servidor/consolaWeb.js.map +1 -0
  307. package/dist/web/servidor/corredorDeTareas.js +1119 -0
  308. package/dist/web/servidor/corredorDeTareas.js.map +1 -0
  309. package/dist/web/servidor/pielWeb.js +269 -0
  310. package/dist/web/servidor/pielWeb.js.map +1 -0
  311. package/dist/web/servidor/servidor.js +257 -0
  312. package/dist/web/servidor/servidor.js.map +1 -0
  313. package/dist/web/servidor/sesiones.js +440 -0
  314. package/dist/web/servidor/sesiones.js.map +1 -0
  315. package/dist/web/servidor/transporte.js +169 -0
  316. package/dist/web/servidor/transporte.js.map +1 -0
  317. package/dist/web/servidor/vestibulo.js +1276 -0
  318. package/dist/web/servidor/vestibulo.js.map +1 -0
  319. package/package.json +30 -8
  320. package/skills/archify/LICENSE +22 -0
  321. package/skills/archify/SKILL.md +109 -0
  322. package/skills/archify/THIRD_PARTY_NOTICES.md +56 -0
  323. package/skills/archify/assets/template.html +14787 -0
  324. package/skills/archify/bin/archify.mjs +1990 -0
  325. package/skills/archify/bin/open-artifact.mjs +86 -0
  326. package/skills/archify/bin/preview.mjs +648 -0
  327. package/skills/archify/bin/visual-check.mjs +829 -0
  328. package/skills/archify/brand-marks/README.md +31 -0
  329. package/skills/archify/brand-marks/catalog.json +131 -0
  330. package/skills/archify/delta/architecture-delta.mjs +1214 -0
  331. package/skills/archify/examples/agent-run.lifecycle.json +71 -0
  332. package/skills/archify/examples/agent-tool-call.workflow.json +94 -0
  333. package/skills/archify/examples/async-job-roundtrip.sequence.json +61 -0
  334. package/skills/archify/examples/brand-aware-delivery.architecture.json +47 -0
  335. package/skills/archify/examples/cache-miss-request.sequence.json +81 -0
  336. package/skills/archify/examples/checkout-platform.base.architecture.json +31 -0
  337. package/skills/archify/examples/checkout-platform.head.architecture.json +31 -0
  338. package/skills/archify/examples/dataflow-product-analytics.html +14898 -0
  339. package/skills/archify/examples/deployment-release.lifecycle.json +49 -0
  340. package/skills/archify/examples/event-stream.dataflow.json +57 -0
  341. package/skills/archify/examples/incident-response.workflow.json +64 -0
  342. package/skills/archify/examples/lifecycle-agent-run.html +14847 -0
  343. package/skills/archify/examples/product-analytics.dataflow.json +76 -0
  344. package/skills/archify/examples/production-deployment.architecture.json +71 -0
  345. package/skills/archify/examples/release-delivery.workflow.json +62 -0
  346. package/skills/archify/examples/sequence-cache-miss-request.html +14913 -0
  347. package/skills/archify/examples/web-app-rendered.html +14862 -0
  348. package/skills/archify/examples/web-app.architecture.json +46 -0
  349. package/skills/archify/examples/workflow-agent-tool-call-rendered.html +14904 -0
  350. package/skills/archify/migrations/workflow-v2.mjs +279 -0
  351. package/skills/archify/package.json +17 -0
  352. package/skills/archify/recipes/scenarios.mjs +391 -0
  353. package/skills/archify/references/authoring-contract.md +198 -0
  354. package/skills/archify/references/brand-marks.md +69 -0
  355. package/skills/archify/references/delivery-contract.md +116 -0
  356. package/skills/archify/references/viewer-runtime.md +45 -0
  357. package/skills/archify/renderers/architecture/grid.mjs +62 -0
  358. package/skills/archify/renderers/architecture/render-architecture.mjs +1078 -0
  359. package/skills/archify/renderers/dataflow/README.md +104 -0
  360. package/skills/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  361. package/skills/archify/renderers/lifecycle/README.md +115 -0
  362. package/skills/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  363. package/skills/archify/renderers/sequence/README.md +114 -0
  364. package/skills/archify/renderers/sequence/render-sequence.mjs +453 -0
  365. package/skills/archify/renderers/shared/brand-marks.mjs +563 -0
  366. package/skills/archify/renderers/shared/cli.mjs +218 -0
  367. package/skills/archify/renderers/shared/desktop-readability.mjs +26 -0
  368. package/skills/archify/renderers/shared/diagnostics.mjs +127 -0
  369. package/skills/archify/renderers/shared/engineering-profiles.mjs +157 -0
  370. package/skills/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
  371. package/skills/archify/renderers/shared/generated-validators.mjs +13 -0
  372. package/skills/archify/renderers/shared/geometry.mjs +1423 -0
  373. package/skills/archify/renderers/shared/i18n.mjs +594 -0
  374. package/skills/archify/renderers/shared/layout-report.mjs +40 -0
  375. package/skills/archify/renderers/shared/legend.mjs +217 -0
  376. package/skills/archify/renderers/shared/output-path.mjs +321 -0
  377. package/skills/archify/renderers/shared/repository-evidence.mjs +235 -0
  378. package/skills/archify/renderers/shared/text-fit.mjs +49 -0
  379. package/skills/archify/renderers/shared/utils.mjs +232 -0
  380. package/skills/archify/renderers/shared/validator.mjs +86 -0
  381. package/skills/archify/renderers/workflow/README.md +223 -0
  382. package/skills/archify/renderers/workflow/render-workflow.mjs +35 -0
  383. package/skills/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  384. package/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  385. package/skills/archify/schemas/README.md +211 -0
  386. package/skills/archify/schemas/architecture.schema.json +176 -0
  387. package/skills/archify/schemas/common.schema.json +115 -0
  388. package/skills/archify/schemas/dataflow.schema.json +243 -0
  389. package/skills/archify/schemas/lifecycle.schema.json +266 -0
  390. package/skills/archify/schemas/sequence.schema.json +223 -0
  391. package/skills/archify/schemas/workflow.schema.json +428 -0
  392. package/skills/archify/scripts/check-render-output.mjs +835 -0
  393. package/skills/archify/scripts/check-update.mjs +1667 -0
  394. package/skills/archify/scripts/render-examples.mjs +26 -0
  395. package/skills/archify/scripts/update-contract.mjs +182 -0
  396. package/skills/archify/skill-release.json +10 -0
  397. package/skills/artifacts-builder/SKILL.md +120 -0
  398. package/skills/artifacts-builder/reference/diagramas.md +123 -0
  399. package/skills/artifacts-builder/reference/estilo.md +123 -0
  400. package/skills/artifacts-builder/reference/exportar-pdf.md +56 -0
  401. package/skills/artifacts-builder/reference/graficas.md +22 -0
  402. package/skills/artifacts-builder/scripts/bundle_artifact.py +109 -0
  403. package/skills/artifacts-builder/scripts/validate_artifact.py +345 -0
  404. package/skills/xone-hotswap/SKILL.md +185 -0
  405. package/skills/xone-hotswap/references/comandos.md +706 -0
  406. package/skills/xone-hotswap/references/conexion-y-despliegue.md +226 -0
@@ -0,0 +1,722 @@
1
+ /**
2
+ * Conexión MCP de CloudStudio.
3
+ *
4
+ * El endpoint protege sus tools con OAuth del IDS. Este adaptador realiza el flujo
5
+ * Authorization Code + PKCE que espera MCP Streamable HTTP: abre el navegador, recibe
6
+ * el callback en loopback y guarda el resultado SOLO en ~/.xonecode, nunca en el repo.
7
+ */
8
+ import { createServer } from "node:http";
9
+ import { randomUUID } from "node:crypto";
10
+ import { homedir } from "node:os";
11
+ import { spawn } from "node:child_process";
12
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
13
+ import { join, dirname } from "node:path";
14
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
15
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
16
+ import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
17
+ import { motivoDeEndpointInaceptable } from "../core/modelos.js";
18
+ const NOMBRE_CARPETA = ".xonecode";
19
+ const NOMBRE_AUTH = "cloudstudio-oauth.json";
20
+ const VERSION = "0.5.0";
21
+ /**
22
+ * Debe ser estable: el IDS registra el redirect_uri del cliente OAuth.
23
+ *
24
+ * Se exporta porque `cli/main.ts` necesita el mismo número para rechazar `--puerto 7634`
25
+ * en el servidor web (el callback de OAuth y la web no pueden compartir puerto). Antes
26
+ * había una copia literal en `main.ts` porque este valor era privado; una sola fuente
27
+ * evita que las dos constantes diverjan si el puerto cambia algún día.
28
+ */
29
+ export const PUERTO_CALLBACK = 7634;
30
+ /**
31
+ * El CloudStudio oficial. Vive aquí y no en `cli/consola.ts` (de donde se movió) porque
32
+ * es la dirección de un SERVIDOR, no un detalle de la piel de consola, y
33
+ * `adoptarLegadoSiProcede` —de este mismo fichero— la necesita para decidir si el fichero
34
+ * plano de antes de los entornos pertenece al oficial: `agent/` no puede importar de
35
+ * `cli/` (convención documentada en `agent/turnoReal.ts`), así que si esto se hubiera
36
+ * quedado en `consola.ts` la migración habría tenido que duplicar el literal.
37
+ */
38
+ export const URL_CLOUDSTUDIO_POR_OMISION = "https://mcp.xonewebstudio.com/mcp";
39
+ /** Permisos mínimos para descubrir el catálogo MCP; no otorgan escritura ni ejecución. */
40
+ export const SCOPES_CLOUDSTUDIO = ["openid", "profile", "email", "offline_access", "mcp.read"];
41
+ export const SCOPES_CLOUDSTUDIO_ESCRITURA = [...SCOPES_CLOUDSTUDIO, "mcp.write"];
42
+ /** Un único consentimiento para el trabajo normal del agente; jamás incluye mcp.admin. */
43
+ export const SCOPES_CLOUDSTUDIO_AGENTE = [
44
+ "openid", "profile", "email", "offline_access", "xonewebstudioapi",
45
+ "mcp.read", "mcp.write", "mcp.execute", "mcp.branch",
46
+ ];
47
+ /** El id reservado para el fichero plano de antes de los entornos: ver `leerEstado`. */
48
+ const CLAVE_LEGADO = "legado";
49
+ const ESTADO_VACIO = { version: 2, porEntorno: {} };
50
+ /**
51
+ * El listado de proyectos por orden de preferencia.
52
+ *
53
+ * El servidor real de XOne CloudStudio la publica como `studio_list_projects`; los dos
54
+ * alias siguientes son endpoints anteriores. NO se codifica un solo nombre: cuando el
55
+ * servidor renombra su catálogo, el arranque entero se queda sin proyectos, y el usuario
56
+ * ve «no publicó la herramienta» en vez de su lista.
57
+ */
58
+ const NOMBRES_LISTAR_PROYECTOS = ["studio_list_projects", "project_list", "list_projects", "projects_list"];
59
+ /** Una tool que exige argumentos no se puede invocar con `{}` en el arranque. */
60
+ function pideArgumentos(definicion) {
61
+ const esquema = definicion.inputSchema;
62
+ if (typeof esquema !== "object" || esquema === null)
63
+ return false;
64
+ const obligatorios = esquema.required;
65
+ return Array.isArray(obligatorios) && obligatorios.length > 0;
66
+ }
67
+ /**
68
+ * Qué tool lista los proyectos. Primero los nombres conocidos, en orden; si ninguno está,
69
+ * una heurística conservadora: tiene que hablar de listar Y de proyectos, y no pedir
70
+ * argumentos. `studio_open_project` o `studio_download_project` no la superan a propósito
71
+ * — abrir el proyecto equivocado en el arranque es peor que no encontrar la tool.
72
+ */
73
+ export function herramientaDeProyectos(tools) {
74
+ for (const nombre of NOMBRES_LISTAR_PROYECTOS) {
75
+ const exacta = tools.find((t) => t.name === nombre);
76
+ if (exacta !== undefined)
77
+ return exacta;
78
+ }
79
+ return tools.find((t) => {
80
+ const nombre = t.name.toLowerCase();
81
+ return /list/.test(nombre) && /project|proyecto/.test(nombre) && !pideArgumentos(t);
82
+ });
83
+ }
84
+ export function proyectosDeResultado(valor) {
85
+ if (typeof valor === "string") {
86
+ try {
87
+ return proyectosDeResultado(JSON.parse(valor));
88
+ }
89
+ catch {
90
+ return [];
91
+ }
92
+ }
93
+ // Medido contra el servidor real: `studio_list_projects` NO devuelve una lista, sino un
94
+ // MAPA indexado por id bajo «recents». Por eso cada clave admite las dos formas y la
95
+ // clave del mapa se conserva: es el id cuando la entrada no lo repite en «pid».
96
+ const entradas = (candidato) => Array.isArray(candidato)
97
+ ? candidato
98
+ : typeof candidato === "object" && candidato !== null
99
+ ? Object.entries(candidato).map(([clave, entrada]) => typeof entrada === "object" && entrada !== null && !Array.isArray(entrada)
100
+ ? { ...entrada, __clave: clave }
101
+ : entrada)
102
+ : [];
103
+ const lista = Array.isArray(valor)
104
+ ? valor
105
+ : typeof valor === "object" && valor !== null
106
+ ? ["projects", "proyectos", "items", "data", "recents"].flatMap((clave) => entradas(valor[clave]))
107
+ : [];
108
+ // Los SDK MCP devuelven el resultado textual dentro de `content`; de todo eso no se
109
+ // conserva más que el id, el nombre y si el proyecto está compartido contigo. El wrapper
110
+ // LangChain de abajo serializa ese resultado para poder usar `.invoke({})` sin traducir
111
+ // los esquemas ajenos.
112
+ if (lista.length === 0 && typeof valor === "object" && valor !== null) {
113
+ const contenido = valor.content;
114
+ if (Array.isArray(contenido)) {
115
+ return contenido.flatMap((bloque) => typeof bloque === "object" && bloque !== null && bloque.type === "text"
116
+ ? proyectosDeResultado(bloque.text)
117
+ : []);
118
+ }
119
+ }
120
+ const vistos = new Set();
121
+ return lista.flatMap((candidato) => {
122
+ if (typeof candidato !== "object" || candidato === null || Array.isArray(candidato))
123
+ return [];
124
+ const dato = candidato;
125
+ const id = [dato.id, dato.pid, dato.projectId, dato.project_id, dato.__clave].find((v) => typeof v === "string" && v.trim() !== "");
126
+ const nombre = [dato.name, dato.nombre, dato.title].find((v) => typeof v === "string" && v.trim() !== "");
127
+ if (id === undefined || nombre === undefined || vistos.has(id))
128
+ return [];
129
+ vistos.add(id);
130
+ // SOLO si de verdad vino un booleano. Este servidor es flojo con los tipos —en la misma
131
+ // respuesta, `rights` es un objeto serializado como cadena—, así que ni «"true"» ni
132
+ // deducirlo de que `suser` no esté vacío: la ausencia se propaga como ausencia y la
133
+ // interfaz no pinta nada, que es la respuesta honesta para un endpoint que no lo dice.
134
+ return [{ id, nombre, ...(typeof dato.shared === "boolean" ? { compartido: dato.shared } : {}) }];
135
+ });
136
+ }
137
+ /** Dónde vive el estado OAuth cuando nadie inyecta otra ruta. Exportada porque
138
+ * `web/servidor/vestibulo.ts` la necesita para `adoptarLegadoSiProcede`: una segunda copia
139
+ * del literal es cómo divergen las rutas el día que una se corrige y la otra no. */
140
+ export function rutaAuthPorDefecto() {
141
+ return join(homedir(), NOMBRE_CARPETA, NOMBRE_AUTH);
142
+ }
143
+ /** Un candidato de `porEntorno` que no es objeto (fichero tocado a mano) no puede colar
144
+ * un valor no indexable: `leerEstado` filtra antes de devolverlo, no cada llamador. */
145
+ function porEntornoValido(bruto) {
146
+ if (typeof bruto !== "object" || bruto === null || Array.isArray(bruto))
147
+ return {};
148
+ const salida = {};
149
+ for (const [id, valor] of Object.entries(bruto)) {
150
+ if (typeof valor === "object" && valor !== null && !Array.isArray(valor)) {
151
+ salida[id] = valor;
152
+ }
153
+ }
154
+ return salida;
155
+ }
156
+ /**
157
+ * Lee el fichero entero. Un formato SIN `version` es el plano de antes de los entornos:
158
+ * se envuelve como `{ version: 2, porEntorno: { legado: <plano> } }` **solo en memoria**
159
+ * — un arranque de solo lectura (`xonecode config`, `describe`) no puede reescribir el
160
+ * disco del usuario, así que la migración se materializa recién en la primera escritura
161
+ * (`guardarEstadoDeEntorno`, `adoptarLegadoSiProcede`), nunca aquí.
162
+ */
163
+ export function leerEstado(ruta) {
164
+ if (!existsSync(ruta))
165
+ return ESTADO_VACIO;
166
+ try {
167
+ const bruto = JSON.parse(readFileSync(ruta, "utf8"));
168
+ if (typeof bruto !== "object" || bruto === null || Array.isArray(bruto))
169
+ return ESTADO_VACIO;
170
+ const objeto = bruto;
171
+ if (objeto.version === undefined) {
172
+ // El formato plano no tenía `version`; el objeto entero ES el juego de un entorno.
173
+ return { version: 2, porEntorno: { [CLAVE_LEGADO]: objeto } };
174
+ }
175
+ if (objeto.version !== 2)
176
+ return ESTADO_VACIO; // formato futuro/desconocido: fail-closed, no se inventa
177
+ return { version: 2, porEntorno: porEntornoValido(objeto.porEntorno) };
178
+ }
179
+ catch {
180
+ // Un fichero corrupto no impide volver a autenticar; nunca se imprime su contenido.
181
+ return ESTADO_VACIO;
182
+ }
183
+ }
184
+ /**
185
+ * Un fichero de una versión futura y desconocida no se puede sobrescribir a ciegas: este
186
+ * código no sabe qué juegos de credenciales lleva dentro, y `leerEstado` lo trata como
187
+ * vacío para no tumbar un arranque de solo lectura. Escribir SOBRE ese vacío destruiría
188
+ * lo que hubiera. Un JSON corrupto (parse roto) es harina de otro costal —ya se
189
+ * sobrescribía así antes de que existieran los entornos, es el comportamiento actual que
190
+ * el enunciado pide no tocar— así que solo se bloquea la versión reconocible pero
191
+ * distinta de 2, nunca el texto irrecuperable.
192
+ */
193
+ export class EstadoOAuthVersionIncompatible extends Error {
194
+ }
195
+ function comprobarVersionEscribible(ruta) {
196
+ if (!existsSync(ruta))
197
+ return;
198
+ let bruto;
199
+ try {
200
+ bruto = JSON.parse(readFileSync(ruta, "utf8"));
201
+ }
202
+ catch {
203
+ return; // corrupto: se sobrescribe, como ya hacía antes de los entornos.
204
+ }
205
+ if (typeof bruto !== "object" || bruto === null || Array.isArray(bruto))
206
+ return;
207
+ const version = bruto.version;
208
+ if (version !== undefined && version !== 2) {
209
+ throw new EstadoOAuthVersionIncompatible(`${ruta}: versión de estado OAuth desconocida (${JSON.stringify(version)}); no se sobrescribe`);
210
+ }
211
+ }
212
+ function guardarEstado(ruta, estado) {
213
+ mkdirSync(dirname(ruta), { recursive: true, mode: 0o700 });
214
+ const temporal = `${ruta}.${randomUUID()}.tmp`;
215
+ let fd;
216
+ try {
217
+ fd = openSync(temporal, "wx", 0o600);
218
+ writeFileSync(fd, JSON.stringify(estado, null, 2) + "\n", "utf8");
219
+ closeSync(fd);
220
+ fd = undefined;
221
+ renameSync(temporal, ruta);
222
+ }
223
+ catch (error) {
224
+ if (fd !== undefined)
225
+ closeSync(fd);
226
+ try {
227
+ unlinkSync(temporal);
228
+ }
229
+ catch { /* no llegó a crearse */ }
230
+ throw error;
231
+ }
232
+ }
233
+ /**
234
+ * Sustituye el juego de credenciales de UN entorno y deja los demás tal cual estaban:
235
+ * lee el fichero entero, reemplaza solo `porEntorno[id]` y reescribe con la misma
236
+ * mecánica de temporal + `rename` de `guardarEstado` — es la que asegura 0600/0700 y que
237
+ * un fallo a mitad de escritura no deja el fichero truncado.
238
+ */
239
+ export function guardarEstadoDeEntorno(ruta, id, datos) {
240
+ comprobarVersionEscribible(ruta);
241
+ const estado = leerEstado(ruta);
242
+ guardarEstado(ruta, { version: 2, porEntorno: { ...estado.porEntorno, [id]: datos } });
243
+ }
244
+ /**
245
+ * Cierra sesión en UN entorno. Si no tenía nada guardado no hay nada que tocar: escribir
246
+ * de todos modos reescribiría el fichero entero (y su mtime) sin que haya cambiado nada
247
+ * observable.
248
+ */
249
+ export function olvidarEntorno(ruta, id) {
250
+ comprobarVersionEscribible(ruta);
251
+ const estado = leerEstado(ruta);
252
+ if (!(id in estado.porEntorno))
253
+ return;
254
+ const { [id]: _omitido, ...resto } = estado.porEntorno;
255
+ guardarEstado(ruta, { version: 2, porEntorno: resto });
256
+ }
257
+ /**
258
+ * Adopta `legado` como el juego del entorno recién registrado, y SOLO si su URL es la
259
+ * oficial por omisión — es la única URL que el fichero plano pudo haber usado sin que el
260
+ * usuario tecleara una distinta a mano (ver el comentario de `EstadoOAuth`). Si `legado`
261
+ * no existe, o ya hay algo guardado bajo `entorno.id`, no hace nada: no hay nada que
262
+ * migrar en el primer caso, y pisar un juego ya autenticado sería peor que dejar el
263
+ * plano huérfano en el segundo.
264
+ */
265
+ export function adoptarLegadoSiProcede(ruta, entorno) {
266
+ if (entorno.url !== URL_CLOUDSTUDIO_POR_OMISION)
267
+ return;
268
+ comprobarVersionEscribible(ruta);
269
+ const estado = leerEstado(ruta);
270
+ const legado = estado.porEntorno[CLAVE_LEGADO];
271
+ if (legado === undefined || entorno.id in estado.porEntorno)
272
+ return;
273
+ const { [CLAVE_LEGADO]: _omitido, ...resto } = estado.porEntorno;
274
+ guardarEstado(ruta, { version: 2, porEntorno: { ...resto, [entorno.id]: legado } });
275
+ }
276
+ /**
277
+ * Abre una URL en el navegador del sistema. Exportada porque la consola web hace lo mismo
278
+ * al arrancar (`web/servidor/arranque.ts`) y tres `spawn` con la misma tabla de comandos
279
+ * en dos ficheros es cómo divergen.
280
+ *
281
+ * El `on("error")` no es decorativo: `spawn` de un comando que no existe —un contenedor
282
+ * sin `xdg-open`, un SSH sin escritorio— no lanza, EMITE `error`, y sin escucha eso es una
283
+ * excepción no capturada que se lleva el proceso por delante. Aquí abrir el navegador es
284
+ * siempre lo ACCESORIO: la URL ya está impresa, y el servidor tiene que seguir en pie.
285
+ */
286
+ export function abrirEnSistema(url) {
287
+ const [comando, args] = process.platform === "darwin"
288
+ ? ["open", [url.toString()]]
289
+ : process.platform === "win32"
290
+ ? ["cmd", ["/c", "start", "", url.toString()]]
291
+ : ["xdg-open", [url.toString()]];
292
+ const proceso = spawn(comando, args, { detached: true, stdio: "ignore" });
293
+ proceso.on("error", () => { });
294
+ proceso.unref();
295
+ }
296
+ export const AVISO_DE_URL_DE_MCP = "la URL del MCP debe ser HTTPS —solo se admite http:// en 127.0.0.1 o localhost, para un on-premise en desarrollo— y no puede incluir credenciales";
297
+ /**
298
+ * La ÚNICA regla de qué URL de MCP vale, y por eso vive aquí y no en cada gate.
299
+ *
300
+ * Había tres puertas con dos criterios: el wizard del navegador aceptaba loopback,
301
+ * `vestibulo.ts#urlDeEntornoValida` exigía HTTPS y lo rechazaba, y esta función —la última,
302
+ * la que de verdad conecta— también. Registrar un entorno on-premise de desarrollo era
303
+ * imposible por la segunda, y aflojar solo la segunda habría dejado registrar uno que
304
+ * después fallaba al primer `conectarCloudStudio`: un alta que se puede escribir y no se
305
+ * puede usar es peor que el rechazo claro de hoy.
306
+ *
307
+ * Se resuelve por el lado PERMISIVO porque el caso existe —un CloudStudio on-premise
308
+ * levantado en local durante el desarrollo— y porque en loopback el texto plano no cruza
309
+ * ninguna red: es el mismo trato que ya se da al `redirect_uri` del callback de OAuth
310
+ * (`http://127.0.0.1:7634`) y a la propia consola web. Fuera de loopback, HTTPS y nada más.
311
+ * La copia del cliente (`apps/web/src/componentes/Wizard.tsx#urlDeEntornoAceptable`) dice
312
+ * lo mismo y no puede importar esta: lo prohíbe `src/web/frontera.test.ts`.
313
+ */
314
+ export function urlDeMcpAceptable(valor) {
315
+ // Delega en `core/`, que es donde vive la regla desde que los proveedores de modelos
316
+ // personalizados necesitaron la misma: HTTPS fuera de la máquina, `http://` solo en
317
+ // loopback, y nunca credenciales dentro de la URL (acabaría en `settings.json` y en cada
318
+ // traza que la enseñe). Dos copias de esto habrían divergido el día que una de las dos
319
+ // aflojara — que es exactamente cómo llegó a haber tres puertas con dos criterios.
320
+ return motivoDeEndpointInaceptable(valor) === undefined;
321
+ }
322
+ function urlSegura(valor) {
323
+ if (!urlDeMcpAceptable(valor))
324
+ throw new Error(AVISO_DE_URL_DE_MCP);
325
+ return new URL(valor);
326
+ }
327
+ /**
328
+ * Qué se le contesta al navegador que vuelve del IDS.
329
+ *
330
+ * Sale del servidor y se prueba aparte porque montarlo exige el puerto 7634 —fijo, porque
331
+ * el IDS registra ese `redirect_uri`— y ahí no se puede escoger uno efímero: un test que
332
+ * atara el puerto real fallaría en cuanto alguien tuviera una autorización en curso.
333
+ */
334
+ export function respuestaDeCallback(codigo, error, redirigirA) {
335
+ if (error || !codigo) {
336
+ // Sin redirección tampoco en modo web: la web aún no sabe del fallo (se entera por la
337
+ // promesa rechazada) y mandarla allí escondería el motivo. El texto no nombra «la
338
+ // consola» porque quien llega aquí puede estar usando el navegador.
339
+ return {
340
+ estado: 400,
341
+ cabeceras: { "content-type": "text/html; charset=utf-8" },
342
+ cuerpo: "<h1>No se pudo completar el acceso a CloudStudio</h1><p>Vuelve a xonecode para ver el detalle.</p>",
343
+ };
344
+ }
345
+ if (redirigirA !== undefined) {
346
+ return { estado: 302, cabeceras: { location: redirigirA }, cuerpo: "" };
347
+ }
348
+ return {
349
+ estado: 200,
350
+ cabeceras: { "content-type": "text/html; charset=utf-8" },
351
+ cuerpo: "<h1>CloudStudio conectado</h1><p>Ya puedes volver a xonecode.</p>",
352
+ };
353
+ }
354
+ /**
355
+ * La URL de callback es determinista —`PUERTO_CALLBACK` es fijo, porque el IDS registra
356
+ * ese `redirect_uri`— así que conocerla no exige que el servidor esté escuchando. El
357
+ * `provider` la necesita SIEMPRE, la escuche `auth()` o no: la lleva en su
358
+ * `clientMetadata` para el registro/descubrimiento aunque el token guardado sea válido y
359
+ * `auth()` nunca llegue a redirigir. Separada de `escucharCallback` (abajo) justo por
360
+ * eso: construir la URL no es un efecto colateral, EMPEZAR A ESCUCHAR sí lo es.
361
+ */
362
+ function urlDeCallback() {
363
+ return `http://127.0.0.1:${PUERTO_CALLBACK}/oauth/callback`;
364
+ }
365
+ /**
366
+ * Arranca el servidor HTTP local que recibe el `code` de la redirección, con su
367
+ * temporizador de espera. Se llama solo cuando `auth()` YA dijo `REDIRECT` —antes se
368
+ * llamaba SIEMPRE, incluso con un token guardado y válido que no iba a redirigir nunca—:
369
+ * MEDIDO, eso ocupaba el puerto 7634 fijo y armaba un temporizador de cinco minutos en
370
+ * cada llamada a `abrirCliente`, y dos llamadas seguidas o solapadas (dos conexiones del
371
+ * vestíbulo listando proyectos del mismo entorno, por ejemplo) chocaban por el mismo
372
+ * puerto sin que hiciera falta ningún login de verdad.
373
+ */
374
+ function escucharCallback(timeoutMs, redirigirA) {
375
+ return new Promise((resolver, rechazar) => {
376
+ let resolverCodigo;
377
+ let rechazarCodigo;
378
+ const codigo = new Promise((ok, fallo) => {
379
+ resolverCodigo = ok;
380
+ rechazarCodigo = fallo;
381
+ });
382
+ const servidor = createServer((peticion, respuesta) => {
383
+ const recibida = new URL(peticion.url ?? "/", "http://127.0.0.1");
384
+ const codigoRecibido = recibida.searchParams.get("code");
385
+ const error = recibida.searchParams.get("error");
386
+ const contestacion = respuestaDeCallback(codigoRecibido, error, redirigirA);
387
+ respuesta.writeHead(contestacion.estado, contestacion.cabeceras);
388
+ respuesta.end(contestacion.cuerpo);
389
+ limpiar();
390
+ if (error || !codigoRecibido)
391
+ rechazarCodigo(new Error(`el IDS rechazó el acceso${error ? `: ${error}` : ""}`));
392
+ else
393
+ resolverCodigo(codigoRecibido);
394
+ });
395
+ const temporizador = setTimeout(() => {
396
+ limpiar();
397
+ rechazarCodigo(new Error("se agotó la espera del login de CloudStudio"));
398
+ }, timeoutMs);
399
+ const limpiar = () => {
400
+ clearTimeout(temporizador);
401
+ servidor.close();
402
+ };
403
+ servidor.once("error", (error) => {
404
+ limpiar();
405
+ rechazar(new Error(`no se pudo abrir el callback local de OAuth: ${error.message}`));
406
+ });
407
+ servidor.listen(PUERTO_CALLBACK, "127.0.0.1", () => {
408
+ resolver({ codigo, cerrar: limpiar });
409
+ });
410
+ });
411
+ }
412
+ /** Provider OAuth persistente, deliberadamente pequeño y compatible con el SDK MCP. */
413
+ /**
414
+ * Exportada SOLO para poder probar `invalidateCredentials` —el gancho del que depende que
415
+ * el SDK sepa recuperarse de un token muerto— sin red ni navegador. Nadie fuera de este
416
+ * módulo la construye para usarla: quien abre sesión llama a `sesionCloudStudio` o a
417
+ * `conectarCloudStudio`, que la montan por dentro.
418
+ */
419
+ export class ProviderCloudStudio {
420
+ ruta;
421
+ entornoId;
422
+ alRedirigir;
423
+ scopes;
424
+ estado;
425
+ metadata;
426
+ constructor(ruta,
427
+ /** El entorno cuyo juego de credenciales lee y escribe este provider: opera SOLO
428
+ * sobre `porEntorno[entornoId]`, nunca sobre el fichero entero, para que abrir
429
+ * sesión en un entorno no pueda pisar el juego de otro. */
430
+ entornoId, redirectUrl, alRedirigir, scopes) {
431
+ this.ruta = ruta;
432
+ this.entornoId = entornoId;
433
+ this.alRedirigir = alRedirigir;
434
+ this.scopes = scopes;
435
+ this.estado = leerEstado(ruta).porEntorno[entornoId] ?? {};
436
+ // Un refresh token de solo lectura no sirve para elevar privilegios. Borrarlo aquí
437
+ // fuerza Authorization Code + consentimiento nuevo en lugar de fingir una elevación.
438
+ const concedidos = new Set(this.estado.scopes ?? (this.estado.tokens?.scope ?? "").split(/\s+/));
439
+ if (this.estado.tokens !== undefined && !this.scopes.every((scope) => concedidos.has(scope))) {
440
+ delete this.estado.tokens;
441
+ this.guardar();
442
+ }
443
+ this.metadata = {
444
+ client_name: "xonecode",
445
+ client_uri: "https://github.com/xonecode/xonecode",
446
+ redirect_uris: [redirectUrl],
447
+ grant_types: ["authorization_code", "refresh_token"],
448
+ response_types: ["code"],
449
+ // Mínimo privilegio: conectar y descubrir tools no necesita escribir ni ejecutar.
450
+ scope: scopes.join(" "),
451
+ };
452
+ }
453
+ /** Solo esta clave se toca en el fichero, nunca `porEntorno` entero: es lo que hace
454
+ * que abrir sesión en un entorno deje intactos los tokens de los demás. */
455
+ guardar() { guardarEstadoDeEntorno(this.ruta, this.entornoId, this.estado); }
456
+ get redirectUrl() { return this.metadata.redirect_uris[0]; }
457
+ get clientMetadata() { return this.metadata; }
458
+ clientInformation() { return this.estado.clientInformation; }
459
+ saveClientInformation(info) { this.estado.clientInformation = info; this.guardar(); }
460
+ tokens() { return this.estado.tokens; }
461
+ saveTokens(tokens) {
462
+ this.estado.tokens = tokens;
463
+ this.estado.scopes = [...this.scopes];
464
+ this.guardar();
465
+ }
466
+ redirectToAuthorization(url) { this.alRedirigir(url); }
467
+ /**
468
+ * El gancho de recuperación del SDK, y no un extra: `auth()` (`client/auth.js`) atrapa
469
+ * `InvalidGrantError` —el refresh token muerto— e `InvalidClientError`, llama a esto y
470
+ * REINTENTA el flujo entero. Sin implementarlo, esa llamada es un no-op: el SDK reintenta
471
+ * con las mismas credenciales podridas, vuelve a fallar y lanza. O sea que un token
472
+ * caducado sin refresco válido era un fallo duro —«hay que borrar el fichero a mano»—
473
+ * cuando el propio SDK sabía volver a autenticar solo.
474
+ *
475
+ * Cada alcance borra LO SUYO y nada más, y se escribe en el acto: lo que se está
476
+ * arreglando es justo un estado a medias, y dejarlo en memoria sin guardar significaría
477
+ * que el siguiente arranque lo vuelve a leer del fichero.
478
+ */
479
+ invalidateCredentials(alcance) {
480
+ if (alcance === "all") {
481
+ // Todo menos el hueco: el entorno sigue existiendo, lo que se va son sus credenciales.
482
+ this.estado = {};
483
+ }
484
+ else if (alcance === "tokens") {
485
+ delete this.estado.tokens;
486
+ // Los scopes concedidos van CON el token: conservarlos haría creer al constructor
487
+ // que ya hay permiso concedido para unos tokens que ya no existen.
488
+ delete this.estado.scopes;
489
+ }
490
+ else if (alcance === "client") {
491
+ delete this.estado.clientInformation;
492
+ }
493
+ else if (alcance === "verifier") {
494
+ delete this.estado.codeVerifier;
495
+ }
496
+ // `discovery` no tiene nada que borrar aquí: este provider no cachea el descubrimiento
497
+ // (no implementa `discoveryState`), así que el SDK lo rehace solo.
498
+ this.guardar();
499
+ }
500
+ saveCodeVerifier(verifier) { this.estado.codeVerifier = verifier; this.guardar(); }
501
+ codeVerifier() {
502
+ if (!this.estado.codeVerifier)
503
+ throw new Error("no hay verificador PKCE para el login de CloudStudio");
504
+ return this.estado.codeVerifier;
505
+ }
506
+ }
507
+ /**
508
+ * El servidor pierde el proyecto abierto al caducar la sesión; lo dice con este texto
509
+ * (medido). Duplicado a propósito del mismo patrón en `agent/cloudstudioClient.ts`: es
510
+ * la señal, no un detalle de transporte, y ninguno de los dos módulos importa del otro.
511
+ */
512
+ const SESION_PERDIDA = /no project is open/i;
513
+ /** Tools cuyo `isError` puede traer de vuelta nuestro propio contenido: un rechazo de
514
+ * `studio_edit_file` en modo `patch`, o de `studio_upload_file`, puede incluir el
515
+ * fragmento o fichero que se intentó mandar. Nunca se reenvía ese cuerpo. */
516
+ const TOOLS_DE_ESCRITURA = new Set(["studio_edit_file", "studio_upload_file"]);
517
+ /** Cuánto de la primera línea de un error se conserva. Basta para «File extension
518
+ * '.jpg' is not allowed…»; no basta para un volcado de fichero entero. */
519
+ const TOPE_MENSAJE_ERROR = 200;
520
+ /** El texto de un error de TOOL (`isError`), tal cual lo manda el servidor. Puede
521
+ * incluir identidad («…not found for user 'sleiva@xone.es'») o, en una tool de
522
+ * escritura, el propio contenido rechazado: por eso `mensajeDeErrorDeTool` decide
523
+ * cuánto de esto sale de aquí, nunca esta función. */
524
+ function textoDeErrorDeTool(resultado) {
525
+ const contenido = typeof resultado === "object" && resultado !== null
526
+ ? resultado.content
527
+ : undefined;
528
+ if (Array.isArray(contenido)) {
529
+ const texto = contenido
530
+ .filter((b) => typeof b === "object" && b !== null && b.type === "text")
531
+ .map((b) => b.text)
532
+ .join(" ")
533
+ .trim();
534
+ if (texto !== "")
535
+ return texto;
536
+ }
537
+ return "CloudStudio devolvió un error sin detalle";
538
+ }
539
+ /** La ruta del fichero de la llamada, cuando la tool la lleva en `filePath`. Solo para
540
+ * nombrar el error; nunca su contenido. */
541
+ function rutaDe(argumentos) {
542
+ return typeof argumentos.filePath === "string" ? argumentos.filePath : undefined;
543
+ }
544
+ /** La primera línea, acotada. Un texto de una sola línea corta pasa intacto. */
545
+ function primeraLineaAcotada(texto) {
546
+ const primeraLinea = texto.split(/\r?\n/, 1)[0] ?? "";
547
+ return primeraLinea.length > TOPE_MENSAJE_ERROR
548
+ ? `${primeraLinea.slice(0, TOPE_MENSAJE_ERROR)}…`
549
+ : primeraLinea;
550
+ }
551
+ /**
552
+ * El mensaje final de la excepción: nombra siempre la tool, para que el error siga
553
+ * sirviendo para diagnosticar, pero decide cuánto del cuerpo del servidor deja pasar.
554
+ *
555
+ * La caducidad de sesión es la excepción a la excepción: es un texto fijo y corto —
556
+ * nunca contenido de fichero—, y es la señal de la que depende toda la reapertura en
557
+ * `clienteCloudStudio`, incluida la de las tools de escritura. Redactar el cuerpo ahí
558
+ * dejaría un `escribirTexto`/`subirBinario` con la sesión caducada sin forma de
559
+ * reabrir y reintentar.
560
+ */
561
+ function mensajeDeErrorDeTool(nombre, argumentos, resultado) {
562
+ const detalle = textoDeErrorDeTool(resultado);
563
+ if (SESION_PERDIDA.test(detalle))
564
+ return `${nombre}: ${primeraLineaAcotada(detalle)}`;
565
+ if (TOOLS_DE_ESCRITURA.has(nombre)) {
566
+ const ruta = rutaDe(argumentos);
567
+ // Sin el cuerpo: en una tool de escritura ese cuerpo puede ser nuestro propio
568
+ // fichero (o el fragmento de un patch) rebotando.
569
+ return `CloudStudio rechazó ${nombre}${ruta !== undefined ? ` (${ruta})` : ""}`;
570
+ }
571
+ return `${nombre}: ${primeraLineaAcotada(detalle)}`;
572
+ }
573
+ /**
574
+ * Envuelve un `callTool` crudo del SDK en la función `invocar` que espera
575
+ * `clienteCloudStudio`. Separada de `sesionCloudStudio` para poder probar la conversión
576
+ * `isError → excepción` con un `callTool` falso, sin tocar OAuth ni el transporte: es la
577
+ * pieza de la que depende toda la reapertura de sesión en `agent/cloudstudioClient.ts`,
578
+ * y una implementación que se limitara a `return callTool(...)` pasaría los tests de
579
+ * `clienteCloudStudio` igual de mal que los de aquí bien, si esto no se probara aparte.
580
+ */
581
+ export function invocarSobre(callTool) {
582
+ return async (nombre, argumentos) => {
583
+ const resultado = await callTool({ name: nombre, arguments: argumentos });
584
+ // El SDK MCP no lanza cuando la TOOL falla (`isError: true` es una respuesta RPC
585
+ // válida, con el motivo dentro de `content`); sin convertirlo en excepción, «No
586
+ // project is open» nunca llegaría a un catch y `clienteCloudStudio` no podría
587
+ // reabrir la sesión.
588
+ if (typeof resultado === "object" && resultado !== null && resultado.isError) {
589
+ throw new Error(mensajeDeErrorDeTool(nombre, argumentos, resultado));
590
+ }
591
+ return resultado;
592
+ };
593
+ }
594
+ /**
595
+ * Abre (o reutiliza) OAuth y conecta el transporte MCP. Es la base común de
596
+ * `sesionCloudStudio` y `conectarCloudStudio`: la segunda necesita además
597
+ * `listTools()`, que no es una tool invocable y por eso no cabe en `{ invocar, cerrar }`.
598
+ *
599
+ * En error se cierra el transporte aquí mismo, porque nadie más llega a recibir el
600
+ * `cerrar()` que lo liberaría; en éxito la sesión queda VIVA a propósito, y es quien
601
+ * llama quien decide cuándo cerrarla.
602
+ */
603
+ async function abrirCliente(urlTexto, opciones) {
604
+ const url = urlSegura(urlTexto);
605
+ const scopes = opciones.scopes ?? SCOPES_CLOUDSTUDIO_AGENTE;
606
+ const ruta = opciones.rutaAuth ?? rutaAuthPorDefecto();
607
+ const provider = new ProviderCloudStudio(ruta, opciones.entornoId ?? CLAVE_LEGADO, urlDeCallback(), (autorizacion) => {
608
+ // La URL de autorización no aporta nada al transcript normal y puede tener parámetros
609
+ // sensibles de OAuth. El navegador se abre sin volcarla; quien use SSH puede inyectar
610
+ // `abrirNavegador` y decidir cómo presentarla.
611
+ (opciones.abrirNavegador ?? abrirEnSistema)(autorizacion);
612
+ }, scopes);
613
+ let transporte;
614
+ // Solo se rellena si `auth()` de verdad redirige — ver `escucharCallback` arriba.
615
+ let callback;
616
+ try {
617
+ // No se usa `client.connect()` para iniciar OAuth: ese transporte queda marcado como
618
+ // iniciado incluso cuando redirige y no admite un segundo connect. `auth()` realiza
619
+ // primero el descubrimiento/PKCE y solo se crea el transporte DESPUÉS del callback.
620
+ const opcionesAuth = {
621
+ serverUrl: url,
622
+ resourceMetadataUrl: new URL("/.well-known/oauth-protected-resource", url.origin),
623
+ scope: scopes.join(" "),
624
+ };
625
+ const inicial = await auth(provider, opcionesAuth);
626
+ if (inicial === "REDIRECT") {
627
+ callback = await escucharCallback(opciones.timeoutMs ?? 5 * 60_000, opciones.redirigirA);
628
+ const codigo = await callback.codigo;
629
+ const final = await auth(provider, { ...opcionesAuth, authorizationCode: codigo });
630
+ if (final !== "AUTHORIZED")
631
+ throw new Error("el IDS no completó la autorización de CloudStudio");
632
+ }
633
+ const cliente = new Client({ name: "xonecode", version: VERSION });
634
+ transporte = new StreamableHTTPClientTransport(url, { authProvider: provider });
635
+ await cliente.connect(transporte);
636
+ const transporteVivo = transporte;
637
+ return { cliente, url, cerrar: () => transporteVivo.close() };
638
+ }
639
+ catch (error) {
640
+ await transporte?.close();
641
+ throw error;
642
+ }
643
+ finally {
644
+ callback?.cerrar();
645
+ }
646
+ }
647
+ /**
648
+ * Sesión MCP viva contra CloudStudio, para `clienteCloudStudio`
649
+ * (`agent/cloudstudioClient.ts`): descarga, escribe y cambia de rama necesitan una
650
+ * conexión que sobreviva a más de una llamada, cosa que `conectarCloudStudio` no ofrece
651
+ * porque cierra el transporte en cuanto termina de mirar el catálogo.
652
+ */
653
+ export async function sesionCloudStudio(urlTexto, opciones = {}) {
654
+ const { cliente, cerrar } = await abrirCliente(urlTexto, opciones);
655
+ return {
656
+ invocar: invocarSobre((peticion) => cliente.callTool(peticion)),
657
+ cerrar,
658
+ };
659
+ }
660
+ /** Lo más largo que se acepta de un nombre que viene del OTRO lado. Cabe cualquier nombre
661
+ * real de servidor y no cabe un párrafo. */
662
+ const TOPE_NOMBRE_DE_SERVIDOR = 60;
663
+ /**
664
+ * El nombre del servidor MCP, saneado, o `undefined` si no publica ninguno usable.
665
+ *
666
+ * Es texto que llega de fuera y acaba en `settings.json` y en la barra lateral, así que
667
+ * pasa por la misma disciplina que cualquier otro dato remoto de este fichero: se quita lo
668
+ * que no es imprimible (un `\n` o un `\r` en un nombre parte la línea de un log y disfraza
669
+ * lo que venga detrás), se colapsan los espacios y se acota. Lo que NUNCA sale de aquí es
670
+ * un identificador: el id del entorno se deduce del host y no de esto, porque el id es un
671
+ * segmento de ruta y el servidor no puede elegir carpetas en el disco de nadie.
672
+ *
673
+ * `title` antes que `name` porque es la convención de MCP: `title` es el nombre para leer y
674
+ * `name` el programático (`xone-cloudstudio`). Si no hay `title`, `name` es mejor que nada.
675
+ */
676
+ export function servidorDeImplementacion(info) {
677
+ const crudo = [info?.title, info?.name].find((v) => typeof v === "string" && v.trim() !== "");
678
+ if (typeof crudo !== "string")
679
+ return undefined;
680
+ const nombre = crudo
681
+ // eslint-disable-next-line no-control-regex
682
+ .replace(/[\u0000-\u001f\u007f]+/g, " ")
683
+ .replace(/\s+/g, " ")
684
+ .trim()
685
+ .slice(0, TOPE_NOMBRE_DE_SERVIDOR);
686
+ if (nombre === "")
687
+ return undefined;
688
+ const version = typeof info?.version === "string" ? info.version.trim().slice(0, TOPE_NOMBRE_DE_SERVIDOR) : "";
689
+ return { nombre, ...(version === "" ? {} : { version }) };
690
+ }
691
+ /**
692
+ * Inicia (o reutiliza) OAuth, completa initialize y lista las tools disponibles.
693
+ * La conexión de tools al grafo vendrá después: esta función prueba la conexión sin
694
+ * incorporar los esquemas de todas las tools al prompt del agente.
695
+ */
696
+ export async function conectarCloudStudio(urlTexto, opciones = {}) {
697
+ const scopes = opciones.scopes ?? SCOPES_CLOUDSTUDIO_AGENTE;
698
+ const { cliente, url, cerrar } = await abrirCliente(urlTexto, opciones);
699
+ try {
700
+ const listado = await cliente.listTools();
701
+ const definicion = herramientaDeProyectos(listado.tools);
702
+ if (definicion === undefined) {
703
+ const disponibles = listado.tools.map((tool) => tool.name).filter(Boolean).join(", ");
704
+ throw new Error(`CloudStudio no publicó ninguna herramienta de listado de proyectos${disponibles ? ` (disponibles: ${disponibles})` : ""}`);
705
+ }
706
+ const proyectos = proyectosDeResultado(await cliente.callTool({ name: definicion.name, arguments: {} }));
707
+ // El `serverInfo` del initialize, ya saneado. Se lee DESPUÉS de `connect`, que es
708
+ // cuando el SDK lo tiene; antes devuelve `undefined`.
709
+ const servidor = servidorDeImplementacion(cliente.getServerVersion());
710
+ return {
711
+ url: url.toString(),
712
+ scopes,
713
+ herramientas: listado.tools.map((tool) => ({ nombre: tool.name, descripcion: tool.description ?? "" })),
714
+ proyectos,
715
+ ...(servidor === undefined ? {} : { servidor }),
716
+ };
717
+ }
718
+ finally {
719
+ await cerrar();
720
+ }
721
+ }
722
+ //# sourceMappingURL=cloudstudioMcp.js.map