xonecode 0.38.15 → 0.38.20
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/{EditorDeFichero-C9BEoDWh.js → EditorDeFichero-DySw4j79.js} +1 -1
- package/apps/web/dist/assets/index-D7OcbgXE.css +1 -0
- package/apps/web/dist/assets/index-vvfKjRva.js +506 -0
- package/apps/web/dist/assets/{php-DIdt3Vgn.js → php-CSyDZ6m1.js} +1 -1
- package/apps/web/dist/assets/{ruby-DYEvg0Dr.js → ruby-3bX3v6o_.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/dist/agent/conectores/gestorJira.js +118 -1
- package/dist/agent/conectores/gestorJira.js.map +1 -1
- package/dist/agent/dispositivos/decodificarImagen.js +8 -0
- package/dist/agent/dispositivos/decodificarImagen.js.map +1 -1
- package/dist/agent/motores/trueforge/convertirAP.js +86 -0
- package/dist/agent/motores/trueforge/convertirAP.js.map +1 -0
- package/dist/agent/motores/trueforge/sesionTrueforge.js +37 -2
- package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
- package/dist/agent/subagentes/agentesEnDisco.js +28 -3
- package/dist/agent/subagentes/agentesEnDisco.js.map +1 -1
- package/dist/agent/subagentes/escrituraExterna.js +8 -1
- package/dist/agent/subagentes/escrituraExterna.js.map +1 -1
- package/dist/agent/subagentes/pluginDeSkills.js +21 -6
- package/dist/agent/subagentes/pluginDeSkills.js.map +1 -1
- package/dist/agent/subagentes/subagenteExterno.js +8 -3
- package/dist/agent/subagentes/subagenteExterno.js.map +1 -1
- package/dist/agent/tareas/reflejoEnElGestor.js +120 -0
- package/dist/agent/tareas/reflejoEnElGestor.js.map +1 -0
- package/dist/agent/tareas/sondeoDelGestor.js +201 -0
- package/dist/agent/tareas/sondeoDelGestor.js.map +1 -0
- package/dist/agent/turno/turnoReal.js +1 -0
- package/dist/agent/turno/turnoReal.js.map +1 -1
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/main.js +2 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/core/agentes.js +34 -0
- package/dist/core/agentes.js.map +1 -1
- package/dist/core/config.js +29 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/convertirAP.js +87 -0
- package/dist/core/convertirAP.js.map +1 -0
- package/dist/core/entrega.js +23 -0
- package/dist/core/entrega.js.map +1 -1
- package/dist/core/geometriaDePantalla.js +33 -0
- package/dist/core/geometriaDePantalla.js.map +1 -1
- package/dist/core/gestorDeTareas.js.map +1 -1
- package/dist/core/notify.js +2 -0
- package/dist/core/notify.js.map +1 -1
- package/dist/core/ports.js +36 -2
- package/dist/core/ports.js.map +1 -1
- package/dist/core/tareas.js.map +1 -1
- package/dist/core/tareasEnElGestor.js +197 -0
- package/dist/core/tareasEnElGestor.js.map +1 -0
- package/dist/web/servidor/arranque.js +446 -42
- package/dist/web/servidor/arranque.js.map +1 -1
- package/dist/web/servidor/consolaDeTarea.js +2 -0
- package/dist/web/servidor/consolaDeTarea.js.map +1 -1
- package/dist/web/servidor/corredorDeTareas.js +14 -2
- package/dist/web/servidor/corredorDeTareas.js.map +1 -1
- package/dist/web/servidor/transporte.js +10 -0
- package/dist/web/servidor/transporte.js.map +1 -1
- package/package.json +1 -1
- package/skills/xone-development/references/css/selectores-unidades-colores.md +2 -0
- package/apps/web/dist/assets/index-BpDt_1vN.js +0 -506
- package/apps/web/dist/assets/index-D_Z-V9n1.css +0 -1
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
* en la omisión (Ollama local) con una clave de Anthropic recién escrita al lado.
|
|
29
29
|
*/
|
|
30
30
|
import { hayMemoriaDeHilo, olvidarMemoriaDeHilo } from "../../agent/sesiones/memoriaDeHilo.js";
|
|
31
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
31
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
32
32
|
import { lineaDeVersion } from "../../core/version.js";
|
|
33
33
|
import { esperarInterrupcion } from "./interrupcion.js";
|
|
34
34
|
import { basename, dirname, join } from "node:path";
|
|
35
35
|
import { fileURLToPath } from "node:url";
|
|
36
36
|
import { randomUUID } from "node:crypto";
|
|
37
37
|
import { homedir } from "node:os";
|
|
38
|
-
import { escribirAgente, leerAgente, motivoDeNombreInaceptable } from "../../core/agentes.js";
|
|
38
|
+
import { conLoQueLaVentanaNoEdita, escribirAgente, leerAgente, motivoDeNombreInaceptable } from "../../core/agentes.js";
|
|
39
39
|
import { motivoDeNombreDeSkillInaceptable, nombreDeSkillSugerido, } from "../../core/skills.js";
|
|
40
40
|
import { borrarSkill, cargarSkills, esDeSerie as esDeSerieLaSkill, guardarSkill, instalarSkillDesdeZip, } from "../../agent/grafo/skills.js";
|
|
41
41
|
import { TOPE_DEL_ZIP } from "../../core/zipDeSkill.js";
|
|
@@ -125,6 +125,9 @@ import { servicioDeConectoresCableado } from "../../agent/conectores/servicioDeC
|
|
|
125
125
|
import { crearGestorJira } from "../../agent/conectores/gestorJira.js";
|
|
126
126
|
import { crearGestorNotion } from "../../agent/conectores/gestorNotion.js";
|
|
127
127
|
import { admiteMiasDelVinculo, motivoDeProyectoInaceptable, transicionPropuesta, comentarioDeCierre, } from "../../core/gestorDeTareas.js";
|
|
128
|
+
import { cerrarTicket, comentarioDeEncargo, encargoConTicket, esEstado, mapeoPropuesto } from "../../core/tareasEnElGestor.js";
|
|
129
|
+
import { crearSondeoDelGestor } from "../../agent/tareas/sondeoDelGestor.js";
|
|
130
|
+
import { anotarAvisoEnElDisco, conReflejoAlGuardar, crearReflejoEnElGestor } from "../../agent/tareas/reflejoEnElGestor.js";
|
|
128
131
|
import { datosDeCierre } from "./datosDeCierre.js";
|
|
129
132
|
import { ErrorDeDescargaCortada, escribirPaquete, listarSoporte, motivoParaNoExportar } from "./soporte.js";
|
|
130
133
|
import { nombreDelPaquete } from "../../core/paqueteDeSoporte.js";
|
|
@@ -242,6 +245,65 @@ function escaparHtml(texto) {
|
|
|
242
245
|
export function sinCorreos(texto) {
|
|
243
246
|
return texto.replace(/[^\s@<>()"'«»]+@[^\s@<>()"'«»]+\.[^\s@<>()"'«»]+/g, "[correo]");
|
|
244
247
|
}
|
|
248
|
+
/** Un error de un gestor de tareas como texto que puede cruzar el cable y quedarse en una tarea: de Node solo el
|
|
249
|
+
* `code`, y del resto la primera línea con los correos TACHADOS y recortada (la regla de `motivoDelGestor`). */
|
|
250
|
+
export function motivoDeGestorParaElCable(error) {
|
|
251
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string"
|
|
252
|
+
? error.code
|
|
253
|
+
: sinCorreos((error instanceof Error ? error.message : String(error)).split(/\r?\n/)[0]).slice(0, 200);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Lo que lleva el comentario de cierre de una tarea: lo mismo que el «Cerrar en …» de un chat sobre SU conversación
|
|
257
|
+
* (ficheros y commits de la sesión, el último veredicto del verificador, la última respuesta). Sin conversación, o si
|
|
258
|
+
* no se puede leer, un cierre sin datos —«no corrió», nunca un «verde» inventado—.
|
|
259
|
+
*/
|
|
260
|
+
/**
|
|
261
|
+
* Las copias locales con el DISPARO del gestor encendido (la puerta 2): `<workspace>/<entorno>/<proyecto>` con su
|
|
262
|
+
* `config.json`, de donde salen su identidad (`cloudstudio.proyecto`: el id con el que la pestaña Tareas filtra) y su
|
|
263
|
+
* vínculo. Se relee en cada pasada: el interruptor se cambia con todo en marcha. Lo que no se puede leer se salta.
|
|
264
|
+
*/
|
|
265
|
+
export function copiasConGestor(base) {
|
|
266
|
+
const copias = [];
|
|
267
|
+
const carpetas = (ruta) => {
|
|
268
|
+
try {
|
|
269
|
+
return readdirSync(ruta, { withFileTypes: true })
|
|
270
|
+
.filter((e) => e.isDirectory() && !e.name.startsWith("."))
|
|
271
|
+
.map((e) => e.name);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
for (const entorno of carpetas(base)) {
|
|
278
|
+
for (const nombre of carpetas(join(base, entorno))) {
|
|
279
|
+
const raiz = join(base, entorno, nombre);
|
|
280
|
+
if (!existsSync(join(raiz, ".xonecode", "config.json")))
|
|
281
|
+
continue;
|
|
282
|
+
try {
|
|
283
|
+
const c = cargar(raiz).config.proyecto;
|
|
284
|
+
const vinculo = c?.gestorDeTareas;
|
|
285
|
+
const identidad = c?.cloudstudio?.proyecto;
|
|
286
|
+
if (vinculo?.automatico !== true || identidad === undefined)
|
|
287
|
+
continue;
|
|
288
|
+
copias.push({ proyecto: { id: identidad.id, raiz, nombre: identidad.nombre }, vinculo });
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// Un config.json roto no para a los demás proyectos.
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return copias;
|
|
296
|
+
}
|
|
297
|
+
export async function datosDeCierreDeTarea(tarea) {
|
|
298
|
+
if (tarea.sesion === undefined)
|
|
299
|
+
return { ficheros: [], commits: [] };
|
|
300
|
+
try {
|
|
301
|
+
return await datosDeCierre(tarea.proyecto.raiz, tarea.sesion);
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return { ficheros: [], commits: [] };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
245
307
|
/**
|
|
246
308
|
* El mensaje `gestor` con los campos que su ACCIÓN pide, o `undefined`. Llega del navegador sin
|
|
247
309
|
* tipos: `atenderGestor` confía en esta forma, así que se comprueba aquí y no dentro.
|
|
@@ -962,11 +1024,17 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
962
1024
|
// mismo fichero que se puede editar a mano, así que el cargador es la única
|
|
963
1025
|
// autoridad sobre si vale. Si no pasara, el agente desaparecería al siguiente
|
|
964
1026
|
// arranque sin que nadie hubiera hecho nada raro.
|
|
965
|
-
|
|
1027
|
+
/**
|
|
1028
|
+
* Los campos que la ventana no edita (`escribeEn`, `llama`, `espera`, `pensamiento`) no viajan por el cable: se
|
|
1029
|
+
* toman del agente que la persona tenía DELANTE —el en vigor con ese nombre, o el de antes en un renombrado—, o
|
|
1030
|
+
* el guardado los borraría (`core/agentes.ts#conLoQueLaVentanaNoEdita`).
|
|
1031
|
+
*/
|
|
1032
|
+
const previo = cargarAgentes(abierto?.raiz).agentes.find((a) => a.nombre === (renombrandoDe ?? mensaje.agente.nombre));
|
|
1033
|
+
const candidato = conLoQueLaVentanaNoEdita(previo, {
|
|
966
1034
|
...mensaje.agente,
|
|
967
1035
|
motor: mensaje.agente.motor,
|
|
968
1036
|
origen: mensaje.ambito,
|
|
969
|
-
};
|
|
1037
|
+
});
|
|
970
1038
|
const comprobado = leerAgente(candidato.nombre, escribirAgente(candidato), mensaje.ambito);
|
|
971
1039
|
if ("error" in comprobado) {
|
|
972
1040
|
informar(`no se guarda «${candidato.nombre}»: ${comprobado.error}`);
|
|
@@ -1640,7 +1708,10 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1640
1708
|
* que la propia ruta de subida aplica: forma de segmento llano, y que no sea ya una
|
|
1641
1709
|
* tarea.
|
|
1642
1710
|
*/
|
|
1643
|
-
borrador
|
|
1711
|
+
borrador,
|
|
1712
|
+
/** El ticket del gestor YA creado (`atenderCrearTareaEnGestor`): se copia a la tarea antes de guardarla, porque
|
|
1713
|
+
* guardar dispara al corredor y el reflejo de «empezar» tiene que encontrarlo. */
|
|
1714
|
+
ticket) => {
|
|
1644
1715
|
if (opciones.colaDeTareas === undefined)
|
|
1645
1716
|
return undefined;
|
|
1646
1717
|
const proyecto = proyectoParaTarea(proyectoId);
|
|
@@ -1668,10 +1739,88 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1668
1739
|
adjuntos: opciones.colaDeTareas.listarAdjuntos(id),
|
|
1669
1740
|
estado: "nuevo",
|
|
1670
1741
|
creada: new Date().toISOString(),
|
|
1742
|
+
...(ticket === undefined ? {} : { ticket }),
|
|
1671
1743
|
};
|
|
1672
1744
|
opciones.colaDeTareas.guardar([...lista, nueva]);
|
|
1673
1745
|
return { id: nueva.id };
|
|
1674
1746
|
};
|
|
1747
|
+
/**
|
|
1748
|
+
* El gestor en el que vivirá una tarea NUEVA de ese proyecto, si lo tiene: su vínculo (del `config.json` del PROYECTO,
|
|
1749
|
+
* leído aquí y no dicho por el cliente: la ventana se abre también desde el escritorio para otro proyecto) y un
|
|
1750
|
+
* gestor que sabe CREAR. Sin vínculo, o con un gestor sin `crear`, `undefined` y la tarea se encola en local como
|
|
1751
|
+
* siempre (límite declarado: lo dice la ventana).
|
|
1752
|
+
*/
|
|
1753
|
+
const gestorParaTareaNueva = (proyectoId) => {
|
|
1754
|
+
const proyecto = proyectoParaTarea(proyectoId);
|
|
1755
|
+
if (proyecto === undefined)
|
|
1756
|
+
return undefined;
|
|
1757
|
+
let vinculo;
|
|
1758
|
+
try {
|
|
1759
|
+
vinculo = cargar(proyecto.raiz).config.proyecto?.gestorDeTareas;
|
|
1760
|
+
}
|
|
1761
|
+
catch {
|
|
1762
|
+
return undefined;
|
|
1763
|
+
}
|
|
1764
|
+
if (vinculo === undefined)
|
|
1765
|
+
return undefined;
|
|
1766
|
+
const gestor = gestorDe(vinculo.conector);
|
|
1767
|
+
if (gestor?.crear === undefined)
|
|
1768
|
+
return undefined;
|
|
1769
|
+
return { proyecto, vinculo, gestor: gestor };
|
|
1770
|
+
};
|
|
1771
|
+
/**
|
|
1772
|
+
* Crear una tarea EN el gestor (`docs/superpowers/specs/2026-10-15-tareas-desde-el-gestor-design.md`, puerta 1): el
|
|
1773
|
+
* ticket primero, luego el encargo como comentario, y SOLO entonces la tarea local con el ticket puesto. Si el gestor
|
|
1774
|
+
* no deja crear, NO se crea la tarea local —con vínculo, una tarea que no está en el gestor es justo lo que se
|
|
1775
|
+
* pierde— y se contesta 409 con el motivo. Si el ticket se creó pero el comentario no, la tarea se crea igual y se
|
|
1776
|
+
* dice: el ticket ya existe.
|
|
1777
|
+
*/
|
|
1778
|
+
const atenderCrearTareaEnGestor = async (destino, peticion, encargo, borrador) => {
|
|
1779
|
+
if (opciones.colaDeTareas === undefined)
|
|
1780
|
+
return { estado: 409, cuerpo: { motivo: "esta ejecución no tiene cola de tareas" } };
|
|
1781
|
+
// El borrador se comprueba ANTES de tocar el gestor: un ticket creado para una tarea que luego no se guarda quedaría huérfano.
|
|
1782
|
+
if (borrador !== undefined && !esBorradorLibre(borrador, opciones.colaDeTareas.listar())) {
|
|
1783
|
+
return { estado: 409, cuerpo: { motivo: "ese borrador de adjuntos no vale (o ya es una tarea)" } };
|
|
1784
|
+
}
|
|
1785
|
+
const { gestor, vinculo } = destino;
|
|
1786
|
+
// Solo para que el ticket llegue con su enlace: sin la url del sitio se crea igual, sin `url`.
|
|
1787
|
+
await asegurarSitios(gestor).catch(() => { });
|
|
1788
|
+
let creada;
|
|
1789
|
+
try {
|
|
1790
|
+
creada = await gestor.crear(vinculo, { titulo: tituloDeTarea(peticion), descripcion: peticion });
|
|
1791
|
+
}
|
|
1792
|
+
catch (error) {
|
|
1793
|
+
return { estado: 409, cuerpo: { motivo: `no se pudo crear la tarea en el gestor: ${motivoDelGestor(error)}` } };
|
|
1794
|
+
}
|
|
1795
|
+
let aviso;
|
|
1796
|
+
try {
|
|
1797
|
+
await gestor.comentar(vinculo, creada.clave, comentarioDeEncargo(encargo));
|
|
1798
|
+
}
|
|
1799
|
+
catch (error) {
|
|
1800
|
+
aviso = `se creó ${creada.clave}, pero no se pudo subir el encargo: ${motivoDelGestor(error)}`;
|
|
1801
|
+
}
|
|
1802
|
+
const ticket = {
|
|
1803
|
+
conector: vinculo.conector,
|
|
1804
|
+
sitio: vinculo.sitio,
|
|
1805
|
+
proyecto: vinculo.proyecto,
|
|
1806
|
+
clave: creada.clave,
|
|
1807
|
+
...(creada.url === undefined ? {} : { url: creada.url }),
|
|
1808
|
+
...(aviso === undefined ? {} : { aviso }),
|
|
1809
|
+
// El mapeo de estados del vínculo, COPIADO: a qué estado pasa al empezar y al terminar.
|
|
1810
|
+
...(vinculo.estados === undefined ? {} : { estados: vinculo.estados }),
|
|
1811
|
+
};
|
|
1812
|
+
// Con la línea del ticket delante (`encargoConTicket`): en Jira el comentario lleva el encargo SIN ella —ya está en el ticket—.
|
|
1813
|
+
const nueva = atenderCrearTarea(destino.proyecto.id, peticion, encargoConTicket(encargo, ticket), borrador, ticket);
|
|
1814
|
+
if (nueva === undefined) {
|
|
1815
|
+
return { estado: 409, cuerpo: { motivo: `se creó ${creada.clave} en el gestor, pero no la tarea local: créala de nuevo o bórrala allí` } };
|
|
1816
|
+
}
|
|
1817
|
+
opciones.revisarTareas?.();
|
|
1818
|
+
emitirTareas();
|
|
1819
|
+
return {
|
|
1820
|
+
estado: 200,
|
|
1821
|
+
cuerpo: { id: nueva.id, ticket: { clave: creada.clave, ...(creada.url === undefined ? {} : { url: creada.url }) }, ...(aviso === undefined ? {} : { aviso }) },
|
|
1822
|
+
};
|
|
1823
|
+
};
|
|
1675
1824
|
/**
|
|
1676
1825
|
* ¿Se puede adoptar ese id de borrador? Segmento llano Y que no sea ya una tarea.
|
|
1677
1826
|
*
|
|
@@ -3671,6 +3820,9 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
3671
3820
|
proyecto: vinculo.proyecto,
|
|
3672
3821
|
...(nombre === undefined ? {} : { nombreDelSitio: nombre }),
|
|
3673
3822
|
...(nombreProyecto === undefined ? {} : { nombreDelProyecto: nombreProyecto }),
|
|
3823
|
+
...(vinculo.estados === undefined ? {} : { estados: vinculo.estados }),
|
|
3824
|
+
...(vinculo.automatico === true ? { automatico: true } : {}),
|
|
3825
|
+
...(vinculo.revisarEncargo === true ? { revisarEncargo: true } : {}),
|
|
3674
3826
|
},
|
|
3675
3827
|
}),
|
|
3676
3828
|
...(admiteMias === undefined ? {} : { admiteMias }),
|
|
@@ -3817,6 +3969,15 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
3817
3969
|
proyecto: m.proyecto,
|
|
3818
3970
|
admiteMias,
|
|
3819
3971
|
...(nombreVisto.trim() === "" ? {} : { nombreDelProyecto: nombreVisto }),
|
|
3972
|
+
// Volver a vincular el MISMO proyecto del gestor conserva su mapeo de estados y su disparo; otro proyecto
|
|
3973
|
+
// tiene otro flujo, y empieza sin ellos (apagado).
|
|
3974
|
+
...(anterior !== undefined && anterior.conector === m.conector && anterior.sitio === m.sitio && anterior.proyecto === m.proyecto
|
|
3975
|
+
? {
|
|
3976
|
+
...(anterior.estados === undefined ? {} : { estados: anterior.estados }),
|
|
3977
|
+
...(anterior.automatico === true ? { automatico: true } : {}),
|
|
3978
|
+
...(anterior.revisarEncargo === true ? { revisarEncargo: true } : {}),
|
|
3979
|
+
}
|
|
3980
|
+
: {}),
|
|
3820
3981
|
};
|
|
3821
3982
|
if (!guardar(() => guardarGestorDeProyecto(raiz, vinculo)))
|
|
3822
3983
|
return;
|
|
@@ -3998,20 +4159,10 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
3998
4159
|
// TICKET no hace falta para escribir, así que basta con lo que la sesión guardó al
|
|
3999
4160
|
// abrirse. Se completa con el del proyecto por si acaso; vacío si ya no hay vínculo.
|
|
4000
4161
|
const vinculo = { conector: ticket.conector, sitio: ticket.sitio, proyecto: delProyecto().vinculo?.proyecto ?? "" };
|
|
4001
|
-
//
|
|
4002
|
-
|
|
4003
|
-
//
|
|
4004
|
-
|
|
4005
|
-
// `transicion` y con el motivo en `falloDeTransicion`.
|
|
4006
|
-
let falloDeTransicion;
|
|
4007
|
-
if (m.transicion !== undefined) {
|
|
4008
|
-
try {
|
|
4009
|
-
await g.transicionar(vinculo, ticket.clave, m.transicion);
|
|
4010
|
-
}
|
|
4011
|
-
catch (error) {
|
|
4012
|
-
falloDeTransicion = motivoDelGestor(error);
|
|
4013
|
-
}
|
|
4014
|
-
}
|
|
4162
|
+
// La secuencia de `core/tareasEnElGestor.ts#cerrarTicket`, la MISMA que cierra una tarea en background: si
|
|
4163
|
+
// `comentar` falla, sale al `catch` de fuera y NO se transiciona; si falla la transición, el comentario YA está
|
|
4164
|
+
// y sale `cerrado` sin `transicion` y con el motivo (contestar `error{cerrar}` invitaría a comentar DOS veces).
|
|
4165
|
+
const { falloDeTransicion } = await cerrarTicket(g, vinculo, ticket.clave, m.comentario, m.transicion, motivoDelGestor);
|
|
4015
4166
|
emitir({
|
|
4016
4167
|
clase: "gestor",
|
|
4017
4168
|
cerrado: {
|
|
@@ -5049,14 +5200,34 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
5049
5200
|
responder(403, "ese nombre no es de un artefacto");
|
|
5050
5201
|
return;
|
|
5051
5202
|
}
|
|
5052
|
-
|
|
5053
|
-
|
|
5203
|
+
// Sin `p` y `s`, el de la conversación ABIERTA. Con ellos, el de ESA conversación, que se está LEYENDO sin
|
|
5204
|
+
// consola (una tarea finalizada, IXCODE-36): resuelta por el servidor como `leerSesion`, nunca por el texto.
|
|
5205
|
+
let origen;
|
|
5206
|
+
const pedidoProyecto = query.get("p");
|
|
5207
|
+
const pedidaSesion = query.get("s");
|
|
5208
|
+
if (pedidoProyecto !== null || pedidaSesion !== null) {
|
|
5209
|
+
const resuelta = sesionDelDisco(pedidoProyecto, pedidaSesion);
|
|
5210
|
+
if ("motivo" in resuelta) {
|
|
5211
|
+
responder(404, "no hay artefacto");
|
|
5212
|
+
return;
|
|
5213
|
+
}
|
|
5214
|
+
origen = resuelta;
|
|
5215
|
+
}
|
|
5216
|
+
else {
|
|
5217
|
+
const abierto = vestibulo.proyectoAbierto();
|
|
5218
|
+
if (abierto === undefined) {
|
|
5219
|
+
responder(404, "no hay artefacto");
|
|
5220
|
+
return;
|
|
5221
|
+
}
|
|
5222
|
+
origen = { raiz: abierto.raiz, sesion: abierto.idDeHilo };
|
|
5223
|
+
}
|
|
5224
|
+
if (opciones.leerArtefactoCrudo === undefined) {
|
|
5054
5225
|
responder(404, "no hay artefacto");
|
|
5055
5226
|
return;
|
|
5056
5227
|
}
|
|
5057
5228
|
let leido;
|
|
5058
5229
|
try {
|
|
5059
|
-
leido = await opciones.leerArtefactoCrudo(
|
|
5230
|
+
leido = await opciones.leerArtefactoCrudo(origen.raiz, origen.sesion, nombre);
|
|
5060
5231
|
}
|
|
5061
5232
|
catch (error) {
|
|
5062
5233
|
// El mensaje de Node lleva la ruta absoluta: al cliente solo le llega el código, y a
|
|
@@ -5114,6 +5285,23 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
5114
5285
|
});
|
|
5115
5286
|
respuesta.end(leido.datos);
|
|
5116
5287
|
});
|
|
5288
|
+
/**
|
|
5289
|
+
* Una conversación de un proyecto, resuelta por el SERVIDOR a partir de los IDS que llegan del cable: el nombre
|
|
5290
|
+
* sale del listado del entorno activo, la raíz la compone el vestíbulo y la sesión tiene que estar en el ÍNDICE
|
|
5291
|
+
* del DISCO de ese proyecto. Nunca se lee nada que el cliente nombre. La comparten `leerSesion` y los artefactos
|
|
5292
|
+
* de una conversación que se LEE sin consola (una tarea finalizada): dos copias de esta barrera divergirían.
|
|
5293
|
+
*/
|
|
5294
|
+
const sesionDelDisco = (proyecto, sesion) => {
|
|
5295
|
+
const identidad = proyectos.find((p) => p.id === proyecto);
|
|
5296
|
+
if (entornoElegido === undefined || identidad === undefined)
|
|
5297
|
+
return { motivo: "ese proyecto no está en el listado del entorno activo" };
|
|
5298
|
+
const raiz = vestibulo.raizDeProyecto(entornoElegido, identidad.nombre);
|
|
5299
|
+
if (!esProyectoEnDisco(raiz))
|
|
5300
|
+
return { motivo: "ese proyecto no está descargado en este equipo" };
|
|
5301
|
+
if (typeof sesion !== "string" || !listarSesiones(raiz).some((s) => s.id === sesion))
|
|
5302
|
+
return { motivo: "esa conversación no está en el proyecto" };
|
|
5303
|
+
return { raiz, sesion };
|
|
5304
|
+
};
|
|
5117
5305
|
/**
|
|
5118
5306
|
* Lo que la pestaña Soporte necesita de UN proyecto, compuesto por el SERVIDOR a partir del
|
|
5119
5307
|
* id: la raíz sale del listado del entorno activo (como `resumen` y `borrar`), las tareas se
|
|
@@ -6008,20 +6196,13 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
6008
6196
|
respuesta.writeHead(409, { "content-type": "application/json" });
|
|
6009
6197
|
respuesta.end(JSON.stringify({ motivo }));
|
|
6010
6198
|
};
|
|
6011
|
-
const identidad = proyectos.find((p) => p.id === mensaje.proyecto);
|
|
6012
|
-
const sesion = mensaje.sesion;
|
|
6013
|
-
if (entornoElegido === undefined || identidad === undefined)
|
|
6014
|
-
return negar("ese proyecto no está en el listado del entorno activo");
|
|
6015
|
-
const raiz = vestibulo.raizDeProyecto(entornoElegido, identidad.nombre);
|
|
6016
|
-
if (!esProyectoEnDisco(raiz))
|
|
6017
|
-
return negar("ese proyecto no está descargado en este equipo");
|
|
6018
6199
|
// El índice del DISCO, la misma fuente que lee `reabrirSesion` justo debajo.
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6200
|
+
const resuelta = sesionDelDisco(mensaje.proyecto, mensaje.sesion);
|
|
6201
|
+
if ("motivo" in resuelta)
|
|
6202
|
+
return negar(resuelta.motivo);
|
|
6022
6203
|
let actos;
|
|
6023
6204
|
try {
|
|
6024
|
-
actos = reabrirSesion(raiz, sesion).actos;
|
|
6205
|
+
actos = reabrirSesion(resuelta.raiz, resuelta.sesion).actos;
|
|
6025
6206
|
}
|
|
6026
6207
|
catch (error) {
|
|
6027
6208
|
contar(error);
|
|
@@ -6210,6 +6391,82 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
6210
6391
|
respuesta.end();
|
|
6211
6392
|
return;
|
|
6212
6393
|
}
|
|
6394
|
+
if (typeof mensaje === "object" &&
|
|
6395
|
+
mensaje !== null &&
|
|
6396
|
+
mensaje.clase === "gestor" &&
|
|
6397
|
+
(mensaje.accion === "estadosDelFlujo" || mensaje.accion === "ajustarVinculo")) {
|
|
6398
|
+
/**
|
|
6399
|
+
* El MAPEO de estados y el disparo del vínculo del proyecto ABIERTO (la puerta 2), contestados en la propia
|
|
6400
|
+
* respuesta. `estadosDelFlujo` lee del gestor los estados de su flujo y propone el mapeo por categoría;
|
|
6401
|
+
* `ajustarVinculo` lo guarda, comprobando que cada estado elegido EXISTE en ese flujo —un nombre que no está
|
|
6402
|
+
* dejaría el disparo mudo—, y vuelve a emitir el estado del gestor.
|
|
6403
|
+
*/
|
|
6404
|
+
const negar = (motivo) => {
|
|
6405
|
+
respuesta.writeHead(409, { "content-type": "application/json" });
|
|
6406
|
+
respuesta.end(JSON.stringify({ motivo }));
|
|
6407
|
+
};
|
|
6408
|
+
const abierto = vestibulo.proyectoAbierto();
|
|
6409
|
+
if (abierto === undefined)
|
|
6410
|
+
return negar("no hay ningún proyecto abierto");
|
|
6411
|
+
let vinculo;
|
|
6412
|
+
try {
|
|
6413
|
+
vinculo = cargar(abierto.raiz).config.proyecto?.gestorDeTareas;
|
|
6414
|
+
}
|
|
6415
|
+
catch {
|
|
6416
|
+
return negar("no se pudo leer la configuración del proyecto");
|
|
6417
|
+
}
|
|
6418
|
+
if (vinculo === undefined)
|
|
6419
|
+
return negar("este proyecto no tiene un gestor de tareas vinculado");
|
|
6420
|
+
const g = gestorDe(vinculo.conector);
|
|
6421
|
+
if (g?.estados === undefined)
|
|
6422
|
+
return negar("este gestor no sabe decir los estados de su flujo");
|
|
6423
|
+
let lista;
|
|
6424
|
+
try {
|
|
6425
|
+
lista = await g.estados(vinculo);
|
|
6426
|
+
}
|
|
6427
|
+
catch (error) {
|
|
6428
|
+
return negar(`no se pudieron leer los estados: ${motivoDelGestor(error)}`);
|
|
6429
|
+
}
|
|
6430
|
+
if (mensaje.accion === "estadosDelFlujo") {
|
|
6431
|
+
respuesta.writeHead(200, { "content-type": "application/json" });
|
|
6432
|
+
respuesta.end(JSON.stringify({ lista, propuesto: mapeoPropuesto(lista), ...(vinculo.estados === undefined ? {} : { actual: vinculo.estados }) }));
|
|
6433
|
+
return;
|
|
6434
|
+
}
|
|
6435
|
+
const pedido = mensaje;
|
|
6436
|
+
if (pedido.revisarEncargo !== undefined && typeof pedido.revisarEncargo !== "boolean")
|
|
6437
|
+
return negar("petición mal formada");
|
|
6438
|
+
if (typeof pedido.automatico !== "boolean" || typeof pedido.estados !== "object" || pedido.estados === null) {
|
|
6439
|
+
return negar("petición mal formada");
|
|
6440
|
+
}
|
|
6441
|
+
const estados = {};
|
|
6442
|
+
for (const papel of ["preparar", "lanzar", "terminar"]) {
|
|
6443
|
+
const e = pedido.estados[papel];
|
|
6444
|
+
if (e === undefined || e === "")
|
|
6445
|
+
continue;
|
|
6446
|
+
if (typeof e !== "string" || !lista.some((x) => esEstado(x.nombre, e)))
|
|
6447
|
+
return negar(`«${String(e)}» no es un estado del flujo de este proyecto`);
|
|
6448
|
+
estados[papel] = lista.find((x) => esEstado(x.nombre, e)).nombre;
|
|
6449
|
+
}
|
|
6450
|
+
// Encender el disparo pide saber cuándo LANZAR: sin ese estado no haría nada y parecería roto.
|
|
6451
|
+
if (pedido.automatico && estados.lanzar === undefined)
|
|
6452
|
+
return negar("para encender el disparo, elige el estado que lanza la tarea");
|
|
6453
|
+
const { estados: _antes, automatico: _a, revisarEncargo: _r, ...resto } = vinculo;
|
|
6454
|
+
try {
|
|
6455
|
+
guardarGestorDeProyecto(abierto.raiz, {
|
|
6456
|
+
...resto,
|
|
6457
|
+
...(Object.keys(estados).length === 0 ? {} : { estados }),
|
|
6458
|
+
...(pedido.automatico ? { automatico: true } : {}),
|
|
6459
|
+
...(pedido.revisarEncargo === true ? { revisarEncargo: true } : {}),
|
|
6460
|
+
});
|
|
6461
|
+
}
|
|
6462
|
+
catch (error) {
|
|
6463
|
+
return negar(`no se pudo guardar en la configuración del proyecto (${codigoDe(error) ?? "su config.json no se puede leer"})`);
|
|
6464
|
+
}
|
|
6465
|
+
void atenderGestor({ clase: "gestor", accion: "estado" }).catch(contar);
|
|
6466
|
+
respuesta.writeHead(204);
|
|
6467
|
+
respuesta.end();
|
|
6468
|
+
return;
|
|
6469
|
+
}
|
|
6213
6470
|
if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "gestor") {
|
|
6214
6471
|
// Los campos se comprueban por ACCIÓN, como `modeloDelCambio`: un mensaje mal formado
|
|
6215
6472
|
// se contesta con su error, no revienta dentro de `atenderGestor`.
|
|
@@ -6272,6 +6529,36 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
6272
6529
|
respuesta.end();
|
|
6273
6530
|
return;
|
|
6274
6531
|
}
|
|
6532
|
+
if (typeof mensaje === "object" &&
|
|
6533
|
+
mensaje !== null &&
|
|
6534
|
+
mensaje.clase === "artefacto" &&
|
|
6535
|
+
typeof mensaje.nombre === "string" &&
|
|
6536
|
+
(mensaje.proyecto !== undefined || mensaje.sesion !== undefined)) {
|
|
6537
|
+
// El artefacto de una conversación que se LEE sin consola: se contesta en la PROPIA respuesta y no por el cable,
|
|
6538
|
+
// donde el store lo guardaría por ruta y pisaría el de la conversación abierta con el mismo nombre.
|
|
6539
|
+
const resuelta = sesionDelDisco(mensaje.proyecto, mensaje.sesion);
|
|
6540
|
+
const negar = (motivo) => {
|
|
6541
|
+
respuesta.writeHead(409, { "content-type": "application/json" });
|
|
6542
|
+
respuesta.end(JSON.stringify({ motivo }));
|
|
6543
|
+
};
|
|
6544
|
+
if ("motivo" in resuelta)
|
|
6545
|
+
return negar(resuelta.motivo);
|
|
6546
|
+
if (!esRutaDeArtefacto(`${RUTA_ARTEFACTOS}${mensaje.nombre}`))
|
|
6547
|
+
return negar("ese nombre no es de un artefacto");
|
|
6548
|
+
if (opciones.leerArtefacto === undefined)
|
|
6549
|
+
return negar("esta ejecución no puede leer los artefactos");
|
|
6550
|
+
let fichero;
|
|
6551
|
+
try {
|
|
6552
|
+
fichero = await opciones.leerArtefacto(resuelta.raiz, resuelta.sesion, mensaje.nombre);
|
|
6553
|
+
}
|
|
6554
|
+
catch (error) {
|
|
6555
|
+
informar(`no se pudo leer el artefacto «${mensaje.nombre}» (${codigoDe(error)})`);
|
|
6556
|
+
return negar("no se pudo leer el artefacto");
|
|
6557
|
+
}
|
|
6558
|
+
respuesta.writeHead(200, { "content-type": "application/json" });
|
|
6559
|
+
respuesta.end(JSON.stringify(fichero));
|
|
6560
|
+
return;
|
|
6561
|
+
}
|
|
6275
6562
|
if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "artefacto" && typeof mensaje.nombre === "string") {
|
|
6276
6563
|
void atenderArtefacto(mensaje.nombre).catch(contar);
|
|
6277
6564
|
respuesta.writeHead(204);
|
|
@@ -6423,6 +6710,34 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
6423
6710
|
respuesta.end();
|
|
6424
6711
|
return;
|
|
6425
6712
|
}
|
|
6713
|
+
if (mensaje.accion === "mirarGestor") {
|
|
6714
|
+
// «Mirar ahora»: una pasada del sondeo de la puerta 2, contestada con lo que hizo. Solo lo hace el proceso que
|
|
6715
|
+
// ejecuta las tareas; en otro, el sondeo no hace nada y lo dice el resultado vacío.
|
|
6716
|
+
if (opciones.mirarGestor === undefined) {
|
|
6717
|
+
respuesta.writeHead(409, { "content-type": "application/json" });
|
|
6718
|
+
respuesta.end(JSON.stringify({ motivo: "esta ejecución no mira el gestor de tareas" }));
|
|
6719
|
+
return;
|
|
6720
|
+
}
|
|
6721
|
+
const r = await opciones.mirarGestor();
|
|
6722
|
+
emitirTareas();
|
|
6723
|
+
respuesta.writeHead(200, { "content-type": "application/json" });
|
|
6724
|
+
respuesta.end(JSON.stringify(r));
|
|
6725
|
+
return;
|
|
6726
|
+
}
|
|
6727
|
+
if (mensaje.accion === "destino") {
|
|
6728
|
+
/**
|
|
6729
|
+
* Dónde vivirá una tarea NUEVA de ese proyecto: con qué gestor, si lo tiene vinculado y sabe crear. Lo pregunta la
|
|
6730
|
+
* ventana «Nueva tarea» al abrirse para decir ANTES del clic qué se va a escribir —crear es la autorización—, y se
|
|
6731
|
+
* decide con la MISMA función que `crear`, así que lo dicho y lo hecho no pueden divergir. Ni red ni gestor: lee el
|
|
6732
|
+
* `config.json` del proyecto. Del vínculo cruza el conector y el nombre del proyecto del gestor, nunca el sitio.
|
|
6733
|
+
*/
|
|
6734
|
+
const destino = gestorParaTareaNueva(mensaje.proyecto);
|
|
6735
|
+
respuesta.writeHead(200, { "content-type": "application/json" });
|
|
6736
|
+
respuesta.end(JSON.stringify(destino === undefined
|
|
6737
|
+
? {}
|
|
6738
|
+
: { gestor: { conector: destino.vinculo.conector, proyecto: destino.vinculo.nombreDelProyecto ?? destino.vinculo.proyecto } }));
|
|
6739
|
+
return;
|
|
6740
|
+
}
|
|
6426
6741
|
if (mensaje.accion === "regenerarEncargo" && opciones.colaDeTareas !== undefined) {
|
|
6427
6742
|
/**
|
|
6428
6743
|
* Volver a PREPARAR el encargo de una tarea que no ha arrancado, desde su petición editada
|
|
@@ -6529,6 +6844,15 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
6529
6844
|
respuesta.end(JSON.stringify({ motivo: "se está cambiando la carpeta de los proyectos: crea la tarea cuando termine" }));
|
|
6530
6845
|
return;
|
|
6531
6846
|
}
|
|
6847
|
+
// Con gestor vinculado, la tarea vive en él: se contesta en la propia respuesta (la ventana no se cierra hasta saber
|
|
6848
|
+
// si el gestor la creó). Sin él, el camino de siempre.
|
|
6849
|
+
const enGestor = mensaje.accion === "crear" ? gestorParaTareaNueva(mensaje.proyecto) : undefined;
|
|
6850
|
+
if (mensaje.accion === "crear" && enGestor !== undefined) {
|
|
6851
|
+
const r = await atenderCrearTareaEnGestor(enGestor, mensaje.peticion, mensaje.encargo, mensaje.borrador);
|
|
6852
|
+
respuesta.writeHead(r.estado, { "content-type": "application/json" });
|
|
6853
|
+
respuesta.end(JSON.stringify(r.cuerpo));
|
|
6854
|
+
return;
|
|
6855
|
+
}
|
|
6532
6856
|
if (mensaje.accion === "crear" && opciones.colaDeTareas !== undefined) {
|
|
6533
6857
|
const creada = atenderCrearTarea(mensaje.proyecto, mensaje.peticion, mensaje.encargo, mensaje.borrador);
|
|
6534
6858
|
if (creada !== undefined) {
|
|
@@ -7098,6 +7422,40 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
7098
7422
|
});
|
|
7099
7423
|
return { emitirTareas, cerrarRemoto: apagarRemoto };
|
|
7100
7424
|
}
|
|
7425
|
+
/**
|
|
7426
|
+
* El sondeo de la puerta 2 CABLEADO: extraído de `arrancarConsolaWeb` para poder probar la composición de verdad (el
|
|
7427
|
+
* patrón de fallo de siempre: una regla de producción en un cierre que todos los tests doblan). Dos cosas que solo se
|
|
7428
|
+
* ven aquí: lo que el sondeo guarda va por la cola ENVUELTA (así el reflejo en el gestor dispara), y tras guardar se
|
|
7429
|
+
* EMITE la cola —el corredor solo emite en sus propias escrituras, y una tarea que no puede arrancar todavía (la
|
|
7430
|
+
* persona tiene la consola de ese proyecto abierta) no salía en la pantalla—. Lo que falla en una pasada, también la
|
|
7431
|
+
* primera, se dice.
|
|
7432
|
+
*/
|
|
7433
|
+
export function construirSondeoCableado(o) {
|
|
7434
|
+
const sondeo = crearSondeoDelGestor({
|
|
7435
|
+
copias: o.copias,
|
|
7436
|
+
gestorDe: o.gestorDe,
|
|
7437
|
+
...(o.augmentar === undefined ? {} : { augmentar: o.augmentar }),
|
|
7438
|
+
cola: o.cola,
|
|
7439
|
+
revisar: () => {
|
|
7440
|
+
o.corredor.revisar();
|
|
7441
|
+
o.emitirTareas();
|
|
7442
|
+
},
|
|
7443
|
+
esDueño: () => o.corredor.corriendoAqui(),
|
|
7444
|
+
motivoDe: motivoDeGestorParaElCable,
|
|
7445
|
+
informar: o.informar,
|
|
7446
|
+
});
|
|
7447
|
+
const decir = (r) => {
|
|
7448
|
+
for (const f of r.fallos)
|
|
7449
|
+
o.informar(`el gestor de ${f.proyecto}${f.clave === undefined ? "" : ` (${f.clave})`}: ${f.motivo}`);
|
|
7450
|
+
if (r.preparadas.length > 0)
|
|
7451
|
+
o.emitirTareas();
|
|
7452
|
+
};
|
|
7453
|
+
return {
|
|
7454
|
+
...sondeo,
|
|
7455
|
+
// «Mirar ahora» y la pasada del arranque dicen sus fallos igual que el reloj.
|
|
7456
|
+
mirar: () => sondeo.mirar().then((r) => (decir(r), r)),
|
|
7457
|
+
};
|
|
7458
|
+
}
|
|
7101
7459
|
/**
|
|
7102
7460
|
* Construye el corredor de tareas y la costura que lo conecta al cable.
|
|
7103
7461
|
*
|
|
@@ -7743,7 +8101,27 @@ export function ajusteDeGestorCableado(ajuste) {
|
|
|
7743
8101
|
};
|
|
7744
8102
|
}
|
|
7745
8103
|
export function construirCorredorDeTareasCableado(opciones) {
|
|
7746
|
-
const
|
|
8104
|
+
const crudo = opciones.tareasFabrica === undefined ? undefined : opciones.tareasFabrica(opciones.informar);
|
|
8105
|
+
/**
|
|
8106
|
+
* El almacén con el `guardar` VIGILADO: un único punto por el que pasan todos los cambios de estado (corredor,
|
|
8107
|
+
* reconciliación, rutas, feedback), y de ahí el reflejo en el gestor. Envuelto AQUÍ, donde nace, para que el
|
|
8108
|
+
* corredor y las rutas (`opcionesDeMontaje.colaDeTareas`) compartan el mismo.
|
|
8109
|
+
*/
|
|
8110
|
+
const reflejo = crudo === undefined || opciones.gestorDeTareas === undefined
|
|
8111
|
+
? undefined
|
|
8112
|
+
: crearReflejoEnElGestor({
|
|
8113
|
+
gestorDe: opciones.gestorDeTareas,
|
|
8114
|
+
datosDeCierre: opciones.datosDeCierreDeTarea ?? datosDeCierreDeTarea,
|
|
8115
|
+
// Escribe en disco y AVISA al cable: el corredor solo emite en sus propias escrituras, y sin esto el aviso
|
|
8116
|
+
// («no se pudo pasar KAN-1 a EN CURSO») no salía en la fila hasta que cambiara otra cosa.
|
|
8117
|
+
anotarAviso: (id, aviso) => {
|
|
8118
|
+
anotarAvisoEnElDisco(crudo)(id, aviso);
|
|
8119
|
+
emitirCambioDeTareas?.();
|
|
8120
|
+
},
|
|
8121
|
+
motivoDe: motivoDeGestorParaElCable,
|
|
8122
|
+
informar: opciones.informar,
|
|
8123
|
+
});
|
|
8124
|
+
const disco = crudo === undefined ? undefined : reflejo === undefined ? crudo : conReflejoAlGuardar(crudo, reflejo);
|
|
7747
8125
|
/**
|
|
7748
8126
|
* El tope de concurrencia se LEE de `settings.json` en cada llamada, nunca se cachea —la
|
|
7749
8127
|
* misma disciplina que `ajustesDeDispositivos` (más abajo) y que `sinAprobacion`: la
|
|
@@ -7986,6 +8364,8 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
7986
8364
|
vestibulo: conVestibulo,
|
|
7987
8365
|
tareasFabrica: opciones.tareas,
|
|
7988
8366
|
informar,
|
|
8367
|
+
// El MISMO gestor memoizado que el panel: con él, una tarea con ticket se refleja en su gestor al cambiar de estado.
|
|
8368
|
+
gestorDeTareas: conectoresYGestor.gestorDeTareas,
|
|
7989
8369
|
olvidarHiloDeSesion: olvidarMemoriaDeHilo,
|
|
7990
8370
|
/**
|
|
7991
8371
|
* El juez de QA de las tareas, con el papel `afilado` — el que `core/modelos.ts` le
|
|
@@ -8040,6 +8420,19 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
8040
8420
|
escribir(`${aviso}\n`);
|
|
8041
8421
|
abierto.consola.consola.escribir(`${aviso}\n`);
|
|
8042
8422
|
}
|
|
8423
|
+
/** El aumentador: el de «Nueva tarea» (por las rutas) y el de preparar encargos del sondeo del gestor. Ver su
|
|
8424
|
+
* comentario en las opciones de `montarRutas`. */
|
|
8425
|
+
const augmentar = augmentacionCableada({
|
|
8426
|
+
aumentador: opciones.guion === true
|
|
8427
|
+
? new AumentadorGuionizado()
|
|
8428
|
+
: crearAumentador({
|
|
8429
|
+
invocar: (papel, prompt, raiz) => invocarParaAumentar(new Modelos(fuentesDelJuez(raiz), proveedoresPersonalizados))(papel, prompt, raiz),
|
|
8430
|
+
}),
|
|
8431
|
+
contexto: contextoDelProyecto,
|
|
8432
|
+
});
|
|
8433
|
+
/** El sondeo de la puerta 2: nace con el corredor (más abajo); «Mirar ahora» llega por las rutas, montadas antes. */
|
|
8434
|
+
let sondeo;
|
|
8435
|
+
const espacioDeTrabajo = ajusteDeWorkspaceCableado({ casa: homedir() });
|
|
8043
8436
|
const cable = montarRutas(servidor, vestibulo, {
|
|
8044
8437
|
informar,
|
|
8045
8438
|
// El interruptor se lee de `settings.json` (o `XONECODE_REMOTO=1`) en cada uso. Sin el
|
|
@@ -8239,14 +8632,8 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
8239
8632
|
* - **Con `--guion` se monta el DOBLE**, no el real: esa bandera significa «sin gastar
|
|
8240
8633
|
* ni conectar», y el doble dice en el propio encargo que es de pega (`[DOBLE]`).
|
|
8241
8634
|
*/
|
|
8242
|
-
augmentar
|
|
8243
|
-
|
|
8244
|
-
? new AumentadorGuionizado()
|
|
8245
|
-
: crearAumentador({
|
|
8246
|
-
invocar: (papel, prompt, raiz) => invocarParaAumentar(new Modelos(fuentesDelJuez(raiz), proveedoresPersonalizados))(papel, prompt, raiz),
|
|
8247
|
-
}),
|
|
8248
|
-
contexto: contextoDelProyecto,
|
|
8249
|
-
}),
|
|
8635
|
+
augmentar,
|
|
8636
|
+
mirarGestor: async () => sondeo?.mirar() ?? { preparadas: [], lanzadas: [], fallos: [] },
|
|
8250
8637
|
});
|
|
8251
8638
|
// Con las rutas ya montadas: la reconciliación y el primer despacho cambian la cola, y
|
|
8252
8639
|
// quien conecte después la recibe entera en su ráfaga de bienvenida.
|
|
@@ -8254,6 +8641,23 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
8254
8641
|
// `cable.emitirTareas` y arranca en el orden correcto, así que no hay manera de llamarlo
|
|
8255
8642
|
// mal desde aquí.
|
|
8256
8643
|
await arrancarCorredorConectado(cable);
|
|
8644
|
+
/**
|
|
8645
|
+
* La puerta 2: el gestor de tareas DISPARA tareas (`agent/tareas/sondeoDelGestor.ts`). Solo con cola y corredor, y
|
|
8646
|
+
* solo hace algo en el proceso que EJECUTA las tareas. Una pasada al arrancar recoge lo que se movió con esto parado.
|
|
8647
|
+
*/
|
|
8648
|
+
if (corredor !== undefined && opcionesDeTareas.colaDeTareas !== undefined) {
|
|
8649
|
+
sondeo = construirSondeoCableado({
|
|
8650
|
+
corredor,
|
|
8651
|
+
cola: opcionesDeTareas.colaDeTareas,
|
|
8652
|
+
copias: () => copiasConGestor(espacioDeTrabajo.workspace()),
|
|
8653
|
+
gestorDe: conectoresYGestor.gestorDeTareas,
|
|
8654
|
+
augmentar,
|
|
8655
|
+
emitirTareas: () => cable.emitirTareas(),
|
|
8656
|
+
informar,
|
|
8657
|
+
});
|
|
8658
|
+
sondeo.arrancar();
|
|
8659
|
+
void sondeo.mirar();
|
|
8660
|
+
}
|
|
8257
8661
|
/**
|
|
8258
8662
|
* Con qué código corre ESTE proceso, antes que la URL.
|
|
8259
8663
|
*
|