xonecode 0.1.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/README.md +167 -0
- package/dist/agent/authEnDisco.js +56 -0
- package/dist/agent/authEnDisco.js.map +1 -0
- package/dist/agent/configEnDisco.js +109 -0
- package/dist/agent/configEnDisco.js.map +1 -0
- package/dist/agent/crearProyecto.js +34 -0
- package/dist/agent/crearProyecto.js.map +1 -0
- package/dist/agent/entorno.js +124 -0
- package/dist/agent/entorno.js.map +1 -0
- package/dist/agent/guionizado.js +53 -0
- package/dist/agent/guionizado.js.map +1 -0
- package/dist/agent/instantanea.js +129 -0
- package/dist/agent/instantanea.js.map +1 -0
- package/dist/agent/interrupts.js +83 -0
- package/dist/agent/interrupts.js.map +1 -0
- package/dist/agent/mensajes.js +24 -0
- package/dist/agent/mensajes.js.map +1 -0
- package/dist/agent/modelos.js +42 -0
- package/dist/agent/modelos.js.map +1 -0
- package/dist/agent/normalizar.js +20 -0
- package/dist/agent/normalizar.js.map +1 -0
- package/dist/agent/perfiles.js +103 -0
- package/dist/agent/perfiles.js.map +1 -0
- package/dist/agent/proyecto.js +107 -0
- package/dist/agent/proyecto.js.map +1 -0
- package/dist/agent/puente.js +142 -0
- package/dist/agent/puente.js.map +1 -0
- package/dist/agent/resumenDeTool.js +48 -0
- package/dist/agent/resumenDeTool.js.map +1 -0
- package/dist/agent/skills.js +65 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/textoDeTool.js +113 -0
- package/dist/agent/textoDeTool.js.map +1 -0
- package/dist/agent/turnoReal.js +173 -0
- package/dist/agent/turnoReal.js.map +1 -0
- package/dist/agent/verificador.js +65 -0
- package/dist/agent/verificador.js.map +1 -0
- package/dist/agent/xoneAgent.js +106 -0
- package/dist/agent/xoneAgent.js.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/aprobar.js +82 -0
- package/dist/cli/aprobar.js.map +1 -0
- package/dist/cli/config.js +133 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/consola.js +346 -0
- package/dist/cli/consola.js.map +1 -0
- package/dist/cli/describe.js +53 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/doctor.js +45 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/main.js +520 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/markdown.js +90 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/run.js +164 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spinner.js +89 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/stdio.js +193 -0
- package/dist/cli/stdio.js.map +1 -0
- package/dist/cli/tema.js +40 -0
- package/dist/cli/tema.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/core/bitacora.js +30 -0
- package/dist/core/bitacora.js.map +1 -0
- package/dist/core/config.js +236 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contextos.js +56 -0
- package/dist/core/contextos.js.map +1 -0
- package/dist/core/deps.js +51 -0
- package/dist/core/deps.js.map +1 -0
- package/dist/core/diff.js +79 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/esqueleto.js +366 -0
- package/dist/core/esqueleto.js.map +1 -0
- package/dist/core/events.js +2 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/modelos.js +85 -0
- package/dist/core/modelos.js.map +1 -0
- package/dist/core/notify.js +106 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/ports.js +114 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/turno.js +127 -0
- package/dist/core/turno.js.map +1 -0
- package/dist/vendor/hitl.js +103 -0
- package/dist/vendor/hitl.js.map +1 -0
- package/dist/vendor/skillLoaders/catalog.js +74 -0
- package/dist/vendor/skillLoaders/catalog.js.map +1 -0
- package/dist/vendor/tokenTracking.js +58 -0
- package/dist/vendor/tokenTracking.js.map +1 -0
- package/package.json +41 -0
- package/skills/xone-debugging/SKILL.md +52 -0
- package/skills/xone-debugging/references/faq.md +561 -0
- package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
- package/skills/xone-development/SKILL.md +105 -0
- package/skills/xone-development/references/anti-patrones.md +94 -0
- package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
- package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
- package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
- package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
- package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
- package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
- package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
- package/skills/xone-development/references/datos/appdata.md +611 -0
- package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
- package/skills/xone-development/references/datos/http.md +366 -0
- package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
- package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
- package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
- package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
- package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
- package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
- package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
- package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
- package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
- package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
- package/skills/xone-development/references/indice-completo.md +77 -0
- package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
- package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
- package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
- package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
- package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
- package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
- package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
- package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
- package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
- package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
- package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
- package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
- package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
- package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
- package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
- package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
- package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
- package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
- package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
- package/skills/xone-development/references/tipos-de-prop.md +28 -0
- package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
- package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
- package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
- package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
- package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
- package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
- package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
- package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
- package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
- package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
- package/skills/xone-development/references/xml-ui/mapas.md +685 -0
- package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
- package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
- package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
- package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
- package/skills/xone-plan-builder/SKILL.md +183 -0
- package/skills/xone-plan-builder/agents/openai.yaml +5 -0
- package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
- package/skills/xone-project-generator/SKILL.md +111 -0
- package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
- package/skills/xone-project-generator/references/colecciones-base.md +16 -0
- package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
- package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
- package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
- package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
- package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
- package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
- package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
- package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
- package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
- package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
- package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
- package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
- package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
- package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
- package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
- package/skills/xone-review/SKILL.md +189 -0
- package/skills/xone-spec-builder/SKILL.md +235 -0
- package/skills/xone-spec-builder/agents/openai.yaml +5 -0
- package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
- package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
- package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { correrTurno } from "../core/turno.js";
|
|
2
|
+
import { pedirDecisiones, preguntarPorStdin } from "./aprobar.js";
|
|
3
|
+
import { crearPielStdio, escribirEnStdout } from "./stdio.js";
|
|
4
|
+
import { AgenteGuionizado } from "../agent/guionizado.js";
|
|
5
|
+
import { esDoble } from "../core/ports.js";
|
|
6
|
+
import { inspeccionar } from "../agent/entorno.js";
|
|
7
|
+
import { tomarInstantanea } from "../agent/instantanea.js";
|
|
8
|
+
import { SkillsEnDisco } from "../agent/skills.js";
|
|
9
|
+
import { Modelos } from "../agent/modelos.js";
|
|
10
|
+
import { abrirSesionReal } from "../agent/turnoReal.js";
|
|
11
|
+
/** La cabecera del turno real: qué hay detrás antes de que empiece a gastar. */
|
|
12
|
+
function cabecera(escribir, modelos, skills, instantanea, entorno) {
|
|
13
|
+
const desc = modelos.descripcion();
|
|
14
|
+
const papeles = ["rapido", "trabajo", "afilado"];
|
|
15
|
+
escribir("--- turno real ---\n");
|
|
16
|
+
for (const p of papeles)
|
|
17
|
+
escribir(` ${p.padEnd(8)} ${desc[p]}\n`);
|
|
18
|
+
escribir(` skills: ${skills.catalogo().length}\n`);
|
|
19
|
+
escribir(` foto del ANTES: ${instantanea.via === "git" ? "git" : "huellas de fichero"}\n`);
|
|
20
|
+
if (entorno.vistasAplanadas.length > 0) {
|
|
21
|
+
escribir(` ${entorno.vistasAplanadas.length} vista(s) aplanada(s) .xml ocultas al agente\n`);
|
|
22
|
+
}
|
|
23
|
+
escribir("\n");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Un disparo: una petición, un turno, y a la calle. Pipeable.
|
|
27
|
+
*
|
|
28
|
+
* Devuelve el código de salida. **Una pausa sin humano no es un éxito**: en modo un
|
|
29
|
+
* disparo no hay a quién preguntar, así que se sale con código distinto de cero para que
|
|
30
|
+
* CI no lea como «hecho» un turno que se quedó esperando una aprobación que nadie iba a
|
|
31
|
+
* dar. No se aprueba por omisión: aprobar EJECUTA, y ante la duda va la opción recuperable.
|
|
32
|
+
*/
|
|
33
|
+
export async function cmdRun(opciones) {
|
|
34
|
+
const escribir = opciones.escribir ?? escribirEnStdout;
|
|
35
|
+
if (opciones.real)
|
|
36
|
+
return correrReal(opciones, escribir);
|
|
37
|
+
const agente = new AgenteGuionizado(opciones.retardoMs ?? 0);
|
|
38
|
+
const piel = crearPielStdio(escribir);
|
|
39
|
+
if (esDoble(agente)) {
|
|
40
|
+
escribir("⚠ AGENTE DE PEGA: esto es un guion, no ha corrido ningún modelo.\n\n");
|
|
41
|
+
}
|
|
42
|
+
let pausado = false;
|
|
43
|
+
const pielQueApunta = {
|
|
44
|
+
...piel,
|
|
45
|
+
pausa: (pendientes) => {
|
|
46
|
+
pausado = true;
|
|
47
|
+
piel.pausa(pendientes);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
const bitacora = await correrTurno(agente.turno(opciones.peticion), pielQueApunta, {
|
|
51
|
+
avisos: (b) => (b.corrio("verify") && esDoble(agente) ? ["⚠ El veredicto es de pega."] : []),
|
|
52
|
+
});
|
|
53
|
+
if (pausado) {
|
|
54
|
+
escribir("\n(sin humano en modo `run`: nada se aprobó y nada se aplicó)\n");
|
|
55
|
+
return 2;
|
|
56
|
+
}
|
|
57
|
+
return bitacora.corrio("bloqueado") ? 1 : 0;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Excepción centinela del corte «sin humano»: privada a este fichero, nunca una genérica
|
|
61
|
+
* (un `Error` suelto se confundiría con un fallo real del turno).
|
|
62
|
+
*
|
|
63
|
+
* Para qué existe: `sesion.turno` SIEMPRE reanuda el grafo tras `pedirAprobacion`, incluso
|
|
64
|
+
* si todas las decisiones son rechazos —es lo correcto para la consola, donde el modelo
|
|
65
|
+
* tiene que saber que se rechazó. Pero el `run` de un disparo sin terminal es otro caso:
|
|
66
|
+
* los rechazos no los dio un humano sino el fail-closed, reanudar gastaría una llamada más
|
|
67
|
+
* al modelo sin que nadie vaya a cambiar de respuesta, y el comportamiento exigido es NO
|
|
68
|
+
* reanudar, dar el diff y salir con 2. La única forma de colgar la reanudación sin tocar
|
|
69
|
+
* `turnoReal.ts` es lanzar en el callback: el corte ocurre DESPUÉS de preguntar y decidir,
|
|
70
|
+
* o sea la pregunta y el «→ rechazado» se ven igual que siempre.
|
|
71
|
+
*/
|
|
72
|
+
class CorteSinHumano extends Error {
|
|
73
|
+
}
|
|
74
|
+
/** El turno real, en el orden del plan: entorno → foto → sesión → cabecera → turno → cambios. */
|
|
75
|
+
async function correrReal(opciones, escribir) {
|
|
76
|
+
const raiz = process.cwd();
|
|
77
|
+
const piel = crearPielStdio(escribir);
|
|
78
|
+
// 1. ¿Hay un proyecto aquí? Mismo diagnóstico que `doctor`, misma frase: no construir
|
|
79
|
+
// NADA sobre un sitio que no es un proyecto XOne.
|
|
80
|
+
const entorno = await inspeccionar(raiz);
|
|
81
|
+
if (!entorno.esProyectoXone) {
|
|
82
|
+
escribir(`${entorno.raiz} (no hay app.xml aquí)\n`);
|
|
83
|
+
escribir("✗ falta algo imprescindible: xonecode no puede trabajar aquí.\n");
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
// 2. La foto del ANTES, ANTES de construir nada: además de anunciarla en la cabecera, es
|
|
87
|
+
// la fuente del diff de la rama «sin humano» —esa rama sale por la centinela ANTES de
|
|
88
|
+
// que el turno devuelva nada, así que no puede usar el diff por-turno de la sesión.
|
|
89
|
+
const instantanea = await tomarInstantanea(raiz, entorno.git);
|
|
90
|
+
// 3. Lo que la sesión necesita. Los ficheros del proyecto NO se calculan aquí: los
|
|
91
|
+
// recorre la propia sesión al construir el agente.
|
|
92
|
+
const skills = new SkillsEnDisco();
|
|
93
|
+
const modelos = new Modelos(opciones.fuentes ?? {});
|
|
94
|
+
// 4. La cabecera va ANTES del turno: qué modelo, cuántas skills, cómo se tomó la foto.
|
|
95
|
+
cabecera(escribir, modelos, skills, instantanea, entorno);
|
|
96
|
+
// Con TTY el Enter aprueba; sin él NO, y es deliberado: en un pipe o en CI una línea en
|
|
97
|
+
// blanco no demuestra que haya nadie mirando, y esto aprueba escrituras sobre un
|
|
98
|
+
// proyecto real. La regla vive en `interpretAnswer`; aquí solo se le dice si hay TTY.
|
|
99
|
+
const interactivo = process.stdin.isTTY === true;
|
|
100
|
+
const preguntar = preguntarPorStdin();
|
|
101
|
+
let sinHumano = false;
|
|
102
|
+
const sesion = await abrirSesionReal({
|
|
103
|
+
raiz,
|
|
104
|
+
modelos,
|
|
105
|
+
skills,
|
|
106
|
+
entorno,
|
|
107
|
+
pedirAprobacion: async (lista, ficheros, diffs) => {
|
|
108
|
+
// **Se pregunta SIEMPRE, con TTY o sin él.** Cortar aquí sin preguntar dejaría
|
|
109
|
+
// muerto el conjunto de respuestas sin-TTY de `interpretAnswer`, que existe
|
|
110
|
+
// precisamente para que en un pipe o en CI se pueda aprobar con un `s` EXPLÍCITO.
|
|
111
|
+
// La garantía no la da negarse a preguntar, la da la asimetría: sin un sí
|
|
112
|
+
// explícito, es rechazo. Y si stdin está cerrado, `preguntarPorStdin` resuelve con
|
|
113
|
+
// cadena vacía — o sea, rechazo.
|
|
114
|
+
if (!interactivo) {
|
|
115
|
+
escribir(`\n⏸ ${lista.length} escritura(s) piden aprobación, y no hay terminal.\n`);
|
|
116
|
+
escribir(" Sin un «s» explícito se rechazan (una línea en blanco NO aprueba).\n");
|
|
117
|
+
}
|
|
118
|
+
const decisiones = await pedirDecisiones(lista, preguntar, escribir, {
|
|
119
|
+
interactive: interactivo,
|
|
120
|
+
fichero: (id) => ficheros.get(id),
|
|
121
|
+
diff: (id) => diffs.get(id),
|
|
122
|
+
});
|
|
123
|
+
if (!interactivo && [...decisiones.values()].every((d) => d.type === "reject")) {
|
|
124
|
+
sinHumano = true;
|
|
125
|
+
throw new CorteSinHumano();
|
|
126
|
+
}
|
|
127
|
+
return decisiones;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
// 5-6. El turno: la sesión conduce el stream y el bucle de aprobación — el tope de
|
|
131
|
+
// rondas lo controla ella. La centinela recorta el caso «sin humano» antes de la
|
|
132
|
+
// reanudación; cualquier otro throw sigue siendo un fallo real y se propaga.
|
|
133
|
+
let bitacora = null;
|
|
134
|
+
let cambios;
|
|
135
|
+
let cortadoPorTope = false;
|
|
136
|
+
try {
|
|
137
|
+
({ bitacora, cambios, cortadoPorTope } = await sesion.turno(opciones.peticion, piel));
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
if (!(e instanceof CorteSinHumano))
|
|
141
|
+
throw e;
|
|
142
|
+
cambios = await instantanea.cambios();
|
|
143
|
+
}
|
|
144
|
+
// 7. El diff contra la foto. Un turno que no tocó nada TIENE que verse igual.
|
|
145
|
+
escribir("\n--- cambios en el proyecto ---\n");
|
|
146
|
+
if (cambios.length === 0) {
|
|
147
|
+
escribir("sin cambios en el proyecto\n");
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
for (const c of cambios)
|
|
151
|
+
escribir(` [${c.clase}] ${c.ruta}\n`);
|
|
152
|
+
}
|
|
153
|
+
if (sinHumano) {
|
|
154
|
+
escribir("\n(sin humano en modo `run`: nada se aprobó y nada se aplicó)\n");
|
|
155
|
+
return 2;
|
|
156
|
+
}
|
|
157
|
+
// Agotar el tope de rondas es el MISMO desenlace que quedarse sin humano: hay escrituras
|
|
158
|
+
// sin resolver y nada se aplicó. Sin esta línea caía al camino normal y salía 0 — o sea,
|
|
159
|
+
// CI habría leído como éxito un turno que se quedó a medias.
|
|
160
|
+
if (cortadoPorTope)
|
|
161
|
+
return 2;
|
|
162
|
+
return bitacora.corrio("bloqueado") ? 1 : 0;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El spinner de fase: `⠋ planificando (12s)` animando LA ÚLTIMA línea.
|
|
3
|
+
*
|
|
4
|
+
* En una consola que no repinta, solo la última línea escrita se puede volver a
|
|
5
|
+
* pintar (con `\r`), y solo mientras nadie más escriba: eso es exactamente una fase
|
|
6
|
+
* del agente, que llena los segundos en que no pasa nada visible. La disciplina de
|
|
7
|
+
* «una sola línea al fondo» es la cascada: **cualquier otra escritura termina el
|
|
8
|
+
* spinner primero**, y el historial no se toca — la fase queda como línea estática,
|
|
9
|
+
* igual que ha salido siempre.
|
|
10
|
+
*
|
|
11
|
+
* Sin TTY (tema sin color) no hay nada que animar: la misma línea estática de
|
|
12
|
+
* siempre, y ningún temporizador.
|
|
13
|
+
*/
|
|
14
|
+
const CUADROS = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
15
|
+
/** Los segundos que lleva la fase: vacío hasta que cumple el primer segundo. */
|
|
16
|
+
export function sufijoDeTiempo(ms) {
|
|
17
|
+
const s = Math.floor(ms / 1000);
|
|
18
|
+
return s > 0 ? ` (${s}s)` : "";
|
|
19
|
+
}
|
|
20
|
+
export class AnimadorDeFase {
|
|
21
|
+
escribir;
|
|
22
|
+
tema;
|
|
23
|
+
intervalo;
|
|
24
|
+
reloj;
|
|
25
|
+
cuadro = 0;
|
|
26
|
+
texto = "";
|
|
27
|
+
t0 = 0;
|
|
28
|
+
temporizador;
|
|
29
|
+
constructor(escribir, tema,
|
|
30
|
+
/** Cómo de seguido repinta el fotograma. Entra por parámetro para los tests. */
|
|
31
|
+
intervalo = 120,
|
|
32
|
+
/** El reloj, inyectado por la misma razón: un test mueve el tiempo, no lo espera. */
|
|
33
|
+
reloj = Date.now) {
|
|
34
|
+
this.escribir = escribir;
|
|
35
|
+
this.tema = tema;
|
|
36
|
+
this.intervalo = intervalo;
|
|
37
|
+
this.reloj = reloj;
|
|
38
|
+
}
|
|
39
|
+
activo() {
|
|
40
|
+
return this.texto !== "";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Empieza a animar una fase. Sin color es la línea estática de siempre y no arranca
|
|
44
|
+
* nada; con color, la fase anterior (si la había) queda como línea estática.
|
|
45
|
+
*/
|
|
46
|
+
empieza(texto) {
|
|
47
|
+
if (this.tema.borrar === "") {
|
|
48
|
+
this.escribir(` · ${texto}\n`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.termina();
|
|
52
|
+
this.texto = texto;
|
|
53
|
+
this.t0 = this.reloj();
|
|
54
|
+
this.cuadro = 0;
|
|
55
|
+
this.pinta();
|
|
56
|
+
this.temporizador = setInterval(() => this.pinta(), this.intervalo);
|
|
57
|
+
// Un spinner olvidado no puede colgar el proceso: el temporizador no mantiene el
|
|
58
|
+
// bucle de eventos vivo por sí solo.
|
|
59
|
+
this.temporizador.unref();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Repinta el fotograma. Público a propósito: el `setInterval` llama aquí, y los
|
|
63
|
+
* tests también — sin esperar 120 ms de verdad.
|
|
64
|
+
*/
|
|
65
|
+
pinta() {
|
|
66
|
+
if (!this.activo())
|
|
67
|
+
return;
|
|
68
|
+
// Se pinta el cuadro y DESPUÉS se avanza: el primer fotograma es el ⠋, no el
|
|
69
|
+
// segundo — la fase que empieza no debería llegar ya movida.
|
|
70
|
+
this.escribir(`\r ${this.tema.mudo}${CUADROS[this.cuadro]} ${this.texto}` +
|
|
71
|
+
`${sufijoDeTiempo(this.reloj() - this.t0)}${this.tema.reset}`);
|
|
72
|
+
this.cuadro = (this.cuadro + 1) % CUADROS.length;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Deja la fase como línea estática en el historial y para el reloj. Sin fase activa
|
|
76
|
+
* no escribe nada: la cascada lo llama antes de CUALQUIER otra escritura.
|
|
77
|
+
*/
|
|
78
|
+
termina() {
|
|
79
|
+
if (!this.activo())
|
|
80
|
+
return;
|
|
81
|
+
if (this.temporizador !== undefined)
|
|
82
|
+
clearInterval(this.temporizador);
|
|
83
|
+
// El borrado limpia lo que el fotograma animado dejara escrito más allá de la
|
|
84
|
+
// línea estática: «(12s)» es más largo que el punto de la línea definitiva.
|
|
85
|
+
this.escribir(`\r${this.tema.borrar} · ${this.texto}\n`);
|
|
86
|
+
this.texto = "";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/cli/spinner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { crearTema } from "./tema.js";
|
|
2
|
+
import { RenderizadorDeMarkdown, puntoSeguro } from "./markdown.js";
|
|
3
|
+
import { AnimadorDeFase } from "./spinner.js";
|
|
4
|
+
/**
|
|
5
|
+
* Por debajo de esto, un párrafo que aún no ha visto su `\n` puede esperar: fluye al
|
|
6
|
+
* confirmarse la línea. Por encima, se corta por un punto seguro (`puntoSeguro`) para
|
|
7
|
+
* que el streaming siga vivo en los párrafos largos.
|
|
8
|
+
*/
|
|
9
|
+
const MINIMO_DE_FLUJO = 80;
|
|
10
|
+
/**
|
|
11
|
+
* La piel de terminal.
|
|
12
|
+
*
|
|
13
|
+
* El streaming del asistente se suelta por LÍNEA CONFIRMADA (y, en párrafos largos, por
|
|
14
|
+
* cortes que no rompen marcadores), y no por trozo: una consola que NO repinta solo
|
|
15
|
+
* puede escribir una línea cuando su texto es definitivo — un `**` partido entre dos
|
|
16
|
+
* escrituras queda literal para siempre. Sigue siendo streaming: la línea confirmada
|
|
17
|
+
* se pinta en el acto, y un colchón que solo se suelta al final sería un `ainvoke`
|
|
18
|
+
* disfrazado.
|
|
19
|
+
*
|
|
20
|
+
* Las otras dos cosas medidas, con el mismo síntoma (el streaming no se ve) y causas
|
|
21
|
+
* distintas:
|
|
22
|
+
*
|
|
23
|
+
* 1. **`flush` explícito** (en `escribirEnStdout`). Sin él la salida se queda en el
|
|
24
|
+
* buffer y no aparece hasta que se vacía sola — un fallo que solo se manifiesta en
|
|
25
|
+
* procesos de larga duración, o sea justo en el caso de uso real.
|
|
26
|
+
* 2. **Un solo `escribir` por línea confirmada**: juntar trozos en un buffer y volcar
|
|
27
|
+
* al final también evita frases partidas, pero deja de ser streaming.
|
|
28
|
+
*/
|
|
29
|
+
export function crearPielStdio(escribir, tema = crearTema(process.stdout.isTTY === true)) {
|
|
30
|
+
const render = new RenderizadorDeMarkdown(tema);
|
|
31
|
+
let colchon = "";
|
|
32
|
+
let inicioDeLinea = true;
|
|
33
|
+
// El spinner de fase: anima la ÚLTIMA línea mientras una fase dura. La cascada es
|
|
34
|
+
// esta `firme`: CUALQUIER otra escritura termina el spinner primero — así solo hay
|
|
35
|
+
// una línea viva al fondo, y el historial queda con la fase como línea estática.
|
|
36
|
+
const animador = new AnimadorDeFase(escribir, tema);
|
|
37
|
+
const firme = (t) => {
|
|
38
|
+
animador.termina();
|
|
39
|
+
escribir(t);
|
|
40
|
+
};
|
|
41
|
+
const volcar = (linea, inicio, conSalto) => {
|
|
42
|
+
firme(render.linea(linea, inicio) + (conSalto ? "\n" : ""));
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
token: (texto) => {
|
|
46
|
+
colchon += texto;
|
|
47
|
+
// Las líneas confirmadas se sueltan enteras y en el acto.
|
|
48
|
+
while (true) {
|
|
49
|
+
const salto = colchon.indexOf("\n");
|
|
50
|
+
if (salto === -1)
|
|
51
|
+
break;
|
|
52
|
+
volcar(colchon.slice(0, salto), inicioDeLinea, true);
|
|
53
|
+
colchon = colchon.slice(salto + 1);
|
|
54
|
+
inicioDeLinea = true;
|
|
55
|
+
}
|
|
56
|
+
// Párrafo largo sin `\n` a la vista: fluir por un corte que no rompa marcadores.
|
|
57
|
+
// **Sin salto final**: la continuación sigue en la MISMA línea de terminal — un
|
|
58
|
+
// salto aquí partiría el párrafo en líneas de la longitud del corte. Y
|
|
59
|
+
// `inicioDeLinea` baja a false para que la continuación no se mire como cabecera
|
|
60
|
+
// ni viñeta: ya no empieza línea.
|
|
61
|
+
const corte = puntoSeguro(colchon, MINIMO_DE_FLUJO);
|
|
62
|
+
if (corte !== undefined) {
|
|
63
|
+
volcar(colchon.slice(0, corte), inicioDeLinea, false);
|
|
64
|
+
colchon = colchon.slice(corte);
|
|
65
|
+
inicioDeLinea = false;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
cerrarLinea: () => {
|
|
69
|
+
// Lo que quede en el colchón es el final de la última línea. Si el mensaje acabó
|
|
70
|
+
// justo en un salto, no hay nada que añadir: esa línea ya se escribió entera.
|
|
71
|
+
if (colchon !== "")
|
|
72
|
+
volcar(colchon, inicioDeLinea, true);
|
|
73
|
+
colchon = "";
|
|
74
|
+
inicioDeLinea = true;
|
|
75
|
+
},
|
|
76
|
+
linea: (texto) => firme(` ${texto}\n`),
|
|
77
|
+
/**
|
|
78
|
+
* La fase, animada: sin TTY el propio animador escribe la línea estática de
|
|
79
|
+
* siempre y no arranca nada.
|
|
80
|
+
*/
|
|
81
|
+
fase: (texto) => animador.empieza(texto),
|
|
82
|
+
pausa: (pendientes) => {
|
|
83
|
+
// Quien pinta el detalle de cada pendiente (origen, descripción, fichero) es
|
|
84
|
+
// `pedirDecisiones`, al preguntar una a una. Aquí sería redundante, y el viejo
|
|
85
|
+
// «responde: approve / reject» además mentía: `interpretAnswer` acepta
|
|
86
|
+
// s/si/sí/y/yes, no esas palabras.
|
|
87
|
+
// En el modo de un disparo lo que sigue a la pausa NO es la respuesta del usuario:
|
|
88
|
+
// es la aprobación, que conduce `pedirDecisiones`. Decir «lo siguiente que escribas
|
|
89
|
+
// es la respuesta» sería mentira y despistaría justo en el paso delicado.
|
|
90
|
+
firme(`\n(turno pausado: ${pendientes.length} aprobación(es) pendiente(s))\n`);
|
|
91
|
+
},
|
|
92
|
+
fin: (ms) => firme(`\n(${(ms / 1000).toFixed(1)}s)\n`),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* El `escribir` de producción. El `flush` es implícito en Node —`process.stdout.write`
|
|
97
|
+
* escribe ya—, pero se pasa por aquí para que exista UN solo sitio que cambiar si algún
|
|
98
|
+
* día hace falta forzarlo.
|
|
99
|
+
*/
|
|
100
|
+
export const escribirEnStdout = (texto) => {
|
|
101
|
+
process.stdout.write(texto);
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* El `preguntar` de producción para la consola: usa el ÚNICO `readline.Interface`
|
|
105
|
+
* compartido con el lazo de líneas y con `leerSecreto`. Tres lectores de stdin no pueden
|
|
106
|
+
* competir por sus eventos `data`; con un solo `rl`, `rl.question` se intercala sin
|
|
107
|
+
* robarle líneas al `for await` del lazo (es readline quien arbitra internamente).
|
|
108
|
+
*
|
|
109
|
+
* **Si el rl ya está cerrado (o se cierra a media pregunta), resuelve con cadena vacía
|
|
110
|
+
* y no lanza.** Es el EOF de un pipe que se agota mientras el turno corre — medido en
|
|
111
|
+
* e2e: la aprobación pregunta 20 segundos después de que stdin hiciera EOF, y el
|
|
112
|
+
* `readline was closed` abortaba el turno con el interrupt colgado. Y la cadena vacía
|
|
113
|
+
* es lo correcto además de lo seguro: `interpretAnswer` sin un «s» explícito RECHAZA.
|
|
114
|
+
* Mismo pacto que `leerSecreto` con su `alCerrar`.
|
|
115
|
+
*/
|
|
116
|
+
export function crearPreguntar(rl) {
|
|
117
|
+
// `rl.closed` existe en runtime pero no está en los tipos de esta versión de
|
|
118
|
+
// @types/node — mismo caso que `rl.history` abajo, y mismo cast justificado.
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
+
const cerrado = () => rl.closed === true;
|
|
121
|
+
return (pregunta) => new Promise((resolver) => {
|
|
122
|
+
const alCerrar = () => resolver("");
|
|
123
|
+
rl.once("close", alCerrar);
|
|
124
|
+
if (cerrado()) {
|
|
125
|
+
rl.off("close", alCerrar);
|
|
126
|
+
resolver("");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
rl.question(pregunta, (respuesta) => {
|
|
130
|
+
rl.off("close", alCerrar);
|
|
131
|
+
resolver(respuesta);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* `leerSecreto` de producción (para `/provider <nombre>`): mismo `rl` que el lazo y
|
|
137
|
+
* `preguntar`, con el eco apagado mientras dura la pregunta.
|
|
138
|
+
*
|
|
139
|
+
* **No hay API pública de readline para silenciar el eco por tecla manteniendo la edición
|
|
140
|
+
* de línea** (backspace, etc.): la única costura es `_writeToOutput`, un método interno y
|
|
141
|
+
* no documentado — verificado contra Node 22 (existe, y es la vía que usan las
|
|
142
|
+
* herramientas de terminal que piden contraseña sin librería aparte). Se sustituye para
|
|
143
|
+
* dejar pasar SOLO el propio prompt (para que se vea la pregunta) y silenciar cualquier
|
|
144
|
+
* otra escritura —el eco de cada tecla— hasta que la pregunta termine. `rl.history`
|
|
145
|
+
* tampoco está en los tipos de esta versión de @types/node aunque existe en runtime
|
|
146
|
+
* (documentado desde Node 15.8), así que comparte el mismo cast justificado.
|
|
147
|
+
*
|
|
148
|
+
* Se restaura SIEMPRE, y por DOS vías, porque un terminal que se queda sin eco obliga al
|
|
149
|
+
* usuario a cerrarlo:
|
|
150
|
+
* 1. En el callback de `rl.question`: el camino normal.
|
|
151
|
+
* 2. En un `close` de `rl` a media pregunta (Ctrl-D/EOF sin dar línea): sin esto la
|
|
152
|
+
* promesa no se resuelve nunca y el manejador de `/provider` se queda colgado para
|
|
153
|
+
* siempre dentro del lazo de la consola.
|
|
154
|
+
*
|
|
155
|
+
* Y se borra la clave de `rl.history` tras leerla: readline añade la respuesta de
|
|
156
|
+
* `question` al historial igual que cualquier línea, y una flecha-arriba la recuperaría
|
|
157
|
+
* en claro en la MISMA sesión.
|
|
158
|
+
*/
|
|
159
|
+
export function crearLeerSecreto(rl) {
|
|
160
|
+
return (pregunta) => new Promise((resolve) => {
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
162
|
+
const rlAny = rl;
|
|
163
|
+
const escribirOriginal = rlAny._writeToOutput.bind(rlAny);
|
|
164
|
+
let restaurado = false;
|
|
165
|
+
const restaurar = () => {
|
|
166
|
+
if (restaurado)
|
|
167
|
+
return;
|
|
168
|
+
restaurado = true;
|
|
169
|
+
rlAny._writeToOutput = escribirOriginal;
|
|
170
|
+
rl.off("close", alCerrar);
|
|
171
|
+
};
|
|
172
|
+
const alCerrar = () => {
|
|
173
|
+
restaurar();
|
|
174
|
+
resolve("");
|
|
175
|
+
};
|
|
176
|
+
rl.once("close", alCerrar);
|
|
177
|
+
rlAny._writeToOutput = (cadena) => {
|
|
178
|
+
if (cadena === pregunta)
|
|
179
|
+
escribirOriginal(cadena);
|
|
180
|
+
};
|
|
181
|
+
rl.question(pregunta, (respuesta) => {
|
|
182
|
+
restaurar();
|
|
183
|
+
// El eco muteado se comió el '\r\n' que readline escribe al aceptar la línea: sin
|
|
184
|
+
// esto, la salida siguiente aparecería pegada a la pregunta.
|
|
185
|
+
escribirOriginal("\n");
|
|
186
|
+
const indice = rlAny.history.indexOf(respuesta);
|
|
187
|
+
if (indice !== -1)
|
|
188
|
+
rlAny.history.splice(indice, 1);
|
|
189
|
+
resolve(respuesta);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/cli/stdio.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/tema.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El vocabulario de color de las pieles: tokens SEMÁNTICOS, nunca colores.
|
|
3
|
+
*
|
|
4
|
+
* Una piel pide significado (`tema.exito`, `tema.mudo`) igual que la TUI de
|
|
5
|
+
* qwen-code pide `theme.status.success`: si mañana hay temas de usuario o el diff
|
|
6
|
+
* quiere fondos en vez de texto, se cambia ESTE fichero y ninguna piel se entera.
|
|
7
|
+
* Es el mismo principio que `core/ports.ts` aplicado al color: lo que puede cambiar
|
|
8
|
+
* entra por una costura, no se pega a quien lo usa.
|
|
9
|
+
*
|
|
10
|
+
* La otra mitad del contrato: **con `conColor: false`, TODO token es cadena vacía.**
|
|
11
|
+
* Las pieles no ramifican por TTY — componen `${t.mudo}texto${t.reset}` igual en una
|
|
12
|
+
* tubería que en un terminal, y lo que mantiene limpios los pipes y los logs de CI es
|
|
13
|
+
* que el tema apagado no tiene nada que poner. (Y `src/cli/tema.test.ts` recorre
|
|
14
|
+
* `src/` entera y falla si alguien escribe un ANSI fuera de aquí.)
|
|
15
|
+
*/
|
|
16
|
+
const CON_COLOR = {
|
|
17
|
+
texto: "",
|
|
18
|
+
mudo: "\x1b[2m",
|
|
19
|
+
negrita: "\x1b[1m",
|
|
20
|
+
exito: "\x1b[32m",
|
|
21
|
+
aviso: "\x1b[33m",
|
|
22
|
+
grave: "\x1b[31m",
|
|
23
|
+
// Mismo código que exito/grave a propósito: son tokens distintos porque significan
|
|
24
|
+
// cosas distintas. Si el diff algún día quiere fondo de color, cambia aquí.
|
|
25
|
+
anadido: "\x1b[32m",
|
|
26
|
+
quitado: "\x1b[31m",
|
|
27
|
+
prompt: "\x1b[36m",
|
|
28
|
+
reset: "\x1b[0m",
|
|
29
|
+
borrar: "\x1b[K",
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* El tema apagado se DERIVA del encendido, no se escribe aparte: un token nuevo que
|
|
33
|
+
* se olvide de apagarse falla en el test que recorre `Object.entries`, no en un pipe
|
|
34
|
+
* de CI a las tantas.
|
|
35
|
+
*/
|
|
36
|
+
const SIN_COLOR = Object.fromEntries(Object.keys(CON_COLOR).map((nombre) => [nombre, ""]));
|
|
37
|
+
export function crearTema(conColor) {
|
|
38
|
+
return conColor ? CON_COLOR : SIN_COLOR;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=tema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tema.js","sourceRoot":"","sources":["../../src/cli/tema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAwBH,MAAM,SAAS,GAAS;IACtB,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,UAAU;IACjB,mFAAmF;IACnF,4EAA4E;IAC5E,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,UAAU;IACnB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF;;;;GAIG;AACH,MAAM,SAAS,GAAS,MAAM,CAAC,WAAW,CACxC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAClC,CAAC;AAErB,MAAM,UAAU,SAAS,CAAC,QAAiB;IACzC,OAAO,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { SimuladorVerifier, ErrorDelSimulador } from "../agent/verificador.js";
|
|
2
|
+
import { huella } from "../core/ports.js";
|
|
3
|
+
import { escribirEnStdout } from "./stdio.js";
|
|
4
|
+
const MARCA = {
|
|
5
|
+
error: "✗",
|
|
6
|
+
warning: "⚠",
|
|
7
|
+
info: "·",
|
|
8
|
+
};
|
|
9
|
+
/** `code fichero:linea` — y sin la línea cuando no viene, que es el caso normal. */
|
|
10
|
+
function donde(h) {
|
|
11
|
+
if (!h.fichero)
|
|
12
|
+
return "";
|
|
13
|
+
return h.linea === undefined ? ` ${h.fichero}` : ` ${h.fichero}:${h.linea}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Corre el verificador REAL sobre un proyecto y cuenta lo que encuentra.
|
|
17
|
+
*
|
|
18
|
+
* Sale con 1 si hay errores. Los avisos no tumban el comando: son de otra severidad y
|
|
19
|
+
* colapsarlos con los errores es una decisión de producto que nadie ha tomado.
|
|
20
|
+
*/
|
|
21
|
+
export async function cmdVerify(ruta, escribir = escribirEnStdout, verificador = new SimuladorVerifier()) {
|
|
22
|
+
let informe;
|
|
23
|
+
try {
|
|
24
|
+
informe = await verificador.verificar(ruta);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
// Que no esté el binario NO es un fallo del proyecto. Distinguirlo importa: si no,
|
|
28
|
+
// un entorno mal montado se lee como «tu proyecto está roto».
|
|
29
|
+
escribir(`✗ no se pudo verificar: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
30
|
+
// La pista solo si viene al caso. Un `ErrorDelSimulador` significa que el binario
|
|
31
|
+
// CORRIÓ y contestó —lo que falla es la ruta, o el proyecto—, así que preguntar por
|
|
32
|
+
// la instalación manda a mirar donde no es: una pista falsa cuesta más que ninguna.
|
|
33
|
+
if (!(e instanceof ErrorDelSimulador)) {
|
|
34
|
+
escribir(" ¿está `xone-simulator` instalado? Compruébalo con `xonecode doctor`.\n");
|
|
35
|
+
}
|
|
36
|
+
return 70; // EX_SOFTWARE: es del entorno, no del proyecto
|
|
37
|
+
}
|
|
38
|
+
const errores = informe.hallazgos.filter((h) => h.severidad === "error");
|
|
39
|
+
const avisos = informe.hallazgos.filter((h) => h.severidad !== "error");
|
|
40
|
+
for (const h of informe.hallazgos) {
|
|
41
|
+
escribir(` ${MARCA[h.severidad]} ${h.code}${donde(h)}\n`);
|
|
42
|
+
escribir(` ${h.mensaje}\n`);
|
|
43
|
+
}
|
|
44
|
+
if (informe.verde) {
|
|
45
|
+
escribir(`\n✓ verde: ${ruta}\n`);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
// Las huellas REPETIDAS son el dato que delata que reparar no avanza. Se cuenta aquí
|
|
49
|
+
// porque es lo que el lazo (fase 7) va a usar para bloquear antes de gastar el tope.
|
|
50
|
+
const repetidas = new Set();
|
|
51
|
+
const vistas = new Set();
|
|
52
|
+
for (const h of informe.hallazgos) {
|
|
53
|
+
const f = huella(h);
|
|
54
|
+
if (vistas.has(f))
|
|
55
|
+
repetidas.add(f);
|
|
56
|
+
vistas.add(f);
|
|
57
|
+
}
|
|
58
|
+
escribir(`\n✗ ${errores.length} error(es), ${avisos.length} aviso(s)`);
|
|
59
|
+
escribir(repetidas.size > 0 ? `, ${repetidas.size} huella(s) repetida(s)\n` : "\n");
|
|
60
|
+
return errores.length > 0 ? 1 : 0;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/cli/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* La bitácora de UN turno. Se construye al empezarlo y muere con él.
|
|
3
|
+
*
|
|
4
|
+
* El alcance es la mitad del valor: una bitácora de proceso haría que los avisos de
|
|
5
|
+
* honestidad («el verificador es de pega») salieran en todos los turnos posteriores al
|
|
6
|
+
* primero, incluido un «cuéntame un chiste» que no verificó nada. Un aviso que salta
|
|
7
|
+
* cuando no ha pasado nada enseña al usuario a ignorarlo, que es lo contrario de lo que
|
|
8
|
+
* se compra con él.
|
|
9
|
+
*
|
|
10
|
+
* Por eso `corrio()` pregunta por lo que ha pasado AHORA, y nunca se le pregunta al objeto
|
|
11
|
+
* de dependencias, que vive en el proceso entero.
|
|
12
|
+
*/
|
|
13
|
+
export class Bitacora {
|
|
14
|
+
lineas = [];
|
|
15
|
+
/** `nodo` es la clave que luego consulta `corrio()`; `detalle` es para el humano. */
|
|
16
|
+
anota(nodo, detalle = "") {
|
|
17
|
+
this.lineas.push(detalle ? `${nodo}: ${detalle}` : `${nodo}:`);
|
|
18
|
+
}
|
|
19
|
+
/** ¿Ha corrido este nodo en ESTE turno? */
|
|
20
|
+
corrio(nodo) {
|
|
21
|
+
return this.lineas.some((l) => l.startsWith(`${nodo}:`));
|
|
22
|
+
}
|
|
23
|
+
get todo() {
|
|
24
|
+
return this.lineas;
|
|
25
|
+
}
|
|
26
|
+
get vacia() {
|
|
27
|
+
return this.lineas.length === 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=bitacora.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitacora.js","sourceRoot":"","sources":["../../src/core/bitacora.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,QAAQ;IACF,MAAM,GAAa,EAAE,CAAC;IAEvC,qFAAqF;IACrF,KAAK,CAAC,IAAY,EAAE,OAAO,GAAG,EAAE;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;IACjE,CAAC;IAED,2CAA2C;IAC3C,MAAM,CAAC,IAAY;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;IAClC,CAAC;CACF"}
|