xonecode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +167 -0
  2. package/dist/agent/authEnDisco.js +56 -0
  3. package/dist/agent/authEnDisco.js.map +1 -0
  4. package/dist/agent/configEnDisco.js +109 -0
  5. package/dist/agent/configEnDisco.js.map +1 -0
  6. package/dist/agent/crearProyecto.js +34 -0
  7. package/dist/agent/crearProyecto.js.map +1 -0
  8. package/dist/agent/entorno.js +124 -0
  9. package/dist/agent/entorno.js.map +1 -0
  10. package/dist/agent/guionizado.js +53 -0
  11. package/dist/agent/guionizado.js.map +1 -0
  12. package/dist/agent/instantanea.js +129 -0
  13. package/dist/agent/instantanea.js.map +1 -0
  14. package/dist/agent/interrupts.js +83 -0
  15. package/dist/agent/interrupts.js.map +1 -0
  16. package/dist/agent/mensajes.js +24 -0
  17. package/dist/agent/mensajes.js.map +1 -0
  18. package/dist/agent/modelos.js +42 -0
  19. package/dist/agent/modelos.js.map +1 -0
  20. package/dist/agent/normalizar.js +20 -0
  21. package/dist/agent/normalizar.js.map +1 -0
  22. package/dist/agent/perfiles.js +103 -0
  23. package/dist/agent/perfiles.js.map +1 -0
  24. package/dist/agent/proyecto.js +107 -0
  25. package/dist/agent/proyecto.js.map +1 -0
  26. package/dist/agent/puente.js +142 -0
  27. package/dist/agent/puente.js.map +1 -0
  28. package/dist/agent/resumenDeTool.js +48 -0
  29. package/dist/agent/resumenDeTool.js.map +1 -0
  30. package/dist/agent/skills.js +65 -0
  31. package/dist/agent/skills.js.map +1 -0
  32. package/dist/agent/textoDeTool.js +113 -0
  33. package/dist/agent/textoDeTool.js.map +1 -0
  34. package/dist/agent/turnoReal.js +173 -0
  35. package/dist/agent/turnoReal.js.map +1 -0
  36. package/dist/agent/verificador.js +65 -0
  37. package/dist/agent/verificador.js.map +1 -0
  38. package/dist/agent/xoneAgent.js +106 -0
  39. package/dist/agent/xoneAgent.js.map +1 -0
  40. package/dist/bin.js +9 -0
  41. package/dist/bin.js.map +1 -0
  42. package/dist/cli/aprobar.js +82 -0
  43. package/dist/cli/aprobar.js.map +1 -0
  44. package/dist/cli/config.js +133 -0
  45. package/dist/cli/config.js.map +1 -0
  46. package/dist/cli/consola.js +346 -0
  47. package/dist/cli/consola.js.map +1 -0
  48. package/dist/cli/describe.js +53 -0
  49. package/dist/cli/describe.js.map +1 -0
  50. package/dist/cli/doctor.js +45 -0
  51. package/dist/cli/doctor.js.map +1 -0
  52. package/dist/cli/main.js +520 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/cli/markdown.js +90 -0
  55. package/dist/cli/markdown.js.map +1 -0
  56. package/dist/cli/run.js +164 -0
  57. package/dist/cli/run.js.map +1 -0
  58. package/dist/cli/spinner.js +89 -0
  59. package/dist/cli/spinner.js.map +1 -0
  60. package/dist/cli/stdio.js +193 -0
  61. package/dist/cli/stdio.js.map +1 -0
  62. package/dist/cli/tema.js +40 -0
  63. package/dist/cli/tema.js.map +1 -0
  64. package/dist/cli/verify.js +62 -0
  65. package/dist/cli/verify.js.map +1 -0
  66. package/dist/core/bitacora.js +30 -0
  67. package/dist/core/bitacora.js.map +1 -0
  68. package/dist/core/config.js +236 -0
  69. package/dist/core/config.js.map +1 -0
  70. package/dist/core/contextos.js +56 -0
  71. package/dist/core/contextos.js.map +1 -0
  72. package/dist/core/deps.js +51 -0
  73. package/dist/core/deps.js.map +1 -0
  74. package/dist/core/diff.js +79 -0
  75. package/dist/core/diff.js.map +1 -0
  76. package/dist/core/esqueleto.js +366 -0
  77. package/dist/core/esqueleto.js.map +1 -0
  78. package/dist/core/events.js +2 -0
  79. package/dist/core/events.js.map +1 -0
  80. package/dist/core/modelos.js +85 -0
  81. package/dist/core/modelos.js.map +1 -0
  82. package/dist/core/notify.js +106 -0
  83. package/dist/core/notify.js.map +1 -0
  84. package/dist/core/ports.js +114 -0
  85. package/dist/core/ports.js.map +1 -0
  86. package/dist/core/turno.js +127 -0
  87. package/dist/core/turno.js.map +1 -0
  88. package/dist/vendor/hitl.js +103 -0
  89. package/dist/vendor/hitl.js.map +1 -0
  90. package/dist/vendor/skillLoaders/catalog.js +74 -0
  91. package/dist/vendor/skillLoaders/catalog.js.map +1 -0
  92. package/dist/vendor/tokenTracking.js +58 -0
  93. package/dist/vendor/tokenTracking.js.map +1 -0
  94. package/package.json +41 -0
  95. package/skills/xone-debugging/SKILL.md +52 -0
  96. package/skills/xone-debugging/references/faq.md +561 -0
  97. package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
  98. package/skills/xone-development/SKILL.md +105 -0
  99. package/skills/xone-development/references/anti-patrones.md +94 -0
  100. package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
  101. package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
  102. package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
  103. package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
  104. package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
  105. package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
  106. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
  107. package/skills/xone-development/references/datos/appdata.md +611 -0
  108. package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
  109. package/skills/xone-development/references/datos/http.md +366 -0
  110. package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
  111. package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
  112. package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
  113. package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
  114. package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
  115. package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
  116. package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
  117. package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
  118. package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
  119. package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
  120. package/skills/xone-development/references/indice-completo.md +77 -0
  121. package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
  122. package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
  123. package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
  124. package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
  125. package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
  126. package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
  127. package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
  128. package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
  129. package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
  130. package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
  131. package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
  132. package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
  133. package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
  134. package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
  135. package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
  136. package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
  137. package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
  138. package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
  139. package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
  140. package/skills/xone-development/references/tipos-de-prop.md +28 -0
  141. package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
  142. package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
  143. package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
  144. package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
  145. package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
  146. package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
  147. package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
  148. package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
  149. package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
  150. package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
  151. package/skills/xone-development/references/xml-ui/mapas.md +685 -0
  152. package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
  153. package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
  154. package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
  155. package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
  156. package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
  157. package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
  158. package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
  159. package/skills/xone-plan-builder/SKILL.md +183 -0
  160. package/skills/xone-plan-builder/agents/openai.yaml +5 -0
  161. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
  162. package/skills/xone-project-generator/SKILL.md +111 -0
  163. package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
  164. package/skills/xone-project-generator/references/colecciones-base.md +16 -0
  165. package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
  166. package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
  167. package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
  168. package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
  169. package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
  170. package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
  171. package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
  172. package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
  173. package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
  174. package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
  175. package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
  176. package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
  177. package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
  178. package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
  179. package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
  180. package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
  181. package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
  182. package/skills/xone-review/SKILL.md +189 -0
  183. package/skills/xone-spec-builder/SKILL.md +235 -0
  184. package/skills/xone-spec-builder/agents/openai.yaml +5 -0
  185. package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
  186. package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
  187. package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # xonecode
