xonecode 0.5.1 → 0.8.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.
- package/apps/web/dist/assets/{index-BB3SamhR.css → index-Boak51UP.css} +1 -1
- package/apps/web/dist/assets/index-Cg28x6nG.js +477 -0
- package/apps/web/dist/assets/{php-D_2--9WK.js → php-Cu6F_QCJ.js} +1 -1
- package/apps/web/dist/assets/{ruby-DSHSDbk1.js → ruby-BiqQPoC6.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/dist/agent/{cloudstudioClient.js → cloudstudio/cloudstudioClient.js} +10 -0
- package/dist/agent/cloudstudio/cloudstudioClient.js.map +1 -0
- package/dist/agent/{cloudstudioMcp.js → cloudstudio/cloudstudioMcp.js} +5 -5
- package/dist/agent/cloudstudio/cloudstudioMcp.js.map +1 -0
- package/dist/agent/{descarga.js → cloudstudio/descarga.js} +17 -7
- package/dist/agent/cloudstudio/descarga.js.map +1 -0
- package/dist/agent/cloudstudio/manifiesto.js.map +1 -0
- package/dist/agent/cloudstudio/ramaActiva.js +11 -0
- package/dist/agent/cloudstudio/ramaActiva.js.map +1 -0
- package/dist/agent/{subida.js → cloudstudio/subida.js} +16 -7
- package/dist/agent/cloudstudio/subida.js.map +1 -0
- package/dist/agent/{zip.js → cloudstudio/zip.js} +1 -1
- package/dist/agent/cloudstudio/zip.js.map +1 -0
- package/dist/agent/{authEnDisco.js → config/authEnDisco.js} +3 -3
- package/dist/agent/config/authEnDisco.js.map +1 -0
- package/dist/agent/{catalogoModelos.js → config/catalogoModelos.js} +47 -1
- package/dist/agent/config/catalogoModelos.js.map +1 -0
- package/dist/agent/{configEnDisco.js → config/configEnDisco.js} +6 -6
- package/dist/agent/config/configEnDisco.js.map +1 -0
- package/dist/agent/{crearProyecto.js → config/crearProyecto.js} +2 -2
- package/dist/agent/config/crearProyecto.js.map +1 -0
- package/dist/agent/config/entorno.js.map +1 -0
- package/dist/agent/config/gemini.js.map +1 -0
- package/dist/agent/config/modelos.js +235 -0
- package/dist/agent/config/modelos.js.map +1 -0
- package/dist/agent/{modelosDeMotor.js → config/modelosDeMotor.js} +1 -1
- package/dist/agent/config/modelosDeMotor.js.map +1 -0
- package/dist/agent/config/mudanzaEnDisco.js +121 -0
- package/dist/agent/config/mudanzaEnDisco.js.map +1 -0
- package/dist/agent/{persona.js → config/persona.js} +1 -1
- package/dist/agent/config/persona.js.map +1 -0
- package/dist/agent/config/selectorEnMaquina.js +45 -0
- package/dist/agent/config/selectorEnMaquina.js.map +1 -0
- package/dist/agent/{settingsEnDisco.js → config/settingsEnDisco.js} +37 -2
- package/dist/agent/config/settingsEnDisco.js.map +1 -0
- package/dist/agent/{versionEnDisco.js → config/versionEnDisco.js} +8 -4
- package/dist/agent/config/versionEnDisco.js.map +1 -0
- package/dist/agent/dispositivos/arranqueDeEmulador.js +146 -0
- package/dist/agent/dispositivos/arranqueDeEmulador.js.map +1 -0
- package/dist/agent/{dispositivosEnMaquina.js → dispositivos/dispositivosEnMaquina.js} +93 -6
- package/dist/agent/dispositivos/dispositivosEnMaquina.js.map +1 -0
- package/dist/agent/dispositivos/hotswap.js.map +1 -0
- package/dist/agent/dispositivos/instalacionEnMaquina.js.map +1 -0
- package/dist/agent/dispositivos/juezVisual.js +152 -0
- package/dist/agent/dispositivos/juezVisual.js.map +1 -0
- package/dist/agent/{lanzamientoEnMaquina.js → dispositivos/lanzamientoEnMaquina.js} +1 -1
- package/dist/agent/dispositivos/lanzamientoEnMaquina.js.map +1 -0
- package/dist/agent/{paqueteDelProyecto.js → dispositivos/paqueteDelProyecto.js} +5 -5
- package/dist/agent/dispositivos/paqueteDelProyecto.js.map +1 -0
- package/dist/agent/dispositivos/procesosEnMaquina.js.map +1 -0
- package/dist/agent/dispositivos/sensorDeArranque.js +154 -0
- package/dist/agent/dispositivos/sensorDeArranque.js.map +1 -0
- package/dist/agent/exportarPdf.js +140 -0
- package/dist/agent/exportarPdf.js.map +1 -0
- package/dist/agent/{arbolDeProyecto.js → grafo/arbolDeProyecto.js} +2 -2
- package/dist/agent/grafo/arbolDeProyecto.js.map +1 -0
- package/dist/agent/{artefactosEnDisco.js → grafo/artefactosEnDisco.js} +2 -2
- package/dist/agent/grafo/artefactosEnDisco.js.map +1 -0
- package/dist/agent/{busquedaRegex.js → grafo/busquedaRegex.js} +41 -1
- package/dist/agent/grafo/busquedaRegex.js.map +1 -0
- package/dist/agent/grafo/criticaVisual.js +115 -0
- package/dist/agent/grafo/criticaVisual.js.map +1 -0
- package/dist/agent/grafo/excluirTools.js +82 -0
- package/dist/agent/grafo/excluirTools.js.map +1 -0
- package/dist/agent/{memoriaDeProyecto.js → grafo/memoriaDeProyecto.js} +1 -1
- package/dist/agent/grafo/memoriaDeProyecto.js.map +1 -0
- package/dist/agent/grafo/navegacionXone.js +231 -0
- package/dist/agent/grafo/navegacionXone.js.map +1 -0
- package/dist/agent/{perfiles.js → grafo/perfiles.js} +96 -10
- package/dist/agent/grafo/perfiles.js.map +1 -0
- package/dist/agent/{proyecto.js → grafo/proyecto.js} +274 -24
- package/dist/agent/grafo/proyecto.js.map +1 -0
- package/dist/agent/grafo/skills.js +425 -0
- package/dist/agent/grafo/skills.js.map +1 -0
- package/dist/agent/grafo/xoneAgent.js +566 -0
- package/dist/agent/grafo/xoneAgent.js.map +1 -0
- package/dist/agent/navegacion/estilosEnDisco.js +110 -0
- package/dist/agent/navegacion/estilosEnDisco.js.map +1 -0
- package/dist/agent/navegacion/hechosEnDisco.js +26 -0
- package/dist/agent/navegacion/hechosEnDisco.js.map +1 -0
- package/dist/agent/navegacion/indiceEnDisco.js +36 -0
- package/dist/agent/navegacion/indiceEnDisco.js.map +1 -0
- package/dist/agent/navegacion/modeloDeProyecto.js +228 -0
- package/dist/agent/navegacion/modeloDeProyecto.js.map +1 -0
- package/dist/agent/raizDelPaquete.js +56 -0
- package/dist/agent/raizDelPaquete.js.map +1 -0
- package/dist/agent/{checkpointer.js → sesiones/checkpointer.js} +1 -1
- package/dist/agent/sesiones/checkpointer.js.map +1 -0
- package/dist/agent/sesiones/git.js.map +1 -0
- package/dist/agent/{gitSync.js → sesiones/gitSync.js} +3 -3
- package/dist/agent/sesiones/gitSync.js.map +1 -0
- package/dist/agent/{sesionGit.js → sesiones/sesionGit.js} +2 -2
- package/dist/agent/sesiones/sesionGit.js.map +1 -0
- package/dist/agent/subagentes/agentesEnDisco.js +818 -0
- package/dist/agent/subagentes/agentesEnDisco.js.map +1 -0
- package/dist/agent/subagentes/consumoExterno.js.map +1 -0
- package/dist/agent/subagentes/escrituraDeCodex.js.map +1 -0
- package/dist/agent/{escrituraDeOpencode.js → subagentes/escrituraDeOpencode.js} +1 -1
- package/dist/agent/subagentes/escrituraDeOpencode.js.map +1 -0
- package/dist/agent/{escrituraExterna.js → subagentes/escrituraExterna.js} +59 -6
- package/dist/agent/subagentes/escrituraExterna.js.map +1 -0
- package/dist/agent/subagentes/pluginDeSkills.js +99 -0
- package/dist/agent/subagentes/pluginDeSkills.js.map +1 -0
- package/dist/agent/subagentes/subagenteCodex.js.map +1 -0
- package/dist/agent/{subagenteExterno.js → subagentes/subagenteExterno.js} +48 -0
- package/dist/agent/subagentes/subagenteExterno.js.map +1 -0
- package/dist/agent/{subagenteOpencode.js → subagentes/subagenteOpencode.js} +53 -1
- package/dist/agent/subagentes/subagenteOpencode.js.map +1 -0
- package/dist/agent/{aumentador.js → tareas/aumentador.js} +2 -2
- package/dist/agent/tareas/aumentador.js.map +1 -0
- package/dist/agent/{juezDeTarea.js → tareas/juezDeTarea.js} +6 -6
- package/dist/agent/tareas/juezDeTarea.js.map +1 -0
- package/dist/agent/{tareasEnDisco.js → tareas/tareasEnDisco.js} +53 -4
- package/dist/agent/tareas/tareasEnDisco.js.map +1 -0
- package/dist/agent/{diagnosticoDeTools.js → turno/diagnosticoDeTools.js} +5 -1
- package/dist/agent/turno/diagnosticoDeTools.js.map +1 -0
- package/dist/agent/{guionizado.js → turno/guionizado.js} +1 -1
- package/dist/agent/turno/guionizado.js.map +1 -0
- package/dist/agent/turno/informeDeTraza.js +245 -0
- package/dist/agent/turno/informeDeTraza.js.map +1 -0
- package/dist/agent/turno/inspectorDePrompt.js +205 -0
- package/dist/agent/turno/inspectorDePrompt.js.map +1 -0
- package/dist/agent/{instantanea.js → turno/instantanea.js} +7 -2
- package/dist/agent/turno/instantanea.js.map +1 -0
- package/dist/agent/{interrupts.js → turno/interrupts.js} +2 -2
- package/dist/agent/turno/interrupts.js.map +1 -0
- package/dist/agent/turno/mensajes.js.map +1 -0
- package/dist/agent/turno/normalizar.js.map +1 -0
- package/dist/agent/{puente.js → turno/puente.js} +71 -4
- package/dist/agent/turno/puente.js.map +1 -0
- package/dist/agent/turno/resumenDeContexto.js +260 -0
- package/dist/agent/turno/resumenDeContexto.js.map +1 -0
- package/dist/agent/{resumenDeTool.js → turno/resumenDeTool.js} +33 -0
- package/dist/agent/turno/resumenDeTool.js.map +1 -0
- package/dist/agent/turno/rubrica.js +91 -0
- package/dist/agent/turno/rubrica.js.map +1 -0
- package/dist/agent/turno/textoDeTool.js.map +1 -0
- package/dist/agent/{turnoReal.js → turno/turnoReal.js} +210 -25
- package/dist/agent/turno/turnoReal.js.map +1 -0
- package/dist/agent/turno/verificador.js.map +1 -0
- package/dist/cli/config.js +1 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/consola.js +72 -9
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/describe.js +2 -2
- package/dist/cli/describe.js.map +1 -1
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/fuentesDeDisco.js +29 -0
- package/dist/cli/fuentesDeDisco.js.map +1 -0
- package/dist/cli/main.js +87 -31
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/run.js +40 -10
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/traza.js +48 -0
- package/dist/cli/traza.js.map +1 -0
- package/dist/cli/tui/correrTui.js +4 -4
- package/dist/cli/tui/correrTui.js.map +1 -1
- package/dist/cli/verify.js +1 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/core/actos.js +1 -1
- package/dist/core/adjuntos.js +2 -2
- package/dist/core/agentes.js +247 -7
- package/dist/core/agentes.js.map +1 -1
- package/dist/core/artefactos.js +4 -4
- package/dist/core/config.js +1 -1
- package/dist/core/contextos.js +12 -1
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/descargas.js +1 -1
- package/dist/core/dispositivos.js +53 -3
- package/dist/core/dispositivos.js.map +1 -1
- package/dist/core/esfuerzo.js +141 -0
- package/dist/core/esfuerzo.js.map +1 -0
- package/dist/core/esqueleto.js +1 -1
- package/dist/core/estilos.js +78 -0
- package/dist/core/estilos.js.map +1 -0
- package/dist/core/exportacion.js +140 -0
- package/dist/core/exportacion.js.map +1 -0
- package/dist/core/hechosDelProyecto.js +87 -0
- package/dist/core/hechosDelProyecto.js.map +1 -0
- package/dist/core/logDeDispositivo.js +143 -0
- package/dist/core/logDeDispositivo.js.map +1 -0
- package/dist/core/modelos.js +48 -3
- package/dist/core/modelos.js.map +1 -1
- package/dist/core/mudanzaDeWorkspace.js +93 -0
- package/dist/core/mudanzaDeWorkspace.js.map +1 -0
- package/dist/core/navegacion.js +134 -0
- package/dist/core/navegacion.js.map +1 -0
- package/dist/core/notify.js +5 -0
- package/dist/core/notify.js.map +1 -1
- package/dist/core/planDeSubida.js +1 -1
- package/dist/core/planes.js +70 -0
- package/dist/core/planes.js.map +1 -0
- package/dist/core/ports.js +4 -0
- package/dist/core/ports.js.map +1 -1
- package/dist/core/rubrica.js +90 -0
- package/dist/core/rubrica.js.map +1 -0
- package/dist/core/selectorDeCarpeta.js +87 -0
- package/dist/core/selectorDeCarpeta.js.map +1 -0
- package/dist/core/settings.js +56 -7
- package/dist/core/settings.js.map +1 -1
- package/dist/core/shellDeAgente.js +144 -0
- package/dist/core/shellDeAgente.js.map +1 -0
- package/dist/core/skills.js +158 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/topeAgotado.js +69 -0
- package/dist/core/topeAgotado.js.map +1 -0
- package/dist/core/zipDeSkill.js +107 -0
- package/dist/core/zipDeSkill.js.map +1 -0
- package/dist/web/servidor/arranque.js +853 -54
- package/dist/web/servidor/arranque.js.map +1 -1
- package/dist/web/servidor/corredorDeTareas.js +3 -3
- package/dist/web/servidor/corredorDeTareas.js.map +1 -1
- package/dist/web/servidor/sesiones.js +34 -6
- package/dist/web/servidor/sesiones.js.map +1 -1
- package/dist/web/servidor/transporte.js +1 -1
- package/dist/web/servidor/transporte.js.map +1 -1
- package/dist/web/servidor/vestibulo.js +62 -13
- package/dist/web/servidor/vestibulo.js.map +1 -1
- package/package.json +7 -4
- package/skills/archify/SKILL.md +25 -2
- package/skills/artifacts-builder/SKILL.md +23 -0
- package/skills/xone-hotswap/SKILL.md +96 -0
- package/skills/xone-hotswap/scripts/xone-arrancar-android +202 -0
- package/skills/xone-hotswap/scripts/xone-arrancar-ios +85 -0
- package/skills/xone-hotswap/scripts/xone-captura-android +93 -0
- package/skills/xone-hotswap/scripts/xone-desplegar-android +439 -0
- package/skills/xone-hotswap/scripts/xone-hotswap +201 -0
- package/skills/xone-hotswap/scripts/xone-log-android +102 -0
- package/skills/xone-hotswap/scripts/xone-reiniciar-android +132 -0
- package/skills/xone-plan-builder/SKILL.md +22 -4
- package/skills/xone-spec-builder/SKILL.md +37 -7
- package/apps/web/dist/assets/index-CFYAoHdw.js +0 -477
- package/dist/agent/agentesEnDisco.js +0 -539
- package/dist/agent/agentesEnDisco.js.map +0 -1
- package/dist/agent/arbolDeProyecto.js.map +0 -1
- package/dist/agent/artefactosEnDisco.js.map +0 -1
- package/dist/agent/aumentador.js.map +0 -1
- package/dist/agent/authEnDisco.js.map +0 -1
- package/dist/agent/busquedaRegex.js.map +0 -1
- package/dist/agent/catalogoModelos.js.map +0 -1
- package/dist/agent/checkpointer.js.map +0 -1
- package/dist/agent/cloudstudioClient.js.map +0 -1
- package/dist/agent/cloudstudioMcp.js.map +0 -1
- package/dist/agent/configEnDisco.js.map +0 -1
- package/dist/agent/consumoExterno.js.map +0 -1
- package/dist/agent/crearProyecto.js.map +0 -1
- package/dist/agent/descarga.js.map +0 -1
- package/dist/agent/diagnosticoDeTools.js.map +0 -1
- package/dist/agent/dispositivosEnMaquina.js.map +0 -1
- package/dist/agent/entorno.js.map +0 -1
- package/dist/agent/escrituraDeCodex.js.map +0 -1
- package/dist/agent/escrituraDeOpencode.js.map +0 -1
- package/dist/agent/escrituraExterna.js.map +0 -1
- package/dist/agent/gemini.js.map +0 -1
- package/dist/agent/git.js.map +0 -1
- package/dist/agent/gitSync.js.map +0 -1
- package/dist/agent/guionizado.js.map +0 -1
- package/dist/agent/hotswap.js.map +0 -1
- package/dist/agent/instalacionEnMaquina.js.map +0 -1
- package/dist/agent/instantanea.js.map +0 -1
- package/dist/agent/interrupts.js.map +0 -1
- package/dist/agent/juezDeTarea.js.map +0 -1
- package/dist/agent/lanzamientoEnMaquina.js.map +0 -1
- package/dist/agent/manifiesto.js.map +0 -1
- package/dist/agent/memoriaDeProyecto.js.map +0 -1
- package/dist/agent/mensajes.js.map +0 -1
- package/dist/agent/modelos.js +0 -130
- package/dist/agent/modelos.js.map +0 -1
- package/dist/agent/modelosDeMotor.js.map +0 -1
- package/dist/agent/normalizar.js.map +0 -1
- package/dist/agent/paqueteDelProyecto.js.map +0 -1
- package/dist/agent/perfiles.js.map +0 -1
- package/dist/agent/persona.js.map +0 -1
- package/dist/agent/procesosEnMaquina.js.map +0 -1
- package/dist/agent/proyecto.js.map +0 -1
- package/dist/agent/puente.js.map +0 -1
- package/dist/agent/resumenDeContexto.js +0 -30
- package/dist/agent/resumenDeContexto.js.map +0 -1
- package/dist/agent/resumenDeTool.js.map +0 -1
- package/dist/agent/sesionGit.js.map +0 -1
- package/dist/agent/settingsEnDisco.js.map +0 -1
- package/dist/agent/skills.js +0 -65
- package/dist/agent/skills.js.map +0 -1
- package/dist/agent/subagenteCodex.js.map +0 -1
- package/dist/agent/subagenteExterno.js.map +0 -1
- package/dist/agent/subagenteOpencode.js.map +0 -1
- package/dist/agent/subida.js.map +0 -1
- package/dist/agent/tareasEnDisco.js.map +0 -1
- package/dist/agent/textoDeTool.js.map +0 -1
- package/dist/agent/turnoReal.js.map +0 -1
- package/dist/agent/verificador.js.map +0 -1
- package/dist/agent/versionEnDisco.js.map +0 -1
- package/dist/agent/xoneAgent.js +0 -347
- package/dist/agent/xoneAgent.js.map +0 -1
- package/dist/agent/zip.js.map +0 -1
- /package/dist/agent/{manifiesto.js → cloudstudio/manifiesto.js} +0 -0
- /package/dist/agent/{entorno.js → config/entorno.js} +0 -0
- /package/dist/agent/{gemini.js → config/gemini.js} +0 -0
- /package/dist/agent/{hotswap.js → dispositivos/hotswap.js} +0 -0
- /package/dist/agent/{instalacionEnMaquina.js → dispositivos/instalacionEnMaquina.js} +0 -0
- /package/dist/agent/{procesosEnMaquina.js → dispositivos/procesosEnMaquina.js} +0 -0
- /package/dist/agent/{git.js → sesiones/git.js} +0 -0
- /package/dist/agent/{consumoExterno.js → subagentes/consumoExterno.js} +0 -0
- /package/dist/agent/{escrituraDeCodex.js → subagentes/escrituraDeCodex.js} +0 -0
- /package/dist/agent/{subagenteCodex.js → subagentes/subagenteCodex.js} +0 -0
- /package/dist/agent/{mensajes.js → turno/mensajes.js} +0 -0
- /package/dist/agent/{normalizar.js → turno/normalizar.js} +0 -0
- /package/dist/agent/{textoDeTool.js → turno/textoDeTool.js} +0 -0
- /package/dist/agent/{verificador.js → turno/verificador.js} +0 -0
package/skills/archify/SKILL.md
CHANGED
|
@@ -10,6 +10,28 @@ metadata:
|
|
|
10
10
|
|
|
11
11
|
# Archify
|
|
12
12
|
|
|
13
|
+
## Antes de escribir nada: dónde va y qué no funciona ahí
|
|
14
|
+
|
|
15
|
+
**Esto se lee aquí porque es donde se lee de verdad.** Vivía en el prompt de cuatro
|
|
16
|
+
especialistas —unos 1.400 caracteres que viajaban en CADA llamada de cada uno, hablaran o no de
|
|
17
|
+
diagramas— y antes de eso vivía en `estilo.md`, que un turno real no llegó a abrir: escribió un
|
|
18
|
+
artefacto con `localStorage`, el script se mató entero y el botón del tema quedó muerto. La
|
|
19
|
+
lección no era «repítelo en todos los prompts», era **ponerlo en el fichero que sí se abre**.
|
|
20
|
+
|
|
21
|
+
- **DÓNDE se guarda**: `/artefactos/<nombre>.html`, nunca en la raíz del proyecto ni dentro de
|
|
22
|
+
`/skills`. `/artefactos/` es la carpeta de ESTA sesión: no es del proyecto, no pasa por
|
|
23
|
+
aprobación, no entra en git y no sube a CloudStudio. Un HTML escrito fuera de ella acaba
|
|
24
|
+
dentro de la app XOne del usuario.
|
|
25
|
+
- **DÓNDE se VE, y qué NO funciona ahí**: la consola lo pinta en un iframe sin
|
|
26
|
+
`allow-same-origin`. `localStorage`, `sessionStorage` e `indexedDB` **LANZAN**, y se llevan por
|
|
27
|
+
delante el resto de tu `<script>`: nada de interruptor de tema ni de recordar nada — el tema se
|
|
28
|
+
resuelve con `matchMedia`, y ya. Y no se llega al origen de la consola, así que **hornea los
|
|
29
|
+
datos dentro del HTML**.
|
|
30
|
+
- **Apóyate en el código real** antes de dibujar: no inventes nombres, componentes ni flujos.
|
|
31
|
+
- **Para un diagrama, esquema, arquitectura, flujo, secuencia, datos o estados, esta skill basta.**
|
|
32
|
+
Si ADEMÁS piden un contenedor interactivo alrededor, eso es otra skill: úsala solo si la
|
|
33
|
+
tienes y después de decidir el diagrama aquí. **No nombres jamás una skill que no tengas.**
|
|
34
|
+
|
|
13
35
|
Create a diagram: architecture, workflow, sequence, dataflow, or lifecycle.
|
|
14
36
|
|
|
15
37
|
## Aquí no hay terminal
|
|
@@ -93,8 +115,9 @@ pide explícitamente una de esas capacidades.
|
|
|
93
115
|
|
|
94
116
|
Construye el diagrama tú mismo, como SVG o mermaid inline dentro de tu propio HTML
|
|
95
117
|
autocontenido — no hay JSON de Archify que escribir ni motor que lo consuma, así que no lleva
|
|
96
|
-
theme switching, pan/zoom, búsqueda ni export automático salvo que tú los programes.
|
|
97
|
-
|
|
118
|
+
theme switching, pan/zoom, búsqueda ni export automático salvo que tú los programes. **Si tienes** la skill
|
|
119
|
+
`artifacts-builder`, su `reference/diagramas.md` te sirve de guía; si no la tienes, sigue aquí y
|
|
120
|
+
no la nombres. La entrega depende de la tool
|
|
98
121
|
que tengas:
|
|
99
122
|
|
|
100
123
|
- **Con `publish_artifact`**: `publish_artifact(path=..., title=...)`. **Cuando confirme, HAS
|
|
@@ -5,6 +5,29 @@ description: "Build a self-contained interactive HTML artifact the user can view
|
|
|
5
5
|
|
|
6
6
|
# Artifacts Builder
|
|
7
7
|
|
|
8
|
+
## Antes de escribir nada: dónde va y qué no funciona ahí
|
|
9
|
+
|
|
10
|
+
**Esto se lee aquí porque es donde se lee de verdad.** Vivía en el prompt de cuatro
|
|
11
|
+
especialistas —unos 1.400 caracteres que viajaban en CADA llamada de cada uno, hablaran o no de
|
|
12
|
+
diagramas— y antes de eso vivía en `estilo.md`, que un turno real no llegó a abrir: escribió un
|
|
13
|
+
artefacto con `localStorage`, el script se mató entero y el botón del tema quedó muerto. La
|
|
14
|
+
lección no era «repítelo en todos los prompts», era **ponerlo en el fichero que sí se abre**.
|
|
15
|
+
|
|
16
|
+
- **DÓNDE se guarda**: `/artefactos/<nombre>.html`, nunca en la raíz del proyecto ni dentro de
|
|
17
|
+
`/skills`. `/artefactos/` es la carpeta de ESTA sesión: no es del proyecto, no pasa por
|
|
18
|
+
aprobación, no entra en git y no sube a CloudStudio. Un HTML escrito fuera de ella acaba
|
|
19
|
+
dentro de la app XOne del usuario.
|
|
20
|
+
- **DÓNDE se VE, y qué NO funciona ahí**: la consola lo pinta en un iframe sin
|
|
21
|
+
`allow-same-origin`. `localStorage`, `sessionStorage` e `indexedDB` **LANZAN**, y se llevan por
|
|
22
|
+
delante el resto de tu `<script>`: nada de interruptor de tema ni de recordar nada — el tema se
|
|
23
|
+
resuelve con `matchMedia`, y ya. Y no se llega al origen de la consola, así que **hornea los
|
|
24
|
+
datos dentro del HTML**.
|
|
25
|
+
- **Apóyate en el código real** antes de dibujar: no inventes nombres, componentes ni flujos.
|
|
26
|
+
- **Esta skill es para dashboards, informes, tablas filtrables y piezas interactivas.** Para un
|
|
27
|
+
diagrama de arquitectura, flujo o secuencia hay otra: úsala si la tienes, y si no la tienes,
|
|
28
|
+
dilo en vez de dibujarlo aquí — un diagrama hecho a mano sale peor y cuesta más. **No nombres
|
|
29
|
+
jamás una skill que no tengas.**
|
|
30
|
+
|
|
8
31
|
You write ONE self-contained HTML file. No build step, no npm, no `execute` anywhere this
|
|
9
32
|
skill is mounted — nothing here runs a script.
|
|
10
33
|
|
|
@@ -34,6 +34,102 @@ responde. Habla dos protocolos por el mismo puerto: **WebSocket** para los coman
|
|
|
34
34
|
comando exista. Cuál es cuál, comando a comando: la columna **Canal (iOS)** de la
|
|
35
35
|
[matriz](references/comandos.md#matriz-de-disponibilidad).
|
|
36
36
|
|
|
37
|
+
## Los scripts que trae esta skill: se llaman por su nombre
|
|
38
|
+
|
|
39
|
+
Esta carpeta trae siete programas, y **están en el PATH**: se llaman como cualquier otro comando,
|
|
40
|
+
sin ruta. Existen por una razón concreta — los comandos del canal viajan por **WebSocket** y
|
|
41
|
+
`curl` no habla WebSocket, así que sin ellos no hay forma de mandar un `getAllElements` desde una
|
|
42
|
+
shell.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# ANTES QUE NADA si `adb devices` no lista ninguno: levanta el emulador y ESPERA a que esté
|
|
46
|
+
# listo de verdad. Ya arrancado, lo dice y no lanza nada, así que se puede llamar sin miedo.
|
|
47
|
+
xone-arrancar-android
|
|
48
|
+
xone-arrancar-android --avd pixel8 # con varios AVDs
|
|
49
|
+
xone-arrancar-android --lista # cuáles hay, sin arrancar ninguno
|
|
50
|
+
|
|
51
|
+
# LO PRIMERO al empezar con un aparato, y lo primero que hay que probar si el canal no contesta:
|
|
52
|
+
# el host reabre la última app ejecutada, y si ésa se quedó en un error, nada responde.
|
|
53
|
+
xone-reiniciar-android
|
|
54
|
+
xone-reiniciar-android --app MiApp
|
|
55
|
+
|
|
56
|
+
# «lanza la app», «despliégala»: la cadena ENTERA (túnel, ZIP, subida, reinicio, lanzamiento)
|
|
57
|
+
# y termina diciendo si la app está VIVA, con su árbol de controles.
|
|
58
|
+
xone-desplegar-android
|
|
59
|
+
xone-desplegar-android --captura # …y deja la captura, en la MISMA orden
|
|
60
|
+
xone-desplegar-android --app MiApp --serie emulator-5554
|
|
61
|
+
|
|
62
|
+
# Cualquier comando del catálogo de más abajo (varios en orden, si le pasas varios):
|
|
63
|
+
xone-hotswap '{"command":"getAllElements","format":"xone"}'
|
|
64
|
+
xone-hotswap '{"command":"getScreenshot"}'
|
|
65
|
+
xone-hotswap '{"command":"click","name":"MAP_BT_ACEPTAR"}'
|
|
66
|
+
|
|
67
|
+
# Por qué algo no se pinta o la app se muere: las excepciones del aparato.
|
|
68
|
+
xone-log-android
|
|
69
|
+
xone-log-android --limpiar # aísla lo que pase a partir de ahora
|
|
70
|
+
|
|
71
|
+
# RESPALDO de la captura, para cuando el canal no contesta: `adb exec-out screencap` pero
|
|
72
|
+
# guardándola donde toca. Nunca redirijas ese `adb` tú: el `cwd` es la raíz del proyecto.
|
|
73
|
+
xone-captura-android
|
|
74
|
+
xone-captura-android --nombre login.png
|
|
75
|
+
|
|
76
|
+
# iOS: levantar el host y dejar el canal listo (desplegar en iOS NO está medido).
|
|
77
|
+
xone-arrancar-ios
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**El log es donde está el porqué.** Una pantalla en blanco no dice nada; el log dice
|
|
81
|
+
`LayoutException … Cannot find font file Inter-Regular`, con la colección y el control. Medido
|
|
82
|
+
sobre un proyecto real: la app arrancaba, el árbol de controles contestaba, y lo que faltaba era
|
|
83
|
+
una fuente. Ni la captura ni `getAllElements` lo cuentan.
|
|
84
|
+
|
|
85
|
+
**Encadenar órdenes cuesta más que la orden.** Lo que devuelve cada una es pequeño —1,6 KB el
|
|
86
|
+
árbol de controles, 108 bytes la captura—, pero cada ida y vuelta reenvía la conversación
|
|
87
|
+
entera. Por eso «lanza la app y sácame una captura» es `xone-desplegar-android --captura`, un
|
|
88
|
+
viaje, y no tres órdenes encadenadas. Y `xone-hotswap` acepta varios comandos de golpe, que es
|
|
89
|
+
la misma idea.
|
|
90
|
+
|
|
91
|
+
Tres cosas de `xone-hotswap` que conviene saber antes de leer su salida:
|
|
92
|
+
|
|
93
|
+
- **Una captura no se imprime**: se guarda en `$XONECODE_ARTEFACTOS` y lo que sale es su nombre y
|
|
94
|
+
su tamaño. Un `getScreenshot` son ~57.000 caracteres de base64, y volcarlos en la salida de un
|
|
95
|
+
comando es meterlos en el contexto sin que nadie pueda mirarlos. Para ENSEÑARLA, di su nombre:
|
|
96
|
+
la interfaz la enseña desde ahí. **No intentes abrirla ni leerla**: es binaria.
|
|
97
|
+
- **La respuesta viene en `status`** —no en un campo `image`— y una captura de Android es
|
|
98
|
+
**JPEG**, no PNG.
|
|
99
|
+
- Si muere con «no se pudo hablar con el aparato», casi siempre falta el túnel
|
|
100
|
+
(`adb forward tcp:8443 tcp:8443`) o la app host no está viva.
|
|
101
|
+
|
|
102
|
+
Y `xone-desplegar-android` **no necesita un `zip` del sistema**: construye el ZIP él mismo con
|
|
103
|
+
`zlib`, así que tampoco depende de que haya uno en Windows.
|
|
104
|
+
|
|
105
|
+
**No uses las herramientas nativas para lo que contesta el canal**: `uiautomator dump` da la
|
|
106
|
+
jerarquía de vistas de ANDROID y `adb exec-out screencap` una foto cruda; ninguna de las dos
|
|
107
|
+
conoce los controles XOne, que es por lo que suelen preguntarte.
|
|
108
|
+
|
|
109
|
+
**Pero si el canal no contesta y aun así hace falta una captura, es `xone-captura-android`, no
|
|
110
|
+
un `adb` compuesto a mano.** Medido: redirigir `adb exec-out screencap -p > captura.png` deja el
|
|
111
|
+
fichero en el `cwd`, que es LA RAÍZ DEL PROYECTO del usuario — de ahí se va a la aprobación, a
|
|
112
|
+
git y a CloudStudio. El script hace lo mismo y lo deja en `$XONECODE_ARTEFACTOS`, e imprime solo
|
|
113
|
+
el nombre. Lo que enseña es lo que pinta el SISTEMA, no lo que la app host dice de sí misma: por
|
|
114
|
+
eso sigue siendo el respaldo y no la vía buena.
|
|
115
|
+
|
|
116
|
+
**Sin aparato no hay nada que probar, y arrancarlo es `xone-arrancar-android`.** Espera a que
|
|
117
|
+
el sistema diga `sys.boot_completed=1` —no solo a que salga en `adb devices`—, porque sobre un
|
|
118
|
+
Android a medio arrancar el despliegue falla de formas que no se leen. Y si ya está arrancado
|
|
119
|
+
lo dice y no lanza nada, así que se puede llamar a la defensiva antes de desplegar.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
adb devices # ¿hay algo?
|
|
123
|
+
xone-arrancar-android # si no; --avd <nombre> con varios AVDs
|
|
124
|
+
xone-desplegar-android
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**No lo compongas a mano.** Un comando que no termina cuelga el turno, y además el binario no
|
|
128
|
+
suele estar donde crees: tus comandos corren con `/bin/sh`, sin el `~/.zshrc` de nadie, y en un
|
|
129
|
+
Mac con Homebrew `emulator` vive en `share/android-commandlinetools/emulator/` aunque `adb` sí
|
|
130
|
+
esté en el PATH. El script recibe la ruta buena del harness (`$XONECODE_EMULATOR`), que la
|
|
131
|
+
resuelve con el mismo localizador que la pestaña Ejecutar.
|
|
132
|
+
|
|
37
133
|
## Cómo llegar, según la plataforma
|
|
38
134
|
|
|
39
135
|
**Android** — por `adb forward`, y entonces la IP es `127.0.0.1` (el localhost del PC, que adb
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Levantar un emulador de Android y dejarlo listo para desplegar.
|
|
4
|
+
*
|
|
5
|
+
* EXISTE PORQUE FALTABA, y el síntoma fue literal: con el emulador cerrado, el agente no lo
|
|
6
|
+
* levantaba. Lo que había era prosa en la skill —«mándalo al fondo y espera a una
|
|
7
|
+
* condición»— y cuatro sitios donde equivocarse: encontrar un binario que NO está en el PATH
|
|
8
|
+
* (en un Mac con Homebrew `emulator` vive en `share/android-commandlinetools/emulator/`
|
|
9
|
+
* mientras `adb` sí está, y `ANDROID_HOME` suele estar vacía), elegir el AVD, mandarlo al
|
|
10
|
+
* fondo sin colgar el turno, y saber cuándo está listo de verdad.
|
|
11
|
+
*
|
|
12
|
+
* LAS RUTAS NO SE ADIVINAN, LLEGAN: `$XONECODE_EMULATOR` y `$XONECODE_ADB` las pone el
|
|
13
|
+
* harness con el MISMO localizador que usa la pestaña Ejecutar
|
|
14
|
+
* (`core/shellDeAgente.ts#variablesDeAndroid` ← `localizadorDeAndroid`). Dos reglas sobre
|
|
15
|
+
* dónde vive el SDK son dos reglas que divergen. Si no están, se cae al PATH, que es lo
|
|
16
|
+
* único honesto que se puede hacer sin ellas.
|
|
17
|
+
*
|
|
18
|
+
* DOS COSAS QUE LO SEPARAN DEL BOTÓN DE LA WEB (`agent/dispositivos/arranqueDeEmulador.ts`),
|
|
19
|
+
* y son deliberadas:
|
|
20
|
+
*
|
|
21
|
+
* - **Espera a `sys.boot_completed`, no solo a que el aparato APAREZCA.** Al botón le basta
|
|
22
|
+
* con que `adb devices` lo liste con su nombre de AVD: quien mira la pantalla ya ve el
|
|
23
|
+
* resto. Aquí el comando siguiente es `xone-desplegar-android`, y sobre un Android a
|
|
24
|
+
* medio arrancar falla de formas que no se leen —un `pm grant` que no responde, un
|
|
25
|
+
* `am start` que se traga—. La condición de «listo» es la del sistema, no la nuestra.
|
|
26
|
+
* - **Es IDEMPOTENTE.** El modelo lo va a llamar a la defensiva antes de desplegar. Si ya
|
|
27
|
+
* hay un emulador vivo con ese AVD se dice y se sale con 0, sin lanzar nada: un segundo
|
|
28
|
+
* `emulator -avd pixel8` falla con ruido y manda al modelo a diagnosticar un problema que
|
|
29
|
+
* no existe.
|
|
30
|
+
*
|
|
31
|
+
* USO:
|
|
32
|
+
* xone-arrancar-android el único AVD que haya
|
|
33
|
+
* xone-arrancar-android --avd pixel8 con varios
|
|
34
|
+
* xone-arrancar-android --lista qué AVDs hay, sin arrancar nada
|
|
35
|
+
*/
|
|
36
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
37
|
+
|
|
38
|
+
/** Lo que tarda un arranque en frío de un AVD con `google_apis`, con margen. Por debajo del
|
|
39
|
+
* tope de reloj de UN comando del harness (600 s), que es quien mataría al script entero. */
|
|
40
|
+
const TOPE_MS = 180_000;
|
|
41
|
+
const ESPERA_S = 2;
|
|
42
|
+
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
const opcion = (nombre) => {
|
|
45
|
+
const i = args.indexOf(`--${nombre}`);
|
|
46
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
50
|
+
console.error(`xone-arrancar-android — levanta un emulador y espera a que esté ARRANCADO.
|
|
51
|
+
|
|
52
|
+
xone-arrancar-android el único AVD que haya
|
|
53
|
+
xone-arrancar-android --avd pixel8 con varios
|
|
54
|
+
xone-arrancar-android --lista qué AVDs hay, sin arrancar nada
|
|
55
|
+
|
|
56
|
+
Si ya hay un emulador vivo con ese AVD, lo dice y no lanza nada. Espera a que el sistema
|
|
57
|
+
diga \`sys.boot_completed=1\`, no solo a que el aparato aparezca en \`adb devices\`: sobre un
|
|
58
|
+
Android a medio arrancar, \`xone-desplegar-android\` falla de formas que no se leen.
|
|
59
|
+
Después: xone-desplegar-android`);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// El harness los pone; el PATH es el respaldo. Un valor vacío no es «no consta», así que no
|
|
64
|
+
// se acepta como ruta: se cae al nombre pelado igual que si no estuviera.
|
|
65
|
+
const EMULATOR = process.env.XONECODE_EMULATOR || "emulator";
|
|
66
|
+
const ADB = process.env.XONECODE_ADB || "adb";
|
|
67
|
+
|
|
68
|
+
const adb = (...partes) =>
|
|
69
|
+
execFileSync(ADB, partes, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
70
|
+
|
|
71
|
+
/** Los emuladores conectados, con el AVD que dice ser cada uno. */
|
|
72
|
+
function emuladoresVivos() {
|
|
73
|
+
let salida;
|
|
74
|
+
try {
|
|
75
|
+
salida = adb("devices");
|
|
76
|
+
} catch {
|
|
77
|
+
// `adb` puede estar levantando su demonio. No es un fallo del arranque, es un sondeo más.
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const vivos = [];
|
|
81
|
+
for (const linea of salida.split(/\r?\n/)) {
|
|
82
|
+
const l = linea.trim();
|
|
83
|
+
if (l === "" || /^List of devices/i.test(l) || l.startsWith("*")) continue;
|
|
84
|
+
const [serie, estado] = l.split(/\s+/);
|
|
85
|
+
if (!serie || !serie.startsWith("emulator-") || estado !== "device") continue;
|
|
86
|
+
let avd;
|
|
87
|
+
try {
|
|
88
|
+
avd = adb("-s", serie, "emu", "avd", "name")
|
|
89
|
+
.split(/\r?\n/)
|
|
90
|
+
.map((x) => x.trim())
|
|
91
|
+
.find((x) => x !== "" && x !== "OK");
|
|
92
|
+
} catch {
|
|
93
|
+
// Ese emulador todavía no contesta a la consola. Se sigue con los demás.
|
|
94
|
+
}
|
|
95
|
+
vivos.push({ serie, avd });
|
|
96
|
+
}
|
|
97
|
+
return vivos;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** «Listo» lo dice el SISTEMA, no nosotros. */
|
|
101
|
+
function arrancadoDelTodo(serie) {
|
|
102
|
+
try {
|
|
103
|
+
return adb("-s", serie, "shell", "getprop", "sys.boot_completed").trim() === "1";
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function avdsDisponibles() {
|
|
110
|
+
try {
|
|
111
|
+
return execFileSync(EMULATOR, ["-list-avds"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] })
|
|
112
|
+
.split(/\r?\n/)
|
|
113
|
+
.map((l) => l.trim())
|
|
114
|
+
.filter((l) => l !== "");
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`no pude listar los AVDs con «${EMULATOR}»: ${error.message.split("\n")[0]}`);
|
|
117
|
+
console.error("Si el SDK está instalado pero no lo encuentro, es que falta ANDROID_HOME o la carpeta emulator/.");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (args.includes("--lista")) {
|
|
123
|
+
const avds = avdsDisponibles();
|
|
124
|
+
if (avds.length === 0) console.error("no hay ningún AVD creado. Créalo con `avdmanager` o desde Android Studio.");
|
|
125
|
+
else console.log(avds.join("\n"));
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** El AVD: el que se diga, o el único que haya. Mismo contrato que `xone-arrancar-ios`. */
|
|
130
|
+
function elegirAvd() {
|
|
131
|
+
const dicho = opcion("avd");
|
|
132
|
+
if (dicho !== undefined) return dicho;
|
|
133
|
+
const avds = avdsDisponibles();
|
|
134
|
+
if (avds.length === 0) {
|
|
135
|
+
console.error("no hay ningún AVD creado. Créalo con `avdmanager` o desde Android Studio.");
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
if (avds.length > 1) {
|
|
139
|
+
console.error(`hay ${avds.length} AVDs; dime cuál con --avd:`);
|
|
140
|
+
for (const a of avds) console.error(` ${a}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
return avds[0];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const avd = elegirAvd();
|
|
147
|
+
|
|
148
|
+
// El nombre se convierte en un ARGUMENTO de proceso, así que se cierra por FORMA. Ojo al guion
|
|
149
|
+
// INICIAL: `emulator` tomaría `-foo` por una bandera suya. Es la misma clase que acepta el
|
|
150
|
+
// harness (`core/dispositivos.ts#nombreDeAvdDeConsola`), a propósito.
|
|
151
|
+
if (!/^[A-Za-z0-9._][A-Za-z0-9._-]*$/.test(avd)) {
|
|
152
|
+
console.error(`«${avd}» no es un nombre de AVD`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// IDEMPOTENTE: si ya está, no se lanza nada.
|
|
157
|
+
const yaEstaba = emuladoresVivos().find((e) => e.avd === avd);
|
|
158
|
+
if (yaEstaba !== undefined) {
|
|
159
|
+
if (arrancadoDelTodo(yaEstaba.serie)) {
|
|
160
|
+
console.log(`${avd} ya está arrancado (${yaEstaba.serie}). No he lanzado nada.`);
|
|
161
|
+
console.log("Siguiente: xone-desplegar-android");
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
console.log(`${avd} (${yaEstaba.serie}) está arrancando; espero a que termine.`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (yaEstaba === undefined) {
|
|
168
|
+
console.log(`1/2 lanzando ${avd}`);
|
|
169
|
+
/**
|
|
170
|
+
* DESPEGADO Y SIN TUBERÍAS. Un emulador no termina nunca: si el script se queda con sus
|
|
171
|
+
* pipes, no sale, y entonces el que muere por el tope de reloj es el SCRIPT —diez minutos
|
|
172
|
+
* de turno tirados—. `unref` es lo que deja que este proceso acabe con el nieto vivo.
|
|
173
|
+
*/
|
|
174
|
+
const hijo = spawn(EMULATOR, ["-avd", avd], { detached: true, stdio: "ignore" });
|
|
175
|
+
let fallo;
|
|
176
|
+
hijo.on("error", (e) => {
|
|
177
|
+
fallo = e.message;
|
|
178
|
+
});
|
|
179
|
+
hijo.unref();
|
|
180
|
+
execFileSync("sleep", [String(ESPERA_S)]);
|
|
181
|
+
if (fallo !== undefined) {
|
|
182
|
+
console.error(`no pude lanzar el emulador: ${fallo}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
console.log(`2/2 esperando a que ${avd} termine de arrancar`);
|
|
188
|
+
const t0 = Date.now();
|
|
189
|
+
while (Date.now() - t0 < TOPE_MS) {
|
|
190
|
+
const mio = emuladoresVivos().find((e) => e.avd === avd);
|
|
191
|
+
if (mio !== undefined && arrancadoDelTodo(mio.serie)) {
|
|
192
|
+
console.log(`${avd} arrancado y listo (${mio.serie}), ${Math.round((Date.now() - t0) / 1000)} s`);
|
|
193
|
+
console.log("Siguiente: xone-desplegar-android");
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
execFileSync("sleep", [String(ESPERA_S)]);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// El proceso sigue vivo: se dice lo que se SABE, que es que todavía no está listo.
|
|
200
|
+
console.error(`${avd} no ha terminado de arrancar en ${TOPE_MS / 1000} s; sigue en marcha.`);
|
|
201
|
+
console.error("Vuelve a llamarme dentro de un rato: si ya está, lo digo y no lanzo nada.");
|
|
202
|
+
process.exit(1);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Levantar el host de iOS y dejar el canal listo. La plantilla equivalente a la de Android.
|
|
4
|
+
*
|
|
5
|
+
* LO QUE CAMBIA RESPECTO A ANDROID, y no es un detalle:
|
|
6
|
+
* - **No hay túnel.** El simulador comparte la pila de red del Mac, así que el servidor se
|
|
7
|
+
* alcanza en `localhost` directamente. En un iPhone físico se llega por la IP de la LAN que
|
|
8
|
+
* enseña la propia app.
|
|
9
|
+
* - **No hay `/file_upload`, ni `push_files`, ni `runScript`, ni `runSql`, ni logs.** El host
|
|
10
|
+
* de iOS no los tiene. O sea que **esto no despliega el proyecto**: arranca el host y abre
|
|
11
|
+
* el canal. Desplegar en iOS no está medido, y fingir un paso que no se ha probado es peor
|
|
12
|
+
* que no tenerlo.
|
|
13
|
+
* - El host es **`es.xone.studioapp.swift`**, y solo existe en el runtime en modo desarrollo.
|
|
14
|
+
*
|
|
15
|
+
* USO:
|
|
16
|
+
* node "$XONECODE_SKILL_XONE_HOTSWAP/scripts/ios-arrancar.mjs"
|
|
17
|
+
* node "$XONECODE_SKILL_XONE_HOTSWAP/scripts/ios-arrancar.mjs" --udid <UDID> --app MiApp
|
|
18
|
+
*
|
|
19
|
+
* Después, los comandos van por el mismo cliente que en Android:
|
|
20
|
+
* HOTSWAP_URL=wss://localhost:8443/hotswap node "$XONECODE_SKILL_XONE_HOTSWAP/scripts/hotswap.mjs" '{"command":"getAllElements","format":"xone"}'
|
|
21
|
+
*
|
|
22
|
+
* DOS AVISOS SOBRE EL HOST DE iOS que te van a costar una sesión si no los sabes: `MyAllXOne`
|
|
23
|
+
* crashea al arrancar (GoogleMaps) —para probar filas, el fixture usable es `FontIconsApp`—, y
|
|
24
|
+
* el host se cae al abrir una lista de menú porque le faltan los `xone_img_*.png`. Si el puerto
|
|
25
|
+
* deja de responder justo después de abrir un menú, es eso y es un defecto AJENO al hotswap.
|
|
26
|
+
*/
|
|
27
|
+
import { execFileSync } from "node:child_process";
|
|
28
|
+
|
|
29
|
+
const HOST = "es.xone.studioapp.swift";
|
|
30
|
+
|
|
31
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
32
|
+
console.error(`xone-arrancar-ios — levanta el host de iOS y deja el canal listo.
|
|
33
|
+
|
|
34
|
+
xone-arrancar-ios usa el único simulador arrancado
|
|
35
|
+
xone-arrancar-ios --udid <UDID> con varios arrancados
|
|
36
|
+
xone-arrancar-ios --app MiApp además lanza esa app
|
|
37
|
+
|
|
38
|
+
NO despliega el proyecto: en iOS no hay /file_upload y eso no está medido. Después, los comandos
|
|
39
|
+
van por: HOTSWAP_URL=wss://localhost:8443/hotswap xone-hotswap '{"command":"getAllElements"}'`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
const opcion = (nombre) => {
|
|
45
|
+
const i = args.indexOf(`--${nombre}`);
|
|
46
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** El simulador arrancado, o el que se diga. Sin ninguno, se dice: no se arranca uno a ciegas. */
|
|
50
|
+
function udid() {
|
|
51
|
+
const dicho = opcion("udid");
|
|
52
|
+
if (dicho !== undefined) return dicho;
|
|
53
|
+
const salida = execFileSync("xcrun", ["simctl", "list", "devices", "booted", "-j"], { encoding: "utf8" });
|
|
54
|
+
const porRuntime = JSON.parse(salida).devices ?? {};
|
|
55
|
+
const arrancados = Object.values(porRuntime).flat();
|
|
56
|
+
if (arrancados.length === 0) {
|
|
57
|
+
console.error("no hay ningún simulador arrancado. Arranca uno (`xcrun simctl boot <UDID>`) o pásame --udid.");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
if (arrancados.length > 1) {
|
|
61
|
+
console.error(`hay ${arrancados.length} simuladores arrancados; dime cuál con --udid:`);
|
|
62
|
+
for (const d of arrancados) console.error(` ${d.udid} ${d.name}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
return arrancados[0].udid;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const destino = udid();
|
|
69
|
+
console.log(`1/2 arrancando el host en ${destino}`);
|
|
70
|
+
// Arrancar el host es lo que levanta el servidor: sin la app viva, el puerto no responde.
|
|
71
|
+
console.log(execFileSync("xcrun", ["simctl", "launch", destino, HOST], { encoding: "utf8" }).trim());
|
|
72
|
+
|
|
73
|
+
const app = opcion("app");
|
|
74
|
+
if (app === undefined) {
|
|
75
|
+
console.log("2/2 host arrancado. El canal es wss://localhost:8443/hotswap");
|
|
76
|
+
} else {
|
|
77
|
+
console.log(`2/2 lanzando ${app}`);
|
|
78
|
+
const cliente = new URL("hotswap.mjs", import.meta.url).pathname;
|
|
79
|
+
console.log(
|
|
80
|
+
execFileSync("node", [cliente, JSON.stringify({ command: "launchApplication", appName: app })], {
|
|
81
|
+
encoding: "utf8",
|
|
82
|
+
env: { ...process.env, HOTSWAP_URL: "wss://localhost:8443/hotswap" },
|
|
83
|
+
}).trim(),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Una captura de la pantalla del aparato, POR LA VÍA NATIVA, a la carpeta de artefactos.
|
|
4
|
+
*
|
|
5
|
+
* POR QUÉ EXISTE, si el canal ya sabe capturar. `xone-hotswap '{"command":"getScreenshot"}'`
|
|
6
|
+
* es la vía buena —la pide la app host, así que sale lo que la app pinta— pero necesita el
|
|
7
|
+
* túnel y la app viva. Cuando eso no está, lo único que queda es `adb exec-out screencap`, y
|
|
8
|
+
* medido: el modelo la compone él y redirige la salida a un fichero, que en el `cwd` de la
|
|
9
|
+
* shell es LA RAÍZ DEL PROYECTO. Así apareció un `screenshot_login.png` de 773 KB entre los
|
|
10
|
+
* `.xne` del cliente, camino de la aprobación, de git y de CloudStudio.
|
|
11
|
+
*
|
|
12
|
+
* Un script no es una barrera —una shell alcanza el disco entero y eso no se arregla con
|
|
13
|
+
* prompts—, pero le quita el motivo: pedir la captura por su nombre cuesta menos que
|
|
14
|
+
* componer el `adb` y elegir dónde dejarla, y ya cae donde tiene que caer.
|
|
15
|
+
*
|
|
16
|
+
* LO QUE NO HACE: no imprime la imagen. Un PNG de pantalla completa son cientos de miles de
|
|
17
|
+
* caracteres en base64, y meterlos en el contexto es pagarlos sin que nadie pueda mirarlos.
|
|
18
|
+
* Sale el NOMBRE, que es con lo que la interfaz la enseña.
|
|
19
|
+
*
|
|
20
|
+
* USO:
|
|
21
|
+
* xone-captura-android captura a $XONECODE_ARTEFACTOS
|
|
22
|
+
* xone-captura-android --nombre login.png con nombre propio
|
|
23
|
+
* xone-captura-android --serie emulator-5554 con varios aparatos conectados
|
|
24
|
+
*/
|
|
25
|
+
import { execFileSync } from "node:child_process";
|
|
26
|
+
import { writeFileSync } from "node:fs";
|
|
27
|
+
import { join, basename } from "node:path";
|
|
28
|
+
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
31
|
+
console.error(`xone-captura-android — captura la pantalla del aparato con \`adb\` y la deja en artefactos.
|
|
32
|
+
|
|
33
|
+
xone-captura-android captura a $XONECODE_ARTEFACTOS
|
|
34
|
+
xone-captura-android --nombre login.png con nombre propio
|
|
35
|
+
xone-captura-android --serie <id> con varios aparatos conectados
|
|
36
|
+
|
|
37
|
+
Es la vía de RESPALDO: si el túnel y la app host están vivos, la buena es
|
|
38
|
+
\`xone-hotswap '{"command":"getScreenshot"}'\`, que captura lo que la app pinta.
|
|
39
|
+
Nunca redirijas \`adb exec-out screencap\` a un fichero: el \`cwd\` es la raíz del
|
|
40
|
+
proyecto del usuario y ahí no se escribe.`);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ARTEFACTOS = process.env.XONECODE_ARTEFACTOS;
|
|
45
|
+
if (ARTEFACTOS === undefined) {
|
|
46
|
+
// Sin sitio donde dejarla no se inventa uno: escribir en el `cwd` es exactamente el fallo
|
|
47
|
+
// que este script existe para no cometer.
|
|
48
|
+
console.error("no hay XONECODE_ARTEFACTOS: esta sesión no tiene carpeta de artefactos y la captura no se puede guardar.");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const opcion = (nombre) => {
|
|
53
|
+
const i = args.indexOf(`--${nombre}`);
|
|
54
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
55
|
+
};
|
|
56
|
+
const serie = opcion("serie");
|
|
57
|
+
|
|
58
|
+
// `basename` y no la cadena tal cual: el nombre lo elige el modelo, y un `../` aquí escribiría
|
|
59
|
+
// fuera de la carpeta de la sesión. Misma regla que `esRutaDeArtefacto` en el host.
|
|
60
|
+
const pedido = opcion("nombre");
|
|
61
|
+
const nombre = pedido === undefined ? `captura-${Date.now()}.png` : basename(pedido);
|
|
62
|
+
if (nombre === "" || nombre === "." || nombre === "..") {
|
|
63
|
+
console.error("ese nombre no vale para un fichero.");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let bytes;
|
|
68
|
+
try {
|
|
69
|
+
// `exec-out` y no `shell`: `shell` traduce los finales de línea y corrompe el PNG.
|
|
70
|
+
bytes = execFileSync("adb", [...(serie ? ["-s", serie] : []), "exec-out", "screencap", "-p"], {
|
|
71
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
72
|
+
});
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error(`no se pudo capturar con adb: ${error.message}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Una captura vacía no es una captura: `screencap` puede salir con 0 y no devolver nada si el
|
|
79
|
+
// aparato está bloqueado. Decirlo es mejor que dejar un fichero de 0 bytes con buena pinta.
|
|
80
|
+
if (bytes.length === 0) {
|
|
81
|
+
console.error("adb devolvió una captura VACÍA: ¿está el aparato despierto y desbloqueado?");
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const destino = join(ARTEFACTOS, nombre);
|
|
86
|
+
try {
|
|
87
|
+
writeFileSync(destino, bytes);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(`no se pudo guardar la captura: ${error.message}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(`captura guardada como ${nombre} (${bytes.length} bytes). Es de la vía nativa: enseña lo que el SISTEMA pinta, no lo que la app host dice.`);
|