xonecode 0.5.1 → 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/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} +1 -1
- package/dist/agent/sesiones/checkpointer.js.map +1 -0
- package/dist/agent/sesiones/git.js.map +1 -0
- package/dist/agent/{gitSync.js → sesiones/gitSync.js} +3 -3
- package/dist/agent/sesiones/gitSync.js.map +1 -0
- package/dist/agent/{sesionGit.js → sesiones/sesionGit.js} +2 -2
- package/dist/agent/sesiones/sesionGit.js.map +1 -0
- package/dist/agent/{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/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 +6 -4
- 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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El índice de navegación XOne: qué hay en el proyecto y qué apunta a qué.
|
|
3
|
+
*
|
|
4
|
+
* **Este módulo es PURO**, y esa es la mitad del diseño. Quién lee el disco y quién entiende
|
|
5
|
+
* el XML es `agent/navegacion/` apoyándose en `xone-linter`; aquí solo vive la forma de las
|
|
6
|
+
* respuestas y cómo se consultan. Así la regla se prueba sin un proyecto delante, y el día
|
|
7
|
+
* que la fuente del modelo cambie no cambia nada de esto.
|
|
8
|
+
*
|
|
9
|
+
* **Por qué existe.** Medido sobre un proyecto real (AppDemo, 41 `.xne`): contestar «¿qué
|
|
10
|
+
* colecciones tiene el proyecto?» leyendo ficheros cuesta unos 18.000 tokens —son todos los
|
|
11
|
+
* `.xne`—, y contestarla desde el modelo cuesta 141. La pregunta cara no es leer un fichero
|
|
12
|
+
* (un `.xne` son un par de kilobytes): es **no saber cuál de los cuarenta y uno leer**. Eso
|
|
13
|
+
* es lo que esto contesta, y por eso la primera versión va POR NOMBRE y no por posición.
|
|
14
|
+
*
|
|
15
|
+
* **Por qué no por posición todavía.** Un editor pregunta «¿qué hay bajo el cursor?»; un
|
|
16
|
+
* agente no tiene cursor, tiene nombres. Y saber la posición exacta de un atributo exige
|
|
17
|
+
* conservar su rango al parsear, que hoy `xone-linter` no hace (su `loc()` devuelve solo el
|
|
18
|
+
* fichero). Lo caro está justo en la mitad que no hace falta para lo que se ha medido.
|
|
19
|
+
*
|
|
20
|
+
* **Las rutas que entran aquí son VIRTUALES** (`/Clientes.xne`), nunca de la máquina. Quien
|
|
21
|
+
* traduce es el adaptador, y no es higiene: esto viaja al modelo y de ahí al cable.
|
|
22
|
+
*/
|
|
23
|
+
/** Parte `Coll.CAMPO` en sus dos mitades. Sin punto, es una colección a secas. */
|
|
24
|
+
export function partirNombre(nombre) {
|
|
25
|
+
const i = nombre.indexOf(".");
|
|
26
|
+
if (i === -1)
|
|
27
|
+
return { coleccion: nombre.trim() };
|
|
28
|
+
return { coleccion: nombre.slice(0, i).trim(), campo: nombre.slice(i + 1).trim() };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Construye el índice. Puro: el modelo entra ya resuelto y traducido.
|
|
32
|
+
*
|
|
33
|
+
* **No cachea, y quien lo llama tampoco debería.** Construirlo sobre un proyecto real cuesta
|
|
34
|
+
* unas decenas de milisegundos —medido— contra los segundos de una llamada al modelo, así
|
|
35
|
+
* que rehacerlo en cada consulta sale gratis y quita de en medio la pregunta de cuándo
|
|
36
|
+
* invalidarlo. Un índice viejo que dice que un campo existe cuando el agente acaba de
|
|
37
|
+
* borrarlo es peor que no tener índice: es una respuesta con autoridad y equivocada.
|
|
38
|
+
*
|
|
39
|
+
* **La comparación de nombres NO distingue mayúsculas.** En XOne se escribe `NOMBRE` en la
|
|
40
|
+
* declaración y `Nombre` en un `mapfld` con la misma intención, y un índice que conteste «no
|
|
41
|
+
* existe» por la caja manda a buscar un fallo que no está. Lo que se DEVUELVE es siempre el
|
|
42
|
+
* nombre tal como está declarado, no el que se preguntó.
|
|
43
|
+
*/
|
|
44
|
+
export function construirIndice(modelo) {
|
|
45
|
+
const igual = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
46
|
+
const declaracionDeColeccion = (c) => ({
|
|
47
|
+
nombre: c.nombre,
|
|
48
|
+
clase: "coleccion",
|
|
49
|
+
fichero: c.fichero,
|
|
50
|
+
});
|
|
51
|
+
const declaracionDeCampo = (c, campo) => ({
|
|
52
|
+
nombre: campo.nombre,
|
|
53
|
+
clase: "campo",
|
|
54
|
+
fichero: c.fichero,
|
|
55
|
+
coleccion: c.nombre,
|
|
56
|
+
...(campo.tipo === undefined ? {} : { tipo: campo.tipo }),
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
inventario: () => modelo.colecciones.map(declaracionDeColeccion),
|
|
60
|
+
definicion(nombre) {
|
|
61
|
+
const { coleccion, campo } = partirNombre(nombre);
|
|
62
|
+
const suyas = modelo.colecciones.filter((c) => igual(c.nombre, coleccion));
|
|
63
|
+
// Sin campo, se pregunta por la colección: se devuelven TODAS las que se llamen así.
|
|
64
|
+
// Dos declaraciones con el mismo nombre no es un caso imposible y callarse una sería
|
|
65
|
+
// contestar a medias sobre justo el proyecto que tiene el problema.
|
|
66
|
+
if (campo === undefined || campo === "")
|
|
67
|
+
return suyas.map(declaracionDeColeccion);
|
|
68
|
+
return suyas.flatMap((c) => c.campos.filter((f) => igual(f.nombre, campo)).map((f) => declaracionDeCampo(c, f)));
|
|
69
|
+
},
|
|
70
|
+
referencias(nombre) {
|
|
71
|
+
const { coleccion, campo } = partirNombre(nombre);
|
|
72
|
+
const encaja = (hacia) => {
|
|
73
|
+
const destino = partirNombre(hacia);
|
|
74
|
+
if (!igual(destino.coleccion, coleccion))
|
|
75
|
+
return false;
|
|
76
|
+
// Preguntando por la coll entera valen todas las que apunten a ella, lleven campo o
|
|
77
|
+
// no: quien pregunta «¿quién usa Clientes?» quiere también los `mapfld`.
|
|
78
|
+
if (campo === undefined || campo === "")
|
|
79
|
+
return true;
|
|
80
|
+
return destino.campo !== undefined && igual(destino.campo, campo);
|
|
81
|
+
};
|
|
82
|
+
const salida = [];
|
|
83
|
+
for (const c of modelo.colecciones) {
|
|
84
|
+
for (const r of c.referencias)
|
|
85
|
+
if (encaja(r.hacia))
|
|
86
|
+
salida.push({ ...r, fichero: c.fichero });
|
|
87
|
+
}
|
|
88
|
+
// Y las de script, que ya traen su fichero porque pueden venir de un `.js` suelto.
|
|
89
|
+
for (const r of modelo.referenciasDeScript)
|
|
90
|
+
if (encaja(r.hacia))
|
|
91
|
+
salida.push(r);
|
|
92
|
+
return salida;
|
|
93
|
+
},
|
|
94
|
+
campos(coleccion) {
|
|
95
|
+
const { coleccion: soloColl } = partirNombre(coleccion);
|
|
96
|
+
return modelo.colecciones
|
|
97
|
+
.filter((c) => igual(c.nombre, soloColl))
|
|
98
|
+
.flatMap((c) => c.campos.map((f) => declaracionDeCampo(c, f)));
|
|
99
|
+
},
|
|
100
|
+
app: () => modelo.app,
|
|
101
|
+
detalle(coleccion) {
|
|
102
|
+
const { coleccion: soloColl } = partirNombre(coleccion);
|
|
103
|
+
const c = modelo.colecciones.find((x) => igual(x.nombre, soloColl));
|
|
104
|
+
// `undefined` y no un detalle vacío: «no existe» y «existe y está vacía» son dos cosas,
|
|
105
|
+
// y contestar lo segundo sobre lo primero hace que el agente deje de buscar.
|
|
106
|
+
if (c === undefined)
|
|
107
|
+
return undefined;
|
|
108
|
+
return {
|
|
109
|
+
nombre: c.nombre,
|
|
110
|
+
fichero: c.fichero,
|
|
111
|
+
campos: c.campos.map((f) => declaracionDeCampo(c, f)),
|
|
112
|
+
eventos: c.eventos,
|
|
113
|
+
nodos: c.nodos,
|
|
114
|
+
conexiones: c.conexiones,
|
|
115
|
+
apuntaA: c.referencias.map((r) => ({ ...r, fichero: c.fichero })),
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
problemas() {
|
|
119
|
+
const existe = (nombre) => modelo.colecciones.some((c) => igual(c.nombre, nombre));
|
|
120
|
+
const rotas = [];
|
|
121
|
+
const todas = [
|
|
122
|
+
...modelo.colecciones.flatMap((c) => c.referencias.map((r) => ({ ...r, fichero: c.fichero }))),
|
|
123
|
+
// Las de script cuentan: dos de las tres referencias rotas de un proyecto real solo se
|
|
124
|
+
// ven desde ahí (`appData.getCollection('Login')`, que no existe).
|
|
125
|
+
...modelo.referenciasDeScript,
|
|
126
|
+
];
|
|
127
|
+
for (const r of todas)
|
|
128
|
+
if (!existe(partirNombre(r.hacia).coleccion))
|
|
129
|
+
rotas.push(r);
|
|
130
|
+
return { rotas };
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=navegacion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navegacion.js","sourceRoot":"","sources":["../../src/core/navegacion.ts"],"names":[],"mappings":"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"}
|
package/dist/core/notify.js
CHANGED
|
@@ -25,6 +25,10 @@ const ICONO = {
|
|
|
25
25
|
grep: "✱",
|
|
26
26
|
regex_search: "✱",
|
|
27
27
|
task: "⊙",
|
|
28
|
+
// Un icono PROPIO, y no el genérico `⚙`: en un turno donde se lanza una app, la línea del
|
|
29
|
+
// comando es la que dice qué está pasando de verdad — y no puede leerse igual que una tool
|
|
30
|
+
// cualquiera que no está en la tabla.
|
|
31
|
+
execute: "$",
|
|
28
32
|
};
|
|
29
33
|
/** El verbo de cada tool conocida, en español. */
|
|
30
34
|
const VERBO = {
|
|
@@ -36,6 +40,7 @@ const VERBO = {
|
|
|
36
40
|
grep: "busca",
|
|
37
41
|
regex_search: "regex",
|
|
38
42
|
task: "delega en",
|
|
43
|
+
execute: "corre",
|
|
39
44
|
};
|
|
40
45
|
/** `→ lee app.xne` para una conocida; `⚙ studio_edit_file` para el resto. */
|
|
41
46
|
function frase(nombre, detalle) {
|
package/dist/core/notify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/core/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAcH,wEAAwE;AACxE,MAAM,KAAK,GAA2B;IACpC,SAAS,EAAE,GAAG;IACd,EAAE,EAAE,GAAG;IACP,UAAU,EAAE,GAAG;IACf,SAAS,EAAE,GAAG;IACd,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,YAAY,EAAE,GAAG;IACjB,IAAI,EAAE,GAAG;
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/core/notify.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,7 +29,7 @@ export const extensionDe = (ruta) => {
|
|
|
29
29
|
/**
|
|
30
30
|
* Lo que no sale de esta máquina NUNCA, mire quien lo mire.
|
|
31
31
|
*
|
|
32
|
-
* Es la misma denegación que `agent/perfiles.ts` le impone al AGENTE
|
|
32
|
+
* Es la misma denegación que `agent/grafo/perfiles.ts` le impone al AGENTE
|
|
33
33
|
* (`permisosDe`/`puedeLeerRuta` deniegan `/.env` y `/.git`, invariante explícito de
|
|
34
34
|
* `CLAUDE.md`), replicada en la ruta de SUBIDA, que no pasa por aquellos permisos y por
|
|
35
35
|
* tanto no la heredaba. Que el agente no pueda leer `.env` no sirve de nada si el `/sync`
|
package/dist/core/settings.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* de un cliente. Vive en global y no en el proyecto porque un entorno sirve a muchos
|
|
7
7
|
* proyectos; el proyecto solo guarda a cuál pertenece.
|
|
8
8
|
*
|
|
9
|
-
* TypeScript puro: ni disco ni red. El disco lo pone `agent/settingsEnDisco.ts`, igual
|
|
10
|
-
* que `core/config.ts` deja el I/O a `agent/configEnDisco.ts` y `agent/authEnDisco.ts`.
|
|
9
|
+
* TypeScript puro: ni disco ni red. El disco lo pone `agent/config/settingsEnDisco.ts`, igual
|
|
10
|
+
* que `core/config.ts` deja el I/O a `agent/config/configEnDisco.ts` y `agent/config/authEnDisco.ts`.
|
|
11
11
|
*/
|
|
12
12
|
import { posix } from "node:path";
|
|
13
13
|
import { CLAVES_DENEGADAS } from "./config.js";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El ENTORNO con el que corre la shell de un subagente con ejecución.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript puro: aquí se decide QUÉ variables ve esa shell, y quien la monta es
|
|
5
|
+
* `agent/grafo/proyecto.ts`. Separado por lo de siempre —compuesto dentro del montaje, que
|
|
6
|
+
* todos los tests doblan, la regla quedaría escrita y no probada—, y porque esto es una
|
|
7
|
+
* decisión sobre secretos, que es justo lo que no puede depender de acordarse.
|
|
8
|
+
*
|
|
9
|
+
* **Lo que se quita, y por qué es obligatorio quitarlo.** `guardarCredencial` escribe la
|
|
10
|
+
* clave de API TAMBIÉN en `process.env`, a propósito (`agent/config/authEnDisco.ts`). Un
|
|
11
|
+
* `inheritEnv: true` a secas le pasaría esas claves a la shell del modelo, y entonces un
|
|
12
|
+
* `printenv` —que es lo primero que hace cualquiera para orientarse— las deja en el
|
|
13
|
+
* resultado de la tool, o sea en el contexto y en el `.jsonl` de la sesión. No es una fuga
|
|
14
|
+
* hipotética: es la salida normal de un comando normal.
|
|
15
|
+
*
|
|
16
|
+
* Se quitan **por la tabla** (`VARIABLES_POR_PROVEEDOR`, el único sitio donde vive el nombre
|
|
17
|
+
* de la variable de cada proveedor — hubo cuatro copias y ya habían divergido) más el
|
|
18
|
+
* prefijo `XONECODE_CLAVE_`, que es el de los proveedores PERSONALIZADOS y por definición no
|
|
19
|
+
* puede estar en ninguna tabla: el slug lo elige el usuario. Y no se quita todo lo que
|
|
20
|
+
* empiece por `XONECODE_`, que se llevaría `XONECODE_TRACE_TOOLS` y `XONECODE_MODELO` sin
|
|
21
|
+
* que sean secretos de nadie.
|
|
22
|
+
*
|
|
23
|
+
* **Límite declarado, y conviene no engañarse con él**: esto quita NUESTRAS credenciales,
|
|
24
|
+
* las que xonecode puso ahí. El entorno de quien arranca el proceso puede traer un
|
|
25
|
+
* `GITHUB_TOKEN` o un `AWS_SECRET_ACCESS_KEY` suyos, y eso no se filtra — ni se podría con
|
|
26
|
+
* honestidad, porque «lo que parece una clave» es una heurística y una heurística que falla
|
|
27
|
+
* en silencio es peor que una regla declarada. Quien concede ejecución concede leer el
|
|
28
|
+
* entorno y el disco; lo que esto evita es que el harness REGALE lo que él mismo escribió.
|
|
29
|
+
*
|
|
30
|
+
* **Y los scripts de una skill se ponen en el PATH, que es lo que quita el problema en vez de
|
|
31
|
+
* explicarlo.** Medido corriéndolo: el agente tenía DOS espacios de rutas —las virtuales de
|
|
32
|
+
* `read_file` y las reales de sus comandos— y se pasó decenas de llamadas mezclándolos, leyendo
|
|
33
|
+
* con `read_file` rutas absolutas que no existen para esa tool y no van a existir nunca. Con los
|
|
34
|
+
* `scripts/` de cada skill montada en el PATH, un script se llama por su NOMBRE
|
|
35
|
+
* (`xone-hotswap …`) y no hay ninguna ruta que acertar, ni que meter en el prompt, ni que viaje
|
|
36
|
+
* en el evento. Un aparato nuevo mañana trae su skill con sus scripts y quedan disponibles solos.
|
|
37
|
+
*
|
|
38
|
+
* **Se AÑADEN al final, no al principio.** Prepender dejaría que una skill —que puede venir en
|
|
39
|
+
* un `.zip` de cualquier sitio— sombreara un binario del sistema llamando a un script suyo `ls`
|
|
40
|
+
* o `git`. Al final, lo que ya existe gana.
|
|
41
|
+
*
|
|
42
|
+
* **Y lo que se AÑADE además son rutas, una variable por cosa.** Una shell ve el disco de verdad,
|
|
43
|
+
* no nuestras rutas virtuales: `/skills/xone-hotswap/` no existe para ella. Pero meter la
|
|
44
|
+
* ruta absoluta en el prompt tendría dos precios —el comando que el modelo componga sería el
|
|
45
|
+
* `detalle` del evento, y ahí no puede viajar una ruta de la máquina (`sinRutas`), y la
|
|
46
|
+
* raíz no es UNA sino tres con la del proyecto ganando—. Con una variable por skill, el
|
|
47
|
+
* prompt dice `$XONECODE_SKILL_XONE_HOTSWAP/scripts/android.mjs` y ni el contexto ni el
|
|
48
|
+
* cable ven una ruta.
|
|
49
|
+
*/
|
|
50
|
+
import { VARIABLES_POR_PROVEEDOR } from "./modelos.js";
|
|
51
|
+
/** El prefijo de las claves de un proveedor personalizado (`core/modelos.ts#variableDeProveedor`). */
|
|
52
|
+
const PREFIJO_DE_CLAVE = "XONECODE_CLAVE_";
|
|
53
|
+
/**
|
|
54
|
+
* La variable que nombra la carpeta donde dejar lo que se quiera enseñar a una persona.
|
|
55
|
+
*
|
|
56
|
+
* Sin ella, un script escribe en el `cwd` —la raíz del proyecto— que es justo lo que
|
|
57
|
+
* `artefactoFueraDeSitio` prohíbe a las tools de fichero, y una shell no pasa por ahí.
|
|
58
|
+
*/
|
|
59
|
+
export const VARIABLE_DE_ARTEFACTOS = "XONECODE_ARTEFACTOS";
|
|
60
|
+
/**
|
|
61
|
+
* Cómo se llama la variable que lleva la ruta real de una skill.
|
|
62
|
+
*
|
|
63
|
+
* La MISMA derivación que `variableDeProveedor` para un personalizado (mayúsculas y los
|
|
64
|
+
* guiones a guión bajo): dos formas de convertir un slug en variable son dos formas de
|
|
65
|
+
* discrepar. El prefijo es otro a propósito — `XONECODE_SKILL_` frente a
|
|
66
|
+
* `XONECODE_CLAVE_`—, así que una skill no puede fabricarse el nombre de una credencial.
|
|
67
|
+
*/
|
|
68
|
+
export function variableDeSkill(nombre) {
|
|
69
|
+
return `XONECODE_SKILL_${nombre.toUpperCase().replace(/-/g, "_")}`;
|
|
70
|
+
}
|
|
71
|
+
export function entornoDeShell(opciones) {
|
|
72
|
+
const credenciales = new Set(Object.values(VARIABLES_POR_PROVEEDOR));
|
|
73
|
+
const limpio = {};
|
|
74
|
+
for (const [clave, valor] of Object.entries(opciones.entorno)) {
|
|
75
|
+
// Una variable sin valor se DESCARTA en vez de pasarse como cadena vacía: un `PATH=""`
|
|
76
|
+
// no es «no consta», es un PATH roto.
|
|
77
|
+
if (valor === undefined)
|
|
78
|
+
continue;
|
|
79
|
+
if (credenciales.has(clave))
|
|
80
|
+
continue;
|
|
81
|
+
if (clave.startsWith(PREFIJO_DE_CLAVE))
|
|
82
|
+
continue;
|
|
83
|
+
limpio[clave] = valor;
|
|
84
|
+
}
|
|
85
|
+
for (const skill of opciones.skills ?? []) {
|
|
86
|
+
limpio[variableDeSkill(skill.nombre)] = skill.dir;
|
|
87
|
+
}
|
|
88
|
+
const binarios = opciones.binarios ?? [];
|
|
89
|
+
if (binarios.length > 0) {
|
|
90
|
+
// Sin PATH heredado no se parte de vacío: `sh` se inventaría el suyo y perderíamos el del
|
|
91
|
+
// proceso, que es donde están el `adb` y el `node` del usuario.
|
|
92
|
+
const actual = limpio["PATH"];
|
|
93
|
+
limpio["PATH"] = actual === undefined || actual === "" ? binarios.join(":") : [actual, ...binarios].join(":");
|
|
94
|
+
}
|
|
95
|
+
if (opciones.artefactos !== undefined)
|
|
96
|
+
limpio[VARIABLE_DE_ARTEFACTOS] = opciones.artefactos;
|
|
97
|
+
return limpio;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=shellDeAgente.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shellDeAgente.js","sourceRoot":"","sources":["../../src/core/shellDeAgente.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,sGAAsG;AACtG,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,kBAAkB,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;AACrE,CAAC;AAQD,MAAM,UAAU,cAAc,CAAC,QAW9B;IACC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAC7E,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9D,uFAAuF;QACvF,sCAAsC;QACtC,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QACtC,IAAI,KAAK,CAAC,UAAU,CAAC,gBAAgB,CAAC;YAAE,SAAS;QACjD,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IACxB,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1C,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC;IACpD,CAAC;IACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC;IACzC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,0FAA0F;QAC1F,gEAAgE;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChH,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS;QAAE,MAAM,CAAC,sBAAsB,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC;IAE5F,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Una skill: el `SKILL.md` que el agente carga bajo demanda, y de quién es.
|
|
3
|
+
*
|
|
4
|
+
* **Este módulo es PURO.** Quién lee el disco es `agent/grafo/skills.ts`; quién las monta
|
|
5
|
+
* para el agente, `agent/grafo/proyecto.ts#backendConSkills`. Aquí solo vive el formato del
|
|
6
|
+
* fichero y la regla de qué nombre vale — las dos cosas que la ventana de Ajustes, el
|
|
7
|
+
* cargador y los tests tienen que entender igual.
|
|
8
|
+
*
|
|
9
|
+
* **Tres orígenes y no dos**, con la misma precedencia que los subagentes y los modelos:
|
|
10
|
+
*
|
|
11
|
+
* | origen | dónde | quién la escribe |
|
|
12
|
+
* |---|---|---|
|
|
13
|
+
* | `serie` | la carpeta `skills/` del PAQUETE | xonecode, y no se toca desde la consola |
|
|
14
|
+
* | `global` | `~/.xonecode/skills/` | el usuario, para todos sus proyectos |
|
|
15
|
+
* | `proyecto` | `<raiz>/.xonecode/skills/` | el usuario, solo para ese proyecto |
|
|
16
|
+
*
|
|
17
|
+
* Las de serie son de SOLO LECTURA y no se borran ni se editan: viven dentro del paquete
|
|
18
|
+
* instalado, así que una edición se la llevaría por delante el siguiente `npm install` sin
|
|
19
|
+
* decir nada. Lo que sí se puede es COPIARLA a las tuyas y cambiarla ahí, que además deja
|
|
20
|
+
* ver de dónde salió. Por eso no hay `.semilla.json` como en los subagentes: nada nuestro
|
|
21
|
+
* se copia nunca a la casa del usuario, así que no hay marca que llevar ni nada que
|
|
22
|
+
* restaurar.
|
|
23
|
+
*
|
|
24
|
+
* El `SKILL.md` es el formato de Claude Code, y eso no es una coincidencia que convenga
|
|
25
|
+
* mantener: es lo que hace que una skill escrita para otro harness se lea aquí tal cual, y
|
|
26
|
+
* que `SkillsMiddleware` de deepagents —que es quien las descubre de verdad— la encuentre
|
|
27
|
+
* sin que xonecode traduzca nada.
|
|
28
|
+
*/
|
|
29
|
+
import { motivoDeNombreInaceptable, nombreSugerido } from "./agentes.js";
|
|
30
|
+
/** 4 caracteres por token, la regla de servilleta de siempre. */
|
|
31
|
+
export const tokensDeSkill = (texto) => Math.ceil(texto.length / 4);
|
|
32
|
+
/**
|
|
33
|
+
* El nombre de una skill es un SEGMENTO DE RUTA —la carpeta donde vive— y también lo que el
|
|
34
|
+
* subagente declara en su `.md`, así que vale la MISMA regla que la de un subagente y por la
|
|
35
|
+
* misma razón: `motivoDeNombreInaceptable` (`core/agentes.ts`). No se escribe una segunda
|
|
36
|
+
* aquí porque dos reglas para el mismo tipo de nombre es donde divergen.
|
|
37
|
+
*/
|
|
38
|
+
export const motivoDeNombreDeSkillInaceptable = motivoDeNombreInaceptable;
|
|
39
|
+
export const nombreDeSkillSugerido = nombreSugerido;
|
|
40
|
+
/** El texto que sale del frontmatter cuando un campo lleva caracteres que lo romperían. */
|
|
41
|
+
const enUnaLinea = (texto) => texto.replace(/\r?\n/g, " ").trim();
|
|
42
|
+
/** ¿Esta línea abre la clave `clave` en el primer nivel del frontmatter? */
|
|
43
|
+
const abreClave = (linea, clave) => linea.startsWith(`${clave}:`) && !linea.startsWith(" ") && !linea.startsWith("\t");
|
|
44
|
+
/**
|
|
45
|
+
* Compone el `SKILL.md`.
|
|
46
|
+
*
|
|
47
|
+
* El frontmatter es el mínimo que `parseFrontmatter` (`vendor/skillLoaders/catalog.ts`) y
|
|
48
|
+
* deepagents leen: `name` y `description`, una línea cada uno. La descripción se aplana a
|
|
49
|
+
* una línea porque ahí un salto la partiría en dos claves y la segunda mitad se perdería en
|
|
50
|
+
* silencio — el mismo motivo por el que el nombre es un slug.
|
|
51
|
+
*
|
|
52
|
+
* **Y lo demás del frontmatter se CONSERVA**: las claves que no son `name` ni `description`
|
|
53
|
+
* se reescriben tal cual, en su orden. Componer uno nuevo desde cero le borraba a una skill
|
|
54
|
+
* escrita a mano su `license`, su `allowed-tools` o su bloque `metadata` en cuanto alguien
|
|
55
|
+
* abriera la ventana y pulsara Guardar — sin error, sin aviso y sobre un fichero suyo. Las
|
|
56
|
+
* líneas INDENTADAS van con la clave que las abre, que es lo que deja pasar un bloque
|
|
57
|
+
* anidado entero sin entender YAML.
|
|
58
|
+
*/
|
|
59
|
+
export function escribirSkill(skill) {
|
|
60
|
+
const cuerpo = skill.cuerpo.replace(/^\n+/, "");
|
|
61
|
+
const resto = [];
|
|
62
|
+
let saltando = false;
|
|
63
|
+
for (const linea of (skill.frontmatter ?? "").split("\n")) {
|
|
64
|
+
if (abreClave(linea, "name") || abreClave(linea, "description")) {
|
|
65
|
+
saltando = true;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Una línea indentada pertenece a la clave de arriba: si se estaba saltando, se salta.
|
|
69
|
+
const indentada = /^[ \t]/.test(linea);
|
|
70
|
+
if (saltando && indentada)
|
|
71
|
+
continue;
|
|
72
|
+
saltando = false;
|
|
73
|
+
if (linea.trim() !== "")
|
|
74
|
+
resto.push(linea);
|
|
75
|
+
}
|
|
76
|
+
const extra = resto.length === 0 ? "" : `${resto.join("\n")}\n`;
|
|
77
|
+
return `---\nname: ${skill.nombre}\ndescription: ${enUnaLinea(skill.descripcion)}\n${extra}---\n\n${cuerpo}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Lee un `SKILL.md`.
|
|
81
|
+
*
|
|
82
|
+
* **El nombre sale de la CARPETA, no del frontmatter**, exactamente como el de un subagente
|
|
83
|
+
* sale del fichero: es lo que la hace direccionable (`/skills/<nombre>/`) y lo que el
|
|
84
|
+
* subagente declara. Un `name:` que diga otra cosa se ignora — mandaría sobre una ruta que
|
|
85
|
+
* ya está decidida, y entonces `rutasDeSkills` apuntaría a una carpeta que no existe.
|
|
86
|
+
*
|
|
87
|
+
* **Sin `description` no se carga.** Es lo que el modelo lee para decidir si esta skill le
|
|
88
|
+
* sirve: sin ella, la skill está en el catálogo y no la usa nadie nunca, que es peor que no
|
|
89
|
+
* estar. Misma regla que la `descripcion` de un subagente.
|
|
90
|
+
*/
|
|
91
|
+
export function leerSkill(texto, nombreDeCarpeta, origen, ficheros = []) {
|
|
92
|
+
const { descripcion, frontmatter, cuerpo } = partirFrontmatter(texto);
|
|
93
|
+
if (descripcion === "")
|
|
94
|
+
return { error: "no tiene `description` en el frontmatter" };
|
|
95
|
+
return {
|
|
96
|
+
skill: {
|
|
97
|
+
nombre: nombreDeCarpeta,
|
|
98
|
+
descripcion,
|
|
99
|
+
cuerpo,
|
|
100
|
+
frontmatter,
|
|
101
|
+
origen,
|
|
102
|
+
tokens: tokensDeSkill(texto),
|
|
103
|
+
ficheros,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* El frontmatter mínimo, sin traer un parser de YAML.
|
|
109
|
+
*
|
|
110
|
+
* Es el mismo criterio que `core/agentes.ts` y que `vendor/skillLoaders/catalog.ts`: la
|
|
111
|
+
* superficie es `clave: valor` de una línea entre `---`, y un parser entero sería una
|
|
112
|
+
* dependencia para leer dos campos. Lo que no se entiende no se inventa: sin frontmatter,
|
|
113
|
+
* el fichero entero es cuerpo y la descripción sale vacía, que es lo que `leerSkill`
|
|
114
|
+
* rechaza.
|
|
115
|
+
*/
|
|
116
|
+
function partirFrontmatter(texto) {
|
|
117
|
+
if (!texto.startsWith("---"))
|
|
118
|
+
return { descripcion: "", frontmatter: "", cuerpo: texto };
|
|
119
|
+
const fin = texto.indexOf("\n---", 3);
|
|
120
|
+
if (fin === -1)
|
|
121
|
+
return { descripcion: "", frontmatter: "", cuerpo: texto };
|
|
122
|
+
// El bloque entero, tal cual, sin los `---` ni el salto que los sigue. Es lo que se enseña
|
|
123
|
+
// y lo que se conserva al guardar: recomponerlo de las claves que entendemos tiraría las
|
|
124
|
+
// que no.
|
|
125
|
+
const frontmatter = texto.slice(3, fin).replace(/^\n/, "");
|
|
126
|
+
let descripcion = "";
|
|
127
|
+
for (const linea of frontmatter.split("\n")) {
|
|
128
|
+
const i = linea.indexOf(":");
|
|
129
|
+
if (i === -1)
|
|
130
|
+
continue;
|
|
131
|
+
if (linea.slice(0, i).trim() === "description")
|
|
132
|
+
descripcion = linea.slice(i + 1).trim();
|
|
133
|
+
}
|
|
134
|
+
const salto = texto.indexOf("\n", fin + 1);
|
|
135
|
+
return {
|
|
136
|
+
descripcion,
|
|
137
|
+
frontmatter,
|
|
138
|
+
cuerpo: salto === -1 ? "" : texto.slice(salto + 1).replace(/^\n+/, ""),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Funde las listas por nombre: la ÚLTIMA gana.
|
|
143
|
+
*
|
|
144
|
+
* Se llama con `serie`, `global`, `proyecto` en ese orden, así que una skill del proyecto
|
|
145
|
+
* tapa a la global del mismo nombre y las dos tapan a la de serie — la misma precedencia
|
|
146
|
+
* que los subagentes y que los modelos, y por lo mismo: lo particular manda sobre lo
|
|
147
|
+
* general. Que una tape a otra NO es un problema que reportar: es la forma de afinar una
|
|
148
|
+
* skill nuestra sin editarla donde el `npm install` la pisaría. Quien lo dice es la ficha,
|
|
149
|
+
* con su origen al lado.
|
|
150
|
+
*/
|
|
151
|
+
export function fusionarSkills(...listas) {
|
|
152
|
+
const porNombre = new Map();
|
|
153
|
+
for (const lista of listas)
|
|
154
|
+
for (const skill of lista)
|
|
155
|
+
porNombre.set(skill.nombre, skill);
|
|
156
|
+
return [...porNombre.values()].sort((a, b) => a.nombre.localeCompare(b.nombre));
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/core/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instalar una skill desde un `.zip`: QUÉ se acepta y con qué nombre queda.
|
|
3
|
+
*
|
|
4
|
+
* **Este módulo es PURO.** Quién descomprime es `agent/grafo/skills.ts`; aquí vive la regla,
|
|
5
|
+
* que es la parte que hay que poder probar sin un fichero de verdad delante.
|
|
6
|
+
*
|
|
7
|
+
* Un `.zip` que alguien descarga de internet y suelta en esta ventana es la entrada menos
|
|
8
|
+
* de fiar de todo el harness: sus nombres de entrada los eligió quien lo empaquetó. De ahí
|
|
9
|
+
* que la lista de lo que se admite sea BLANCA y no una lista de lo que se rechaza:
|
|
10
|
+
*
|
|
11
|
+
* - **Ninguna entrada puede salir de la carpeta de destino.** Es el «zip slip» de siempre:
|
|
12
|
+
* una entrada llamada `../../.ssh/authorized_keys` escribe donde le da la gana. Se exige
|
|
13
|
+
* una ruta relativa de segmentos llanos, y cualquier otra cosa —`..`, una absoluta, una
|
|
14
|
+
* con `\` (que en Windows ES separador), una con dos puntos de unidad, un byte nulo— se
|
|
15
|
+
* rechaza. No se «normaliza» nada: normalizar es interpretar, e interpretar es donde
|
|
16
|
+
* aparecen los agujeros.
|
|
17
|
+
* - **Tres topes, y los tres son contra un zip BOMBA**: cuánto ocupa descomprimido, cuántas
|
|
18
|
+
* entradas trae y cuánto ocupa la mayor. Un `.zip` de cien kilobytes puede descomprimirse
|
|
19
|
+
* en gigabytes, y el tope del cuerpo HTTP no lo ve.
|
|
20
|
+
* - **Tiene que traer un `SKILL.md`**, porque sin él no es una skill: sería una carpeta de
|
|
21
|
+
* ficheros sueltos que el modelo no puede descubrir.
|
|
22
|
+
*
|
|
23
|
+
* Y el NOMBRE no se pregunta: se DEDUCE, y si no vale se dice cuál valdría. Un campo más en
|
|
24
|
+
* la ventana para algo que el propio `.zip` ya contesta es una pregunta que no lo es.
|
|
25
|
+
*/
|
|
26
|
+
import { motivoDeNombreDeSkillInaceptable, nombreDeSkillSugerido } from "./skills.js";
|
|
27
|
+
/** Lo que ocupa el `.zip` que se sube, en bytes. Es el tope del cuerpo HTTP. */
|
|
28
|
+
export const TOPE_DEL_ZIP = 8 * 1024 * 1024;
|
|
29
|
+
/** Lo que puede ocupar DESCOMPRIMIDO. El de arriba no lo ve: un zip bomba cabe en nada. */
|
|
30
|
+
export const TOPE_DESCOMPRIMIDO = 32 * 1024 * 1024;
|
|
31
|
+
/** Cuántos ficheros puede traer. Una skill son unas decenas; mil es otra cosa. */
|
|
32
|
+
export const TOPE_DE_ENTRADAS = 500;
|
|
33
|
+
/**
|
|
34
|
+
* ¿Esta ruta de dentro del zip se puede escribir bajo una carpeta?
|
|
35
|
+
*
|
|
36
|
+
* Lista BLANCA de forma, no un `startsWith` ni un `includes("..")`: es la misma postura que
|
|
37
|
+
* `esRutaDeArtefacto`. Se admite una sucesión de segmentos llanos separados por `/`, y nada
|
|
38
|
+
* más — así lo que no se entiende queda FUERA por omisión en vez de colarse por el hueco que
|
|
39
|
+
* no se pensó.
|
|
40
|
+
*/
|
|
41
|
+
export function rutaDeZipAceptable(ruta) {
|
|
42
|
+
if (ruta === "" || ruta.length > 255)
|
|
43
|
+
return false;
|
|
44
|
+
// Un byte nulo trunca la ruta en las capas de C de debajo: lo que el chequeo ve y lo que
|
|
45
|
+
// el sistema abre dejarían de ser lo mismo.
|
|
46
|
+
if (ruta.includes("\0"))
|
|
47
|
+
return false;
|
|
48
|
+
// `\` es separador en Windows, así que una entrada con él no es un segmento llano aquí.
|
|
49
|
+
if (ruta.includes("\\"))
|
|
50
|
+
return false;
|
|
51
|
+
// Absoluta, o con unidad: `/etc/passwd`, `C:/…`.
|
|
52
|
+
if (ruta.startsWith("/") || /^[A-Za-z]:/.test(ruta))
|
|
53
|
+
return false;
|
|
54
|
+
const segmentos = ruta.split("/");
|
|
55
|
+
return segmentos.every((s) => s !== "" && s !== "." && s !== ".." && !/^\s|\s$/.test(s));
|
|
56
|
+
}
|
|
57
|
+
/** El único segmento de primer nivel que comparten TODAS las entradas, si lo hay. */
|
|
58
|
+
function carpetaComun(rutas) {
|
|
59
|
+
const primeros = new Set(rutas.map((r) => r.split("/")[0] ?? ""));
|
|
60
|
+
if (primeros.size !== 1)
|
|
61
|
+
return undefined;
|
|
62
|
+
const unico = [...primeros][0];
|
|
63
|
+
// Solo cuenta como carpeta envolvente si TODAS cuelgan de ella: un zip con un solo
|
|
64
|
+
// fichero en la raíz no tiene carpeta que quitar.
|
|
65
|
+
return rutas.every((r) => r.startsWith(`${unico}/`)) ? unico : undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Decide qué se instala y cómo se va a llamar. Devuelve el motivo si no se puede, nunca lanza.
|
|
69
|
+
*
|
|
70
|
+
* `nombreDelFichero` es el nombre del `.zip` que subió la persona, y solo se usa cuando el
|
|
71
|
+
* zip trae el `SKILL.md` en su raíz — o sea, cuando no hay carpeta dentro de la que sacar el
|
|
72
|
+
* nombre. Es lo que la persona ve en su disco, así que es lo que menos la sorprende.
|
|
73
|
+
*/
|
|
74
|
+
export function planDeInstalacion(entradas, nombreDelFichero) {
|
|
75
|
+
if (entradas.length === 0)
|
|
76
|
+
return { error: "el .zip está vacío" };
|
|
77
|
+
if (entradas.length > TOPE_DE_ENTRADAS) {
|
|
78
|
+
return { error: `el .zip trae más de ${TOPE_DE_ENTRADAS} ficheros: eso no es una skill` };
|
|
79
|
+
}
|
|
80
|
+
const mala = entradas.find((e) => !rutaDeZipAceptable(e.ruta));
|
|
81
|
+
if (mala !== undefined) {
|
|
82
|
+
// El nombre de la entrada NO se devuelve: lo eligió quien empaquetó el zip, y esto se
|
|
83
|
+
// pinta en la ventana de alguien. Se dice QUÉ pasa, que es lo accionable.
|
|
84
|
+
return { error: "el .zip trae una ruta que se sale de su carpeta, y no se instala" };
|
|
85
|
+
}
|
|
86
|
+
const total = entradas.reduce((suma, e) => suma + e.bytes, 0);
|
|
87
|
+
if (total > TOPE_DESCOMPRIMIDO) {
|
|
88
|
+
return { error: "el .zip ocupa demasiado descomprimido" };
|
|
89
|
+
}
|
|
90
|
+
const prefijo = carpetaComun(entradas.map((e) => e.ruta));
|
|
91
|
+
const ficheros = prefijo === undefined ? entradas.map((e) => e.ruta) : entradas.map((e) => e.ruta.slice(prefijo.length + 1));
|
|
92
|
+
if (!ficheros.includes("SKILL.md")) {
|
|
93
|
+
return { error: "el .zip no trae ningún SKILL.md: sin él no es una skill" };
|
|
94
|
+
}
|
|
95
|
+
const crudo = prefijo ?? nombreDelFichero.replace(/\.zip$/i, "");
|
|
96
|
+
const motivo = motivoDeNombreDeSkillInaceptable(crudo);
|
|
97
|
+
if (motivo !== undefined) {
|
|
98
|
+
const sugerido = nombreDeSkillSugerido(crudo);
|
|
99
|
+
return {
|
|
100
|
+
error: sugerido === undefined
|
|
101
|
+
? `«${crudo}» no vale como nombre de skill: ${motivo}`
|
|
102
|
+
: `«${crudo}» no vale como nombre de skill (${motivo}): renombra el .zip o su carpeta a «${sugerido}»`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return { nombre: crudo, ficheros, prefijo: prefijo === undefined ? "" : `${prefijo}/` };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=zipDeSkill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zipDeSkill.js","sourceRoot":"","sources":["../../src/core/zipDeSkill.ts"],"names":[],"mappings":"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"}
|