2
+
3
+ Consola CLI y harness de agente para desarrollar aplicaciones **XOne**, sobre LangChain,
4
+ LangGraph y deepagents.
5
+
6
+ XOne es una plataforma propietaria de apps móviles nativas: se programa con XML (ficheros
7
+ `.xne`), JavaScript ES5 y un CSS propio. **No es desarrollo web** — no existe el DOM, ni
8
+ `async/await` en el runtime, ni ningún framework tipo React.
9
+
10
+ ```
11
+ $ cd MiApp && xonecode
12
+
13
+ xonecode · MiApp (7 colls) · ollama/glm-5.3-flash:cloud · 0 tokens
14
+ 13 comandos: /ayuda /config /describe /doctor /verify /modelo … /salir
15
+
16
+ ─ MiApp (7 colls) · ollama/glm-5.3-flash:cloud · 0 tokens · /ayuda
17
+ ❯ ¿qué colecciones tiene este proyecto?
18
+ 🔧 task · ls · glob · read_file ×7
19
+ El proyecto tiene 9 colecciones repartidas en 7 ficheros .xne: …
20
+ ⚠ el verificador no ha corrido en este turno
21
+ (9.0s)
22
+ ```
23
+
24
+ ## Estado
25
+
26
+ **Funciona y es usable, pero no es un producto terminado.** Lo que hay:
27
+
28
+ | | |
29
+ |---|---|
30
+ | Consola interactiva | prompt, barra de estado, 13 comandos de barra, autocompletado con Tab |
31
+ | Un disparo | `xonecode run "…"` — pipeable, para tuberías y CI |
32
+ | Aprobación humana | el agente **para y pregunta** antes de escribir; sin un sí explícito, rechaza |
33
+ | Verificador real | `xone-simulator` sobre el proyecto, con `/verify` y `xonecode verify` |
34
+ | Multi-proveedor | Gemini, OpenAI, Anthropic y **Ollama local**, con modelo por papel |
35
+ | Diagnóstico sin coste | `describe`, `doctor` y `config` no gastan una llamada al modelo |
36
+
37
+ Lo que **todavía no hay**, y conviene saber antes de fiarse:
38
+
39
+ - **Nadie verifica lo que el agente escribe.** El verificador existe y funciona, pero no
40
+ corre dentro del turno: la consola te lo avisa en cada uno. Hay que lanzar `/verify`.
41
+ - **No hay lazo** de plan → ejecuta → verifica → juzga → repara, con presupuestos.
42
+ - **No usa los servidores MCP de XOne Studio**: el proyecto tiene que estar en disco.
43
+ - **No hay TUI con paneles**; la consola es stdio.
44
+
45
+ ## Instalación
46
+
47
+ Requiere **Node ≥ 20** y, para verificar, [`xone-linter`](https://www.npmjs.com/package/xone-linter)
48
+ (que instala el binario `xone-simulator`).
49
+
50
+ ```sh
51
+ git clone https://github.com/sleiva/xonecode && cd xonecode
52
+ npm install
53
+ npm run build
54
+ npm link # o: ln -sf "$PWD/dist/bin.js" ~/.local/bin/xonecode
55
+ ```
56
+
57
+ Comprueba que el entorno está listo, sin gastar nada:
58
+
59
+ ```sh
60
+ cd /ruta/a/TuApp
61
+ xonecode doctor
62
+ ```
63
+
64
+ ## Configuración
65
+
66
+ Dos ficheros con dos ciclos de vida distintos, y **las claves nunca van en el proyecto**:
67
+
68
+ | Fichero | Qué lleva |
69
+ |---|---|
70
+ | `<proyecto>/.xonecode/config.json` | modelos y proveedores. **Rechaza claves de API.** |
71
+ | `~/.xonecode/config.json` | lo mismo, para todos los proyectos |
72
+ | `~/.xonecode/auth.json` | las credenciales, con modo **0600**. Se escribe con `/provider <nombre>` |
73
+
74
+ ```json
75
+ {
76
+ "modelo": "ollama/glm-5.3-flash:cloud",
77
+ "modelos": { "afilado": "anthropic/claude-sonnet-4-5-20250929" }
78
+ }
79
+ ```
80
+
81
+ Precedencia, y cada valor recuerda de dónde vino (`xonecode config` lo dice):
82
+
83
+ ```
84
+ --modelo-<papel> > --modelo > XONECODE_MODELO > proyecto > global > omisión
85
+ ```
86
+
87
+ Hay **tres papeles** porque el reparto es la palanca de coste: `rapido` corre en todos los
88
+ turnos, `trabajo` desarrolla y `afilado` está reservado al juez. Por omisión, Ollama local.
89
+
90
+ ## Cómo está construido, y por qué
91
+
92
+ Las decisiones que explican la forma del código. Ninguna es preferencia estética: todas
93
+ salen de un fallo medido.
94
+
95
+ **El motor de turno no sabe pintar.** `core/turno.ts` consume eventos de dominio y decide
96
+ qué se cuenta; la consola de stdio y (en su día) la TUI son pieles. Así el turno se prueba
97
+ entero sin montar un renderizador.
98
+
99
+ **Frontera de imports, y está probada.** `core/` es TypeScript puro: no importa langchain,
100
+ langgraph, deepagents, MCP, Ink ni React. Toda la suciedad del grafo vive en `agent/`. Hay
101
+ un test que recorre los ficheros de `core/` y falla si aparece un import prohibido — una
102
+ convención que nadie verifica dura dos semanas.
103
+
104
+ **Todo lo caro entra por un puerto, con su doble al lado.** Consecuencia buscada: la suite
105
+ entera corre **sin API, sin red y sin el simulador**.
106
+
107
+ **Un doble nunca se disfraza de verdad.** La marca de «esto es de pega» es un `Symbol` que
108
+ solo los dobles llevan, no un booleano que alguien rellena: un campo se puede olvidar, o
109
+ poner mal, y entonces el aviso calla justo cuando hace falta. `xonecode describe` lo canta.
110
+
111
+ **Los avisos son código, no prompt.** A un modelo al que le pides «avisa de que el
112
+ verificador es de pega» a veces no avisa — y ese es justo el aviso que no puede faltar.
113
+
114
+ **Y tienen alcance de TURNO.** Un aviso que salta cuando no ha pasado nada enseña al
115
+ usuario a ignorarlo, que es lo contrario de lo que compra.
116
+
117
+ **Aprobar ejecuta; rechazar no toca nada.** Así que lo que no se entiende es **rechazo**, y
118
+ el Enter a secas solo aprueba con un terminal de verdad detrás: en un pipe o en CI una línea
119
+ en blanco no demuestra que haya nadie mirando.
120
+
121
+ **El agente está confinado.** `FilesystemBackend` con `virtualMode: true` — medido: con el
122
+ default de la librería, el backend **leyó una ruta absoluta de fuera de la raíz**. Nada de
123
+ backends con shell: los comandos acceden a cualquier ruta y la propia librería lo impide.
124
+
125
+ **Se le ocultan las vistas aplanadas.** En XOne cada colección existe como `.xne` (la
126
+ fuente) y como `.xml` que genera Studio. La regla «se edita el `.xne`» no puede vivir solo
127
+ en un prompt: un permiso solo protege a quien lo choca. Se retira del backend, y así es una
128
+ propiedad del proyecto.
129
+
130
+ **La foto del ANTES es un árbol de git en un índice privado.** `GIT_INDEX_FILE` a un
131
+ temporal propio, `git add` y `write-tree`. No necesita commits, no necesita que el proyecto
132
+ sea la raíz del repo, y **no toca tu índice ni tu staging**. Medido contra un repo con cero
133
+ commits: `git status` daba **una** línea; el árbol privado, 107 ficheros.
134
+
135
+ ## Códigos de salida
136
+
137
+ | | |
138
+ |---|---|
139
+ | 0 | bien |
140
+ | 1 | el proyecto tiene errores, o no es un proyecto XOne |
141
+ | 2 | había escrituras esperando aprobación y **nada se aplicó** |
142
+ | 64 | error de uso (bandera o modelo mal escritos) |
143
+ | 70 | fallo del **entorno**, no del proyecto |
144
+
145
+ El 2 es deliberado: un turno que se quedó esperando un permiso que nadie dio **no es un
146
+ éxito**, y CI no puede leerlo como tal.
147
+
148
+ ## Desarrollo
149
+
150
+ ```sh
151
+ npm run typecheck # tsc --noEmit
152
+ npm test # la suite entera, sin red ni claves
153
+ ./bin/xonecode … # corre el TypeScript con tsx: siempre el código actual
154
+ ```
155
+
156
+ `npm test` no puede necesitar una clave ni una conexión. Si un cambio lo rompe, está mal el
157
+ cambio, no el test.
158
+
159
+ `src/vendor/` son módulos propios traídos de un laboratorio anterior, con sus tests. Se
160
+ copiaron en vez de publicarse aparte porque son 290 líneas y no merecen un paquete.
161
+
162
+ ## Más
163
+
164
+ - [`docs/COMO-PROBARLO.md`](docs/COMO-PROBARLO.md) — la guía de pruebas, paso a paso
165
+ - [`docs/PROBLEMA.md`](docs/PROBLEMA.md) — el enunciado del que salió el diseño
166
+ - [`docs/SEGUNDA-OPINION-K3.md`](docs/SEGUNDA-OPINION-K3.md) — una segunda opinión
167
+ independiente sobre la arquitectura, pedida a ciegas
@@ -0,0 +1,56 @@
1
+ /**
2
+ * El ESCRITOR de `~/.xonecode/auth.json` (el lector está en `configEnDisco.ts`).
3
+ *
4
+ * El contrato con el fichero del usuario: una escritura NUNCA destruye lo que ya
5
+ * había. Por eso la base de la fusión es el objeto CRUDO tal cual (no el resultado
6
+ * de `validarAuth`, que descarta en silencio entradas raras) y por eso ante un JSON
7
+ * roto se PARA sin escribir, en vez de recuperar el fichero por su cuenta.
8
+ */
9
+ import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
10
+ import { dirname } from "node:path";
11
+ import { validarAuth } from "../core/config.js";
12
+ import { rutaAuth } from "./configEnDisco.js";
13
+ /** Un `auth.json` existente que no se puede fusionar: no se escribe nada encima. */
14
+ export class AuthRotoEnDisco extends Error {
15
+ }
16
+ /** Misma regla que en core/config.ts: un JSON-array NO es una raíz de fusionado. */
17
+ function esObjeto(v) {
18
+ return typeof v === "object" && v !== null && !Array.isArray(v);
19
+ }
20
+ export function guardarCredencial(proveedor, clave) {
21
+ const ruta = rutaAuth();
22
+ // 0700 en la misma llamada que crea la carpeta: un mkdir + chmod posterior dejaría
23
+ // una ventana con la carpeta de credenciales legible.
24
+ mkdirSync(dirname(ruta), { recursive: true, mode: 0o700 });
25
+ let base = {};
26
+ let avisos = [];
27
+ if (existsSync(ruta)) {
28
+ let bruto;
29
+ try {
30
+ bruto = JSON.parse(readFileSync(ruta, "utf8"));
31
+ }
32
+ catch {
33
+ // El mensaje solo nombra la ruta, nunca el contenido: eso acaba en logs y captures.
34
+ throw new AuthRotoEnDisco(`«${ruta}»: el JSON es inválido; no se sobrescribe. Edita el fichero a mano.`);
35
+ }
36
+ if (!esObjeto(bruto)) {
37
+ throw new AuthRotoEnDisco(`«${ruta}»: el JSON raíz debe ser un objeto; no se sobrescribe. Edita el fichero a mano.`);
38
+ }
39
+ // Fusionar desde el result de validarAuth BORRARÍA del fichero del usuario todo lo
40
+ // que la validación descarta (proveedores desconocidos, credenciales de forma rara).
41
+ // La base es el objeto crudo; a validarAuth solo se le piden los avisos.
42
+ base = { ...bruto };
43
+ const modoReal = statSync(ruta).mode & 0o777;
44
+ avisos = validarAuth(bruto, ruta, modoReal, "global").avisos;
45
+ }
46
+ // Siempre la forma canónica { key } por proveedor, para que el resultado sea homogéneo
47
+ // aunque el fichero previo mezclara formas.
48
+ const fusionado = { ...base, [proveedor]: { key: clave } };
49
+ // El { mode } solo aplica al CREAR; en el caso de fusión el fichero conserva su modo
50
+ // previo, que puede haber sido malo. chmod después no viola la atomicidad: la creación
51
+ // sigue siendo 0600 desde el primer byte.
52
+ writeFileSync(ruta, JSON.stringify(fusionado, null, 2) + "\n", { mode: 0o600 });
53
+ chmodSync(ruta, 0o600);
54
+ return { ruta, avisos };
55
+ }
56
+ //# sourceMappingURL=authEnDisco.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authEnDisco.js","sourceRoot":"","sources":["../../src/agent/authEnDisco.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,oFAAoF;AACpF,MAAM,OAAO,eAAgB,SAAQ,KAAK;CAAG;AAE7C,oFAAoF;AACpF,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAoB,EAAE,KAAa;IACnE,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;IAExB,mFAAmF;IACnF,sDAAsD;IACtD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAE3D,IAAI,IAAI,GAA4B,EAAE,CAAC;IACvC,IAAI,MAAM,GAAY,EAAE,CAAC;IAEzB,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,IAAI,KAAc,CAAC;QACnB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,oFAAoF;YACpF,MAAM,IAAI,eAAe,CACvB,IAAI,IAAI,qEAAqE,CAC9E,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,eAAe,CACvB,IAAI,IAAI,iFAAiF,CAC1F,CAAC;QACJ,CAAC;QAED,mFAAmF;QACnF,qFAAqF;QACrF,yEAAyE;QACzE,IAAI,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;QAC7C,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC;IAC/D,CAAC;IAED,uFAAuF;IACvF,4CAA4C;IAC5C,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC;IAE3D,qFAAqF;IACrF,uFAAuF;IACvF,0CAA0C;IAC1C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChF,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAEvB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Leer los dos ficheros de configuración del disco y volcar las credenciales en el
3
+ * entorno. Aquí está el I/O que `core/config.ts` no quiere: rutas, `readFileSync` y
4
+ * `statSync`; lo que llega a `validar`/`validarAuth` es solo datos.
5
+ */
6
+ import { existsSync, readFileSync, statSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { validar, validarAuth, } from "../core/config.js";
10
+ import { PROVEEDORES } from "../core/modelos.js";
11
+ export const NOMBRE_CARPETA = ".xonecode";
12
+ export function rutaConfigGlobal() {
13
+ // homedir() en el momento de la llamada: si se cacheara en una constante de módulo,
14
+ // un test que reescribe HOME después del import vería la ruta vieja.
15
+ return join(homedir(), NOMBRE_CARPETA, "config.json");
16
+ }
17
+ export function rutaConfigDeProyecto(raiz) {
18
+ return join(raiz, NOMBRE_CARPETA, "config.json");
19
+ }
20
+ export function rutaAuth() {
21
+ return join(homedir(), NOMBRE_CARPETA, "auth.json");
22
+ }
23
+ export function cargar(raiz) {
24
+ const rutas = [];
25
+ const avisos = [];
26
+ const config = {};
27
+ let auth = {};
28
+ const rutaProyecto = rutaConfigDeProyecto(raiz);
29
+ const existeProyecto = existsSync(rutaProyecto);
30
+ rutas.push({ ruta: rutaProyecto, existe: existeProyecto, procedencia: "proyecto" });
31
+ if (existeProyecto) {
32
+ try {
33
+ const bruto = JSON.parse(readFileSync(rutaProyecto, "utf8"));
34
+ const res = validar(bruto, rutaProyecto, "proyecto");
35
+ config.proyecto = res.config;
36
+ avisos.push(...res.avisos);
37
+ }
38
+ catch {
39
+ // El mensaje no interpola err.message: el contenido del fichero puede acabar en
40
+ // logs y el aviso solo dice QUÉ pasó, nunca qué contenía.
41
+ avisos.push({
42
+ texto: `«${rutaProyecto}»: el JSON es inválido; se ignora el fichero.`,
43
+ severidad: "aviso",
44
+ });
45
+ }
46
+ }
47
+ const rutaGlobal = rutaConfigGlobal();
48
+ const existeGlobal = existsSync(rutaGlobal);
49
+ rutas.push({ ruta: rutaGlobal, existe: existeGlobal, procedencia: "global" });
50
+ if (existeGlobal) {
51
+ try {
52
+ const bruto = JSON.parse(readFileSync(rutaGlobal, "utf8"));
53
+ const res = validar(bruto, rutaGlobal, "global");
54
+ config.global = res.config;
55
+ avisos.push(...res.avisos);
56
+ }
57
+ catch {
58
+ avisos.push({
59
+ texto: `«${rutaGlobal}»: el JSON es inválido; se ignora el fichero.`,
60
+ severidad: "aviso",
61
+ });
62
+ }
63
+ }
64
+ const rutaAuthFichero = rutaAuth();
65
+ const existeAuth = existsSync(rutaAuthFichero);
66
+ rutas.push({ ruta: rutaAuthFichero, existe: existeAuth, procedencia: "auth" });
67
+ if (existeAuth) {
68
+ try {
69
+ const bruto = JSON.parse(readFileSync(rutaAuthFichero, "utf8"));
70
+ const modo = statSync(rutaAuthFichero).mode & 0o777;
71
+ const res = validarAuth(bruto, rutaAuthFichero, modo, "global");
72
+ auth = res.auth;
73
+ avisos.push(...res.avisos);
74
+ }
75
+ catch {
76
+ avisos.push({
77
+ texto: `«${rutaAuthFichero}»: el JSON es inválido; se ignora el fichero.`,
78
+ severidad: "aviso",
79
+ });
80
+ }
81
+ }
82
+ return { config, auth, rutas, avisos };
83
+ }
84
+ /** Las claves ya presentes en el entorno MANDAN: `auth.json` no las machaca. */
85
+ const VARIABLES_POR_PROVEEDOR = {
86
+ anthropic: "ANTHROPIC_API_KEY",
87
+ openai: "OPENAI_API_KEY",
88
+ gemini: "GOOGLE_API_KEY",
89
+ };
90
+ /**
91
+ * Vuelca las credenciales en `process.env` y devuelve los proveedores que SÍ se
92
+ * aplicaron. Las que ya estaban en el entorno se dejan intactas y no se cuentan.
93
+ * `ollama` no tiene variable: no aparece en el mapa y se ignora.
94
+ */
95
+ export function aplicarAuth(auth) {
96
+ const aplicadas = [];
97
+ for (const proveedor of PROVEEDORES) {
98
+ const variable = VARIABLES_POR_PROVEEDOR[proveedor];
99
+ const credencial = auth[proveedor];
100
+ if (variable === undefined || credencial === undefined)
101
+ continue;
102
+ if (process.env[variable] !== undefined)
103
+ continue;
104
+ process.env[variable] = credencial.key;
105
+ aplicadas.push(proveedor);
106
+ }
107
+ return aplicadas;
108
+ }
109
+ //# sourceMappingURL=configEnDisco.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configEnDisco.js","sourceRoot":"","sources":["../../src/agent/configEnDisco.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * La escritura del esqueleto en el disco del usuario.
3
+ *
4
+ * QUÉ se escribe lo decide `core/esqueleto.ts` (puro, probado sin disco); AQUÍ
5
+ * solo se ejecuta, al estilo de `agent/configEnDisco.ts`. La regla dura es la
6
+ * misma que en todo xonecode: **el disco es del usuario**, así que un fichero
7
+ * que ya exista no se pisa — se salta y se declara en el informe. Un proyecto
8
+ * a medias se completa en la siguiente corrida; un fichero del usuario
9
+ * destruido no se recupera.
10
+ */
11
+ import { mkdirSync, writeFileSync, existsSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { generarEsqueleto, carpetasDelEsqueleto } from "../core/esqueleto.js";
14
+ export function crearProyecto(raiz, datos) {
15
+ const creados = [];
16
+ const saltados = [];
17
+ for (const carpeta of carpetasDelEsqueleto()) {
18
+ // `recursive` y no un existsSync previo: crean lo que falte sin quejarse
19
+ // de lo que ya esté, que es justo lo que pide reintentar una creación a
20
+ // medias.
21
+ mkdirSync(join(raiz, carpeta), { recursive: true });
22
+ }
23
+ for (const ficha of generarEsqueleto(datos)) {
24
+ const destino = join(raiz, ficha.ruta);
25
+ if (existsSync(destino)) {
26
+ saltados.push(ficha.ruta);
27
+ continue;
28
+ }
29
+ writeFileSync(destino, ficha.contenido);
30
+ creados.push(ficha.ruta);
31
+ }
32
+ return { creados, saltados, carpetas: carpetasDelEsqueleto() };
33
+ }
34
+ //# sourceMappingURL=crearProyecto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crearProyecto.js","sourceRoot":"","sources":["../../src/agent/crearProyecto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAyB,MAAM,sBAAsB,CAAC;AAWrG,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAuB;IACjE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,OAAO,IAAI,oBAAoB,EAAE,EAAE,CAAC;QAC7C,yEAAyE;QACzE,wEAAwE;QACxE,UAAU;QACV,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,oBAAoB,EAAE,EAAE,CAAC;AACjE,CAAC"}
@@ -0,0 +1,124 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { existsSync, readdirSync, statSync, realpathSync } from "node:fs";
4
+ import { join, basename, resolve } from "node:path";
5
+ const ejecutar = promisify(execFile);
6
+ /** Recorre la raíz sin bajar a `node_modules` ni a `.git`. */
7
+ function ficheros(raiz, prof = 0) {
8
+ if (prof > 4 || !existsSync(raiz))
9
+ return [];
10
+ const salida = [];
11
+ for (const entrada of readdirSync(raiz)) {
12
+ if (entrada === "node_modules" || entrada === ".git")
13
+ continue;
14
+ const ruta = join(raiz, entrada);
15
+ try {
16
+ if (statSync(ruta).isDirectory())
17
+ salida.push(...ficheros(ruta, prof + 1));
18
+ else
19
+ salida.push(ruta);
20
+ }
21
+ catch {
22
+ // Un enlace roto o un permiso no es motivo para que `doctor` no conteste.
23
+ }
24
+ }
25
+ return salida;
26
+ }
27
+ /**
28
+ * Las vistas aplanadas: todo `X.xml` que tenga un `X.xne` al lado.
29
+ *
30
+ * En XOne cada colección existe en dos formas, y **la que se edita es el `.xne`**. La regla
31
+ * no puede vivir solo en el prompt del ejecutor —un permiso solo protege a quien lo
32
+ * choca—, así que estos ficheros se le OCULTAN al agente. Ocultarlos y no borrarlos: el
33
+ * disco es del usuario.
34
+ *
35
+ * `app.xml` no tiene hermano `.xne`, así que el propio predicado lo conserva.
36
+ */
37
+ export function vistasAplanadas(rutas) {
38
+ const xne = new Set(rutas.filter((r) => r.endsWith(".xne")).map((r) => r.slice(0, -4)));
39
+ return rutas.filter((r) => r.endsWith(".xml") && xne.has(r.slice(0, -4)));
40
+ }
41
+ /**
42
+ * ¿Sirve git para dar el diff y el deshacer de ESTE proyecto?
43
+ *
44
+ * Las tres condiciones salen de medir, no de suponer, contra el disco real del usuario:
45
+ *
46
+ * 1. **El repo tiene que ser el del PROYECTO.** Medido: los proyectos viven dentro de un
47
+ * repo cuya raíz está por encima, con cientos de entradas sin trackear que no tienen nada
48
+ * que ver. Un diff de ese repo no habla del proyecto.
49
+ * 2. **Tiene que haber al menos un commit.** Ese repo está en `main` con CERO commits:
50
+ * `git rev-parse HEAD` falla, y sin commit no hay rama de la que partir, ni worktree
51
+ * que añadir, ni nada contra lo que diffear.
52
+ * 3. **Y aunque acotes al subdirectorio, no vale.** Medido: `git status --porcelain -- .`
53
+ * desde dentro del proyecto devuelve UNA línea —`?? proyecto_example/`— porque git no
54
+ * desciende en directorios sin trackear. O sea que el diff habría dicho «1 fichero
55
+ * nuevo» tocara el agente lo que tocara: un fallo MUDO que reporta algo plausible.
56
+ * (`--untracked-files=all` lo expande, pero eso no arregla 1 ni 2.)
57
+ */
58
+ export async function inspeccionarGit(raiz) {
59
+ let raizRepo;
60
+ try {
61
+ const { stdout } = await ejecutar("git", ["rev-parse", "--show-toplevel"], { cwd: raiz });
62
+ raizRepo = stdout.trim() || undefined;
63
+ }
64
+ catch {
65
+ return { dentro: false, esRaiz: false, tieneCommits: false, prefijo: "", usable: false };
66
+ }
67
+ if (!raizRepo)
68
+ return { dentro: false, esRaiz: false, tieneCommits: false, prefijo: "", usable: false };
69
+ // `realpath` en los DOS lados, y no `resolve`. Medido en macOS: git devuelve el
70
+ // toplevel con los symlinks ya resueltos (`/private/var/...` para un `/var/...`), así
71
+ // que comparar rutas sin resolver da «no es la raíz» sobre un proyecto que SÍ lo es —
72
+ // y xonecode caería al modo huellas sin necesidad. `/tmp` y `/var` son symlinks en
73
+ // macOS, o sea que el caso no es exótico: es cualquier proyecto bajo uno de ellos.
74
+ const real = (r) => {
75
+ try {
76
+ return realpathSync(r);
77
+ }
78
+ catch {
79
+ return resolve(r);
80
+ }
81
+ };
82
+ const esRaiz = real(raizRepo) === real(raiz);
83
+ let tieneCommits = false;
84
+ try {
85
+ await ejecutar("git", ["rev-parse", "HEAD"], { cwd: raiz });
86
+ tieneCommits = true;
87
+ }
88
+ catch {
89
+ tieneCommits = false; // repo recién creado, sin nada commiteado
90
+ }
91
+ let prefijo = "";
92
+ try {
93
+ const { stdout } = await ejecutar("git", ["rev-parse", "--show-prefix"], { cwd: raiz });
94
+ prefijo = stdout.trim();
95
+ }
96
+ catch {
97
+ prefijo = "";
98
+ }
99
+ return { dentro: true, raizRepo, esRaiz, tieneCommits, prefijo, usable: true };
100
+ }
101
+ export async function inspeccionar(raiz = process.cwd()) {
102
+ const rutas = ficheros(raiz);
103
+ let responde = false;
104
+ let ruta = "xone-simulator";
105
+ try {
106
+ const { stdout } = await ejecutar("xone-simulator", ["help"]);
107
+ responde = stdout.includes("xone-simulator");
108
+ const cual = await ejecutar("which", ["xone-simulator"]).catch(() => ({ stdout: "" }));
109
+ if (cual.stdout.trim())
110
+ ruta = cual.stdout.trim();
111
+ }
112
+ catch {
113
+ responde = false;
114
+ }
115
+ return {
116
+ raiz,
117
+ esProyectoXone: existsSync(join(raiz, "app.xml")),
118
+ colecciones: rutas.filter((r) => r.endsWith(".xne")).length,
119
+ vistasAplanadas: vistasAplanadas(rutas).map((r) => basename(r)),
120
+ git: await inspeccionarGit(raiz),
121
+ simulador: { ruta, responde },
122
+ };
123
+ }
124
+ //# sourceMappingURL=entorno.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entorno.js","sourceRoot":"","sources":["../../src/agent/entorno.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { ES_DOBLE } from "../core/ports.js";
2
+ /**
3
+ * Un turno completo de pega, para ver el motor correr sin API ni red.
4
+ *
5
+ * Lleva la marca de doble: `xonecode describe` (fase 5) tiene que poder decir que lo que
6
+ * está montado no es el agente de verdad. Un mock que no se declara hace creer que el
7
+ * sistema funciona.
8
+ *
9
+ * El guion recorre a propósito las cosas que cuestan de pintar: una racha de tools, una
10
+ * verificación en rojo, una reparación, y una respuesta troceada en varios mensajes.
11
+ */
12
+ export class AgenteGuionizado {
13
+ retardoMs;
14
+ [ES_DOBLE] = true;
15
+ constructor(retardoMs = 0) {
16
+ this.retardoMs = retardoMs;
17
+ }
18
+ async *turno(peticion) {
19
+ const pausa = async () => {
20
+ if (this.retardoMs > 0)
21
+ await new Promise((r) => setTimeout(r, this.retardoMs));
22
+ };
23
+ yield { tipo: "fase", fase: "entendiendo" };
24
+ await pausa();
25
+ yield { tipo: "fase", fase: "planificando" };
26
+ yield {
27
+ tipo: "plan",
28
+ tareas: [
29
+ { id: "T1", descripcion: `[GUION] ${peticion}`, aceptacion: [] },
30
+ ],
31
+ };
32
+ await pausa();
33
+ yield { tipo: "tarea", id: "T1", indice: 1, total: 1, estado: "en-curso" };
34
+ yield { tipo: "fase", fase: "ejecutando" };
35
+ for (let i = 0; i < 3; i++) {
36
+ yield { tipo: "tool", nombre: "read_file" };
37
+ await pausa();
38
+ }
39
+ yield { tipo: "tool", nombre: "grep" };
40
+ yield { tipo: "fase", fase: "verificando" };
41
+ yield { tipo: "verificacion", verde: false, errores: 2, avisos: 1 };
42
+ yield { tipo: "reparacion", intento: 1, tope: 3 };
43
+ yield { tipo: "tool", nombre: "edit_file" };
44
+ yield { tipo: "verificacion", verde: true, errores: 0, avisos: 0 };
45
+ yield { tipo: "tarea", id: "T1", indice: 1, total: 1, estado: "hecha" };
46
+ yield { tipo: "fase", fase: "respondiendo" };
47
+ for (const trozo of ["Listo. ", "He recorrido ", "el turno entero ", "sin llamar a ningún modelo."]) {
48
+ yield { tipo: "token", texto: trozo, msgId: "guion-1" };
49
+ await pausa();
50
+ }
51
+ }
52
+ }
53
+ //# sourceMappingURL=guionizado.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guionizado.js","sourceRoot":"","sources":["../../src/agent/guionizado.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C;;;;;;;;;GASG;AACH,MAAM,OAAO,gBAAgB;IAGE;IAFpB,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAE3B,YAA6B,YAAY,CAAC;QAAb,cAAS,GAAT,SAAS,CAAI;IAAG,CAAC;IAE9C,KAAK,CAAC,CAAC,KAAK,CAAC,QAAgB;QAC3B,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;YACtC,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC;gBAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC;QAEF,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;QAC5C,MAAM,KAAK,EAAE,CAAC;QAEd,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;QAC7C,MAAM;YACJ,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE;gBACN,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;aACjE;SACF,CAAC;QACF,MAAM,KAAK,EAAE,CAAC;QAEd,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QAC3E,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;YAC5C,MAAM,KAAK,EAAE,CAAC;QAChB,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAEvC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;QAC5C,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACpE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAClD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAC5C,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACnE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAExE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;QAC7C,KAAK,MAAM,KAAK,IAAI,CAAC,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,6BAA6B,CAAC,EAAE,CAAC;YACpG,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;YACxD,MAAM,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;CACF"}