xonecode 0.5.0 → 0.6.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 +1 -1
- package/THIRD_PARTY_NOTICES.md +32 -0
- package/apps/web/dist/assets/index-DBZTodio.js +477 -0
- package/apps/web/dist/assets/{index-BB3SamhR.css → index-rqecK6s2.css} +1 -1
- package/apps/web/dist/assets/{php-D_2--9WK.js → php-BBij3Lhc.js} +1 -1
- package/apps/web/dist/assets/{ruby-DSHSDbk1.js → ruby-Dq2YEaLo.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/dist/agent/cloudstudio/cloudstudioClient.js.map +1 -0
- package/dist/agent/{cloudstudioMcp.js → cloudstudio/cloudstudioMcp.js} +5 -5
- package/dist/agent/cloudstudio/cloudstudioMcp.js.map +1 -0
- package/dist/agent/{descarga.js → cloudstudio/descarga.js} +3 -3
- package/dist/agent/cloudstudio/descarga.js.map +1 -0
- package/dist/agent/cloudstudio/manifiesto.js.map +1 -0
- package/dist/agent/{subida.js → cloudstudio/subida.js} +3 -3
- package/dist/agent/cloudstudio/subida.js.map +1 -0
- package/dist/agent/{zip.js → cloudstudio/zip.js} +1 -1
- package/dist/agent/cloudstudio/zip.js.map +1 -0
- package/dist/agent/{authEnDisco.js → config/authEnDisco.js} +3 -3
- package/dist/agent/config/authEnDisco.js.map +1 -0
- package/dist/agent/{catalogoModelos.js → config/catalogoModelos.js} +2 -1
- package/dist/agent/config/catalogoModelos.js.map +1 -0
- package/dist/agent/{configEnDisco.js → config/configEnDisco.js} +6 -6
- package/dist/agent/config/configEnDisco.js.map +1 -0
- package/dist/agent/{crearProyecto.js → config/crearProyecto.js} +2 -2
- package/dist/agent/config/crearProyecto.js.map +1 -0
- package/dist/agent/config/entorno.js.map +1 -0
- package/dist/agent/config/gemini.js.map +1 -0
- package/dist/agent/{modelos.js → config/modelos.js} +17 -3
- package/dist/agent/config/modelos.js.map +1 -0
- package/dist/agent/{modelosDeMotor.js → config/modelosDeMotor.js} +1 -1
- package/dist/agent/config/modelosDeMotor.js.map +1 -0
- package/dist/agent/{persona.js → config/persona.js} +1 -1
- package/dist/agent/config/persona.js.map +1 -0
- package/dist/agent/{settingsEnDisco.js → config/settingsEnDisco.js} +2 -2
- package/dist/agent/config/settingsEnDisco.js.map +1 -0
- package/dist/agent/{versionEnDisco.js → config/versionEnDisco.js} +8 -4
- package/dist/agent/config/versionEnDisco.js.map +1 -0
- package/dist/agent/dispositivos/arranqueDeEmulador.js +146 -0
- package/dist/agent/dispositivos/arranqueDeEmulador.js.map +1 -0
- package/dist/agent/{dispositivosEnMaquina.js → dispositivos/dispositivosEnMaquina.js} +93 -6
- package/dist/agent/dispositivos/dispositivosEnMaquina.js.map +1 -0
- package/dist/agent/dispositivos/hotswap.js.map +1 -0
- package/dist/agent/dispositivos/instalacionEnMaquina.js.map +1 -0
- package/dist/agent/{lanzamientoEnMaquina.js → dispositivos/lanzamientoEnMaquina.js} +1 -1
- package/dist/agent/dispositivos/lanzamientoEnMaquina.js.map +1 -0
- package/dist/agent/{paqueteDelProyecto.js → dispositivos/paqueteDelProyecto.js} +5 -5
- package/dist/agent/dispositivos/paqueteDelProyecto.js.map +1 -0
- package/dist/agent/dispositivos/procesosEnMaquina.js.map +1 -0
- package/dist/agent/{arbolDeProyecto.js → grafo/arbolDeProyecto.js} +2 -2
- package/dist/agent/grafo/arbolDeProyecto.js.map +1 -0
- package/dist/agent/{artefactosEnDisco.js → grafo/artefactosEnDisco.js} +2 -2
- package/dist/agent/grafo/artefactosEnDisco.js.map +1 -0
- package/dist/agent/{busquedaRegex.js → grafo/busquedaRegex.js} +41 -1
- package/dist/agent/grafo/busquedaRegex.js.map +1 -0
- package/dist/agent/grafo/excluirTools.js +82 -0
- package/dist/agent/grafo/excluirTools.js.map +1 -0
- package/dist/agent/{memoriaDeProyecto.js → grafo/memoriaDeProyecto.js} +1 -1
- package/dist/agent/grafo/memoriaDeProyecto.js.map +1 -0
- package/dist/agent/grafo/navegacionXone.js +198 -0
- package/dist/agent/grafo/navegacionXone.js.map +1 -0
- package/dist/agent/{perfiles.js → grafo/perfiles.js} +51 -8
- package/dist/agent/grafo/perfiles.js.map +1 -0
- package/dist/agent/{proyecto.js → grafo/proyecto.js} +207 -24
- package/dist/agent/grafo/proyecto.js.map +1 -0
- package/dist/agent/grafo/skills.js +393 -0
- package/dist/agent/grafo/skills.js.map +1 -0
- package/dist/agent/{xoneAgent.js → grafo/xoneAgent.js} +196 -67
- package/dist/agent/grafo/xoneAgent.js.map +1 -0
- package/dist/agent/navegacion/indiceEnDisco.js +21 -0
- package/dist/agent/navegacion/indiceEnDisco.js.map +1 -0
- package/dist/agent/navegacion/modeloDeProyecto.js +228 -0
- package/dist/agent/navegacion/modeloDeProyecto.js.map +1 -0
- package/dist/agent/raizDelPaquete.js +56 -0
- package/dist/agent/raizDelPaquete.js.map +1 -0
- package/dist/agent/{checkpointer.js → sesiones/checkpointer.js} +2 -2
- package/dist/agent/sesiones/checkpointer.js.map +1 -0
- package/dist/agent/sesiones/git.js.map +1 -0
- package/dist/agent/{gitSync.js → sesiones/gitSync.js} +3 -3
- package/dist/agent/sesiones/gitSync.js.map +1 -0
- package/dist/agent/{sesionGit.js → sesiones/sesionGit.js} +2 -2
- package/dist/agent/sesiones/sesionGit.js.map +1 -0
- package/dist/agent/{agentesEnDisco.js → subagentes/agentesEnDisco.js} +304 -86
- package/dist/agent/subagentes/agentesEnDisco.js.map +1 -0
- package/dist/agent/subagentes/consumoExterno.js.map +1 -0
- package/dist/agent/subagentes/escrituraDeCodex.js.map +1 -0
- package/dist/agent/{escrituraDeOpencode.js → subagentes/escrituraDeOpencode.js} +1 -1
- package/dist/agent/subagentes/escrituraDeOpencode.js.map +1 -0
- package/dist/agent/{escrituraExterna.js → subagentes/escrituraExterna.js} +6 -6
- package/dist/agent/subagentes/escrituraExterna.js.map +1 -0
- package/dist/agent/subagentes/subagenteCodex.js.map +1 -0
- package/dist/agent/subagentes/subagenteExterno.js.map +1 -0
- package/dist/agent/subagentes/subagenteOpencode.js.map +1 -0
- package/dist/agent/{aumentador.js → tareas/aumentador.js} +2 -2
- package/dist/agent/tareas/aumentador.js.map +1 -0
- package/dist/agent/{juezDeTarea.js → tareas/juezDeTarea.js} +5 -5
- package/dist/agent/tareas/juezDeTarea.js.map +1 -0
- package/dist/agent/{tareasEnDisco.js → tareas/tareasEnDisco.js} +4 -4
- package/dist/agent/tareas/tareasEnDisco.js.map +1 -0
- package/dist/agent/turno/diagnosticoDeTools.js.map +1 -0
- package/dist/agent/{guionizado.js → turno/guionizado.js} +1 -1
- package/dist/agent/turno/guionizado.js.map +1 -0
- package/dist/agent/turno/informeDeTraza.js +222 -0
- package/dist/agent/turno/informeDeTraza.js.map +1 -0
- package/dist/agent/turno/inspectorDePrompt.js +205 -0
- package/dist/agent/turno/inspectorDePrompt.js.map +1 -0
- package/dist/agent/{instantanea.js → turno/instantanea.js} +7 -2
- package/dist/agent/turno/instantanea.js.map +1 -0
- package/dist/agent/{interrupts.js → turno/interrupts.js} +2 -2
- package/dist/agent/turno/interrupts.js.map +1 -0
- package/dist/agent/turno/mensajes.js.map +1 -0
- package/dist/agent/turno/normalizar.js.map +1 -0
- package/dist/agent/{puente.js → turno/puente.js} +47 -3
- package/dist/agent/turno/puente.js.map +1 -0
- package/dist/agent/turno/resumenDeContexto.js +176 -0
- package/dist/agent/turno/resumenDeContexto.js.map +1 -0
- package/dist/agent/{resumenDeTool.js → turno/resumenDeTool.js} +15 -0
- package/dist/agent/turno/resumenDeTool.js.map +1 -0
- package/dist/agent/turno/textoDeTool.js.map +1 -0
- package/dist/agent/{turnoReal.js → turno/turnoReal.js} +38 -14
- package/dist/agent/turno/turnoReal.js.map +1 -0
- package/dist/agent/turno/verificador.js.map +1 -0
- package/dist/cli/config.js +1 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/consola.js +9 -9
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/describe.js +2 -2
- package/dist/cli/describe.js.map +1 -1
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/fuentesDeDisco.js +29 -0
- package/dist/cli/fuentesDeDisco.js.map +1 -0
- package/dist/cli/main.js +37 -28
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/run.js +32 -10
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/traza.js +48 -0
- package/dist/cli/traza.js.map +1 -0
- package/dist/cli/tui/correrTui.js +4 -4
- package/dist/cli/tui/correrTui.js.map +1 -1
- package/dist/cli/verify.js +1 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/core/actos.js +1 -1
- package/dist/core/adjuntos.js +2 -2
- package/dist/core/agentes.js +145 -7
- package/dist/core/agentes.js.map +1 -1
- package/dist/core/artefactos.js +4 -4
- package/dist/core/config.js +1 -1
- package/dist/core/contextos.js +12 -1
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/descargas.js +1 -1
- package/dist/core/dispositivos.js +53 -3
- package/dist/core/dispositivos.js.map +1 -1
- package/dist/core/esqueleto.js +1 -1
- package/dist/core/modelos.js +16 -3
- package/dist/core/modelos.js.map +1 -1
- package/dist/core/navegacion.js +134 -0
- package/dist/core/navegacion.js.map +1 -0
- package/dist/core/notify.js +5 -0
- package/dist/core/notify.js.map +1 -1
- package/dist/core/planDeSubida.js +1 -1
- package/dist/core/settings.js +2 -2
- package/dist/core/shellDeAgente.js +99 -0
- package/dist/core/shellDeAgente.js.map +1 -0
- package/dist/core/skills.js +158 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/zipDeSkill.js +107 -0
- package/dist/core/zipDeSkill.js.map +1 -0
- package/dist/vendor/sqliteSaver.js +389 -0
- package/dist/vendor/sqliteSaver.js.map +1 -0
- package/dist/web/servidor/arranque.js +531 -47
- package/dist/web/servidor/arranque.js.map +1 -1
- package/dist/web/servidor/corredorDeTareas.js +3 -3
- package/dist/web/servidor/corredorDeTareas.js.map +1 -1
- package/dist/web/servidor/sesiones.js +4 -4
- package/dist/web/servidor/transporte.js +1 -1
- package/dist/web/servidor/transporte.js.map +1 -1
- package/dist/web/servidor/vestibulo.js +7 -7
- package/dist/web/servidor/vestibulo.js.map +1 -1
- package/package.json +9 -5
- package/skills/archify/SKILL.md +25 -2
- package/skills/artifacts-builder/SKILL.md +23 -0
- package/skills/xone-hotswap/SKILL.md +74 -0
- package/skills/xone-hotswap/scripts/xone-arrancar-ios +85 -0
- package/skills/xone-hotswap/scripts/xone-desplegar-android +411 -0
- package/skills/xone-hotswap/scripts/xone-hotswap +171 -0
- package/skills/xone-hotswap/scripts/xone-log-android +102 -0
- package/skills/xone-hotswap/scripts/xone-reiniciar-android +104 -0
- package/apps/web/dist/assets/index-CFYAoHdw.js +0 -477
- package/dist/agent/agentesEnDisco.js.map +0 -1
- package/dist/agent/arbolDeProyecto.js.map +0 -1
- package/dist/agent/artefactosEnDisco.js.map +0 -1
- package/dist/agent/aumentador.js.map +0 -1
- package/dist/agent/authEnDisco.js.map +0 -1
- package/dist/agent/busquedaRegex.js.map +0 -1
- package/dist/agent/catalogoModelos.js.map +0 -1
- package/dist/agent/checkpointer.js.map +0 -1
- package/dist/agent/cloudstudioClient.js.map +0 -1
- package/dist/agent/cloudstudioMcp.js.map +0 -1
- package/dist/agent/configEnDisco.js.map +0 -1
- package/dist/agent/consumoExterno.js.map +0 -1
- package/dist/agent/crearProyecto.js.map +0 -1
- package/dist/agent/descarga.js.map +0 -1
- package/dist/agent/diagnosticoDeTools.js.map +0 -1
- package/dist/agent/dispositivosEnMaquina.js.map +0 -1
- package/dist/agent/entorno.js.map +0 -1
- package/dist/agent/escrituraDeCodex.js.map +0 -1
- package/dist/agent/escrituraDeOpencode.js.map +0 -1
- package/dist/agent/escrituraExterna.js.map +0 -1
- package/dist/agent/gemini.js.map +0 -1
- package/dist/agent/git.js.map +0 -1
- package/dist/agent/gitSync.js.map +0 -1
- package/dist/agent/guionizado.js.map +0 -1
- package/dist/agent/hotswap.js.map +0 -1
- package/dist/agent/instalacionEnMaquina.js.map +0 -1
- package/dist/agent/instantanea.js.map +0 -1
- package/dist/agent/interrupts.js.map +0 -1
- package/dist/agent/juezDeTarea.js.map +0 -1
- package/dist/agent/lanzamientoEnMaquina.js.map +0 -1
- package/dist/agent/manifiesto.js.map +0 -1
- package/dist/agent/memoriaDeProyecto.js.map +0 -1
- package/dist/agent/mensajes.js.map +0 -1
- package/dist/agent/modelos.js.map +0 -1
- package/dist/agent/modelosDeMotor.js.map +0 -1
- package/dist/agent/normalizar.js.map +0 -1
- package/dist/agent/paqueteDelProyecto.js.map +0 -1
- package/dist/agent/perfiles.js.map +0 -1
- package/dist/agent/persona.js.map +0 -1
- package/dist/agent/procesosEnMaquina.js.map +0 -1
- package/dist/agent/proyecto.js.map +0 -1
- package/dist/agent/puente.js.map +0 -1
- package/dist/agent/resumenDeContexto.js +0 -30
- package/dist/agent/resumenDeContexto.js.map +0 -1
- package/dist/agent/resumenDeTool.js.map +0 -1
- package/dist/agent/sesionGit.js.map +0 -1
- package/dist/agent/settingsEnDisco.js.map +0 -1
- package/dist/agent/skills.js +0 -65
- package/dist/agent/skills.js.map +0 -1
- package/dist/agent/subagenteCodex.js.map +0 -1
- package/dist/agent/subagenteExterno.js.map +0 -1
- package/dist/agent/subagenteOpencode.js.map +0 -1
- package/dist/agent/subida.js.map +0 -1
- package/dist/agent/tareasEnDisco.js.map +0 -1
- package/dist/agent/textoDeTool.js.map +0 -1
- package/dist/agent/turnoReal.js.map +0 -1
- package/dist/agent/verificador.js.map +0 -1
- package/dist/agent/versionEnDisco.js.map +0 -1
- package/dist/agent/xoneAgent.js.map +0 -1
- package/dist/agent/zip.js.map +0 -1
- /package/dist/agent/{cloudstudioClient.js → cloudstudio/cloudstudioClient.js} +0 -0
- /package/dist/agent/{manifiesto.js → cloudstudio/manifiesto.js} +0 -0
- /package/dist/agent/{entorno.js → config/entorno.js} +0 -0
- /package/dist/agent/{gemini.js → config/gemini.js} +0 -0
- /package/dist/agent/{hotswap.js → dispositivos/hotswap.js} +0 -0
- /package/dist/agent/{instalacionEnMaquina.js → dispositivos/instalacionEnMaquina.js} +0 -0
- /package/dist/agent/{procesosEnMaquina.js → dispositivos/procesosEnMaquina.js} +0 -0
- /package/dist/agent/{git.js → sesiones/git.js} +0 -0
- /package/dist/agent/{consumoExterno.js → subagentes/consumoExterno.js} +0 -0
- /package/dist/agent/{escrituraDeCodex.js → subagentes/escrituraDeCodex.js} +0 -0
- /package/dist/agent/{subagenteCodex.js → subagentes/subagenteCodex.js} +0 -0
- /package/dist/agent/{subagenteExterno.js → subagentes/subagenteExterno.js} +0 -0
- /package/dist/agent/{subagenteOpencode.js → subagentes/subagenteOpencode.js} +0 -0
- /package/dist/agent/{diagnosticoDeTools.js → turno/diagnosticoDeTools.js} +0 -0
- /package/dist/agent/{mensajes.js → turno/mensajes.js} +0 -0
- /package/dist/agent/{normalizar.js → turno/normalizar.js} +0 -0
- /package/dist/agent/{textoDeTool.js → turno/textoDeTool.js} +0 -0
- /package/dist/agent/{verificador.js → turno/verificador.js} +0 -0
|
@@ -33,9 +33,12 @@ import { basename, dirname, join } from "node:path";
|
|
|
33
33
|
import { fileURLToPath } from "node:url";
|
|
34
34
|
import { randomUUID } from "node:crypto";
|
|
35
35
|
import { homedir } from "node:os";
|
|
36
|
-
import { escribirAgente, leerAgente } from "../../core/agentes.js";
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
36
|
+
import { escribirAgente, leerAgente, motivoDeNombreInaceptable } from "../../core/agentes.js";
|
|
37
|
+
import { motivoDeNombreDeSkillInaceptable, nombreDeSkillSugerido, } from "../../core/skills.js";
|
|
38
|
+
import { borrarSkill, cargarSkills, esDeSerie as esDeSerieLaSkill, guardarSkill, instalarSkillDesdeZip, } from "../../agent/grafo/skills.js";
|
|
39
|
+
import { TOPE_DEL_ZIP } from "../../core/zipDeSkill.js";
|
|
40
|
+
import { borrarAgente, cargarAgentes, esDeSerie, guardarAgente, renombrarAgente, restaurarAgente, } from "../../agent/subagentes/agentesEnDisco.js";
|
|
41
|
+
import { detectarDispositivos, frameworkEnDispositivo } from "../../agent/dispositivos/dispositivosEnMaquina.js";
|
|
39
42
|
/**
|
|
40
43
|
* El veredicto de «¿se puede lanzar?» y el lanzamiento entero.
|
|
41
44
|
*
|
|
@@ -45,41 +48,43 @@ import { detectarDispositivos, frameworkEnDispositivo } from "../../agent/dispos
|
|
|
45
48
|
* donde puede dejar de estar montado, que es el patrón de fallo de esta casa. Lo único que este
|
|
46
49
|
* lado aporta es `alFase`, que es cómo se cuenta el recorrido.
|
|
47
50
|
*/
|
|
48
|
-
import { lanzarEnDispositivo, } from "../../agent/lanzamientoEnMaquina.js";
|
|
51
|
+
import { lanzarEnDispositivo, } from "../../agent/dispositivos/lanzamientoEnMaquina.js";
|
|
49
52
|
import { motivoDeBloqueo, puedeLanzarse, } from "../../core/puedeLanzarse.js";
|
|
50
53
|
import { PLATAFORMAS_DE_DISPOSITIVO } from "../../core/settings.js";
|
|
51
|
-
import { instalarHerramientaDeDispositivos, verificarDispositivo } from "../../agent/dispositivosEnMaquina.js";
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
+
import { instalarHerramientaDeDispositivos, verificarDispositivo } from "../../agent/dispositivos/dispositivosEnMaquina.js";
|
|
55
|
+
import { arrancarEmulador } from "../../agent/dispositivos/arranqueDeEmulador.js";
|
|
56
|
+
import { correrPasoDeReceta } from "../../agent/dispositivos/instalacionEnMaquina.js";
|
|
57
|
+
import { modelosDeMotor } from "../../agent/config/modelosDeMotor.js";
|
|
54
58
|
import { parsear, compatibleConOpenAi, esProveedorPersonalizado, idDeProveedorPersonalizado, motivoDeEndpointInaceptable, motivoDeSlugInaceptable, nombreDeProveedor, PROVEEDORES, slugDesdeNombre, resolver, SIN_CREDENCIAL, PAPELES, } from "../../core/modelos.js";
|
|
55
59
|
import { hayCredencial } from "../../cli/consola.js";
|
|
56
60
|
import { motivoDeClaveInaceptable } from "../../core/config.js";
|
|
57
|
-
import { aplicarAuth, aplicarCredencialAlProceso, cargar, guardarModeloGlobal, } from "../../agent/configEnDisco.js";
|
|
58
|
-
import { borrarCredencial, guardarCredencial } from "../../agent/authEnDisco.js";
|
|
59
|
-
import { borrarProveedorPersonalizado, guardarProveedorPersonalizado, proveedoresPersonalizados, } from "../../agent/configEnDisco.js";
|
|
60
|
-
import { crearCheckpointerDeProyecto, hayCheckpoint, olvidarHilo, } from "../../agent/checkpointer.js";
|
|
61
|
-
import { cargarSettings, guardarConcurrenciaDeTareas, guardarDispositivos, guardarEntorno as guardarEntornoEnDisco, } from "../../agent/settingsEnDisco.js";
|
|
61
|
+
import { aplicarAuth, aplicarCredencialAlProceso, cargar, guardarModeloGlobal, } from "../../agent/config/configEnDisco.js";
|
|
62
|
+
import { borrarCredencial, guardarCredencial } from "../../agent/config/authEnDisco.js";
|
|
63
|
+
import { borrarProveedorPersonalizado, guardarProveedorPersonalizado, proveedoresPersonalizados, } from "../../agent/config/configEnDisco.js";
|
|
64
|
+
import { crearCheckpointerDeProyecto, hayCheckpoint, olvidarHilo, } from "../../agent/sesiones/checkpointer.js";
|
|
65
|
+
import { cargarSettings, guardarConcurrenciaDeTareas, guardarDispositivos, guardarEntorno as guardarEntornoEnDisco, } from "../../agent/config/settingsEnDisco.js";
|
|
62
66
|
import { dentroDelWorkspace, seAplicaSinAprobacion } from "../../core/settings.js";
|
|
63
|
-
import { cloudstudioDelProyecto } from "../../agent/configEnDisco.js";
|
|
64
|
-
import { abrirEnSistema } from "../../agent/cloudstudioMcp.js";
|
|
65
|
-
import { nombreDePersona } from "../../agent/persona.js";
|
|
66
|
-
import { cambiosDeSesion, fotoDeApertura, olvidarSesion, parcheDeSesion } from "../../agent/sesionGit.js";
|
|
67
|
-
import { commitDeTurno, cambiosPendientes, trabajoSinCommitear } from "../../agent/gitSync.js";
|
|
67
|
+
import { cloudstudioDelProyecto } from "../../agent/config/configEnDisco.js";
|
|
68
|
+
import { abrirEnSistema } from "../../agent/cloudstudio/cloudstudioMcp.js";
|
|
69
|
+
import { nombreDePersona } from "../../agent/config/persona.js";
|
|
70
|
+
import { cambiosDeSesion, fotoDeApertura, olvidarSesion, parcheDeSesion } from "../../agent/sesiones/sesionGit.js";
|
|
71
|
+
import { commitDeTurno, cambiosPendientes, trabajoSinCommitear } from "../../agent/sesiones/gitSync.js";
|
|
68
72
|
import { marcarTareaDeSesion, sembrarConsumosPendientes } from "./sesiones.js";
|
|
69
73
|
import { RUTA_ARTEFACTOS, esRutaDeArtefacto } from "../../core/artefactos.js";
|
|
70
|
-
import { arbolDeProyecto, leerFicheroDeProyecto, motivoDeRutaInaceptable, } from "../../agent/arbolDeProyecto.js";
|
|
71
|
-
import { leerArtefactoCrudo, leerArtefactoDeSesion, } from "../../agent/artefactosEnDisco.js";
|
|
72
|
-
import { CatalogoModelos } from "../../agent/catalogoModelos.js";
|
|
73
|
-
import { Modelos } from "../../agent/modelos.js";
|
|
74
|
-
import { crearJuezDeTarea, invocarConModelos } from "../../agent/juezDeTarea.js";
|
|
74
|
+
import { arbolDeProyecto, leerFicheroDeProyecto, motivoDeRutaInaceptable, } from "../../agent/grafo/arbolDeProyecto.js";
|
|
75
|
+
import { leerArtefactoCrudo, leerArtefactoDeSesion, } from "../../agent/grafo/artefactosEnDisco.js";
|
|
76
|
+
import { CatalogoModelos } from "../../agent/config/catalogoModelos.js";
|
|
77
|
+
import { Modelos } from "../../agent/config/modelos.js";
|
|
78
|
+
import { crearJuezDeTarea, invocarConModelos } from "../../agent/tareas/juezDeTarea.js";
|
|
79
|
+
import { SIN_CONSUMO } from "../../agent/subagentes/consumoExterno.js";
|
|
75
80
|
import { AumentadorGuionizado } from "../../core/ports.js";
|
|
76
81
|
import { arrancarServidor } from "./servidor.js";
|
|
77
82
|
import { consolaParaTarea, crearCorredorDeTareas, revisionConGit, } from "./corredorDeTareas.js";
|
|
78
83
|
import { CONCURRENCIA_POR_OMISION, conEstado, darPorBuenaAMano, tituloDeTarea, } from "../../core/tareas.js";
|
|
79
|
-
import { aplicarFeedback, TOPE_DE_ADJUNTO } from "../../agent/tareasEnDisco.js";
|
|
84
|
+
import { aplicarFeedback, TOPE_DE_ADJUNTO } from "../../agent/tareas/tareasEnDisco.js";
|
|
80
85
|
import { nombreDeAdjuntoAceptable } from "../../core/adjuntos.js";
|
|
81
|
-
import { rutaMemoriaDeProyecto } from "../../agent/memoriaDeProyecto.js";
|
|
82
|
-
import { crearAumentador, invocarParaAumentar } from "../../agent/aumentador.js";
|
|
86
|
+
import { rutaMemoriaDeProyecto } from "../../agent/grafo/memoriaDeProyecto.js";
|
|
87
|
+
import { crearAumentador, invocarParaAumentar } from "../../agent/tareas/aumentador.js";
|
|
83
88
|
import { baseDeWorkspacePorOmision, conexionDeVestibulo, crearVestibulo, escribirProyectoEnDisco, esProyectoEnDisco, } from "./vestibulo.js";
|
|
84
89
|
// Valor y no tipo: la traducción de una `Tarea` a lo que viaja vive JUNTO al tipo que
|
|
85
90
|
// produce y no aquí. Estuvo en este cierre, y ahí se cayó `veredicto` sin que nada se
|
|
@@ -97,6 +102,24 @@ import { filaDeTarea } from "./transporte.js";
|
|
|
97
102
|
* definición de que el aviso frena, que es justo lo que su propia regla prohíbe.
|
|
98
103
|
*/
|
|
99
104
|
export const MS_DE_TRABAJO_AL_ABRIR = 2000;
|
|
105
|
+
/**
|
|
106
|
+
* Cuánto se espera al arranque antes de entrar igual.
|
|
107
|
+
*
|
|
108
|
+
* Lo que se prepara es la sesión MCP y la lista de proyectos del primer entorno registrado,
|
|
109
|
+
* y el usuario pidió no entrar al Escritorio hasta tenerlo: entrar antes significa un
|
|
110
|
+
* Escritorio vacío que se rellena delante. Pero esperar SIN plazo es lo que convierte una
|
|
111
|
+
* espera en un cuelgue — medido con un MCP que descarta paquetes, el lienzo se quedaba para
|
|
112
|
+
* siempre, y eso es peor que lo que veníamos a arreglar.
|
|
113
|
+
*
|
|
114
|
+
* Generoso a propósito: una sesión MCP fría lleva descubrimiento OAuth y handshake, y
|
|
115
|
+
* cortarla pronto haría entrar en vacío justo a quien tiene la red lenta, que es a quien más
|
|
116
|
+
* le duele. Y como el lienzo DICE lo que está haciendo (`preparando`), la espera se lee como
|
|
117
|
+
* trabajo y no como una pantalla muerta.
|
|
118
|
+
*
|
|
119
|
+
* Igual que `MS_DE_TRABAJO_AL_ABRIR`, lo que se acota es la ESPERA y no el trabajo: la
|
|
120
|
+
* promesa sigue viva y lo que llegue tarde sale en el siguiente anuncio.
|
|
121
|
+
*/
|
|
122
|
+
export const MS_DE_PREPARACION = 10_000;
|
|
100
123
|
export const RUTA_EVENTOS = "/eventos";
|
|
101
124
|
export const RUTA_ACCION = "/accion";
|
|
102
125
|
/**
|
|
@@ -109,6 +132,13 @@ export const RUTA_ACCION = "/accion";
|
|
|
109
132
|
* el contrato de las skills que los escriben es «autocontenido».
|
|
110
133
|
*/
|
|
111
134
|
export const RUTA_ARTEFACTO = "/artefacto";
|
|
135
|
+
/**
|
|
136
|
+
* `POST /skill?nombre=<fichero.zip>&ambito=<global|proyecto>` — los BYTES de un `.zip`.
|
|
137
|
+
*
|
|
138
|
+
* Por HTTP y no por el cable, que lleva JSON: la misma razón y el mismo molde que
|
|
139
|
+
* `POST /adjunto`. Una ruta EXACTA, porque el servidor casa por ruta exacta.
|
|
140
|
+
*/
|
|
141
|
+
export const RUTA_SKILL = "/skill";
|
|
112
142
|
/**
|
|
113
143
|
* `POST /adjunto?tarea=<id>&nombre=<fichero>` — los BYTES de un adjunto.
|
|
114
144
|
*
|
|
@@ -245,6 +275,14 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
245
275
|
* en la Trayectoria —la otra pestaña—, y el wizard repintaba el mismo paso sin decir nada.
|
|
246
276
|
*/
|
|
247
277
|
let aviso;
|
|
278
|
+
/**
|
|
279
|
+
* Qué está preparando el arranque, mientras lo prepara. `undefined` = listo.
|
|
280
|
+
*
|
|
281
|
+
* Vive aquí y no en el anuncio porque el `alta` se emite también en los dos flancos de
|
|
282
|
+
* cada turno, y esos anuncios no están preparando nada: quien lo pone y lo quita es el
|
|
283
|
+
* arranque de la conexión, y los demás leen lo que haya.
|
|
284
|
+
*/
|
|
285
|
+
let preparando;
|
|
248
286
|
const destinoActual = () => vestibulo.proyectoAbierto() ?? vestibulo.consola;
|
|
249
287
|
const emitir = (mensaje) => {
|
|
250
288
|
for (const cliente of clientes)
|
|
@@ -436,6 +474,10 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
436
474
|
: {}),
|
|
437
475
|
...(vestibulo.nombre === undefined ? {} : { nombre: vestibulo.nombre }),
|
|
438
476
|
...(aviso === undefined ? {} : { aviso }),
|
|
477
|
+
// Ausente = listo, y solo entonces el cliente sale del lienzo. Se lee de la variable
|
|
478
|
+
// en cada anuncio en vez de pasarse por parámetro: los anuncios de los flancos del
|
|
479
|
+
// turno no preparan nada y tienen que decir lo que hay, no lo que había.
|
|
480
|
+
...(preparando === undefined ? {} : { preparando }),
|
|
439
481
|
});
|
|
440
482
|
};
|
|
441
483
|
/**
|
|
@@ -471,9 +513,15 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
471
513
|
motor: a.motor,
|
|
472
514
|
...(a.modelo === undefined ? {} : { modelo: a.modelo }),
|
|
473
515
|
soloLectura: a.soloLectura,
|
|
516
|
+
// Ausente = no la tiene. No se emite `false` para que el cliente no tenga que
|
|
517
|
+
// distinguir entre «no» y «no consta»: aquí son lo mismo y la ausencia lo dice.
|
|
518
|
+
...(a.ejecucion === true ? { ejecucion: true } : {}),
|
|
474
519
|
skills: a.skills,
|
|
475
520
|
instrucciones: a.instrucciones,
|
|
476
521
|
origen: a.origen,
|
|
522
|
+
// Ausente cuando el `.md` es del usuario, y eso es el dato: es lo que le pone la
|
|
523
|
+
// papelera en vez de un «Restaurar el de serie».
|
|
524
|
+
...(a.semilla === undefined ? {} : { semilla: a.semilla }),
|
|
477
525
|
})),
|
|
478
526
|
problemas,
|
|
479
527
|
};
|
|
@@ -489,6 +537,52 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
489
537
|
*/
|
|
490
538
|
const atenderAgente = (mensaje) => {
|
|
491
539
|
const abierto = vestibulo.proyectoAbierto();
|
|
540
|
+
/**
|
|
541
|
+
* `restaurar` va ANTES de la guarda del ámbito porque no tiene ámbito que elegir: la
|
|
542
|
+
* siembra solo escribe en el global, así que la versión de serie solo puede volver ahí.
|
|
543
|
+
* Pasarlo por el `ambito` del mensaje sería ofrecer una decisión que no existe, y con un
|
|
544
|
+
* `ambito: "proyecto"` daría un «no hay ningún proyecto abierto» que no viene a cuento.
|
|
545
|
+
*/
|
|
546
|
+
if (mensaje.accion === "restaurar") {
|
|
547
|
+
informar(restaurarAgente(homedir(), mensaje.agente.nombre)
|
|
548
|
+
? `subagente «${mensaje.agente.nombre}» restaurado al de serie`
|
|
549
|
+
: `«${mensaje.agente.nombre}» no es uno de los de serie: no hay ninguna versión nuestra que poner`);
|
|
550
|
+
emitirAgentes();
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Y un de serie NO se borra. La negativa vive AQUÍ y no solo en el cliente, que le
|
|
555
|
+
* esconde el icono: esconderlo es presentación, y este mensaje lo puede mandar cualquier
|
|
556
|
+
* cosa que hable por el cable. Borrarlo no devolvía el de serie —`sembrarAgentes` no
|
|
557
|
+
* resiembra lo que consta entregado— así que era perderlo para siempre; lo que sí hace
|
|
558
|
+
* lo que aquello prometía es `restaurar`, y se dice en el mismo sitio donde se rechaza.
|
|
559
|
+
*/
|
|
560
|
+
if (mensaje.accion === "borrar" && mensaje.ambito === "global" && esDeSerie(mensaje.agente.nombre)) {
|
|
561
|
+
informar(`«${mensaje.agente.nombre}» lo trae xonecode y no se borra: edítalo, o restaura el de serie si quieres el original`);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Un renombrado de verdad: `renombrandoDe` puesto y DISTINTO del nombre nuevo. Con el
|
|
566
|
+
* mismo nombre es un guardado normal, y tratarlo de renombrado lo haría fallar por
|
|
567
|
+
* «destino ocupado» contra sí mismo.
|
|
568
|
+
*/
|
|
569
|
+
const renombrandoDe = mensaje.accion === "guardar" &&
|
|
570
|
+
mensaje.renombrandoDe !== undefined &&
|
|
571
|
+
mensaje.renombrandoDe !== mensaje.agente.nombre
|
|
572
|
+
? mensaje.renombrandoDe
|
|
573
|
+
: undefined;
|
|
574
|
+
/**
|
|
575
|
+
* Y un de serie tampoco se RENOMBRA, por lo mismo que no se borra: su nombre es lo que lo
|
|
576
|
+
* ata a la marca de la siembra (`.semilla.json` guarda el hash POR NOMBRE), así que
|
|
577
|
+
* moverlo lo convierte en un subagente del usuario y hace que el de serie vuelva a
|
|
578
|
+
* sembrarse al arrancar — dos especialistas donde había uno. La regla vive aquí y no en
|
|
579
|
+
* `renombrarAgente`, que mueve un fichero: dos sitios donde decidir lo mismo es cómo uno
|
|
580
|
+
* de los dos se queda sin la regla.
|
|
581
|
+
*/
|
|
582
|
+
if (renombrandoDe !== undefined && mensaje.ambito === "global" && esDeSerie(renombrandoDe)) {
|
|
583
|
+
informar(`«${renombrandoDe}» lo trae xonecode y su nombre no se cambia: es lo que lo ata a la versión de serie`);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
492
586
|
if (mensaje.ambito === "proyecto" && abierto === undefined) {
|
|
493
587
|
informar("no hay ningún proyecto abierto: ese subagente solo se puede guardar como global");
|
|
494
588
|
return;
|
|
@@ -515,8 +609,35 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
515
609
|
informar(`no se guarda «${candidato.nombre}»: ${comprobado.error}`);
|
|
516
610
|
return;
|
|
517
611
|
}
|
|
518
|
-
|
|
519
|
-
|
|
612
|
+
/**
|
|
613
|
+
* Y el NOMBRE tiene que ser un slug, aquí y no en el cargador: cargando solo se DICE
|
|
614
|
+
* (`leerCarpetaDeAgentes`), porque rechazar al cargar haría desaparecer un subagente
|
|
615
|
+
* que funciona. Guardar es el único momento con alguien delante para arreglarlo.
|
|
616
|
+
*
|
|
617
|
+
* No lo cubre `leerAgente`: el nombre no sale del `.md`, sale del FICHERO, así que
|
|
618
|
+
* escribir y volver a leer no lo mira. Y tampoco `segmentoSeguro`, que impide salir de
|
|
619
|
+
* la carpeta y nada más — `Documentador` pasa esa y no es un slug.
|
|
620
|
+
*/
|
|
621
|
+
const malNombre = motivoDeNombreInaceptable(candidato.nombre);
|
|
622
|
+
if (malNombre !== undefined) {
|
|
623
|
+
informar(`no se guarda «${candidato.nombre}»: el nombre ${malNombre}`);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (renombrandoDe === undefined) {
|
|
627
|
+
guardarAgente(base, candidato);
|
|
628
|
+
informar(`subagente «${candidato.nombre}» guardado en ${mensaje.ambito}`);
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
// Los tres desenlaces se dicen distintos: el destino ocupado y el origen que ya no
|
|
632
|
+
// está son dos situaciones que el usuario arregla de formas distintas, y un «no se
|
|
633
|
+
// pudo» para las dos le haría probar la equivocada.
|
|
634
|
+
const como = renombrarAgente(base, renombrandoDe, candidato);
|
|
635
|
+
informar(como === "hecho"
|
|
636
|
+
? `subagente «${renombrandoDe}» renombrado a «${candidato.nombre}»`
|
|
637
|
+
: como === "sin-origen"
|
|
638
|
+
? `«${renombrandoDe}» ya no existía en ${mensaje.ambito}: no se ha renombrado`
|
|
639
|
+
: `ya hay un subagente «${candidato.nombre}» en ${mensaje.ambito}: elige otro nombre, o bórralo primero`);
|
|
640
|
+
}
|
|
520
641
|
}
|
|
521
642
|
}
|
|
522
643
|
catch (error) {
|
|
@@ -524,6 +645,132 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
524
645
|
}
|
|
525
646
|
emitirAgentes();
|
|
526
647
|
};
|
|
648
|
+
/**
|
|
649
|
+
* Las skills en vigor, compuestas pero sin mandar.
|
|
650
|
+
*
|
|
651
|
+
* `cargarSkills` cada vez y no una lista cacheada, por lo mismo que `mensajeDeAgentes`:
|
|
652
|
+
* las carpetas se pueden tocar a mano con la consola abierta, y una lista congelada al
|
|
653
|
+
* arrancar haría creer que el cambio no se aplicó.
|
|
654
|
+
*
|
|
655
|
+
* **El `cuerpo` viaja solo en las del USUARIO.** Las de serie son nueve ficheros que pasan
|
|
656
|
+
* de veinte mil caracteres cada uno; mandarlos en la ráfaga de bienvenida es pagar cientos
|
|
657
|
+
* de kilobytes por rellenar un formulario que nadie puede guardar. El suyo se pide a mano
|
|
658
|
+
* (`cuerpoDeSkill`), que es cuando alguien la está mirando de verdad.
|
|
659
|
+
*/
|
|
660
|
+
const mensajeDeSkills = () => {
|
|
661
|
+
const abierto = vestibulo.proyectoAbierto();
|
|
662
|
+
const { skills, problemas } = cargarSkills(abierto?.raiz);
|
|
663
|
+
return {
|
|
664
|
+
clase: "skills",
|
|
665
|
+
skills: skills.map((s) => ({
|
|
666
|
+
nombre: s.nombre,
|
|
667
|
+
descripcion: s.descripcion,
|
|
668
|
+
origen: s.origen,
|
|
669
|
+
tokens: s.tokens,
|
|
670
|
+
ficheros: s.ficheros,
|
|
671
|
+
// El frontmatter va SIEMPRE: son cuatro líneas, y es lo que contesta qué declara el
|
|
672
|
+
// fichero. El CUERPO no, en las de serie: eso sí son decenas de miles de caracteres.
|
|
673
|
+
...(s.frontmatter === undefined ? {} : { frontmatter: s.frontmatter }),
|
|
674
|
+
...(s.origen === "serie" ? {} : { cuerpo: s.cuerpo }),
|
|
675
|
+
})),
|
|
676
|
+
problemas,
|
|
677
|
+
};
|
|
678
|
+
};
|
|
679
|
+
const emitirSkills = () => emitir(mensajeDeSkills());
|
|
680
|
+
/**
|
|
681
|
+
* El cuerpo de UNA skill, bajo demanda: al abrir la ficha de una de serie o al copiarla.
|
|
682
|
+
*
|
|
683
|
+
* Se contesta SOLO a quien lo pidió no se puede —el transporte habla con todos—, así que
|
|
684
|
+
* va a todos y el cliente se queda con la que tiene abierta. Es un fichero de texto que ya
|
|
685
|
+
* está en la lista, no un secreto.
|
|
686
|
+
*
|
|
687
|
+
* Lo que no se pudo leer se DICE con un `error`: una ficha que se abre en blanco se lee
|
|
688
|
+
* como que la skill está vacía, y entonces copiarla produciría una copia vacía.
|
|
689
|
+
*/
|
|
690
|
+
const atenderCuerpoDeSkill = (mensaje) => {
|
|
691
|
+
const abierto = vestibulo.proyectoAbierto();
|
|
692
|
+
const skill = cargarSkills(abierto?.raiz).skills.find((s) => s.nombre === mensaje.nombre);
|
|
693
|
+
emitir(skill === undefined
|
|
694
|
+
? { clase: "cuerpoDeSkill", nombre: mensaje.nombre, error: "ya no está en el catálogo" }
|
|
695
|
+
: { clase: "cuerpoDeSkill", nombre: mensaje.nombre, cuerpo: skill.cuerpo });
|
|
696
|
+
};
|
|
697
|
+
/**
|
|
698
|
+
* Alta, cambio y borrado de una skill del usuario. El mismo molde que `atenderAgente`.
|
|
699
|
+
*
|
|
700
|
+
* **Las de serie no se tocan, y la barrera está AQUÍ.** Viven dentro del paquete
|
|
701
|
+
* instalado: una edición se la llevaría el siguiente `npm install` sin decir nada, y un
|
|
702
|
+
* borrado no se puede reponer porque nada nuestro se copia nunca a la casa del usuario
|
|
703
|
+
* —no hay marca de siembra que consultar, a diferencia de los subagentes—. Lo que la
|
|
704
|
+
* consola ofrece sobre una de serie es COPIARLA, que es un `guardar` con otro nombre.
|
|
705
|
+
*
|
|
706
|
+
* `guardarSkill` DEVUELVE el motivo en vez de lanzar, así que los tres desenlaces —el
|
|
707
|
+
* nombre ocupado, el nombre de una de serie y el fallo de escritura— se dicen distintos:
|
|
708
|
+
* son tres cosas que el usuario arregla de tres formas.
|
|
709
|
+
*/
|
|
710
|
+
const atenderSkill = (mensaje) => {
|
|
711
|
+
const abierto = vestibulo.proyectoAbierto();
|
|
712
|
+
const nombre = mensaje.skill.nombre;
|
|
713
|
+
if (mensaje.ambito === "proyecto" && abierto === undefined) {
|
|
714
|
+
informar("no hay ningún proyecto abierto: esa skill solo se puede guardar como global");
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const base = mensaje.ambito === "proyecto" ? abierto.raiz : homedir();
|
|
718
|
+
/**
|
|
719
|
+
* Borrar se INTENTA y el motivo sale de lo que pasó, no de una guarda previa por NOMBRE.
|
|
720
|
+
*
|
|
721
|
+
* La guarda por nombre era demasiado ancha: una skill del usuario que se llame como una
|
|
722
|
+
* de serie —tapándola, que es la forma de afinarla sin editar donde el `npm install` la
|
|
723
|
+
* pisaría— no se podía borrar nunca, porque `esDeSerie` mira el catálogo del paquete y no
|
|
724
|
+
* la carpeta del usuario. Lo que hay en la carpeta del usuario es del usuario y se borra;
|
|
725
|
+
* lo que no está ahí y ADEMÁS lo traemos nosotros es lo que se niega, con el camino al
|
|
726
|
+
* lado. Un solo sitio decide, que es `borrarSkill` contestando si había algo.
|
|
727
|
+
*/
|
|
728
|
+
if (mensaje.accion === "borrar") {
|
|
729
|
+
informar(borrarSkill(base, nombre)
|
|
730
|
+
? `skill «${nombre}» borrada`
|
|
731
|
+
: esDeSerieLaSkill(nombre)
|
|
732
|
+
? `la skill «${nombre}» la trae xonecode y no se borra: cópiala si quieres partir de ella`
|
|
733
|
+
: `«${nombre}» no existía en ${mensaje.ambito}`);
|
|
734
|
+
emitirSkills();
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* El nombre tiene que ser un slug, y se comprueba al GUARDAR y no al cargar: rechazar al
|
|
739
|
+
* cargar haría desaparecer una skill que funciona, y guardar es el único momento con
|
|
740
|
+
* alguien delante para arreglarlo. `segmentoSeguro` —que `guardarSkill` sí aplica— solo
|
|
741
|
+
* impide salir de la carpeta: `Mi Skill` la pasa y no es un slug, y el nombre de una
|
|
742
|
+
* skill viaja además dentro del `.md` de un subagente.
|
|
743
|
+
*/
|
|
744
|
+
const malNombre = motivoDeNombreDeSkillInaceptable(nombre);
|
|
745
|
+
if (malNombre !== undefined) {
|
|
746
|
+
const sugerido = nombreDeSkillSugerido(nombre);
|
|
747
|
+
informar(`no se guarda «${nombre}»: el nombre ${malNombre}` +
|
|
748
|
+
(sugerido === undefined ? "" : ` (prueba «${sugerido}»)`));
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (mensaje.skill.descripcion.trim() === "") {
|
|
752
|
+
// Es lo que el modelo lee para decidir si le sirve: sin ella la skill está en el
|
|
753
|
+
// catálogo y no la usa nadie nunca, que es peor que no estar.
|
|
754
|
+
informar(`no se guarda «${nombre}»: sin descripción, el orquestador no sabría cuándo usarla`);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const renombrandoDe = mensaje.renombrandoDe !== undefined && mensaje.renombrandoDe !== nombre
|
|
758
|
+
? mensaje.renombrandoDe
|
|
759
|
+
: undefined;
|
|
760
|
+
const escrita = guardarSkill(base, {
|
|
761
|
+
nombre,
|
|
762
|
+
descripcion: mensaje.skill.descripcion,
|
|
763
|
+
cuerpo: mensaje.skill.cuerpo ?? "",
|
|
764
|
+
// El frontmatter de ANTES, para conservar lo que no editamos. Ausente en un alta.
|
|
765
|
+
...(mensaje.skill.frontmatter === undefined ? {} : { frontmatter: mensaje.skill.frontmatter }),
|
|
766
|
+
}, mensaje.renombrandoDe);
|
|
767
|
+
informar("error" in escrita
|
|
768
|
+
? `no se guarda «${nombre}»: ${escrita.error}`
|
|
769
|
+
: renombrandoDe === undefined
|
|
770
|
+
? `skill «${nombre}» guardada en ${mensaje.ambito}`
|
|
771
|
+
: `skill «${renombrandoDe}» renombrada a «${nombre}»`);
|
|
772
|
+
emitirSkills();
|
|
773
|
+
};
|
|
527
774
|
/** El mensaje de modelos, compuesto pero sin mandar: `adjuntar` se lo da SOLO al cliente
|
|
528
775
|
* que acaba de llegar, y el resto de sitios lo emite a todos. */
|
|
529
776
|
const mensajeDeModelos = () => {
|
|
@@ -602,6 +849,10 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
602
849
|
// se puede abrir en cuanto conecta, y sin esto enseñaría una lista vacía hasta que algo
|
|
603
850
|
// los cambiara — que es indistinguible de «no tienes ninguno».
|
|
604
851
|
const agentes = mensajeDeAgentes();
|
|
852
|
+
// Y las skills, por lo mismo que los subagentes y con una razón más: el editor de un
|
|
853
|
+
// subagente pinta sus skills como una lista de casillas, así que sin esto ese formulario
|
|
854
|
+
// se abriría sin ninguna casilla que marcar — que es indistinguible de «no hay ninguna».
|
|
855
|
+
const skills = mensajeDeSkills();
|
|
605
856
|
// La cola de tareas, si esta ejecución las tiene: la misma regla que `agentes`, sin
|
|
606
857
|
// esto la pestaña de tareas se quedaría vacía hasta el primer cambio de la cola.
|
|
607
858
|
const tareas = mensajeDeTareas();
|
|
@@ -619,6 +870,7 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
619
870
|
cliente({ clase: "reemision", actos: [...actos] });
|
|
620
871
|
cliente(modelos);
|
|
621
872
|
cliente(agentes);
|
|
873
|
+
cliente(skills);
|
|
622
874
|
if (tareas !== undefined)
|
|
623
875
|
cliente(tareas);
|
|
624
876
|
if (consumoDeLaSesion !== undefined) {
|
|
@@ -708,20 +960,33 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
708
960
|
* heredar un «ya se intentó» que se quedó en `proyectos: []` para siempre. Esto puede
|
|
709
961
|
* abrir el navegador de verdad si el token necesita reautenticar —`conectarCloudStudio`
|
|
710
962
|
* ya lo hace así—, y es lo correcto: un token vivo no toca el puerto de callback en
|
|
711
|
-
* absoluto (arreglado en `agent/cloudstudioMcp.ts#abrirCliente`), así que dos conexiones
|
|
963
|
+
* absoluto (arreglado en `agent/cloudstudio/cloudstudioMcp.ts#abrirCliente`), así que dos conexiones
|
|
712
964
|
* seguidas no chocan por intentarlo cada una.
|
|
713
965
|
*/
|
|
714
|
-
|
|
966
|
+
/**
|
|
967
|
+
* De qué entorno se poblarían los proyectos al arrancar, o nada si no hay nada que
|
|
968
|
+
* preparar.
|
|
969
|
+
*
|
|
970
|
+
* Es la MISMA decisión que toma `poblarProyectosSiProcede`, extraída para que el arranque
|
|
971
|
+
* pueda anunciar la fase ANTES de empezarla. En dos sitios divergirían, y el precio sería
|
|
972
|
+
* de los peores: el lienzo esperando por un trabajo que nadie va a pedir (`preparando`
|
|
973
|
+
* puesto y nada que lo quite), o entrar en vacío justo cuando sí había algo que traer.
|
|
974
|
+
*/
|
|
975
|
+
const entornoAPoblar = () => {
|
|
715
976
|
if (vestibulo.proyectoAbierto() !== undefined)
|
|
716
|
-
return;
|
|
977
|
+
return undefined;
|
|
717
978
|
if (entornoElegido !== undefined)
|
|
718
|
-
return;
|
|
979
|
+
return undefined;
|
|
719
980
|
const [primero] = vestibulo.entornosRegistrados();
|
|
720
|
-
|
|
981
|
+
return primero === undefined ? undefined : { id: primero.id, nombre: primero.nombre };
|
|
982
|
+
};
|
|
983
|
+
const poblarProyectosSiProcede = async () => {
|
|
984
|
+
const destino = entornoAPoblar();
|
|
985
|
+
if (destino === undefined)
|
|
721
986
|
return;
|
|
722
987
|
try {
|
|
723
|
-
proyectos = await vestibulo.proyectosDe(
|
|
724
|
-
entornoElegido =
|
|
988
|
+
proyectos = await vestibulo.proyectosDe(destino.id);
|
|
989
|
+
entornoElegido = destino.id;
|
|
725
990
|
}
|
|
726
991
|
catch (error) {
|
|
727
992
|
aviso = error instanceof Error ? error.message : String(error);
|
|
@@ -1033,7 +1298,7 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1033
1298
|
/**
|
|
1034
1299
|
* «Se edita la tarea y se agrega el feedback del usuario» (§0 del diseño, textual):
|
|
1035
1300
|
* añadir un feedback a una tarea «esperando feedback» la devuelve al lazo. Vive detrás de
|
|
1036
|
-
* `aplicarFeedback` (`agent/tareasEnDisco.ts`) y no repite su lógica aquí, por el mismo
|
|
1301
|
+
* `aplicarFeedback` (`agent/tareas/tareasEnDisco.ts`) y no repite su lógica aquí, por el mismo
|
|
1037
1302
|
* motivo que `atenderCrearTarea`/`atenderAccionDeTarea` no reimplementan `conEstado`: la
|
|
1038
1303
|
* regla de qué feedback vale y qué transición es legal está en una sola función, probada
|
|
1039
1304
|
* sola y sin necesitar un servidor de mentira alrededor.
|
|
@@ -1051,6 +1316,12 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1051
1316
|
/** Los ajustes de destino, o `{}` —que es «se miran todos»— si esta ejecución no los lee. */
|
|
1052
1317
|
const ajustesDeDispositivos = () => opciones.ajustesDeDispositivos?.() ?? {};
|
|
1053
1318
|
let deteccionEnVuelo;
|
|
1319
|
+
/**
|
|
1320
|
+
* Cómo acabó el último «Arrancar». Viaja con la foto porque el resultado de arrancar ES la
|
|
1321
|
+
* foto: `emulator` devuelve el control enseguida y lo que importa es si el aparato apareció.
|
|
1322
|
+
* Se limpia al pedir una medida sin arranque de por medio, para no repetir un acuse viejo.
|
|
1323
|
+
*/
|
|
1324
|
+
let ultimoArranque;
|
|
1054
1325
|
const atenderDispositivos = () => {
|
|
1055
1326
|
if (opciones.detectarDispositivos === undefined)
|
|
1056
1327
|
return Promise.resolve();
|
|
@@ -1060,7 +1331,12 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1060
1331
|
deteccionEnVuelo = (async () => {
|
|
1061
1332
|
try {
|
|
1062
1333
|
informeDeDispositivos = sinRutas(await detectar());
|
|
1063
|
-
emitir({
|
|
1334
|
+
emitir({
|
|
1335
|
+
clase: "dispositivos",
|
|
1336
|
+
informe: informeDeDispositivos,
|
|
1337
|
+
ajustes: ajustesDeDispositivos(),
|
|
1338
|
+
...(ultimoArranque === undefined ? {} : { arranque: ultimoArranque }),
|
|
1339
|
+
});
|
|
1064
1340
|
}
|
|
1065
1341
|
catch (error) {
|
|
1066
1342
|
// El detector reparte los fallos por herramienta y no lanza por ninguno; que
|
|
@@ -1075,6 +1351,55 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1075
1351
|
})();
|
|
1076
1352
|
return deteccionEnVuelo;
|
|
1077
1353
|
};
|
|
1354
|
+
/**
|
|
1355
|
+
* **Arrancar un AVD, y contarlo con la foto de después.**
|
|
1356
|
+
*
|
|
1357
|
+
* Tres guardas, en este orden:
|
|
1358
|
+
*
|
|
1359
|
+
* 1. **Que esta ejecución pueda.** Sin el puerto no hay arranque y se DICE: un botón que
|
|
1360
|
+
* llega a un servidor que no sabe arrancar nada es el control muerto de siempre.
|
|
1361
|
+
* 2. **Que ese AVD esté en NUESTRA última medida.** El nombre llega por el cable y acaba
|
|
1362
|
+
* siendo un argumento de proceso; la lista de AVDs es lo único que lo autoriza. Sin
|
|
1363
|
+
* medida todavía no se arranca nada — no hay con qué comprobarlo.
|
|
1364
|
+
* 3. **Uno a la vez.** Dos arranques del mismo AVD a la vez son dos emuladores peleándose
|
|
1365
|
+
* por el mismo puerto, y de otro AVD sería trabajo pesado en paralelo en la máquina de
|
|
1366
|
+
* quien mira. Se reenvía el estado, que es lo que hace la receta.
|
|
1367
|
+
*
|
|
1368
|
+
* Al acabar se vuelve a medir SIEMPRE, salga bien o mal: la foto es la que dice si arrancó.
|
|
1369
|
+
*/
|
|
1370
|
+
let arranqueEnVuelo;
|
|
1371
|
+
const atenderArranqueDeEmulador = async (avd) => {
|
|
1372
|
+
const arrancar = opciones.arrancarEmulador;
|
|
1373
|
+
if (arrancar === undefined) {
|
|
1374
|
+
informar("esta ejecución no puede arrancar emuladores");
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
if (arranqueEnVuelo !== undefined) {
|
|
1378
|
+
informar(`ya estoy arrancando ${arranqueEnVuelo}`);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
if (informeDeDispositivos === undefined || !informeDeDispositivos.avds.includes(avd)) {
|
|
1382
|
+
ultimoArranque = { avd, ok: false, detalle: `no consta ningún AVD llamado «${avd}» en la última medida` };
|
|
1383
|
+
await atenderDispositivos().catch(contar);
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
arranqueEnVuelo = avd;
|
|
1387
|
+
try {
|
|
1388
|
+
const r = await arrancar(avd);
|
|
1389
|
+
ultimoArranque = { avd, ...r };
|
|
1390
|
+
}
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
// El puerto promete no lanzar; si lanzara, el botón no puede quedarse mudo.
|
|
1393
|
+
ultimoArranque = { avd, ok: false, detalle: error instanceof Error ? error.message : String(error) };
|
|
1394
|
+
contar(error);
|
|
1395
|
+
}
|
|
1396
|
+
finally {
|
|
1397
|
+
arranqueEnVuelo = undefined;
|
|
1398
|
+
// La medida nueva, pase lo que pase: es la que dice la verdad.
|
|
1399
|
+
informeDeDispositivos = undefined;
|
|
1400
|
+
await atenderDispositivos().catch(contar);
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1078
1403
|
/**
|
|
1079
1404
|
* Verificar la conexión con UN dispositivo, y volver a emitir la foto con lo que contestó.
|
|
1080
1405
|
*
|
|
@@ -1129,7 +1454,7 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1129
1454
|
}
|
|
1130
1455
|
catch (error) {
|
|
1131
1456
|
// `ErrorCatalogoModelos` es publicable por contrato: nunca lleva la clave ni el
|
|
1132
|
-
// cuerpo remoto (`agent/catalogoModelos.ts`).
|
|
1457
|
+
// cuerpo remoto (`agent/config/catalogoModelos.ts`).
|
|
1133
1458
|
catalogos.set(id, { error: error instanceof Error ? error.message : String(error) });
|
|
1134
1459
|
}
|
|
1135
1460
|
emitirModelos();
|
|
@@ -1470,6 +1795,31 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1470
1795
|
// El cable se muda a la consola del proyecto, como en el alta: sin esto el usuario
|
|
1471
1796
|
// mira un transcript vivo cuyas aprobaciones se rechazan solas al otro lado.
|
|
1472
1797
|
adjuntar();
|
|
1798
|
+
/**
|
|
1799
|
+
* Y el contador se REEMITE, porque el gasto es de UNA conversación.
|
|
1800
|
+
*
|
|
1801
|
+
* `consumo` solo viaja cuando el consumo CAMBIA (`alCambiarConsumo`), y abrir otra
|
|
1802
|
+
* sesión no cambia nada: cambia de quién es la cuenta. Sin esto —medido en la pantalla
|
|
1803
|
+
* del usuario— la caja seguía enseñando los tokens de la conversación anterior hasta la
|
|
1804
|
+
* primera llamada al modelo de la nueva, que puede tardar.
|
|
1805
|
+
*
|
|
1806
|
+
* **No se usa `emitirConsumo()`, y ese es el detalle que costó el primer intento**: esa
|
|
1807
|
+
* función CALLA cuando no consta la cuenta, y al abrir no consta — el `SesionReal` no se
|
|
1808
|
+
* registra hasta que el primer turno lo construye. O sea que justo en el instante que
|
|
1809
|
+
* hay que limpiar, la vía normal no manda nada. Aquí el cero SÍ está medido: una
|
|
1810
|
+
* conversación recién abierta no ha gastado nada, y su tracker nace a cero aunque la
|
|
1811
|
+
* sesión sea vieja. El cliente no pinta un cero, así que el contador desaparece.
|
|
1812
|
+
*
|
|
1813
|
+
* Y si la cuenta SÍ consta —volver al foco de una consola que está corriendo un turno—
|
|
1814
|
+
* se manda la de verdad: mandar cero ahí borraría un contador vivo a mitad de turno.
|
|
1815
|
+
*/
|
|
1816
|
+
const alAbrir = vestibulo.consumoDeSesion();
|
|
1817
|
+
emitir({
|
|
1818
|
+
clase: "consumo",
|
|
1819
|
+
modelo: alAbrir?.modelo ?? SIN_CONSUMO,
|
|
1820
|
+
externo: alAbrir?.externo ?? SIN_CONSUMO,
|
|
1821
|
+
ventana: ventanaDeAhora(alAbrir?.contexto ?? 0),
|
|
1822
|
+
});
|
|
1473
1823
|
// Y la cola se vuelve a mirar: este proyecto queda bloqueado para las tareas —gana la
|
|
1474
1824
|
// persona— y el que estuviera abierto antes acaba de quedar libre.
|
|
1475
1825
|
opciones.revisarTareas?.();
|
|
@@ -1761,7 +2111,7 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1761
2111
|
};
|
|
1762
2112
|
/**
|
|
1763
2113
|
* La sincronización con CloudStudio del proyecto abierto, pedida desde un control
|
|
1764
|
-
* (la banda de arriba de Revisión, y «
|
|
2114
|
+
* (la banda de arriba de Revisión, y «Refrescar»).
|
|
1765
2115
|
*
|
|
1766
2116
|
* **`estado` se mide aquí; `subir` y `bajar` se ENCOLAN**, y la asimetría es deliberada.
|
|
1767
2117
|
* Medir es leer una ref de git que ya está en local (`lecturaDeSync`), así que encolarla
|
|
@@ -1778,7 +2128,7 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
1778
2128
|
* Y lo que NO se hace es volver a emitir la lectura después de encolar: el servidor no
|
|
1779
2129
|
* sabe cuándo termina una línea de la cola, y un «3 ficheros por subir» recién emitido
|
|
1780
2130
|
* tras pulsar Subir sería una cifra que nadie ha vuelto a medir. La pestaña se refresca
|
|
1781
|
-
* al volver a ella y al pulsar «
|
|
2131
|
+
* al volver a ella y al pulsar «Refrescar», que es cuando de verdad se mira.
|
|
1782
2132
|
*/
|
|
1783
2133
|
const atenderSync = (accion) => {
|
|
1784
2134
|
const abierto = vestibulo.proyectoAbierto();
|
|
@@ -2512,10 +2862,48 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
2512
2862
|
// documenta la medida).
|
|
2513
2863
|
// Solo al recién llegado: los demás ya recibieron su saludo al conectar.
|
|
2514
2864
|
sumidero({ clase: "bienvenida", ...(vestibulo.nombre === undefined ? {} : { nombre: vestibulo.nombre }) });
|
|
2865
|
+
/**
|
|
2866
|
+
* **Dos anuncios, y el primero va ANTES de preguntar a CloudStudio.**
|
|
2867
|
+
*
|
|
2868
|
+
* `pasos` se calcula del DISCO (`pasosPendientes`: de dónde vino el modelo y qué entornos
|
|
2869
|
+
* hay registrados), así que en una máquina configurada ya se sabe «no falta nada» sin
|
|
2870
|
+
* tocar la red. Con un solo anuncio detrás de `poblarProyectosSiProcede` —que abre la
|
|
2871
|
+
* sesión MCP— el cliente no podía saberlo hasta que ese viaje terminara, y `App.tsx` lee
|
|
2872
|
+
* un `alta` ausente como «todavía no consta»: enseñaba la pantalla del alta a alguien que
|
|
2873
|
+
* no tiene nada que dar de alta. Medido en la máquina del usuario, con CloudStudio
|
|
2874
|
+
* caliente: el `alta` llegaba a 1436 ms, y con el MCP lento o la red caída esa ventana
|
|
2875
|
+
* crece hasta su tope. Es la misma forma que `MS_DE_TRABAJO_AL_ABRIR`: un dato que se
|
|
2876
|
+
* sabe y no se dice porque va detrás de otro que tarda.
|
|
2877
|
+
*
|
|
2878
|
+
* El segundo anuncio lleva los proyectos, y sigue en un `finally` por lo de siempre: que
|
|
2879
|
+
* `poblarProyectosSiProcede` acabe mal no puede dejar al cliente sin el anuncio. Es el
|
|
2880
|
+
* mismo reparto que ya usaba el cambio de entorno (anunciar, luego preguntar).
|
|
2881
|
+
*/
|
|
2882
|
+
/**
|
|
2883
|
+
* La fase se pone ANTES del primer anuncio y se quita en el `finally`, así que el primer
|
|
2884
|
+
* `alta` sale con ella y el segundo sin ella. Es lo que hace que el Escritorio no entre
|
|
2885
|
+
* hasta estar preparado —lo que el usuario pidió— sin volver a esconder el «no falta
|
|
2886
|
+
* ningún paso», que es lo que el `alta` adelantado vino a arreglar: el cliente sabe
|
|
2887
|
+
* desde el primer mensaje que no hay nada que dar de alta Y que todavía está trayendo
|
|
2888
|
+
* los proyectos, que son dos cosas distintas.
|
|
2889
|
+
*
|
|
2890
|
+
* Con la cuenta pendiente y un entorno ya registrado las dos cosas pasan a la vez, y eso
|
|
2891
|
+
* está bien: la tarjeta pinta el paso de cuenta mientras el MCP trabaja por detrás. Es el
|
|
2892
|
+
* cliente quien decide qué se ve, porque la fase es un dato, no una orden de pintar.
|
|
2893
|
+
*/
|
|
2894
|
+
const aPreparar = entornoAPoblar();
|
|
2895
|
+
preparando = aPreparar === undefined ? undefined : `conectando con ${aPreparar.nombre}…`;
|
|
2515
2896
|
void conducirCuenta()
|
|
2516
2897
|
.catch(contar)
|
|
2517
|
-
.then(() =>
|
|
2518
|
-
.
|
|
2898
|
+
.then(() => anunciarAlta().catch(contar))
|
|
2899
|
+
.then(() => conPlazo(poblarProyectosSiProcede(), MS_DE_PREPARACION))
|
|
2900
|
+
.finally(() => {
|
|
2901
|
+
// Se quita SIEMPRE, también cuando el plazo vence con el MCP todavía colgado: el
|
|
2902
|
+
// lienzo no puede quedarse esperando a quien no contesta. Lo que llegue tarde entra
|
|
2903
|
+
// en el siguiente anuncio, y lo que falló ya viaja en `aviso`.
|
|
2904
|
+
preparando = undefined;
|
|
2905
|
+
void anunciarAlta().catch(contar);
|
|
2906
|
+
});
|
|
2519
2907
|
peticion.on("close", () => {
|
|
2520
2908
|
// Se va ESTE cliente, no «el cliente». La guarda de antes (`enviar !== sumidero`)
|
|
2521
2909
|
// existía porque el `close` de una pestaña recargada puede llegar DESPUÉS de que el
|
|
@@ -2675,6 +3063,61 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
2675
3063
|
* - **Ninguna respuesta lleva una ruta de la máquina** ni nada de lo recibido, que es la
|
|
2676
3064
|
* regla de `POST /accion`.
|
|
2677
3065
|
*/
|
|
3066
|
+
/**
|
|
3067
|
+
* `POST /skill?nombre=<fichero.zip>&ambito=<global|proyecto>` — instalar una skill.
|
|
3068
|
+
*
|
|
3069
|
+
* Por HTTP y no por el cable, con el mismo molde que `POST /adjunto`: esto son bytes y el
|
|
3070
|
+
* cable lleva JSON. `Host`, `Origin` y token los comprueba `servidor.ts` antes de llegar
|
|
3071
|
+
* aquí, igual que a todas las rutas. Lo propio de ésta:
|
|
3072
|
+
*
|
|
3073
|
+
* - **El `nombre` de la query es el del FICHERO que subió la persona, y solo se usa como
|
|
3074
|
+
* nombre de respaldo** cuando el zip no trae una carpeta de la que sacarlo
|
|
3075
|
+
* (`core/zipDeSkill.ts`). No se compone ninguna ruta con él sin pasar por la regla del
|
|
3076
|
+
* slug, que es la misma de un subagente.
|
|
3077
|
+
* - **El cuerpo se lee con el tope del ZIP y se corta ahí** (413), sin acumular. Y el otro
|
|
3078
|
+
* tope, el de lo DESCOMPRIMIDO, lo pone `planDeInstalacion`: éste no lo ve, que es
|
|
3079
|
+
* justamente lo que un zip bomba aprovecha.
|
|
3080
|
+
* - **Ninguna respuesta lleva una ruta de la máquina ni nada de lo recibido**, que es la
|
|
3081
|
+
* regla de `POST /accion` — y aquí lo recibido es un fichero de alguien.
|
|
3082
|
+
*/
|
|
3083
|
+
servidor.registrarRuta("POST", RUTA_SKILL, async (peticion, respuesta) => {
|
|
3084
|
+
const responder = (codigo, texto) => {
|
|
3085
|
+
respuesta.writeHead(codigo, { "Content-Type": "text/plain; charset=utf-8" });
|
|
3086
|
+
respuesta.end(texto);
|
|
3087
|
+
};
|
|
3088
|
+
const query = new URLSearchParams((peticion.url ?? "").split("?")[1] ?? "");
|
|
3089
|
+
const nombre = query.get("nombre");
|
|
3090
|
+
const ambito = query.get("ambito") === "proyecto" ? "proyecto" : "global";
|
|
3091
|
+
if (nombre === null || nombre === "") {
|
|
3092
|
+
responder(400, "falta «nombre»");
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
const abierto = vestibulo.proyectoAbierto();
|
|
3096
|
+
if (ambito === "proyecto" && abierto === undefined) {
|
|
3097
|
+
responder(409, "no hay ningún proyecto abierto: esa skill solo se puede instalar como global");
|
|
3098
|
+
return;
|
|
3099
|
+
}
|
|
3100
|
+
let datos;
|
|
3101
|
+
try {
|
|
3102
|
+
datos = await leerCuerpoCrudo(peticion, TOPE_DEL_ZIP);
|
|
3103
|
+
}
|
|
3104
|
+
catch {
|
|
3105
|
+
responder(413, "el .zip es demasiado grande");
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
const instalada = instalarSkillDesdeZip(ambito === "proyecto" ? abierto.raiz : homedir(), new Uint8Array(datos), nombre);
|
|
3109
|
+
if ("error" in instalada) {
|
|
3110
|
+
// El motivo lo escribe el módulo y no lleva ninguna ruta de la máquina ni el nombre de
|
|
3111
|
+
// una entrada del zip (sus tests lo vigilan). 422: el zip llegó entero y no vale.
|
|
3112
|
+
informar(`no se instala la skill: ${instalada.error}`);
|
|
3113
|
+
responder(422, instalada.error);
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
informar(`skill «${instalada.nombre}» instalada en ${ambito}`);
|
|
3117
|
+
emitirSkills();
|
|
3118
|
+
respuesta.writeHead(204);
|
|
3119
|
+
respuesta.end();
|
|
3120
|
+
});
|
|
2678
3121
|
servidor.registrarRuta("POST", RUTA_ADJUNTO, async (peticion, respuesta) => {
|
|
2679
3122
|
const responder = (codigo, texto) => {
|
|
2680
3123
|
respuesta.writeHead(codigo, { "Content-Type": "text/plain; charset=utf-8" });
|
|
@@ -2808,6 +3251,17 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
2808
3251
|
respuesta.end();
|
|
2809
3252
|
return;
|
|
2810
3253
|
}
|
|
3254
|
+
if (typeof mensaje === "object" &&
|
|
3255
|
+
mensaje !== null &&
|
|
3256
|
+
mensaje.clase === "arrancarEmulador" &&
|
|
3257
|
+
typeof mensaje.avd === "string") {
|
|
3258
|
+
// Se contesta ya: arrancar tarda lo que tarda un emulador y la respuesta del POST no es
|
|
3259
|
+
// el sitio donde se cuenta — eso viaja por el SSE con la foto nueva.
|
|
3260
|
+
void atenderArranqueDeEmulador(mensaje.avd).catch(contar);
|
|
3261
|
+
respuesta.writeHead(204);
|
|
3262
|
+
respuesta.end();
|
|
3263
|
+
return;
|
|
3264
|
+
}
|
|
2811
3265
|
/**
|
|
2812
3266
|
* ANTES del `recibir` de la consola, y también antes que las demás clases de tarea: por
|
|
2813
3267
|
* aquí no entra nada hacia ningún turno. Ver `atenderMirar`.
|
|
@@ -2918,6 +3372,18 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
2918
3372
|
respuesta.end();
|
|
2919
3373
|
return;
|
|
2920
3374
|
}
|
|
3375
|
+
if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "skill") {
|
|
3376
|
+
atenderSkill(mensaje);
|
|
3377
|
+
respuesta.writeHead(204);
|
|
3378
|
+
respuesta.end();
|
|
3379
|
+
return;
|
|
3380
|
+
}
|
|
3381
|
+
if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "cuerpoDeSkill") {
|
|
3382
|
+
atenderCuerpoDeSkill(mensaje);
|
|
3383
|
+
respuesta.writeHead(204);
|
|
3384
|
+
respuesta.end();
|
|
3385
|
+
return;
|
|
3386
|
+
}
|
|
2921
3387
|
if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "proveedor") {
|
|
2922
3388
|
atenderProveedor(mensaje);
|
|
2923
3389
|
respuesta.writeHead(204);
|
|
@@ -3034,6 +3500,19 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
3034
3500
|
respuesta.end();
|
|
3035
3501
|
return;
|
|
3036
3502
|
}
|
|
3503
|
+
/**
|
|
3504
|
+
* **Y el acuse del último arranque se OLVIDA.**
|
|
3505
|
+
*
|
|
3506
|
+
* Sin esto se queda pegado para siempre: el usuario vio `pixel8 · apagado` con un
|
|
3507
|
+
* «✓ pixel8 arrancado» al lado, o sea las dos cosas a la vez. El acuse pertenece a la
|
|
3508
|
+
* foto que se tomó justo después de arrancar, no a todas las de después — y ahora que
|
|
3509
|
+
* la sección remide al entrar y al volver, «todas las de después» son muchas.
|
|
3510
|
+
*
|
|
3511
|
+
* Ausente vuelve a significar «no se ha pedido ningún arranque en esta medida», que es
|
|
3512
|
+
* lo que el campo declara. El `finally` de `atenderArranqueDeEmulador` lo pone y se
|
|
3513
|
+
* salta este camino, así que su propia medida sí lo lleva.
|
|
3514
|
+
*/
|
|
3515
|
+
ultimoArranque = undefined;
|
|
3037
3516
|
informeDeDispositivos = undefined;
|
|
3038
3517
|
void atenderDispositivos().catch(contar);
|
|
3039
3518
|
respuesta.writeHead(204);
|
|
@@ -3080,7 +3559,7 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
|
|
|
3080
3559
|
* Construye el corredor de tareas y la costura que lo conecta al cable.
|
|
3081
3560
|
*
|
|
3082
3561
|
* Extraído de `arrancarConsolaWeb` a su propia función por la MISMA razón que
|
|
3083
|
-
* `backendDeAgente` (`agent/proyecto.ts`): la composición vivía inline dentro de una
|
|
3562
|
+
* `backendDeAgente` (`agent/grafo/proyecto.ts`): la composición vivía inline dentro de una
|
|
3084
3563
|
* función que todos sus tests DOBLAN —`vestibulo`, `crearServidor`, `crearEjecutor`—, así
|
|
3085
3564
|
* que esta costura concreta podía dejar de estar montada con el resto en verde. Aquí
|
|
3086
3565
|
* tiene su propio test que la compone de VERDAD: construye un corredor real
|
|
@@ -3673,7 +4152,7 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
3673
4152
|
borrarCredencial,
|
|
3674
4153
|
guardarCredencial,
|
|
3675
4154
|
/**
|
|
3676
|
-
* El defecto se GUARDA en el `config.json` global (`agent/configEnDisco.ts#guardarModeloGlobal`),
|
|
4155
|
+
* El defecto se GUARDA en el `config.json` global (`agent/config/configEnDisco.ts#guardarModeloGlobal`),
|
|
3677
4156
|
* que es el último escalón de la precedencia y el único que sobrevive al proceso. Se
|
|
3678
4157
|
* escribe para los TRES papeles porque elegir un modelo en la interfaz es una frase
|
|
3679
4158
|
* sobre el producto y no sobre un papel — el mismo criterio que `/modelo`, que fija los
|
|
@@ -3699,7 +4178,7 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
3699
4178
|
cambiosDeSesion,
|
|
3700
4179
|
parcheDeSesion,
|
|
3701
4180
|
// El proyecto tal como lo ve el agente, para la pestaña Ficheros: mismo filtro, misma
|
|
3702
|
-
// barrera de rutas (`agent/arbolDeProyecto.ts`).
|
|
4181
|
+
// barrera de rutas (`agent/grafo/arbolDeProyecto.ts`).
|
|
3703
4182
|
arbolDelProyecto: async (raiz) => arbolDeProyecto(raiz),
|
|
3704
4183
|
leerFichero: leerFicheroDeProyecto,
|
|
3705
4184
|
leerArtefacto: leerArtefactoDeSesion,
|
|
@@ -3714,6 +4193,11 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
3714
4193
|
guardarAjustesDeDispositivos: (ajustes) => void guardarDispositivos(undefined, ajustes),
|
|
3715
4194
|
instalarHerramienta: instalarHerramientaDeDispositivos,
|
|
3716
4195
|
verificarDispositivo,
|
|
4196
|
+
// Con la función REAL, y no declarada y sin pasar: es la trampa que este repo ha pagado
|
|
4197
|
+
// nueve veces, y la que el comentario de justo abajo describe para las cuatro del
|
|
4198
|
+
// lanzamiento. `montarRutas` lo trata como ausente si falta, así que olvidarlo aquí
|
|
4199
|
+
// dejaría el botón diciendo «esta ejecución no puede arrancar emuladores» con todo verde.
|
|
4200
|
+
arrancarEmulador: (avd) => arrancarEmulador(avd),
|
|
3717
4201
|
/**
|
|
3718
4202
|
* Las cuatro de «¿se puede lanzar y lánzalo?», compuestas con las funciones REALES.
|
|
3719
4203
|
*
|
|
@@ -3750,7 +4234,7 @@ export async function arrancarConsolaWeb(opciones) {
|
|
|
3750
4234
|
guardarProveedor: (declarado) => guardarProveedorPersonalizado(declarado),
|
|
3751
4235
|
borrarProveedor: (slug) => borrarProveedorPersonalizado(slug),
|
|
3752
4236
|
/**
|
|
3753
|
-
* El AUMENTADOR de la ventana de crear una tarea (`agent/aumentador.ts`), con el papel
|
|
4237
|
+
* El AUMENTADOR de la ventana de crear una tarea (`agent/tareas/aumentador.ts`), con el papel
|
|
3754
4238
|
* `trabajo` — es una tarea de redacción, no una clasificación.
|
|
3755
4239
|
*
|
|
3756
4240
|
* Tres cosas que se deciden aquí:
|
|
@@ -3885,7 +4369,7 @@ function vestibuloReal(opciones, servidor, escribir, informar) {
|
|
|
3885
4369
|
// Se resuelve UNA vez, aquí, y no en cada `anunciarAlta`: `git config`/`os.userInfo`
|
|
3886
4370
|
// no cambian a media conexión, y repetir el subproceso en cada anuncio del alta sería
|
|
3887
4371
|
// gastar sin motivo. Nunca viaja hacia CloudStudio ni hacia ningún acto —
|
|
3888
|
-
// `agent/persona.ts` lo documenta—.
|
|
4372
|
+
// `agent/config/persona.ts` lo documenta—.
|
|
3889
4373
|
nombre: nombreDePersona(opciones.cwd),
|
|
3890
4374
|
catalogoModelos: new CatalogoModelos(),
|
|
3891
4375
|
guardarCredencial,
|
|
@@ -3898,7 +4382,7 @@ function vestibuloReal(opciones, servidor, escribir, informar) {
|
|
|
3898
4382
|
guardarModeloGlobal,
|
|
3899
4383
|
guardarConfigDeProyecto: escribirProyectoEnDisco,
|
|
3900
4384
|
// El «antes» de cada sesión: se fotografía al abrir el proyecto y se nombra cuando la
|
|
3901
|
-
// sesión tiene id. Ver `agent/sesionGit.ts` para por qué es una ref y no un tag.
|
|
4385
|
+
// sesión tiene id. Ver `agent/sesiones/sesionGit.ts` para por qué es una ref y no un tag.
|
|
3902
4386
|
marcarSesion: fotoDeApertura,
|
|
3903
4387
|
// Lo que ya había sin commitear al abrir, para poder decirlo. Comparte el hueco
|
|
3904
4388
|
// declarado de `marcarSesion`: esta composición vive en un cierre que `vestibuloReal`
|