xonecode 0.3.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 +56 -8
  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,706 @@
1
+ # Comandos del servidor hotswap, por plataforma
2
+
3
+ Los mensajes llegan como un JSON con el campo `command`; las respuestas son subclases de
4
+ `ResultMessage`. El contrato de Android es la referencia **literal** de lo que implementa
5
+ `handleClientResponse()` en la app XoneStudio, copiada de la documentación del framework. iOS
6
+ implementa **el mismo JSON y los mismos mensajes de error literales** en los comandos que tiene,
7
+ para que un cliente escrito contra Android siga funcionando; donde no puede, la desviación está
8
+ anotada y marcada **(iOS)**.
9
+
10
+ **Declarar una diferencia vale más que fingir paridad.** Todo lo marcado **(iOS)** es una
11
+ desviación real y medida: si la lees antes de actuar, puedes trabajar con ella; si te crees una
12
+ promesa de paridad que no se cumple, diagnosticarás mal. Las que más caro salen —las que un
13
+ `result:true` no basta para descubrir— están reunidas al final, en
14
+ [Divergencias con Android](#divergencias-con-android).
15
+
16
+ Cómo llegar al dispositivo y cómo desplegar: [conexion-y-despliegue.md](conexion-y-despliegue.md).
17
+
18
+ ## Tipos de respuesta
19
+
20
+ | Tipo | JSON resultante | iOS |
21
+ |---|---|---|
22
+ | `ResultMessage()` | `{ result: true, status: "" }` | sí |
23
+ | `ResultMessage(string)` | `{ result: true, status: "<string>" }` | sí |
24
+ | `ResultMessage(false, string)` | `{ result: false, status: "<string>" }` | sí |
25
+ | `ResultMessage(exception)` | `{ result: false, exceptionClass: "...", exceptionMessage: "..." }` | sí, pero ver la nota al final |
26
+ | `SqlMessage` | `{ result: true/false, sql: "...", data: [...filas...] }` | no |
27
+ | `ListDirectoryMessage` | `{ result: true, status: "listAppFiles", paths: [...] }` | sí |
28
+ | `GetFileChecksumMessage` | `{ result: { "<archivo>": "<checksum>", ... } }` — *`result` es un objeto, no boolean* | no |
29
+ | `FullLogMessage` | `{ result: true, basicInfo: {...}, logcat: "...", exception: "...", stackTrace: "..." }` | no |
30
+ | `DebugMessage` | JSON específico del depurador JS | no |
31
+
32
+ ## Matriz de disponibilidad
33
+
34
+ La columna **Canal (iOS)** dice por dónde se le puede hablar a cada comando, y **no es un detalle
35
+ de transporte**: los comandos legados responden escribiendo en el socket, que en una petición HTTP
36
+ no existe, así que un `POST /command` con uno de ellos contesta `Unknown command '<x>'` aunque la
37
+ columna iOS diga "sí". Solo el bloque de automatización (**Ver la pantalla** + **Tocar**) se sirve
38
+ por los dos canales.
39
+
40
+ | Comando | Android | iOS | Canal (iOS) |
41
+ |---|---|---|---|
42
+ | `launchApplication` | sí | sí | **solo WS** |
43
+ | `exitApplication` | sí | — | — |
44
+ | `directdownload` | sí | sí | **solo WS** |
45
+ | `loadCollection` | sí | sí | **solo WS** |
46
+ | `loadIncludeFile` | sí | — | — |
47
+ | `setAttribute` | sí | sí | **solo WS** |
48
+ | `deleteAttribute` | sí | sí | **solo WS** |
49
+ | `replaceProperty` | sí | — | — |
50
+ | `setCssAttribute` / `deleteCssAttribute` | sí | — | — |
51
+ | `refresh` / `relayout` | sí | — | — |
52
+ | `status` / `state` | sí | sí | **solo WS** |
53
+ | `uploadFile` | sí | — | — |
54
+ | `listAppFiles` | sí | sí | **solo WS** |
55
+ | `getFileChecksum` | sí | — | — |
56
+ | `runScript` | sí | — | — |
57
+ | `runSql` / `getDatabaseTableNames` / `queryDatabaseTable` | sí | — | — |
58
+ | `getLog` / `enableDeviceLogging` / `disableDeviceLogging` / `setLogEnabled` / `setLogDisabled` | sí | — | — |
59
+ | `enableLiveEditionMode` / `disableLiveEditionMode` | sí | — | — |
60
+ | `setMockData` / `clearMockData` | sí | — | — |
61
+ | `debug` (+13 subcomandos) | sí | — | — |
62
+ | `getAllElements` | sí | sí (tres nombres de formato, dos formatos) | WS + HTTP |
63
+ | `getCurrentScreen` | sí | sí (`activity` es un nombre de clase, no una Activity) | WS + HTTP |
64
+ | `getScreenshot` | sí | sí (JPEG/PNG) | WS + HTTP |
65
+ | `getText` | sí | sí (+ `source`) | WS + HTTP |
66
+ | `getUiAttribute` | sí | sí (ver la desviación) | WS + HTTP |
67
+ | `getRows` | sí | sí (ver las desviaciones) | WS + HTTP |
68
+ | `isVisible` / `isEnabled` | sí | sí | WS + HTTP |
69
+ | `waitForElement` | sí | sí | WS + HTTP |
70
+ | `click` / `tap` | sí | sí (colapsan) | WS + HTTP |
71
+ | `doubleTap` / `longPress` | sí | se niegan siempre | WS + HTTP |
72
+ | `openSelector` | sí | sí | WS + HTTP |
73
+ | `fill` / `clear` | sí | sí (no multilínea ni fecha/hora) | WS + HTTP |
74
+ | `scroll` | sí | sí (necesita ancestro scrollable **en el eje pedido**) | WS + HTTP |
75
+ | `pressKey` | sí | solo `4`/`back` y `66`/`enter` | WS + HTTP |
76
+
77
+ **Doce** de ellos aceptan además `row` / `content` / `where` para apuntar a una fila:
78
+ [Apuntar a una fila](#apuntar-a-una-fila).
79
+
80
+ ---
81
+
82
+ ## Un nombre de control puede existir en dos pantallas
83
+
84
+ Los nombres son **por colección**, no globales. En AliviaApp, `MAP_LOGIN_BTN` es el botón "Inicia
85
+ Sesión" en `EntradaApp` **y** el botón de enviar en `Login`: el mismo nombre, dos controles con
86
+ distinta jerarquía, distinto estado y distinto comportamiento ante `scroll` o `isEnabled`. Antes
87
+ de creerte una respuesta, confirma en qué pantalla estás (el `collectionName` que devuelve
88
+ `getAllElements` lo dice). Es una fuente de diagnósticos falsos que parece imposible hasta que
89
+ te pasa.
90
+
91
+ ## Apuntar a una fila
92
+
93
+ Los **doce** comandos de interacción y lectura por nombre (`getText`, `isVisible`, `isEnabled`,
94
+ `waitForElement`, `click`, `tap`, `doubleTap`, `longPress`, `openSelector`, `fill`, `clear`,
95
+ `scroll`) aceptan tres campos más, todos opcionales, para decir **qué fila** de una lista:
96
+
97
+ | Campo | Qué hace |
98
+ |---|---|
99
+ | `row` | Índice de fila **en la colección** (el mismo que publica `getRows` y el volcado) |
100
+ | `content` | Nombre del prop de tipo content, para desempatar cuando hay dos listas |
101
+ | `where` | Objeto `{campo: valor}`: la fila **por lo que contiene**, p.ej. `{"ID":"2938733"}` |
102
+
103
+ Sin ninguno de los tres, el nombre resuelve al control de pantalla de siempre. Se combinan:
104
+
105
+ - **solo `content`** → la fila `0` de esa lista (**fija**, no "la primera pintada": tras
106
+ desplazar, el mismo comando devolvería otra fila sin que nada lo dijera);
107
+ - **solo `where`** → la primera fila **pintada** que lo cumple;
108
+ - **`row` + `where`** → se exigen los DOS, o sea que `row` pasa de atajo a **comprobación**.
109
+
110
+ ```
111
+ $ curl … -d '{"command":"getText","name":"MAP_NAME","row":12,"content":"MAP_CONTENT"}'
112
+ $ curl … -d '{"command":"click","name":"MAP_BORRAR","where":{"ID":"2938733"}}'
113
+ ```
114
+
115
+ ### Las tres reglas que hay que saber antes de usarlo
116
+
117
+ 1. **Solo existen las filas PINTADAS.** El apuntado por fila baja a la celda, y una celda que el
118
+ reciclado no tiene montada no existe: `Element '<name> row N' not found`. Para leer las que no
119
+ están a la vista está `getRows`, que lee el modelo y no las vistas — o desplaza la lista con
120
+ `scroll` y vuelve a pedirla.
121
+ 2. **El índice NO se renumera con el scroll.** Es el de la colección, así que sigue valiendo
122
+ después de desplazar. Medido en FontIconsApp/LottieAnimation (13 filas): antes del scroll el
123
+ volcado publica `[0..7]`; después, `[6..12]`, y `row:12` —que antes no se podía pedir— pasa a
124
+ contestar su valor. La ventana se mueve; la numeración no.
125
+ 3. **`waitForElement` con `row` espera a que la FILA ESTÉ PINTADA**, no a que la colección tenga
126
+ datos. Es lo correcto —lo que estás esperando es poder tocarla— pero significa que una fila
127
+ que la lista nunca llega a montar agota el `timeout`, por muchos datos que haya cargado. La
128
+ única excepción, que no espera nunca, es un `where` que nombra un campo inexistente: eso no va
129
+ a aparecer esperando, así que se contesta el error del campo en vez de agotar el tope.
130
+
131
+ ### Qué contesta cuando no resuelve
132
+
133
+ | Respuesta | Qué pasó | Qué arregla |
134
+ |---|---|---|
135
+ | `Element '<name> row N' not found` | La fila no está pintada (o el índice se sale) | Desplazar la lista, o usar `getRows` |
136
+ | `Element '<name>' not found` | La fila SÍ está pintada y el que no está es el nombre; o no hay content con ese nombre; o se pidió por `where` sin `row` y ninguna casa | Corregir el nombre |
137
+ | `Element '<name>' is ambiguous, pass 'content' to choose between 'A', 'B'` | El nombre resuelve en DOS contents | Pasar `content` |
138
+ | `Field 'X' does not exist in collection 'Y'` | El `where` preguntó por un campo que la colección no tiene | Corregir el `where`, no la fila |
139
+
140
+ **(iOS)** El mensaje de ambigüedad lleva la lista **ordenada** de contents: los nombres salen de
141
+ un diccionario de Obj-C sin orden fijado, y sin ordenarlos el MISMO comando contestaba dos frases
142
+ distintas en dos llamadas seguidas. Si ninguno de los dos contents tiene nombre utilizable, se
143
+ dice el hecho **sin** la lista (`…resolves in more than one content, pass 'content' to choose
144
+ one`): mandar a elegir entre nada es peor que no decirlo.
145
+
146
+ ## Ver la pantalla
147
+
148
+ ### `getAllElements` — las dos plataformas
149
+
150
+ Devuelve la información de los controles visibles. `format` elige el formato: `"xone"` (default)
151
+ o, en Android, `"uiautomator"`; en iOS, `"uikit"` — y **`"uiautomator"` se acepta como alias de
152
+ `"uikit"`** para que un cliente de Android no rompa. **El alias vale para la llamada, no para el
153
+ contenido**: lee la [advertencia](#los-dos-volcados-no-son-compatibles-atributo-a-atributo) antes
154
+ de escribir nada que lea esos atributos.
155
+
156
+ **Salida:** `ResultMessage(string)`, con el payload dentro de `status`.
157
+ **Errores:** formato desconocido → Android dice
158
+ `"Unknown format '...', expected 'xone' or 'uiautomator'"`; **(iOS)** dice
159
+ `"Unknown format '<x>', expected 'xone', 'uikit' or 'uiautomator'"`, nombrando las tres formas
160
+ que acepta. `"App is not running"` **(iOS)** solo lo contesta el formato `xone`: el `uikit` vuelca
161
+ las ventanas de UIKit y no necesita que haya ninguna app XONE lanzada. Y si hay app pero su
162
+ pantalla es de otro camino de render, `"Screen '<clase>' is not readable by the 'xone' format"`.
163
+
164
+ `format: "xone"` — array JSON con un descriptor por control: `name`, `type`, `collectionName`,
165
+ `visible`, `enabled`, `bounds` y `text` si aplica. **(iOS)** además `propType`, `window`,
166
+ `created`, `floating` (en los frames) y, condicionales, `editDisabled` y `row`+`content` — ver
167
+ [los campos nuevos](#los-campos-nuevos-del-descriptor-xone-ios).
168
+
169
+ - **(iOS)** `bounds` va en **puntos** y en coordenadas de ventana, `"[x1,y1][x2,y2]"`; Android los
170
+ da en píxeles de pantalla.
171
+ - **(iOS)** el array trae `propType`, y `text` **solo aparece si el campo tiene valor**: un prop
172
+ con campo vacío no lleva la clave. Eso es útil — los elementos sin `text` son exactamente los
173
+ de campo vacío.
174
+ - **(iOS)** el **orden no es determinista** entre llamadas (viene de un diccionario). No compares
175
+ dos capturas por posición.
176
+ - **(iOS)** un prop declarado `visible="0"` en el XML **no sale**, porque el motor no llega a
177
+ crear el control; preguntarle por él da `not found`, no `visible:false`.
178
+ - **(iOS)** con un **diálogo modal delante**, el volcado `xone` contesta `[]`: lo de debajo está
179
+ tapado y publicarlo serviría para actuar sobre algo que una persona no podría tocar. Un `[]`
180
+ no significa "pantalla vacía"; pregunta `getCurrentScreen` para ver qué hay encima.
181
+
182
+ #### Los campos NUEVOS del descriptor `xone` **(iOS)**
183
+
184
+ | Campo | Qué dice | Cuándo sale |
185
+ |---|---|---|
186
+ | `window` | De qué ventana publicada sale el control | siempre |
187
+ | `created` | Si el control ha pasado por su construcción | siempre, y **siempre `true`** (ver divergencia 3) |
188
+ | `editDisabled` | El control **se escribe**, pero tiene la edición bloqueada | **solo cuando es `true`** |
189
+ | `row` | El índice de fila, el mismo que se pasa en `row` | solo en los controles de una fila |
190
+ | `content` | El nombre del prop de tipo content de esa fila | solo en los controles de una fila |
191
+
192
+ **`editDisabled` es el ÚNICO campo que distingue un campo bloqueado de uno escribible.**
193
+ `enabled` devuelve lo mismo en los dos que no se pueden escribir —un botón con `disableedit` y un
194
+ campo de texto con `locked` salen los dos con `enabled:false`—, y solo el segundo lleva
195
+ `editDisabled:true`. Medido en AliviaApp/Login:
196
+
197
+ | Control | Volcado | Qué contesta `fill` |
198
+ |---|---|---|
199
+ | `MAP_LOGIN_BTN` (`type="B"`, `disableedit`) | `enabled:false`, **sin** `editDisabled` | `Element 'MAP_LOGIN_BTN' is not editable` |
200
+ | `MAP_EMAIL` (`type="T"`, libre) | `enabled:true`, **sin** `editDisabled` | `result:true` |
201
+ | `MAP_BOTTOM_TEXT3` (`type="T"`, `locked`) | `enabled:false`, **con** `editDisabled:true` | `Element 'MAP_BOTTOM_TEXT3' has editing disabled` |
202
+
203
+ Léelo **en una sola dirección**: si `editDisabled` está, el `fill` se va a negar por eso. Su
204
+ **ausencia no promete** que el `fill` funcione — un memo o un campo de fecha salen sin él y aun
205
+ así los rechaza este canal (ver `.unsupportedField` en la divergencia 7).
206
+
207
+ Los controles de FILA **no lo traen nunca** (divergencia 4), y `created` no vale `false` jamás
208
+ (divergencia 3).
209
+
210
+ `format: "uiautomator"` (Android) — XML equivalente al de `uiautomator dump`, con los 17
211
+ atributos clásicos más `hint` y `displayed`, construido pidiendo a cada vista su
212
+ `AccessibilityNodeInfo`. Comprobado contra `adb shell uiautomator dump`: mismo número de nodos,
213
+ misma profundidad, mismos `resource-id`.
214
+
215
+ `format: "uikit"` (iOS) — XML con la jerarquía de vistas de las ventanas de la app: `index`,
216
+ `class`, `accessibility-id`, `label`, `text`, `enabled`, `visible`, `bounds`, y `xone-name` /
217
+ `xone-type` cuando la vista es un control XOne. **No se inventan** los atributos que iOS no
218
+ tiene (`checkable`, `password`, `long-clickable`…). Tiene topes (profundidad 80, 4000 nodos) y
219
+ si alguno salta lo dice con un `<truncated reason="..."/>`.
220
+
221
+ **Los dos formatos NO cubren la misma población, y eso es el punto.** Medido en `EntradaApp` de
222
+ AliviaApp (un carrusel de tres pestañas): `xone` devuelve 22 controles —todo lo que el motor
223
+ indexa, incluidas las páginas que no están montadas— y `uikit` trae los 8 que UIKit tiene
224
+ montados. La correspondencia es exacta: **los visibles del `xone` están todos en el `uikit`, y
225
+ ninguno de los no visibles**. La invariante que se comprueba es "todo control **visible** del
226
+ `xone` está en el `uikit`". Un control que sale en `xone` con `visible:false` y no sale en el
227
+ volcado no es una incoherencia: es la respuesta a "existe pero no está en pantalla".
228
+
229
+ #### Los dos volcados NO son compatibles atributo a atributo
230
+
231
+ **El `uikit` de iOS y el `uiautomator` de Android no llevan los mismos atributos.** iOS emite
232
+ **8** y Android **19**, con solo **5 comunes**:
233
+
234
+ | | Atributos |
235
+ |---|---|
236
+ | Comunes (5) | `index`, `class`, `text`, `enabled`, `bounds` |
237
+ | Solo Android (14) | `resource-id`, `package`, `content-desc`, `checkable`, `checked`, `clickable`, `focusable`, `focused`, `scrollable`, `long-clickable`, `password`, `selected`, `hint`, `displayed` |
238
+ | Solo iOS (3) | `accessibility-id`, `label`, `visible` |
239
+
240
+ Lo que esto rompe, en concreto:
241
+
242
+ - **iOS no tiene `resource-id`**, que es con lo que en Android se nombran las piezas de un
243
+ diálogo (`android:id/button1`…). Un guion que localice botones así **no tiene equivalente**:
244
+ en iOS se localizan por `class`, por `accessibility-id` o por `label`.
245
+ - **iOS no tiene `content-desc`** ni **ninguno de los booleanos** (`checkable`, `clickable`,
246
+ `scrollable`, `password`…). Preguntar "¿es scrollable?" por el volcado no se puede; lo que hay
247
+ es la respuesta de `scroll`, que dice si había ancestro y en qué eje.
248
+ - iOS añade `xone-name` / `xone-type` cuando la vista es un control XOne — eso sí es el puente
249
+ entre los dos formatos, y es por donde se cruza un volcado con el otro.
250
+
251
+ **En iOS el nombre canónico es `uikit`.** `uiautomator` se acepta como **alias** para que una
252
+ llamada multiplataforma no falle, pero **devuelve datos con forma de iOS**: pedir `uiautomator` a
253
+ un dispositivo iOS no te da el volcado de Android, te da el de iOS con otro nombre. Es la
254
+ situación **contraria** a la de `getCurrentScreen`, donde el campo conserva el nombre `activity`
255
+ porque el contenido sí cumple el mismo papel (divergencia 9).
256
+
257
+ ### `getCurrentScreen` — las dos plataformas
258
+
259
+ En qué pantalla estás, sin traerte el volcado entero. Es lo primero después de cada navegación.
260
+
261
+ **Entrada:** `command`, y nada más — es una pregunta por el estado, no por un control.
262
+ **Salida:** `ResultMessage(string)` con un objeto JSON dentro de `status`, **siempre con los
263
+ cinco campos** (vacíos si hace falta):
264
+
265
+ | Campo | Qué es |
266
+ |---|---|
267
+ | `app` | La app XONE cargada |
268
+ | `activity` | La pantalla al frente (**(iOS)** el nombre de CLASE del controlador: `EditViewController`, `CollectionViewController`…) |
269
+ | `collection` | La colección que esa pantalla edita o lista |
270
+ | `dialogs` | Los diálogos encima, de abajo arriba, cada uno con `name` y `modal`. **(iOS)** un **toast** sale aquí, y `activity`/`collection` pasan a ser las suyas: lectura e interacción responden por la misma pantalla |
271
+ | `window` | La ventana que se lleva los toques. **(iOS)** con un toast delante es la suya; las ventanas del **sistema** (el teclado, el *tracking* de UIKit) **no** se listan y siguen contadas dentro de `activity` — publicarlas volvía este campo no determinista entre apps |
272
+
273
+ ```
274
+ $ curl -sk -X POST $B -d '{"command":"getCurrentScreen"}'
275
+ {"result":true,"status":"{\"activity\":\"EditViewController\",\"app\":\"FontIconsApp\",
276
+ \"collection\":\"TestApp\",\"dialogs\":[{\"modal\":true,\"name\":\"MapCollController\"}],
277
+ \"window\":\"MapCollController\"}"}
278
+ ```
279
+
280
+ **Error:** `"App is not running"` — y se decide por el proyecto XONE cargado, **no** por que haya
281
+ pantalla: con app cargada y sin pantalla al frente contesta `result:true` con `activity` y
282
+ `collection` **vacíos**, que es otro hecho y lleva a otro sitio.
283
+
284
+ - **(iOS)** `activity` conserva el nombre del campo de Android aunque en iOS no haya actividades
285
+ (divergencia 9). Su VALOR es el nombre de la clase del `UIViewController`, y sirve para lo
286
+ mismo: saber en qué camino de render estás, o sea qué comandos van a funcionar.
287
+ - **(iOS)** `dialogs` **solo lista desde el modal de más arriba hacia arriba** (divergencia 8).
288
+
289
+ ### `getScreenshot` — las dos plataformas
290
+
291
+ **Entrada:** `command`; **(iOS)** `format` opcional, `jpeg` (default) o `png`.
292
+
293
+ Android: `ResultMessage(base64WebP)` → base64 de un WebP con pérdida al 50%.
294
+ **(iOS):** `{result:true, status:"<base64>", imageFormat:"jpeg"|"png"}` — **iOS no puede
295
+ codificar WebP** (`ImageIO` lo decodifica pero no lo escribe). Y la captura **no incluye** cámara
296
+ en vivo, capas Metal/GL, teselas de mapa ni la barra de estado del sistema.
297
+
298
+ Sirve para **enseñar** un estado a una persona. No es una prueba de que algo funcione.
299
+
300
+ ### `getText` — las dos plataformas
301
+
302
+ **Entrada:** `command`, `name`. **Salida:** `ResultMessage(texto)`.
303
+ **Error:** `"Element '<name>' not found"`.
304
+
305
+ - **(iOS)** añade `source`: `"field"` cuando el valor sale del campo de datos del control,
306
+ `"view"` cuando el control no tiene campo y se cae al texto visible. Un control **con** campo
307
+ siempre responde `source:"field"`, aunque el valor esté vacío — así se distingue "campo vacío"
308
+ de "control sin campo".
309
+ - **(iOS)** sobre un contenedor (`frame`) devuelve el primer texto visible que encuentra dentro,
310
+ con `source:"view"`.
311
+ - **(iOS)** dentro de una fila (`row`/`content`/`where`) devuelve el **campo**, no lo pintado, con
312
+ `source:"field"` — igual que Android.
313
+
314
+ ### `getUiAttribute` — las dos plataformas
315
+
316
+ Lee un atributo del **nodo XML de definición**, no el valor en runtime.
317
+
318
+ **Entrada:** `command`, `collectionName`, `nodeType` (p.ej. `"prop"`), `name`, `attributeName`.
319
+ **Salida:** `ResultMessage(valor)`; si el atributo no existe, `status` es `""` con `result:true`.
320
+
321
+ - **(iOS)** los dos caminos **no son equivalentes**. Con `nodeType` distinto de `prop` se lee el
322
+ atributo **literal** del XML. Con `nodeType="prop"` se pasa por `FieldPropertyValue`, que aplica
323
+ las cascadas del motor (overrides de plataforma, reglas CSS de clase, herencia de la colección
324
+ padre) **y el fallback de título del bug F11113001**: pedir el `title` de un prop que no lo
325
+ tiene devuelve el **nombre del prop**. Es lo que ve el motor al pintar, no el texto del fichero.
326
+
327
+ ### `isVisible` / `isEnabled` — las dos plataformas
328
+
329
+ **Entrada:** `command`, `name`. **Salida:** `ResultMessage("true")` o `ResultMessage("false")` —
330
+ el booleano viaja como **string** en `status`.
331
+
332
+ - **(iOS)** `isVisible` mira el `isHidden`/`alpha` del control **y de todos sus ancestros**, que
333
+ esté en una ventana, y la intersección real con el scroll contenedor.
334
+ - **(iOS)** `isEnabled` combina `isUserInteractionEnabled` con los flags de XOne (`readonly`,
335
+ `locked`, `disableedit`), que son los que de verdad deciden si el motor deja tocar el control.
336
+ - **(iOS)** un prop `visible="0"` responde `not found`, no `"false"`.
337
+ - **(iOS)** sobre una fila que no está pintada responden `"Element '<name> row N' not found"`, **no
338
+ `"false"`**: "no lo veo porque está fuera de la ventana" no es lo mismo que "está y no se ve", y
339
+ lo primero se arregla desplazando la lista.
340
+
341
+ ### `waitForElement` — las dos plataformas
342
+
343
+ **Entrada:** `command`, `name`, `timeout` en ms (default `10000`).
344
+ **Salida:** `ResultMessage()` cuando el elemento se vuelve visible.
345
+ **Error:** `"Timeout waiting for element '<name>'"`. Polling cada 250 ms en las dos.
346
+
347
+ - **(iOS)** acepta `row`/`content`/`where`, y entonces espera a que **la fila esté pintada**
348
+ (regla 3 de [Apuntar a una fila](#apuntar-a-una-fila)). No es una espera a que la colección
349
+ cargue.
350
+
351
+ ### `getRows` — las dos plataformas
352
+
353
+ Las filas de un content: **todas las que tiene, estén o no pintadas**. Es lo contrario del
354
+ volcado del árbol —que solo describe lo que hay montado como vista— y por eso existe: comprobar
355
+ el contenido de una lista era lo único del bloque que obligaba a escribir un guion a mano.
356
+
357
+ **Entrada:** `content` (obligatorio), `fields` (obligatorio), `limit` (default `100`, negativo =
358
+ todas), `offset` (default `0`).
359
+
360
+ `fields` admite las tres formas: array (`["A","B"]`), valor suelto (`"A"`) y lista separada por
361
+ comas (`"A,B"`). Se pide **explícitamente** porque leer un campo puede evaluar una fórmula: no
362
+ hay "todos los campos".
363
+
364
+ **Salida:** `ResultMessage(string)` con un objeto JSON dentro de `status`, **siempre con los
365
+ cinco campos**: `content`, `collection`, `count`, `offset`, `rows`. Cada fila lleva su `row` (el
366
+ índice de la colección, el mismo que aceptan los doce comandos) y el valor de cada campo pedido.
367
+
368
+ ```
369
+ $ curl … -d '{"command":"getRows","content":"MAP_CONTENT","fields":["MAP_NAME"],"limit":5}'
370
+ {"result":true,"status":"{\"collection\":\"LottieFiles\",\"content\":\"MAP_CONTENT\",\"count\":13,
371
+ \"offset\":0,\"rows\":[{\"MAP_NAME\":\"scan qr code success\",\"row\":0}, …]}"}
372
+ ```
373
+
374
+ **`count` es el de la colección ENTERA**, no el de lo devuelto ni el de lo pintado. Las tres
375
+ cifras son distintas y esa es la gracia: en FontIconsApp/LottieAnimation el volcado publicaba 8
376
+ filas pintadas, `count` decía 13 y esa llamada devolvió 5.
377
+
378
+ Un campo **sin valor** sale como `null` **con su clave puesta**; un campo que **no existe** es un
379
+ error con su nombre y su colección. No se confunden.
380
+
381
+ **Errores:** `"Missing parameter 'content'"` · `"Missing parameter 'fields'"` (también con
382
+ `fields: []`) · `"Element '<name>' not found"` (no hay nada con ese nombre) ·
383
+ `"Element '<name>' is not a content, it has no 'contents' attribute"` (el nombre es de otra cosa)
384
+ · `"Element '<name>' has no data yet"` (el content existe pero aún no ha montado su lista —
385
+ espera, no corrijas el comando) · `"Content '<name>' is ambiguous: more than one content on this
386
+ screen carries that name"` **(iOS)** · `"Field 'X' does not exist in collection 'Y'"` ·
387
+ `"App is not running"`.
388
+
389
+ - **(iOS)** el `offset` que devuelve es el **ACOTADO, no el pedido**: sobre 13 filas,
390
+ `offset:900` contesta `offset:13` y `rows:[]` (y `offset:-5` contesta `offset:0`). Pedir una
391
+ página que no existe es una pregunta legítima, y la respuesta dice exactamente lo que pasó —
392
+ pero **no confirmes un offset comparándolo con el que mandaste**: no es el mismo número.
393
+ - **(iOS)** `limit:-1` sobre una colección grande puede **tardar**, y si pasa de los 5 s de tope
394
+ del hilo principal contesta `"UI is busy"` **mientras el trabajo sigue corriendo** (el bloque ya
395
+ empezado termina). Reintentar en ese momento encola una segunda lectura sobre un main ocupado:
396
+ espera, o pagina con `limit`/`offset`.
397
+ - **(iOS)** los nombres de CAMPO distinguen mayúsculas (`MAP_IMAGE` ≠ `map_image`), aunque los
398
+ de CONTROL no. El motor los compara con `strcmp`.
399
+
400
+ ---
401
+
402
+ ## Tocar
403
+
404
+ En Android estos comandos **inyectan eventos reales** (`MotionEvent`, `KeyEvent`) o llaman a
405
+ `performClick()`/`performLongClick()`. **En iOS no se puede inyectar nada** sin API privada, así
406
+ que se invoca el manejador del propio control.
407
+
408
+ Todos menos `pressKey` piden `name` y aceptan `row`/`content`/`where`
409
+ ([Apuntar a una fila](#apuntar-a-una-fila)). Sin `name`: `"Missing parameter 'name'"`. Las
410
+ consecuencias, todas medidas:
411
+
412
+ | Comando | Entrada | Nota (iOS) |
413
+ |---|---|---|
414
+ | `click` | `name` | Invoca la acción del control |
415
+ | `tap` | `name` | **Colapsa en `click`.** Sobre un control que existe pero está deshabilitado **se niega** (Android inyectaría el toque y diría éxito) |
416
+ | `doubleTap` | `name` | **Siempre** `"Element '<name>' has no double tap gesture"`: el runtime iOS no tiene ese evento XONE. No se simulan dos `click` |
417
+ | `longPress` | `name` | **Siempre** `"Element '<name>' has no long click handler"`: solo las filas de lista escuchan pulsación larga, un prop o un frame no |
418
+ | `openSelector` | `name` | **Abre el desplegable** de un control (`mapcol`/`linkedto`, o el selector de fecha/hora). **ABRE, no elige**: lo que salga después se recorre con `getCurrentScreen`, `getAllElements` y `click` con `row` |
419
+ | `fill` | `name`, `value` (default `""`) | Escribe por el camino real de commit del campo, así que dispara los mismos eventos XONE que una pulsación |
420
+ | `clear` | `name` | Igual que `fill` con valor vacío |
421
+ | `scroll` | `name`, `direction` (default `down`), `amount` px (default 300) | Mueve de forma **síncrona** (sin animación, para que un `getAllElements` inmediatamente después ya vea el resultado) el `UIScrollView` **ancestro que pueda moverse en el eje pedido**, con clamp. Si el eje sí desborda pero ya estás en el límite, responde `true` sin mover: el contenido no puede ir más allá, igual que un swipe en Android |
422
+ | `pressKey` | `keyCode` (int) o **(iOS)** `key` simbólico (`back`, `enter`, `done`) | Solo `4`/`back` (volver atrás) y `66`/`enter` (confirmar el campo con foco); otro código → `"Key <n> has no iOS equivalent"`. Sin `keyCode` válido, `"Argument 'keyCode' is missing or invalid"`. En la pantalla **raíz** el atrás se niega con `"Cannot go back: '<pantalla>' is the root screen"` en vez de fingir, y con una acción en curso contesta `"UI is busy"` |
423
+
424
+ Que `click` responda `result:true` significa **que se invocó el manejador**, no que hubiera un
425
+ efecto visible: la lógica del control puede decidir no hacer nada, igual que con un toque real.
426
+ Comprueba el efecto con un dato.
427
+
428
+ ### El catálogo de errores, desdoblado **(iOS)**
429
+
430
+ Un control **ausente** ya NO se confunde con uno **intocable**. Antes había tres mensajes
431
+ fundidos (`not found or not enabled`, `not found or not editable`, `not found or not clearable`)
432
+ y el cliente no podía saber si corregir el nombre o rellenar antes la pantalla. Ahora cada
433
+ mensaje dice UN hecho:
434
+
435
+ | Mensaje | Qué pasó | Qué arregla |
436
+ |---|---|---|
437
+ | `Element 'X' not found` | No existe en ninguna pantalla alcanzable | Corregir el nombre, o navegar |
438
+ | `Element 'X row N' not found` | El control existe, la FILA no está pintada | Desplazar la lista, o `getRows` |
439
+ | `Element 'X' is not enabled` | Existe y está deshabilitado | Rellenar antes la pantalla / esperar |
440
+ | `Element 'X' has editing disabled` | Es de los que se escriben, pero bloqueado (`locked`/`readonly`/`disableedit`) | Desbloquear en la app: escribirlo desde aquí fabricaría un defecto que el usuario no puede provocar |
441
+ | `Element 'X' is not editable` | Existe pero NO es de los que se escriben (botón, imagen, content) | Apuntar a otro control |
442
+ | `Element 'X' is not clearable` | Lo mismo, pedido por `clear` | Apuntar a otro control |
443
+ | `Element 'X' is editable, but this channel cannot write a multiline\|date/time field` | El usuario SÍ puede editarlo; el que no puede es **este canal** | Nada por tu parte: es un límite del canal (divergencia 7) |
444
+ | `Element 'X' has no click handler` | Existe y está habilitado, pero nadie escucha su click | Apuntar al control que sí tiene manejador |
445
+ | `Element 'X' has no long click handler` | `longPress` sobre un prop o un frame | — |
446
+ | `Element 'X' has no double tap gesture` | `doubleTap`, siempre | — |
447
+ | `Element 'X' has no selector to open` | `openSelector` sobre algo sin desplegable | Apuntar al prop que lo tiene |
448
+ | `Element 'X' has its selector button disabled` | El control está bien y el que no deja es el BOTÓN. Caso medido: el desplegable ya está abierto, y el runtime apaga su propio botón mientras dura | Cerrar lo abierto, o esperar |
449
+ | `Element 'X' is not inside a scrollable view` | `scroll` sin ningún ancestro desplazable | — |
450
+ | `Element 'X' has no scrollable ancestor on the vertical\|horizontal axis` | Hay scroller, pero no se mueve en ESE eje (el caso típico: un carrusel horizontal y le pides vertical) | Pedir el otro eje |
451
+ | `Element 'X' is ambiguous, pass 'content' to choose between 'A', 'B'` | El nombre resuelve en dos contents | Pasar `content` |
452
+ | `Field 'F' does not exist in collection 'C'` | El `where` (o los `fields` de `getRows`) nombran un campo que no está | Corregir el campo, no la fila |
453
+
454
+ `openSelector` contesta además `"Element 'X' has editing disabled"` cuando el control entero está
455
+ bloqueado: es el MISMO hecho que en `fill`, así que lleva el mismo mensaje.
456
+
457
+ ### En qué ORDEN se busca un control **(iOS)**
458
+
459
+ La interacción busca **primero lo que está ENCIMA** de la pantalla actual —diálogos, toasts,
460
+ popovers—, de arriba abajo, y **la pantalla al final**. Un diálogo tapa la pantalla: si publica
461
+ un control con el mismo nombre, es el suyo el que recibiría el toque de una persona.
462
+
463
+ - **Alcanzar un control tapado nombrándolo sigue permitido**: la pantalla no se quita de la
464
+ lista, se pone la última. Lo que cambió es el orden, no lo alcanzable.
465
+ - **Los niveles ANTERIORES de navegación NO se buscan.** XONE navega *presentando*, así que la
466
+ pila de presentación es la pila de navegación entera: buscar en ella haría que un `fill` sobre
467
+ una pantalla que ya dejaste atrás contestara `result:true`. Un `not found` sobre un control de
468
+ la pantalla anterior es la señal de que **todavía no has navegado** — no un fallo del canal.
469
+ - **La LECTURA responde por lo mismo que el toque.** `getAllElements` y `getCurrentScreen` usan la
470
+ misma noción de "lo que está encima" que la búsqueda, así que con un **toast** delante el volcado
471
+ lista los props del toast, `collection` es la suya, y el toast sale en `dialogs` y en `window`.
472
+ Importa porque durante un tiempo no fue así: el toque caía en el toast y el volcado describía la
473
+ pantalla de debajo, de modo que no había forma de enumerar el control sobre el que estabas
474
+ actuando. Un toast **no se presenta** —se monta como raíz de otra ventana—, y esa es la razón por
475
+ la que las dos mitades llegaron a divergir.
476
+ - **Queda una asimetría, y el canal la dice en vez de fingirla:** con una **lista de menú** delante,
477
+ la lectura contesta por ella (`activity: CollectionViewController`) y el volcado `xone` responde
478
+ `Screen '...' is not readable by the 'xone' format`, mientras la interacción sigue buscando en la
479
+ pantalla de debajo. No hay forma de buscar dentro de ese controlador; preferimos decirlo.
480
+
481
+ ---
482
+
483
+ ## Ciclo de vida de la app
484
+
485
+ ### `launchApplication` — las dos plataformas
486
+ Lanza una app XOne por nombre. En Android limpia la caché de imágenes antes de lanzar.
487
+ **(iOS)** es un comando **legado**: solo por WebSocket, no por `POST /command`.
488
+
489
+ ### `exitApplication` — solo Android
490
+ ### `directdownload` — las dos plataformas
491
+ Descarga un proyecto desde el cloud. Ver el detalle del flujo y del formato de respuesta del
492
+ servidor cloud en la documentación del framework.
493
+
494
+ ### `status` / `state` — las dos plataformas
495
+ Estado de la conexión y del despliegue.
496
+
497
+ ---
498
+
499
+ ## Colecciones y atributos
500
+
501
+ ### `loadCollection` — las dos plataformas
502
+ Carga o recarga una colección XML en caliente. **Entrada:** `xmlNode` (XML en **Base64**),
503
+ `encoding` (default `ISO-8859-15`). **Error:** `ResultMessage(exception)` si el XML es inválido;
504
+ `"App is not running"` si la app no está iniciada.
505
+
506
+ ### `setAttribute` — las dos plataformas
507
+ **Entrada:** `collectionName`, `nodeType`, `name`, `attributeName`, `attributeValue`. Provoca
508
+ relayout o refresh según el atributo. **(iOS)** también es legado: solo WebSocket.
509
+
510
+ ### `deleteAttribute` — las dos plataformas
511
+ **Entrada:** `collectionName`, `nodeType`, `name`, `attributeName`.
512
+
513
+ ### `loadIncludeFile` — solo Android
514
+ `file`, `language`, `encoding` (default UTF-8), `delayCompilation`, `compile`.
515
+ **Error:** `"Cannot load include file <nombre>"`.
516
+
517
+ ### `replaceProperty` — solo Android
518
+ `attributeNames[]` + `attributeValues[]` en el mismo orden.
519
+
520
+ ### `setCssAttribute` / `deleteCssAttribute` — solo Android
521
+ `fileName`, `className`, `attributeName` (+ `attributeValue` en el set).
522
+
523
+ ### `refresh` / `relayout` — solo Android
524
+ En iOS las ramas existen en el dispatcher pero están **vacías**: no hacen nada.
525
+
526
+ ---
527
+
528
+ ## Ficheros
529
+
530
+ ### `listAppFiles` — las dos plataformas
531
+ **Entrada:** `appName`. **Salida:** `ListDirectoryMessage` con `paths`.
532
+
533
+ ### `uploadFile` — solo Android
534
+ `destinationPath` (relativa al directorio `data/` de la app), `fileData` en **Base64**.
535
+
536
+ ### `getFileChecksum` — solo Android
537
+ `appName`, `files[]`, `checksumType` (default `crc32`). Ojo: en su respuesta, `result` es un
538
+ **objeto** `{fichero: checksum}`, no un booleano.
539
+
540
+ ---
541
+
542
+ ## Scripts y base de datos — solo Android
543
+
544
+ - **`runScript`**: `scriptLanguage`, `scriptText` en Base64. Devuelve el valor de retorno como
545
+ string, o `ResultMessage(exception)`. Errores: `"No edit view is visible"`,
546
+ `"No data object found..."`.
547
+ - **`runSql`**: `appName`, `sql`. Devuelve `SqlMessage` con `data` como array de filas.
548
+ - **`getDatabaseTableNames`**: `appName`. Ejecuta `SELECT name FROM sqlite_master WHERE type='table'`.
549
+ - **`queryDatabaseTable`**: `appName`, `tableName`. Ejecuta `SELECT * FROM <tableName>`.
550
+
551
+ En iOS no existen. Nota adicional: el `.xcframework` de SQLCipher del repo solo trae slice
552
+ `x86_64` para simulador, así que una app con base de datos cifrada falla en tiempo de ejecución
553
+ en simulador aunque estos comandos existieran.
554
+
555
+ ---
556
+
557
+ ## Logs — solo Android
558
+
559
+ - **`getLog`**: volcado completo (logcat + info básica) en `FullLogMessage`.
560
+ - **`enableDeviceLogging`** / **`disableDeviceLogging`**: push de cada línea del logger interno
561
+ de XOne como `LoggerMessage`.
562
+ - **`setLogEnabled`** / **`setLogDisabled`**: streaming continuo del logcat del sistema (un hilo
563
+ lo lee cada 3 s y envía las líneas nuevas).
564
+
565
+ Déjalos desactivados al acabar.
566
+
567
+ ---
568
+
569
+ ## Edición en vivo — solo Android
570
+
571
+ `enableLiveEditionMode` / `disableLiveEditionMode`: arrastrar y soltar controles para cambiar
572
+ `lmargin`, `tmargin` y `frame` en caliente; al soltar, envía un `UpdateNodeMessage` push con
573
+ `{ command: "updateNode", collectionName, nodeType, name, attributeNames[], attributeValues[] }`.
574
+
575
+ ---
576
+
577
+ ## Mock de hardware — solo Android
578
+
579
+ `setMockData` alimenta al framework con datos simulados de GPS, NFC y cámara sin hardware real;
580
+ todos los campos son opcionales (`latitude`, `longitude`, `altitude`, `accuracy`, `bearing`,
581
+ `speed`, `date`, `tagId`, `payload`, `imageData` en Base64). Varios tipos pueden estar activos a
582
+ la vez y cada subsistema consume solo los campos que le corresponden. `clearMockData` los quita
583
+ y el framework vuelve al hardware real: **hazlo al acabar**.
584
+
585
+ ---
586
+
587
+ ## Depurador JavaScript — solo Android
588
+
589
+ `debug` es un contenedor cuyo campo `action` elige el subcomando: `init`, `end`, `breakpoints`,
590
+ `break`, `go`, `stepInto`, `stepOver`, `stepOut`, `expand`, `eval`, `inspect`, `addBreakpoint`,
591
+ `removeBreakpoint`. Devuelve `DebugMessage`; los mensajes de pausa llegan asíncronos por el
592
+ listener que registra `init`.
593
+
594
+ **Por qué no está en iOS:** el motor de scripting iOS es **JavaScriptCore**, que no expone API
595
+ pública de depuración — no hay dónde colgar un breakpoint. Las salidas serían instrumentar el
596
+ fuente antes de evaluarlo o hablar el protocolo del Web Inspector, y ninguna se ha hecho.
597
+
598
+ ---
599
+
600
+ ## Mensajes que solo existen en iOS
601
+
602
+ Los mensajes propios de iOS, reunidos aquí porque un cliente de Android no los espera:
603
+
604
+ | Mensaje | Cuándo |
605
+ |---|---|
606
+ | `"UI is busy"` | El hilo principal no atendió el comando dentro del tope de 5 s. **El trabajo puede seguir corriendo**: un bloque que ya había empezado termina aunque tú ya tengas la respuesta |
607
+ | `"Element '<name>' is not inside a scrollable view"` | `scroll` sobre algo sin ningún ancestro scrollable |
608
+ | `"Element '<name>' has no scrollable ancestor on the <eje> axis"` | Hay scroller encima, pero no puede moverse en el eje pedido |
609
+ | `"Key <n> has no iOS equivalent"` | `pressKey` con un código que no es 4 ni 66 |
610
+ | `"Element '<name>' has no double tap gesture"` | `doubleTap`, siempre |
611
+ | `"Cannot go back: '<pantalla>' is the root screen"` | `pressKey 4` en la pantalla raíz |
612
+ | `"No field has the focus"` | `pressKey 66` sin ningún campo con el foco |
613
+ | `"The focused field refused to release the focus"` | `pressKey 66` con un campo que no suelta el foco (un delegado que valida y rechaza): el commit **no** ha pasado |
614
+ | `"Element '<name>' is editable, but this channel cannot write a <clase> field"` | `fill`/`clear` sobre un memo multilínea o un campo de fecha/hora |
615
+ | `"Element '<name>' has no selector to open"` / `"... has its selector button disabled"` | `openSelector` |
616
+ | `"Element '<name>' is ambiguous, pass 'content' to choose between 'A', 'B'"` | El nombre resuelve en dos contents **de nombres distintos**. El mensaje trae los dos, entrecomillados y en orden estable, para copiarlos al comando siguiente |
617
+ | `"Element '<name>' is ambiguous: the same name resolves in more than one content, and 'content' cannot choose between them"` | Los dos contents **se llaman igual**, así que pasar `content` no desempata. Existe para no mandarte a hacer lo que ya hiciste |
618
+ | `"Content '<name>' is ambiguous: more than one content on this screen carries that name"` | Lo mismo visto desde `getRows`, que nombra el content directamente en vez de un control de dentro |
619
+ | `"Element '<name>' has no data yet"` | `getRows` sobre un content que todavía no ha montado su lista |
620
+ | `"Screen '<clase>' is not readable by the 'xone' format"` | Hay app corriendo, pero su pantalla es de un camino de render que el formato `xone` no lee. **No confundir con `"App is not running"`** |
621
+ | `"Missing parameter '<x>'"` | Falta un parámetro que se nombra: `name` —los doce comandos **y `getUiAttribute`**—, y `content` y `fields` de `getRows` |
622
+ | `"Argument '<x>' is missing or invalid"` | El resto: `keyCode`, un `where` mal formado, y `collectionName` / `nodeType` / `attributeName` de `getUiAttribute`. **Su `name` no**: ese sale por `Missing parameter`, como en todos los demás — es propiedad del parámetro, no del comando que lo pide |
623
+ | `"Unknown command '<x>'"` y `"Invalid JSON"` | Android no documenta respuesta para ninguno de los dos; iOS contesta, porque un comando sin respuesta deja al cliente esperando |
624
+ | `"Cannot serve a hotswap command from the main thread"` | El comando llegó EN el hilo principal: servirlo ahí sería esperarse a uno mismo |
625
+ | `"Field '<f>' does not exist in the collection"` | Variante **sin nombrar la colección** de `Field '<f>' does not exist in collection '<c>'`, para cuando no se puede saber cuál es: un `collection ''` mandaría a buscar una colección que no existe, y eso es peor que no decir cuál. Sale de una rama **viva** |
626
+ | `"Screen descriptor is not serializable"` / `"Rows payload is not serializable"` | `getCurrentScreen` / `getRows`. **Hoy son inalcanzables** —lo que compone esos payloads son String, Int y Bool— y existen para que esos caminos no tengan la opción de contestar `result:true` con un objeto vacío el día que dejen de serlo |
627
+
628
+ **Sobre `ResultMessage(exception)` en iOS:** los puntos de entrada están envueltos en
629
+ `@try/@catch` que emite esa forma, pero el `catch` envuelve una llamada que por dentro cruza una
630
+ frontera de GCD, y Apple documenta lanzar excepciones Obj-C a través de esa frontera como no
631
+ soportado. Es decir: **la red existe, pero puede no cubrir la clase de excepción para la que se
632
+ puso**. Pendiente de comprobar en un build aislado; hasta entonces, no des por hecho que una
633
+ excepción dentro de un comando te va a llegar como JSON.
634
+
635
+ ---
636
+
637
+ ## Divergencias con Android
638
+
639
+ Las once que hay que saber antes de escribir nada multiplataforma. Ninguna es una carencia que
640
+ se vaya a "arreglar": son consecuencias de la plataforma, y por eso están declaradas.
641
+
642
+ 1. **Los volcados no son compatibles atributo a atributo.** 8 atributos en iOS, 19 en Android,
643
+ 5 comunes. Sin `resource-id`, sin `content-desc`, sin booleanos.
644
+ [Detalle](#los-dos-volcados-no-son-compatibles-atributo-a-atributo).
645
+ 2. **`has no click handler` va SIN la coletilla `use 'tap' to send a real touch`** con que
646
+ Android remata el suyo. En iOS `click` y `tap` caen en la **misma función**, así que ese
647
+ consejo manda a repetir la llamada que acaba de fallar: un callejón sin salida.
648
+ 3. **`created` es SIEMPRE `true` en iOS.** No existe el objeto "declarado y sin construir": el
649
+ motor descarta los props sin bit visible **antes** de crear su vista, y lo que crea lo crea
650
+ entero antes de indexarlo (verificado en el código de iOS). **No filtres por `created` en
651
+ iOS**: no descarta nada. *Que en Android `created:false` sí sea alcanzable —porque su montador
652
+ de frames deja una vista con el nombre puesto— es lo que se dedujo al portar el campo; **no
653
+ está verificado contra el código de Android**, así que no lo uses para ramificar sin
654
+ comprobarlo allí.*
655
+ 4. **`editDisabled` NO se emite en las filas.** Dentro de una fila no hay ningún
656
+ `EditPropertyControl`, así que `fill` rechaza por otra vía (`is not editable`): publicar el
657
+ campo ahí anunciaría un bloqueo distinto del que el canal va a alegar. *Se anotó como
658
+ divergencia porque se dio por hecho que Android sí lo emite en las filas de una pantalla de
659
+ lista, pero eso **no está en ninguna spec ni verificado contra su código**. Lo firme es el lado
660
+ iOS: en una fila el campo no sale nunca. Si tu código multiplataforma lee `editDisabled` dentro
661
+ de una fila, comprueba primero que Android lo ponga — en iOS esa rama está muerta.*
662
+ 5. **`Content '<c>' does not have the collection '<n>'`** —error de `getRows` en el contrato de
663
+ Android— **es INALCANZABLE en iOS.** Un `XoneTableContent` pinta **una sola** colección, así
664
+ que no hay elección que el cliente pueda equivocar: el `collection` que devuelve el payload es
665
+ informativo, no un parámetro. Si tu código ramifica por ese mensaje, en iOS esa rama está
666
+ muerta.
667
+ 6. **`getRows`:** el `offset` que devuelve es el **acotado, no el pedido** (`offset:900` sobre 13
668
+ filas contesta `offset:13`); y `limit:-1` puede tardar y contestar `"UI is busy"` **mientras
669
+ el trabajo sigue corriendo**.
670
+ 7. **`.unsupportedField`:** un memo multilínea o un campo de fecha/hora contestan
671
+ `Element 'X' is editable, but this channel cannot write a <clase> field`. **El usuario SÍ
672
+ puede editarlos**; lo que no puede es este canal — el commit de un memo es asíncrono y no
673
+ tiene versión síncrona pública, y el `onSetValue:` de un campo de fecha espera un `NSDate`.
674
+ Se dice así, y no `is not editable`, porque lo segundo mandaría a cambiar de control y el
675
+ control era el correcto.
676
+ 8. **`dialogs` solo lista desde el modal de más arriba hacia arriba.** Consecuencia del corte
677
+ modal: lo que queda debajo de un modal no se publica porque un toque no lo alcanzaría. Con
678
+ dos diálogos encima, uno modal y otro no, solo sale el tramo alcanzable. Una pila entera de
679
+ diálogos **no** modales sí sale completa.
680
+ 9. **`getCurrentScreen` conserva el nombre de campo `activity`** aunque en iOS no haya
681
+ actividades: el valor —el nombre de clase del controlador— cumple el mismo papel, y
682
+ renombrarlo rompería el payload entre plataformas sin aportar nada. Es la situación
683
+ **contraria** a la del formato del volcado, donde el contenido difiere de verdad y por eso el
684
+ nombre difiere (`uikit`, no `uiautomator`).
685
+ 10. **Las tres de siempre:** `getScreenshot` es **JPEG/PNG** (iOS no codifica WebP); `pressKey`
686
+ solo mapea `back`(4) y `enter`(66); `doubleTap` y `longPress` **no tienen evento XONE** en
687
+ props/frames, así que se niegan siempre.
688
+ 11. **Orden de búsqueda:** primero lo que está **encima** (diálogos, toasts), la pantalla al
689
+ final, y los niveles **anteriores** de navegación **fuera**.
690
+ [Detalle](#en-qué-orden-se-busca-un-control-ios).
691
+
692
+ ---
693
+
694
+ ## Dos avisos operativos sobre el host iOS
695
+
696
+ Ninguno de los dos es del hotswap, pero los dos te los vas a encontrar:
697
+
698
+ - **`MyAllXOne` crashea al arrancar** en el host `XOneStudioSwift` (GoogleMaps,
699
+ `preLaunchServices`: el host no trae los recursos de GoogleMaps). Para probar **filas**, el
700
+ fixture usable es **`FontIconsApp`** (3 contents; `LottieAnimation` tiene 13
701
+ filas sobre la colección `LottieFiles`).
702
+ - **El host se cae al abrir una lista de menú.** A `XOneStudioSwift.app` le faltan los
703
+ `xone_img_*.png`, y `CollectionViewController`/`SortView` meten esas `UIImage` nil en arrays.
704
+ Es un defecto **ajeno** al hotswap y anterior a él: si tu recorrido pasa por un menú, la app se
705
+ va a caer. El síntoma es **conexión rechazada en el puerto**, no un `result:false` — no lo
706
+ diagnostiques como un fallo del canal.