xonecode 0.3.0 → 0.5.1
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.
- package/README.md +50 -8
- package/THIRD_PARTY_NOTICES.md +160 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/apps/web/dist/assets/c-BIGW1oBm.js +1 -0
- package/apps/web/dist/assets/cpp-DddhVWhK.js +1 -0
- package/apps/web/dist/assets/csharp-DSvCPggb.js +1 -0
- package/apps/web/dist/assets/css-CLj8gQPS.js +1 -0
- package/apps/web/dist/assets/go-C27-OAKa.js +1 -0
- package/apps/web/dist/assets/html-CfGypltT.js +1 -0
- package/apps/web/dist/assets/index-BB3SamhR.css +1 -0
- package/apps/web/dist/assets/index-CFYAoHdw.js +477 -0
- package/apps/web/dist/assets/ini-BEwlwnbL.js +1 -0
- package/apps/web/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/apps/web/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/apps/web/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/apps/web/dist/assets/java-CylS5w8V.js +1 -0
- package/apps/web/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/apps/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/apps/web/dist/assets/less-B1dDrJ26.js +1 -0
- package/apps/web/dist/assets/lua-BaeVxFsk.js +1 -0
- package/apps/web/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/apps/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/apps/web/dist/assets/php-D_2--9WK.js +1 -0
- package/apps/web/dist/assets/python-B6aJPvgy.js +1 -0
- package/apps/web/dist/assets/ruby-DSHSDbk1.js +1 -0
- package/apps/web/dist/assets/rust-B1yitclQ.js +1 -0
- package/apps/web/dist/assets/scss-D5BDwBP9.js +1 -0
- package/apps/web/dist/assets/sql-CRqJ_cUM.js +1 -0
- package/apps/web/dist/assets/swift-C2oV4EkX.js +1 -0
- package/apps/web/dist/assets/toml-vGWfd6FD.js +1 -0
- package/apps/web/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/apps/web/dist/assets/yaml-Buea-lGh.js +1 -0
- package/apps/web/dist/iconos/xonecode.png +0 -0
- package/apps/web/dist/index.html +20 -0
- package/dist/agent/agentesEnDisco.js +539 -0
- package/dist/agent/agentesEnDisco.js.map +1 -0
- package/dist/agent/arbolDeProyecto.js +245 -0
- package/dist/agent/arbolDeProyecto.js.map +1 -0
- package/dist/agent/artefactosEnDisco.js +139 -0
- package/dist/agent/artefactosEnDisco.js.map +1 -0
- package/dist/agent/aumentador.js +175 -0
- package/dist/agent/aumentador.js.map +1 -0
- package/dist/agent/authEnDisco.js +76 -1
- package/dist/agent/authEnDisco.js.map +1 -1
- package/dist/agent/busquedaRegex.js +92 -0
- package/dist/agent/busquedaRegex.js.map +1 -0
- package/dist/agent/catalogoModelos.js +345 -0
- package/dist/agent/catalogoModelos.js.map +1 -0
- package/dist/agent/checkpointer.js +127 -0
- package/dist/agent/checkpointer.js.map +1 -0
- package/dist/agent/cloudstudioClient.js +194 -0
- package/dist/agent/cloudstudioClient.js.map +1 -0
- package/dist/agent/cloudstudioMcp.js +722 -0
- package/dist/agent/cloudstudioMcp.js.map +1 -0
- package/dist/agent/configEnDisco.js +309 -11
- package/dist/agent/configEnDisco.js.map +1 -1
- package/dist/agent/consumoExterno.js +99 -0
- package/dist/agent/consumoExterno.js.map +1 -0
- package/dist/agent/descarga.js +149 -0
- package/dist/agent/descarga.js.map +1 -0
- package/dist/agent/diagnosticoDeTools.js +51 -0
- package/dist/agent/diagnosticoDeTools.js.map +1 -0
- package/dist/agent/dispositivosEnMaquina.js +596 -0
- package/dist/agent/dispositivosEnMaquina.js.map +1 -0
- package/dist/agent/escrituraDeCodex.js +134 -0
- package/dist/agent/escrituraDeCodex.js.map +1 -0
- package/dist/agent/escrituraDeOpencode.js +80 -0
- package/dist/agent/escrituraDeOpencode.js.map +1 -0
- package/dist/agent/escrituraExterna.js +742 -0
- package/dist/agent/escrituraExterna.js.map +1 -0
- package/dist/agent/gemini.js +81 -0
- package/dist/agent/gemini.js.map +1 -0
- package/dist/agent/git.js +83 -0
- package/dist/agent/git.js.map +1 -0
- package/dist/agent/gitSync.js +441 -0
- package/dist/agent/gitSync.js.map +1 -0
- package/dist/agent/hotswap.js +448 -0
- package/dist/agent/hotswap.js.map +1 -0
- package/dist/agent/instalacionEnMaquina.js +252 -0
- package/dist/agent/instalacionEnMaquina.js.map +1 -0
- package/dist/agent/instantanea.js +7 -10
- package/dist/agent/instantanea.js.map +1 -1
- package/dist/agent/interrupts.js +11 -2
- package/dist/agent/interrupts.js.map +1 -1
- package/dist/agent/juezDeTarea.js +345 -0
- package/dist/agent/juezDeTarea.js.map +1 -0
- package/dist/agent/lanzamientoEnMaquina.js +377 -0
- package/dist/agent/lanzamientoEnMaquina.js.map +1 -0
- package/dist/agent/manifiesto.js +59 -0
- package/dist/agent/manifiesto.js.map +1 -0
- package/dist/agent/memoriaDeProyecto.js +60 -0
- package/dist/agent/memoriaDeProyecto.js.map +1 -0
- package/dist/agent/modelos.js +105 -17
- package/dist/agent/modelos.js.map +1 -1
- package/dist/agent/modelosDeMotor.js +204 -0
- package/dist/agent/modelosDeMotor.js.map +1 -0
- package/dist/agent/paqueteDelProyecto.js +181 -0
- package/dist/agent/paqueteDelProyecto.js.map +1 -0
- package/dist/agent/perfiles.js +92 -31
- package/dist/agent/perfiles.js.map +1 -1
- package/dist/agent/persona.js +40 -0
- package/dist/agent/persona.js.map +1 -0
- package/dist/agent/procesosEnMaquina.js +204 -0
- package/dist/agent/procesosEnMaquina.js.map +1 -0
- package/dist/agent/proyecto.js +311 -13
- package/dist/agent/proyecto.js.map +1 -1
- package/dist/agent/puente.js +63 -6
- package/dist/agent/puente.js.map +1 -1
- package/dist/agent/resumenDeContexto.js +30 -0
- package/dist/agent/resumenDeContexto.js.map +1 -0
- package/dist/agent/resumenDeTool.js +64 -18
- package/dist/agent/resumenDeTool.js.map +1 -1
- package/dist/agent/sesionGit.js +481 -0
- package/dist/agent/sesionGit.js.map +1 -0
- package/dist/agent/settingsEnDisco.js +189 -0
- package/dist/agent/settingsEnDisco.js.map +1 -0
- package/dist/agent/subagenteCodex.js +368 -0
- package/dist/agent/subagenteCodex.js.map +1 -0
- package/dist/agent/subagenteExterno.js +423 -0
- package/dist/agent/subagenteExterno.js.map +1 -0
- package/dist/agent/subagenteOpencode.js +389 -0
- package/dist/agent/subagenteOpencode.js.map +1 -0
- package/dist/agent/subida.js +202 -0
- package/dist/agent/subida.js.map +1 -0
- package/dist/agent/tareasEnDisco.js +466 -0
- package/dist/agent/tareasEnDisco.js.map +1 -0
- package/dist/agent/turnoReal.js +663 -50
- package/dist/agent/turnoReal.js.map +1 -1
- package/dist/agent/versionEnDisco.js +48 -0
- package/dist/agent/versionEnDisco.js.map +1 -0
- package/dist/agent/xoneAgent.js +290 -49
- package/dist/agent/xoneAgent.js.map +1 -1
- package/dist/agent/zip.js +52 -0
- package/dist/agent/zip.js.map +1 -0
- package/dist/cli/acuseDeModelo.js +30 -0
- package/dist/cli/acuseDeModelo.js.map +1 -0
- package/dist/cli/aprobar.js +55 -2
- package/dist/cli/aprobar.js.map +1 -1
- package/dist/cli/config.js +9 -13
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/consola.js +750 -19
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/main.js +740 -81
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/markdown.js +188 -2
- package/dist/cli/markdown.js.map +1 -1
- package/dist/cli/run.js +32 -4
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/stdio.js +46 -19
- package/dist/cli/stdio.js.map +1 -1
- package/dist/cli/tema.js +96 -8
- package/dist/cli/tema.js.map +1 -1
- package/dist/cli/tokens.js +29 -0
- package/dist/cli/tokens.js.map +1 -0
- package/dist/cli/tui/app.js +171 -0
- package/dist/cli/tui/app.js.map +1 -0
- package/dist/cli/tui/aprobarTui.js +80 -0
- package/dist/cli/tui/aprobarTui.js.map +1 -0
- package/dist/cli/tui/barra.js +37 -0
- package/dist/cli/tui/barra.js.map +1 -0
- package/dist/cli/tui/correrTui.js +478 -0
- package/dist/cli/tui/correrTui.js.map +1 -0
- package/dist/cli/tui/entrada.js +166 -0
- package/dist/cli/tui/entrada.js.map +1 -0
- package/dist/cli/tui/filas.js +24 -0
- package/dist/cli/tui/filas.js.map +1 -0
- package/dist/cli/tui/logo.js +49 -0
- package/dist/cli/tui/logo.js.map +1 -0
- package/dist/cli/tui/pielTui.js +14 -0
- package/dist/cli/tui/pielTui.js.map +1 -0
- package/dist/cli/tui/raton.js +126 -0
- package/dist/cli/tui/raton.js.map +1 -0
- package/dist/cli/tui/sidebar.js +86 -0
- package/dist/cli/tui/sidebar.js.map +1 -0
- package/dist/cli/tui/store.js +143 -0
- package/dist/cli/tui/store.js.map +1 -0
- package/dist/cli/tui/tarjeta.js +19 -0
- package/dist/cli/tui/tarjeta.js.map +1 -0
- package/dist/cli/tui/temaInk.js +114 -0
- package/dist/cli/tui/temaInk.js.map +1 -0
- package/dist/cli/tui/transcript.js +254 -0
- package/dist/cli/tui/transcript.js.map +1 -0
- package/dist/cli/wizardInicial.js +240 -0
- package/dist/cli/wizardInicial.js.map +1 -0
- package/dist/core/actos.js +113 -0
- package/dist/core/actos.js.map +1 -0
- package/dist/core/adjuntos.js +97 -0
- package/dist/core/adjuntos.js.map +1 -0
- package/dist/core/agentes.js +227 -0
- package/dist/core/agentes.js.map +1 -0
- package/dist/core/artefactos.js +149 -0
- package/dist/core/artefactos.js.map +1 -0
- package/dist/core/cloudstudio.js +7 -0
- package/dist/core/cloudstudio.js.map +1 -0
- package/dist/core/config.js +212 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/contextos.js +53 -1
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/descargas.js +106 -0
- package/dist/core/descargas.js.map +1 -0
- package/dist/core/descriptoresDeApp.js +279 -0
- package/dist/core/descriptoresDeApp.js.map +1 -0
- package/dist/core/dispositivos.js +466 -0
- package/dist/core/dispositivos.js.map +1 -0
- package/dist/core/entrega.js +141 -0
- package/dist/core/entrega.js.map +1 -0
- package/dist/core/entrelazar.js +90 -0
- package/dist/core/entrelazar.js.map +1 -0
- package/dist/core/esqueleto.js +10 -0
- package/dist/core/esqueleto.js.map +1 -1
- package/dist/core/modelos.js +235 -3
- package/dist/core/modelos.js.map +1 -1
- package/dist/core/notify.js +27 -9
- package/dist/core/notify.js.map +1 -1
- package/dist/core/planDeSubida.js +127 -0
- package/dist/core/planDeSubida.js.map +1 -0
- package/dist/core/ports.js +189 -1
- package/dist/core/ports.js.map +1 -1
- package/dist/core/puedeLanzarse.js +239 -0
- package/dist/core/puedeLanzarse.js.map +1 -0
- package/dist/core/rutaVirtual.js +42 -0
- package/dist/core/rutaVirtual.js.map +1 -0
- package/dist/core/settings.js +246 -0
- package/dist/core/settings.js.map +1 -0
- package/dist/core/tareas.js +230 -0
- package/dist/core/tareas.js.map +1 -0
- package/dist/core/textos.js +36 -0
- package/dist/core/textos.js.map +1 -0
- package/dist/core/turno.js +56 -8
- package/dist/core/turno.js.map +1 -1
- package/dist/core/version.js +31 -0
- package/dist/core/version.js.map +1 -0
- package/dist/vendor/sqliteSaver.js +389 -0
- package/dist/vendor/sqliteSaver.js.map +1 -0
- package/dist/vendor/tokenTracking.js +22 -1
- package/dist/vendor/tokenTracking.js.map +1 -1
- package/dist/web/servidor/arranque.js +3964 -0
- package/dist/web/servidor/arranque.js.map +1 -0
- package/dist/web/servidor/consolaDeTarea.js +161 -0
- package/dist/web/servidor/consolaDeTarea.js.map +1 -0
- package/dist/web/servidor/consolaWeb.js +389 -0
- package/dist/web/servidor/consolaWeb.js.map +1 -0
- package/dist/web/servidor/corredorDeTareas.js +1119 -0
- package/dist/web/servidor/corredorDeTareas.js.map +1 -0
- package/dist/web/servidor/pielWeb.js +269 -0
- package/dist/web/servidor/pielWeb.js.map +1 -0
- package/dist/web/servidor/servidor.js +257 -0
- package/dist/web/servidor/servidor.js.map +1 -0
- package/dist/web/servidor/sesiones.js +440 -0
- package/dist/web/servidor/sesiones.js.map +1 -0
- package/dist/web/servidor/transporte.js +169 -0
- package/dist/web/servidor/transporte.js.map +1 -0
- package/dist/web/servidor/vestibulo.js +1276 -0
- package/dist/web/servidor/vestibulo.js.map +1 -0
- package/package.json +33 -9
- package/skills/archify/LICENSE +22 -0
- package/skills/archify/SKILL.md +109 -0
- package/skills/archify/THIRD_PARTY_NOTICES.md +56 -0
- package/skills/archify/assets/template.html +14787 -0
- package/skills/archify/bin/archify.mjs +1990 -0
- package/skills/archify/bin/open-artifact.mjs +86 -0
- package/skills/archify/bin/preview.mjs +648 -0
- package/skills/archify/bin/visual-check.mjs +829 -0
- package/skills/archify/brand-marks/README.md +31 -0
- package/skills/archify/brand-marks/catalog.json +131 -0
- package/skills/archify/delta/architecture-delta.mjs +1214 -0
- package/skills/archify/examples/agent-run.lifecycle.json +71 -0
- package/skills/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/archify/examples/brand-aware-delivery.architecture.json +47 -0
- package/skills/archify/examples/cache-miss-request.sequence.json +81 -0
- package/skills/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/archify/examples/dataflow-product-analytics.html +14898 -0
- package/skills/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/archify/examples/incident-response.workflow.json +64 -0
- package/skills/archify/examples/lifecycle-agent-run.html +14847 -0
- package/skills/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/archify/examples/sequence-cache-miss-request.html +14913 -0
- package/skills/archify/examples/web-app-rendered.html +14862 -0
- package/skills/archify/examples/web-app.architecture.json +46 -0
- package/skills/archify/examples/workflow-agent-tool-call-rendered.html +14904 -0
- package/skills/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/archify/package.json +17 -0
- package/skills/archify/recipes/scenarios.mjs +391 -0
- package/skills/archify/references/authoring-contract.md +198 -0
- package/skills/archify/references/brand-marks.md +69 -0
- package/skills/archify/references/delivery-contract.md +116 -0
- package/skills/archify/references/viewer-runtime.md +45 -0
- package/skills/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/archify/renderers/dataflow/README.md +104 -0
- package/skills/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/archify/renderers/lifecycle/README.md +115 -0
- package/skills/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/archify/renderers/sequence/README.md +114 -0
- package/skills/archify/renderers/sequence/render-sequence.mjs +453 -0
- package/skills/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/archify/renderers/shared/cli.mjs +218 -0
- package/skills/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/archify/renderers/shared/diagnostics.mjs +127 -0
- package/skills/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- package/skills/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/archify/renderers/shared/i18n.mjs +594 -0
- package/skills/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/archify/renderers/shared/legend.mjs +217 -0
- package/skills/archify/renderers/shared/output-path.mjs +321 -0
- package/skills/archify/renderers/shared/repository-evidence.mjs +235 -0
- package/skills/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/archify/renderers/shared/utils.mjs +232 -0
- package/skills/archify/renderers/shared/validator.mjs +86 -0
- package/skills/archify/renderers/workflow/README.md +223 -0
- package/skills/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/archify/schemas/README.md +211 -0
- package/skills/archify/schemas/architecture.schema.json +176 -0
- package/skills/archify/schemas/common.schema.json +115 -0
- package/skills/archify/schemas/dataflow.schema.json +243 -0
- package/skills/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/archify/schemas/sequence.schema.json +223 -0
- package/skills/archify/schemas/workflow.schema.json +428 -0
- package/skills/archify/scripts/check-render-output.mjs +835 -0
- package/skills/archify/scripts/check-update.mjs +1667 -0
- package/skills/archify/scripts/render-examples.mjs +26 -0
- package/skills/archify/scripts/update-contract.mjs +182 -0
- package/skills/archify/skill-release.json +10 -0
- package/skills/artifacts-builder/SKILL.md +120 -0
- package/skills/artifacts-builder/reference/diagramas.md +123 -0
- package/skills/artifacts-builder/reference/estilo.md +123 -0
- package/skills/artifacts-builder/reference/exportar-pdf.md +56 -0
- package/skills/artifacts-builder/reference/graficas.md +22 -0
- package/skills/artifacts-builder/scripts/bundle_artifact.py +109 -0
- package/skills/artifacts-builder/scripts/validate_artifact.py +345 -0
- package/skills/xone-hotswap/SKILL.md +185 -0
- package/skills/xone-hotswap/references/comandos.md +706 -0
- 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.
|