xonecode 0.8.27 → 0.8.37
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-LdUr7EoT.js → index-Bm4ur93-.js} +69 -69
- package/apps/web/dist/assets/index-F-tnEn80.css +1 -0
- package/apps/web/dist/assets/{php-Cg-eWGpQ.js → php-D2qE525B.js} +1 -1
- package/apps/web/dist/assets/{ruby-EcV7Hon3.js → ruby-SvGm1EAa.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/dist/agent/grafo/escriturasEnSerie.js +57 -0
- package/dist/agent/grafo/escriturasEnSerie.js.map +1 -0
- package/dist/agent/grafo/proyecto.js +69 -3
- package/dist/agent/grafo/proyecto.js.map +1 -1
- package/dist/agent/grafo/traerDeLaMaquina.js +117 -0
- package/dist/agent/grafo/traerDeLaMaquina.js.map +1 -0
- package/dist/agent/grafo/validacionXone.js +79 -4
- package/dist/agent/grafo/validacionXone.js.map +1 -1
- package/dist/agent/grafo/xoneAgent.js +33 -1
- package/dist/agent/grafo/xoneAgent.js.map +1 -1
- package/dist/agent/subagentes/agentesEnDisco.js +34 -6
- package/dist/agent/subagentes/agentesEnDisco.js.map +1 -1
- package/dist/agent/trazaDeErroresEnDisco.js +45 -0
- package/dist/agent/trazaDeErroresEnDisco.js.map +1 -0
- package/dist/agent/turno/diagnosticoDeTools.js +10 -1
- package/dist/agent/turno/diagnosticoDeTools.js.map +1 -1
- package/dist/agent/turno/informeDeTraza.js +129 -6
- package/dist/agent/turno/informeDeTraza.js.map +1 -1
- package/dist/agent/turno/puente.js +89 -5
- package/dist/agent/turno/puente.js.map +1 -1
- package/dist/agent/turno/resumenDeTool.js +28 -0
- package/dist/agent/turno/resumenDeTool.js.map +1 -1
- package/dist/agent/turno/turnoReal.js +20 -2
- package/dist/agent/turno/turnoReal.js.map +1 -1
- package/dist/core/serieDeEscrituras.js +63 -0
- package/dist/core/serieDeEscrituras.js.map +1 -0
- package/dist/core/traerDeLaMaquina.js +80 -0
- package/dist/core/traerDeLaMaquina.js.map +1 -0
- package/dist/core/trazaDeErrores.js +108 -0
- package/dist/core/trazaDeErrores.js.map +1 -0
- package/dist/web/servidor/consolaWeb.js +6 -0
- package/dist/web/servidor/consolaWeb.js.map +1 -1
- package/package.json +2 -1
- package/skills/xone-hotswap/SKILL.md +76 -8
- package/skills/xone-hotswap/references/comandos.md +33 -0
- package/skills/xone-hotswap/references/conexion-y-despliegue.md +61 -1
- package/skills/xone-hotswap/scripts/xone-arrancar-ios +1 -1
- package/skills/xone-hotswap/scripts/xone-captura-android +91 -3
- package/skills/xone-hotswap/scripts/xone-desplegar-android +80 -5
- package/skills/xone-hotswap/scripts/xone-hotswap +151 -8
- package/apps/web/dist/assets/index-DKZqgHtg.css +0 -1
|
@@ -236,6 +236,26 @@ function concederPermisos() {
|
|
|
236
236
|
console.log(" no se pudo leer qué permisos pide; sigo");
|
|
237
237
|
return;
|
|
238
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* **El de dibujar sobre otras apps NO se concede con `pm grant`, y sin él el arranque es una
|
|
241
|
+
* carrera contra el foco.**
|
|
242
|
+
*
|
|
243
|
+
* `SYSTEM_ALERT_WINDOW` es un appop, no un permiso de ejecución, así que el bucle de abajo lo
|
|
244
|
+
* intenta y falla en silencio con los demás. Y lo que concede no es cosmético: Android 10 y
|
|
245
|
+
* superiores BLOQUEAN que una app en segundo plano arranque una actividad, y ese permiso es la
|
|
246
|
+
* exención que reconocen. El framework se va a segundo plano con solo apagarse la pantalla, o
|
|
247
|
+
* en cuanto se cierra la app — así que sin esto `launchApplication` contesta
|
|
248
|
+
* «Cannot launch app while the framework is in the background» y el despliegue parece que
|
|
249
|
+
* funcionó sin que arrancara nada. Es buena parte del síntoma que teníamos apuntado como
|
|
250
|
+
* «`result:true` significa aceptado, no arrancó».
|
|
251
|
+
*
|
|
252
|
+
* Idempotente, y su fallo no para nada: en un aparato donde no aplique, se sigue.
|
|
253
|
+
*/
|
|
254
|
+
try {
|
|
255
|
+
adbCallado("shell", "appops", "set", PAQUETE, "SYSTEM_ALERT_WINDOW", "allow");
|
|
256
|
+
} catch {
|
|
257
|
+
// Aparato que no lo admite. El despliegue sigue; lo que se pierde es la garantía de foco.
|
|
258
|
+
}
|
|
239
259
|
let dados = 0;
|
|
240
260
|
for (const permiso of declaradas) {
|
|
241
261
|
try {
|
|
@@ -260,7 +280,24 @@ function concederPermisos() {
|
|
|
260
280
|
function excepcionDeXOne() {
|
|
261
281
|
let log;
|
|
262
282
|
try {
|
|
263
|
-
|
|
283
|
+
/**
|
|
284
|
+
* **Acotado al PROCESO, no a las últimas N líneas del aparato.** Era `-t 600` a secas, y
|
|
285
|
+
* eso no es «lo último de la app»: son 600 líneas de TODO —Chromium, el sistema, lo que
|
|
286
|
+
* haya—, que en este emulador son un par de minutos. La excepción de XOne se salía de esa
|
|
287
|
+
* ventana enseguida, el despliegue no la veía y no contaba nada.
|
|
288
|
+
*
|
|
289
|
+
* Y lo que pasa después es el motivo de arreglarlo: sin el texto, a quien diagnostica solo
|
|
290
|
+
* le queda **fotografiar el diálogo de error** —visto en una sesión real—, que cuesta 3.375
|
|
291
|
+
* tokens y dice exactamente lo mismo que estas seis líneas.
|
|
292
|
+
*
|
|
293
|
+
* Con `--pid`, 600 líneas son 600 líneas DE LA APP. Es como ya leía `xone-log-android`;
|
|
294
|
+
* que dos sitios miraran el mismo log de forma distinta era la razón de que uno lo
|
|
295
|
+
* encontrara y el otro no.
|
|
296
|
+
*/
|
|
297
|
+
const pid = adbCallado("shell", "pidof", PAQUETE).trim().split(/\s+/)[0];
|
|
298
|
+
log = pid
|
|
299
|
+
? adbCallado("logcat", "-d", "--pid", pid, "-t", "600")
|
|
300
|
+
: adbCallado("logcat", "-d", "-t", "600");
|
|
264
301
|
} catch {
|
|
265
302
|
return undefined;
|
|
266
303
|
}
|
|
@@ -340,8 +377,27 @@ function exigirAparato() {
|
|
|
340
377
|
|
|
341
378
|
console.log("5/5 esperando al canal, lanzando y comprobando que está VIVA");
|
|
342
379
|
const cliente = join(import.meta.dirname, "xone-hotswap");
|
|
343
|
-
|
|
344
|
-
|
|
380
|
+
/**
|
|
381
|
+
* **En `clave=valor`, que es lo único que el cliente entiende.**
|
|
382
|
+
*
|
|
383
|
+
* Esto mandaba `JSON.stringify(comando)` y se rompió ENTERO el día que el cliente dejó de
|
|
384
|
+
* aceptar JSON: el objeto se metía como NOMBRE del comando, el aparato contestaba
|
|
385
|
+
* `Unknown command: {…}` y el despliegue no lanzaba nada. Lanzar la app, pedir el árbol y
|
|
386
|
+
* capturar —las tres cosas que hace este paso— pasaban por aquí, así que se cayeron las
|
|
387
|
+
* tres a la vez, y en silencio: el script reintenta diez veces y acaba diciendo que el
|
|
388
|
+
* canal no levantó, que manda a mirar el puerto y el paquete en vez de al cliente.
|
|
389
|
+
*
|
|
390
|
+
* La conversión es la misma que hace el cliente al revés: cada campo que no es `command`
|
|
391
|
+
* sale como `clave=valor`, y un objeto o un array se serializan porque el tipado del
|
|
392
|
+
* cliente los reconoce por la forma.
|
|
393
|
+
*/
|
|
394
|
+
const mandar = (comando) => {
|
|
395
|
+
const { command, ...resto } = comando;
|
|
396
|
+
const pares = Object.entries(resto).map(([k, v]) =>
|
|
397
|
+
`${k}=${typeof v === "object" && v !== null ? JSON.stringify(v) : String(v)}`,
|
|
398
|
+
);
|
|
399
|
+
return execFileSync("node", [cliente, command, ...pares], { encoding: "utf8" }).trim();
|
|
400
|
+
};
|
|
345
401
|
const esperar = (segundos) => execFileSync("sleep", [String(segundos)]);
|
|
346
402
|
|
|
347
403
|
/**
|
|
@@ -380,18 +436,37 @@ function exigirAparato() {
|
|
|
380
436
|
* hay que volver a preguntar. Tres órdenes encadenadas por el modelo son tres viajes;
|
|
381
437
|
* ésta es una.
|
|
382
438
|
*/
|
|
383
|
-
|
|
439
|
+
/**
|
|
440
|
+
* **El log ANTES que la captura, y por un fallo visto en vivo.**
|
|
441
|
+
*
|
|
442
|
+
* El orden era al revés y producía una foto que engaña: la app contesta el árbol, se
|
|
443
|
+
* cae mientras se pide la captura, y lo que sale fotografiado es la **pantalla del
|
|
444
|
+
* propio servidor hotswap** —«Applications / MyAllXOne / Explore / Delete», con
|
|
445
|
+
* «Awaiting connection…» abajo—. Eso no es la app, y quien lo mire después no tiene
|
|
446
|
+
* forma de saberlo: parece una app en blanco. Ocurrió con un `--captura` real y el
|
|
447
|
+
* usuario lo cazó mirando el artefacto.
|
|
448
|
+
*
|
|
449
|
+
* Cuando XOne ha reventado al cargar, la respuesta entera está en el LOG y la captura
|
|
450
|
+
* solo añade ruido —y 3.375 tokens si alguien se la enseña al revisor visual—. Así que
|
|
451
|
+
* primero se mira el log; si reventó, se cuenta y NO se captura.
|
|
452
|
+
*/
|
|
453
|
+
const roto = excepcionDeXOne();
|
|
454
|
+
if (args.includes("--captura") && roto === undefined) {
|
|
384
455
|
try {
|
|
385
456
|
console.log(mandar({ command: "getScreenshot" }));
|
|
386
457
|
} catch {
|
|
387
458
|
console.error("la app arrancó pero la captura falló; el árbol de arriba sí es bueno.");
|
|
388
459
|
}
|
|
460
|
+
} else if (args.includes("--captura")) {
|
|
461
|
+
console.error(
|
|
462
|
+
"NO capturo: XOne reventó al cargar y lo que saldría en la foto es la pantalla del " +
|
|
463
|
+
"host, no tu app. Lo que pasó está en el log de aquí abajo.",
|
|
464
|
+
);
|
|
389
465
|
}
|
|
390
466
|
// **Estar VIVA no es estar BIEN.** Una app que revienta al cargar la configuración se
|
|
391
467
|
// queda con su diálogo de error delante, y el canal contesta igual: el árbol llega, la
|
|
392
468
|
// captura sale, y todo parece haber ido bien. Quien lo cuenta es el log, y no pedirlo
|
|
393
469
|
// aquí obliga a que alguien se acuerde de hacerlo — que es justo lo que no pasa.
|
|
394
|
-
const roto = excepcionDeXOne();
|
|
395
470
|
if (roto !== undefined) {
|
|
396
471
|
console.error("");
|
|
397
472
|
console.error("La app ARRANCÓ pero XOne lanzó una excepción al cargar:");
|
|
@@ -95,9 +95,18 @@ if (pedidos.length === 0 || pedidos.includes("--help") || pedidos.includes("-h")
|
|
|
95
95
|
// un viaje y deja al modelo adivinando argumentos.
|
|
96
96
|
console.error(`xone-hotswap — manda comandos al servidor hotswap de la app (WebSocket).
|
|
97
97
|
|
|
98
|
-
xone-hotswap
|
|
99
|
-
xone-hotswap
|
|
100
|
-
xone-hotswap
|
|
98
|
+
xone-hotswap click name=MAP_BT
|
|
99
|
+
xone-hotswap waitForElement name=BTN_OK timeout=5000 -- click name=BTN_OK
|
|
100
|
+
xone-hotswap screen en qué pantalla estás (barato)
|
|
101
|
+
xone-hotswap elements el árbol de controles (va a $XONECODE_HOTSWAP)
|
|
102
|
+
xone-hotswap shot la captura va a $XONECODE_ARTEFACTOS
|
|
103
|
+
xone-hotswap shot name=MAP_ACEPTAR scale=0.5 quality=40 recortada a un control
|
|
104
|
+
xone-hotswap sql MiApp "SELECT * FROM CLIENTES LIMIT 20"
|
|
105
|
+
|
|
106
|
+
Cada comando es "<nombre> clave=valor ..."; con -- se encadenan varios por la MISMA
|
|
107
|
+
conexion, que es lo que hay que hacer (el servidor admite una sola y cierra la anterior).
|
|
108
|
+
Los valores se tipan solos: número, true/false y {...}/[...] como JSON.
|
|
109
|
+
Con --dry-run se imprime lo que se enviaría, sin conectar.
|
|
101
110
|
|
|
102
111
|
Antes, en Android, hace falta el túnel: adb forward tcp:8443 tcp:8443
|
|
103
112
|
Una respuesta larga no se imprime: una IMAGEN va a $XONECODE_ARTEFACTOS (y sale en el chat),
|
|
@@ -107,13 +116,124 @@ El catálogo entero de comandos está en el SKILL.md de xone-hotswap.`);
|
|
|
107
116
|
process.exit(pedidos.length === 0 ? 1 : 0);
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
/**
|
|
120
|
+
* De `clave=valor` a los objetos del protocolo, con `--` separando comandos.
|
|
121
|
+
*
|
|
122
|
+
* **POR QUÉ SE FUE EL JSON.** Antes cada comando era un objeto JSON entre comillas
|
|
123
|
+
* (`'{"command":"click","name":"BTN_OK"}'`). Medido sobre cinco comandos típicos, esta
|
|
124
|
+
* sintaxis los escribe en un **38 % menos de caracteres** —un `click` pasa de 37 a 17— y
|
|
125
|
+
* quita el escapado, que es donde un modelo se equivoca y gasta un viaje en volver a
|
|
126
|
+
* intentarlo. No se aceptan las dos: una capa de compatibilidad es deuda desde el primer día,
|
|
127
|
+
* y el JSON solo se escribía dentro de esta misma skill.
|
|
128
|
+
*
|
|
129
|
+
* Los valores se TIPAN solos —número, `true`/`false`, y `{…}`/`[…]` como JSON— porque el
|
|
130
|
+
* protocolo distingue `timeout=5000` de `"5000"` y `where={"ID":"7"}` es un objeto. Lo que no
|
|
131
|
+
* encaje en ninguno se queda como cadena, que es lo que casi todo es.
|
|
132
|
+
*/
|
|
133
|
+
function valorTipado(bruto) {
|
|
134
|
+
if (bruto === "true") return true;
|
|
135
|
+
if (bruto === "false") return false;
|
|
136
|
+
if (bruto !== "" && !Number.isNaN(Number(bruto))) return Number(bruto);
|
|
137
|
+
if (/^[{[]/.test(bruto)) {
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(bruto);
|
|
140
|
+
} catch {
|
|
141
|
+
salir(`«${bruto.slice(0, 60)}» empieza como JSON pero no lo es`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return bruto;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Los ATAJOS, que existen para lo que si no cuesta dos viajes o un base64 a mano.
|
|
149
|
+
*
|
|
150
|
+
* Cada uno es exactamente un comando del protocolo con sus campos ya puestos: no añaden
|
|
151
|
+
* capacidades, quitan la parte que se escribe mal. `shot` es el que más se nota — decodifica
|
|
152
|
+
* el JPEG y lo deja donde el harness lo anuncia.
|
|
153
|
+
*/
|
|
154
|
+
const ATAJOS = {
|
|
155
|
+
elements: (resto) => ({ command: "getAllElements", format: resto[0] ?? "xone" }),
|
|
156
|
+
screen: () => ({ command: "getCurrentScreen" }),
|
|
157
|
+
shot: (resto) => aplicarPares({ command: "getScreenshot" }, resto),
|
|
158
|
+
tables: (resto) => ({ command: "getDatabaseTableNames", appName: resto[0] }),
|
|
159
|
+
sql: (resto) => ({ command: "runSql", appName: resto[0], sql: resto.slice(1).join(" ") }),
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
function aplicarPares(json, pares) {
|
|
163
|
+
for (const par of pares) {
|
|
164
|
+
const corte = par.indexOf("=");
|
|
165
|
+
if (corte <= 0) salir(`«${par}» no es clave=valor`);
|
|
166
|
+
json[par.slice(0, corte)] = valorTipado(par.slice(corte + 1));
|
|
167
|
+
}
|
|
168
|
+
return json;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* `--dry-run`: imprime lo que se enviaría y no conecta.
|
|
173
|
+
*
|
|
174
|
+
* No es comodidad: es lo que hace comprobable el tipado de los valores sin un aparato
|
|
175
|
+
* delante —`timeout=5000` tiene que salir como número y `where={...}` como objeto, y si salen
|
|
176
|
+
* como cadena el servidor los rechaza sin decir por qué—. También deja probar un comando
|
|
177
|
+
* escrito a mano antes de gastarle un viaje al canal.
|
|
178
|
+
*/
|
|
179
|
+
const seco = pedidos.includes("--dry-run");
|
|
180
|
+
const utiles = pedidos.filter((a) => a !== "--dry-run");
|
|
181
|
+
|
|
110
182
|
const comandos = [];
|
|
111
|
-
for (const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
183
|
+
for (const trozo of partirPorSeparador(utiles)) {
|
|
184
|
+
const [cabeza, ...resto] = trozo;
|
|
185
|
+
/**
|
|
186
|
+
* **El JSON viejo se RECHAZA con la forma buena escrita, nunca se acepta a medias.**
|
|
187
|
+
*
|
|
188
|
+
* Al quitar el JSON, una llamada con la sintaxis de antes no fallaba: metía el objeto
|
|
189
|
+
* ENTERO como nombre del comando y mandaba
|
|
190
|
+
* `{"command":"{\"command\":\"getAllElements\",…}"}`. El aparato contestaba
|
|
191
|
+
* `Unknown command: {"command":"getAllElements","format":"xone"}` —visto en pantalla— y
|
|
192
|
+
* quien lo leyera no tenía forma de saber que el problema era la sintaxis del cliente y no
|
|
193
|
+
* el comando: parecía que el framework no conocía `getAllElements`. Un fallo que miente
|
|
194
|
+
* sobre dónde está invita a reintentar, y reintentar es lo que cuesta.
|
|
195
|
+
*
|
|
196
|
+
* No se ACEPTA —eso era la capa de compatibilidad que se decidió no tener— pero tampoco se
|
|
197
|
+
* traga: se dice qué escribir, que es el patrón de `porQueNo` y el de `xone_navegacion`
|
|
198
|
+
* cuando no sabe contestar.
|
|
199
|
+
*/
|
|
200
|
+
if (cabeza.startsWith("{")) {
|
|
201
|
+
let comoJson;
|
|
202
|
+
try {
|
|
203
|
+
comoJson = JSON.parse(cabeza);
|
|
204
|
+
} catch {
|
|
205
|
+
comoJson = undefined;
|
|
206
|
+
}
|
|
207
|
+
const nombre = typeof comoJson?.command === "string" ? comoJson.command : undefined;
|
|
208
|
+
const pares = nombre
|
|
209
|
+
? Object.entries(comoJson)
|
|
210
|
+
.filter(([k]) => k !== "command")
|
|
211
|
+
.map(([k, v]) => `${k}=${typeof v === "object" ? JSON.stringify(v) : String(v)}`)
|
|
212
|
+
.join(" ")
|
|
213
|
+
: "";
|
|
214
|
+
salir(
|
|
215
|
+
`este cliente ya NO recibe JSON. Escríbelo como "<comando> clave=valor".` +
|
|
216
|
+
(nombre ? `\n Lo que querías es: xone-hotswap ${nombre}${pares ? ` ${pares}` : ""}` : "") +
|
|
217
|
+
`\n Y con -- se encadenan varios por la misma conexión. Mira --help.`,
|
|
218
|
+
);
|
|
116
219
|
}
|
|
220
|
+
const atajo = ATAJOS[cabeza];
|
|
221
|
+
comandos.push(atajo ? atajo(resto) : aplicarPares({ command: cabeza }, resto));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (seco) {
|
|
225
|
+
for (const c of comandos) console.log(JSON.stringify(c));
|
|
226
|
+
process.exit(0);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Parte `a x=1 -- b y=2` en `[["a","x=1"],["b","y=2"]]`. Un `--` suelto no abre comando. */
|
|
230
|
+
function partirPorSeparador(args) {
|
|
231
|
+
const trozos = [[]];
|
|
232
|
+
for (const a of args) {
|
|
233
|
+
if (a === "--") trozos.push([]);
|
|
234
|
+
else trozos[trozos.length - 1].push(a);
|
|
235
|
+
}
|
|
236
|
+
return trozos.filter((t) => t.length > 0);
|
|
117
237
|
}
|
|
118
238
|
|
|
119
239
|
|
|
@@ -230,6 +350,29 @@ ws.onmessage = (ev) => {
|
|
|
230
350
|
siguiente();
|
|
231
351
|
return;
|
|
232
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* **Las tramas PUSH no son la respuesta de nadie, y tragárselas DESINCRONIZA el canal.**
|
|
355
|
+
*
|
|
356
|
+
* El protocolo **no correla peticiones con respuestas**: no hay identificador de comando,
|
|
357
|
+
* así que se emparejan por ORDEN —se manda de uno en uno y se toma la siguiente trama que
|
|
358
|
+
* no sea un push—. Eso lo documenta el cliente del equipo del framework
|
|
359
|
+
* (`pack_ia/scripts/hotswap-cli.mjs`), y aquí solo se filtraba el saludo.
|
|
360
|
+
*
|
|
361
|
+
* La consecuencia de que falten las otras dos no es perder una línea: es que **el push se
|
|
362
|
+
* cuenta como la respuesta del comando pendiente y TODO lo que viene después va
|
|
363
|
+
* desfasado** — cada comando recibe la contestación del anterior. Y no falla siempre, solo
|
|
364
|
+
* cuando llega un push en medio, que es lo que lo hace difícil de ver.
|
|
365
|
+
*
|
|
366
|
+
* Las dos que faltaban:
|
|
367
|
+
* - `logger: true` — una línea de log que el aparato empuja. Las produce cualquier cosa
|
|
368
|
+
* que encienda el registro, o sea justo lo que se hace para diagnosticar.
|
|
369
|
+
* - `state` — el progreso de una descarga.
|
|
370
|
+
*/
|
|
371
|
+
if (mensaje.logger === true || mensaje.state !== undefined || mensaje.command === "server_hello") {
|
|
372
|
+
// No se imprime: quien pidió un comando no pidió esto, y meterlo en la salida es meterlo
|
|
373
|
+
// en el contexto del agente. Lo que se conserva es el ORDEN.
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
233
376
|
console.log(JSON.stringify({ comando: pendiente?.command, ...sinBinarios(mensaje) }));
|
|
234
377
|
pendiente = undefined;
|
|
235
378
|
siguiente();
|