xonecode 0.2.0 → 0.5.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 +49 -7
- package/THIRD_PARTY_NOTICES.md +128 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/apps/web/dist/assets/c-BIGW1oBm.js +1 -0
- package/apps/web/dist/assets/cpp-DddhVWhK.js +1 -0
- package/apps/web/dist/assets/csharp-DSvCPggb.js +1 -0
- package/apps/web/dist/assets/css-CLj8gQPS.js +1 -0
- package/apps/web/dist/assets/go-C27-OAKa.js +1 -0
- package/apps/web/dist/assets/html-CfGypltT.js +1 -0
- package/apps/web/dist/assets/index-BB3SamhR.css +1 -0
- package/apps/web/dist/assets/index-CFYAoHdw.js +477 -0
- package/apps/web/dist/assets/ini-BEwlwnbL.js +1 -0
- package/apps/web/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/apps/web/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/apps/web/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/apps/web/dist/assets/java-CylS5w8V.js +1 -0
- package/apps/web/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/apps/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/apps/web/dist/assets/less-B1dDrJ26.js +1 -0
- package/apps/web/dist/assets/lua-BaeVxFsk.js +1 -0
- package/apps/web/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/apps/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/apps/web/dist/assets/php-D_2--9WK.js +1 -0
- package/apps/web/dist/assets/python-B6aJPvgy.js +1 -0
- package/apps/web/dist/assets/ruby-DSHSDbk1.js +1 -0
- package/apps/web/dist/assets/rust-B1yitclQ.js +1 -0
- package/apps/web/dist/assets/scss-D5BDwBP9.js +1 -0
- package/apps/web/dist/assets/sql-CRqJ_cUM.js +1 -0
- package/apps/web/dist/assets/swift-C2oV4EkX.js +1 -0
- package/apps/web/dist/assets/toml-vGWfd6FD.js +1 -0
- package/apps/web/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/apps/web/dist/assets/yaml-Buea-lGh.js +1 -0
- package/apps/web/dist/iconos/xonecode.png +0 -0
- package/apps/web/dist/index.html +20 -0
- package/dist/agent/agentesEnDisco.js +539 -0
- package/dist/agent/agentesEnDisco.js.map +1 -0
- package/dist/agent/arbolDeProyecto.js +245 -0
- package/dist/agent/arbolDeProyecto.js.map +1 -0
- package/dist/agent/artefactosEnDisco.js +139 -0
- package/dist/agent/artefactosEnDisco.js.map +1 -0
- package/dist/agent/aumentador.js +175 -0
- package/dist/agent/aumentador.js.map +1 -0
- package/dist/agent/authEnDisco.js +76 -1
- package/dist/agent/authEnDisco.js.map +1 -1
- package/dist/agent/busquedaRegex.js +92 -0
- package/dist/agent/busquedaRegex.js.map +1 -0
- package/dist/agent/catalogoModelos.js +345 -0
- package/dist/agent/catalogoModelos.js.map +1 -0
- package/dist/agent/checkpointer.js +127 -0
- package/dist/agent/checkpointer.js.map +1 -0
- package/dist/agent/cloudstudioClient.js +194 -0
- package/dist/agent/cloudstudioClient.js.map +1 -0
- package/dist/agent/cloudstudioMcp.js +722 -0
- package/dist/agent/cloudstudioMcp.js.map +1 -0
- package/dist/agent/configEnDisco.js +309 -11
- package/dist/agent/configEnDisco.js.map +1 -1
- package/dist/agent/consumoExterno.js +99 -0
- package/dist/agent/consumoExterno.js.map +1 -0
- package/dist/agent/descarga.js +149 -0
- package/dist/agent/descarga.js.map +1 -0
- package/dist/agent/diagnosticoDeTools.js +51 -0
- package/dist/agent/diagnosticoDeTools.js.map +1 -0
- package/dist/agent/dispositivosEnMaquina.js +596 -0
- package/dist/agent/dispositivosEnMaquina.js.map +1 -0
- package/dist/agent/escrituraDeCodex.js +134 -0
- package/dist/agent/escrituraDeCodex.js.map +1 -0
- package/dist/agent/escrituraDeOpencode.js +80 -0
- package/dist/agent/escrituraDeOpencode.js.map +1 -0
- package/dist/agent/escrituraExterna.js +742 -0
- package/dist/agent/escrituraExterna.js.map +1 -0
- package/dist/agent/gemini.js +81 -0
- package/dist/agent/gemini.js.map +1 -0
- package/dist/agent/git.js +83 -0
- package/dist/agent/git.js.map +1 -0
- package/dist/agent/gitSync.js +441 -0
- package/dist/agent/gitSync.js.map +1 -0
- package/dist/agent/hotswap.js +448 -0
- package/dist/agent/hotswap.js.map +1 -0
- package/dist/agent/instalacionEnMaquina.js +252 -0
- package/dist/agent/instalacionEnMaquina.js.map +1 -0
- package/dist/agent/instantanea.js +7 -10
- package/dist/agent/instantanea.js.map +1 -1
- package/dist/agent/interrupts.js +11 -2
- package/dist/agent/interrupts.js.map +1 -1
- package/dist/agent/juezDeTarea.js +345 -0
- package/dist/agent/juezDeTarea.js.map +1 -0
- package/dist/agent/lanzamientoEnMaquina.js +377 -0
- package/dist/agent/lanzamientoEnMaquina.js.map +1 -0
- package/dist/agent/manifiesto.js +59 -0
- package/dist/agent/manifiesto.js.map +1 -0
- package/dist/agent/memoriaDeProyecto.js +60 -0
- package/dist/agent/memoriaDeProyecto.js.map +1 -0
- package/dist/agent/modelos.js +105 -17
- package/dist/agent/modelos.js.map +1 -1
- package/dist/agent/modelosDeMotor.js +204 -0
- package/dist/agent/modelosDeMotor.js.map +1 -0
- package/dist/agent/paqueteDelProyecto.js +181 -0
- package/dist/agent/paqueteDelProyecto.js.map +1 -0
- package/dist/agent/perfiles.js +92 -31
- package/dist/agent/perfiles.js.map +1 -1
- package/dist/agent/persona.js +40 -0
- package/dist/agent/persona.js.map +1 -0
- package/dist/agent/procesosEnMaquina.js +204 -0
- package/dist/agent/procesosEnMaquina.js.map +1 -0
- package/dist/agent/proyecto.js +311 -13
- package/dist/agent/proyecto.js.map +1 -1
- package/dist/agent/puente.js +63 -6
- package/dist/agent/puente.js.map +1 -1
- package/dist/agent/resumenDeContexto.js +30 -0
- package/dist/agent/resumenDeContexto.js.map +1 -0
- package/dist/agent/resumenDeTool.js +64 -18
- package/dist/agent/resumenDeTool.js.map +1 -1
- package/dist/agent/sesionGit.js +481 -0
- package/dist/agent/sesionGit.js.map +1 -0
- package/dist/agent/settingsEnDisco.js +189 -0
- package/dist/agent/settingsEnDisco.js.map +1 -0
- package/dist/agent/subagenteCodex.js +368 -0
- package/dist/agent/subagenteCodex.js.map +1 -0
- package/dist/agent/subagenteExterno.js +423 -0
- package/dist/agent/subagenteExterno.js.map +1 -0
- package/dist/agent/subagenteOpencode.js +389 -0
- package/dist/agent/subagenteOpencode.js.map +1 -0
- package/dist/agent/subida.js +202 -0
- package/dist/agent/subida.js.map +1 -0
- package/dist/agent/tareasEnDisco.js +466 -0
- package/dist/agent/tareasEnDisco.js.map +1 -0
- package/dist/agent/turnoReal.js +663 -50
- package/dist/agent/turnoReal.js.map +1 -1
- package/dist/agent/versionEnDisco.js +48 -0
- package/dist/agent/versionEnDisco.js.map +1 -0
- package/dist/agent/xoneAgent.js +290 -49
- package/dist/agent/xoneAgent.js.map +1 -1
- package/dist/agent/zip.js +52 -0
- package/dist/agent/zip.js.map +1 -0
- package/dist/cli/acuseDeModelo.js +30 -0
- package/dist/cli/acuseDeModelo.js.map +1 -0
- package/dist/cli/aprobar.js +55 -2
- package/dist/cli/aprobar.js.map +1 -1
- package/dist/cli/config.js +9 -13
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/consola.js +750 -19
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/main.js +740 -81
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/markdown.js +188 -2
- package/dist/cli/markdown.js.map +1 -1
- package/dist/cli/run.js +32 -4
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/stdio.js +46 -19
- package/dist/cli/stdio.js.map +1 -1
- package/dist/cli/tema.js +96 -8
- package/dist/cli/tema.js.map +1 -1
- package/dist/cli/tokens.js +29 -0
- package/dist/cli/tokens.js.map +1 -0
- package/dist/cli/tui/app.js +171 -0
- package/dist/cli/tui/app.js.map +1 -0
- package/dist/cli/tui/aprobarTui.js +80 -0
- package/dist/cli/tui/aprobarTui.js.map +1 -0
- package/dist/cli/tui/barra.js +37 -0
- package/dist/cli/tui/barra.js.map +1 -0
- package/dist/cli/tui/correrTui.js +478 -0
- package/dist/cli/tui/correrTui.js.map +1 -0
- package/dist/cli/tui/entrada.js +166 -0
- package/dist/cli/tui/entrada.js.map +1 -0
- package/dist/cli/tui/filas.js +24 -0
- package/dist/cli/tui/filas.js.map +1 -0
- package/dist/cli/tui/logo.js +49 -0
- package/dist/cli/tui/logo.js.map +1 -0
- package/dist/cli/tui/pielTui.js +14 -0
- package/dist/cli/tui/pielTui.js.map +1 -0
- package/dist/cli/tui/raton.js +126 -0
- package/dist/cli/tui/raton.js.map +1 -0
- package/dist/cli/tui/sidebar.js +86 -0
- package/dist/cli/tui/sidebar.js.map +1 -0
- package/dist/cli/tui/store.js +143 -0
- package/dist/cli/tui/store.js.map +1 -0
- package/dist/cli/tui/tarjeta.js +19 -0
- package/dist/cli/tui/tarjeta.js.map +1 -0
- package/dist/cli/tui/temaInk.js +114 -0
- package/dist/cli/tui/temaInk.js.map +1 -0
- package/dist/cli/tui/transcript.js +254 -0
- package/dist/cli/tui/transcript.js.map +1 -0
- package/dist/cli/wizardInicial.js +240 -0
- package/dist/cli/wizardInicial.js.map +1 -0
- package/dist/core/actos.js +113 -0
- package/dist/core/actos.js.map +1 -0
- package/dist/core/adjuntos.js +97 -0
- package/dist/core/adjuntos.js.map +1 -0
- package/dist/core/agentes.js +227 -0
- package/dist/core/agentes.js.map +1 -0
- package/dist/core/artefactos.js +149 -0
- package/dist/core/artefactos.js.map +1 -0
- package/dist/core/cloudstudio.js +7 -0
- package/dist/core/cloudstudio.js.map +1 -0
- package/dist/core/config.js +212 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/contextos.js +53 -1
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/descargas.js +106 -0
- package/dist/core/descargas.js.map +1 -0
- package/dist/core/descriptoresDeApp.js +279 -0
- package/dist/core/descriptoresDeApp.js.map +1 -0
- package/dist/core/dispositivos.js +466 -0
- package/dist/core/dispositivos.js.map +1 -0
- package/dist/core/entrega.js +141 -0
- package/dist/core/entrega.js.map +1 -0
- package/dist/core/entrelazar.js +90 -0
- package/dist/core/entrelazar.js.map +1 -0
- package/dist/core/esqueleto.js +10 -0
- package/dist/core/esqueleto.js.map +1 -1
- package/dist/core/modelos.js +235 -3
- package/dist/core/modelos.js.map +1 -1
- package/dist/core/notify.js +27 -9
- package/dist/core/notify.js.map +1 -1
- package/dist/core/planDeSubida.js +127 -0
- package/dist/core/planDeSubida.js.map +1 -0
- package/dist/core/ports.js +189 -1
- package/dist/core/ports.js.map +1 -1
- package/dist/core/puedeLanzarse.js +239 -0
- package/dist/core/puedeLanzarse.js.map +1 -0
- package/dist/core/rutaVirtual.js +42 -0
- package/dist/core/rutaVirtual.js.map +1 -0
- package/dist/core/settings.js +246 -0
- package/dist/core/settings.js.map +1 -0
- package/dist/core/tareas.js +230 -0
- package/dist/core/tareas.js.map +1 -0
- package/dist/core/textos.js +36 -0
- package/dist/core/textos.js.map +1 -0
- package/dist/core/turno.js +70 -10
- package/dist/core/turno.js.map +1 -1
- package/dist/core/version.js +31 -0
- package/dist/core/version.js.map +1 -0
- package/dist/vendor/tokenTracking.js +22 -1
- package/dist/vendor/tokenTracking.js.map +1 -1
- package/dist/web/servidor/arranque.js +3964 -0
- package/dist/web/servidor/arranque.js.map +1 -0
- package/dist/web/servidor/consolaDeTarea.js +161 -0
- package/dist/web/servidor/consolaDeTarea.js.map +1 -0
- package/dist/web/servidor/consolaWeb.js +389 -0
- package/dist/web/servidor/consolaWeb.js.map +1 -0
- package/dist/web/servidor/corredorDeTareas.js +1119 -0
- package/dist/web/servidor/corredorDeTareas.js.map +1 -0
- package/dist/web/servidor/pielWeb.js +269 -0
- package/dist/web/servidor/pielWeb.js.map +1 -0
- package/dist/web/servidor/servidor.js +257 -0
- package/dist/web/servidor/servidor.js.map +1 -0
- package/dist/web/servidor/sesiones.js +440 -0
- package/dist/web/servidor/sesiones.js.map +1 -0
- package/dist/web/servidor/transporte.js +169 -0
- package/dist/web/servidor/transporte.js.map +1 -0
- package/dist/web/servidor/vestibulo.js +1276 -0
- package/dist/web/servidor/vestibulo.js.map +1 -0
- package/package.json +30 -8
- package/skills/archify/LICENSE +22 -0
- package/skills/archify/SKILL.md +109 -0
- package/skills/archify/THIRD_PARTY_NOTICES.md +56 -0
- package/skills/archify/assets/template.html +14787 -0
- package/skills/archify/bin/archify.mjs +1990 -0
- package/skills/archify/bin/open-artifact.mjs +86 -0
- package/skills/archify/bin/preview.mjs +648 -0
- package/skills/archify/bin/visual-check.mjs +829 -0
- package/skills/archify/brand-marks/README.md +31 -0
- package/skills/archify/brand-marks/catalog.json +131 -0
- package/skills/archify/delta/architecture-delta.mjs +1214 -0
- package/skills/archify/examples/agent-run.lifecycle.json +71 -0
- package/skills/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/archify/examples/brand-aware-delivery.architecture.json +47 -0
- package/skills/archify/examples/cache-miss-request.sequence.json +81 -0
- package/skills/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/archify/examples/dataflow-product-analytics.html +14898 -0
- package/skills/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/archify/examples/incident-response.workflow.json +64 -0
- package/skills/archify/examples/lifecycle-agent-run.html +14847 -0
- package/skills/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/archify/examples/sequence-cache-miss-request.html +14913 -0
- package/skills/archify/examples/web-app-rendered.html +14862 -0
- package/skills/archify/examples/web-app.architecture.json +46 -0
- package/skills/archify/examples/workflow-agent-tool-call-rendered.html +14904 -0
- package/skills/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/archify/package.json +17 -0
- package/skills/archify/recipes/scenarios.mjs +391 -0
- package/skills/archify/references/authoring-contract.md +198 -0
- package/skills/archify/references/brand-marks.md +69 -0
- package/skills/archify/references/delivery-contract.md +116 -0
- package/skills/archify/references/viewer-runtime.md +45 -0
- package/skills/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/archify/renderers/dataflow/README.md +104 -0
- package/skills/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/archify/renderers/lifecycle/README.md +115 -0
- package/skills/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/archify/renderers/sequence/README.md +114 -0
- package/skills/archify/renderers/sequence/render-sequence.mjs +453 -0
- package/skills/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/archify/renderers/shared/cli.mjs +218 -0
- package/skills/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/archify/renderers/shared/diagnostics.mjs +127 -0
- package/skills/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- package/skills/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/archify/renderers/shared/i18n.mjs +594 -0
- package/skills/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/archify/renderers/shared/legend.mjs +217 -0
- package/skills/archify/renderers/shared/output-path.mjs +321 -0
- package/skills/archify/renderers/shared/repository-evidence.mjs +235 -0
- package/skills/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/archify/renderers/shared/utils.mjs +232 -0
- package/skills/archify/renderers/shared/validator.mjs +86 -0
- package/skills/archify/renderers/workflow/README.md +223 -0
- package/skills/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/archify/schemas/README.md +211 -0
- package/skills/archify/schemas/architecture.schema.json +176 -0
- package/skills/archify/schemas/common.schema.json +115 -0
- package/skills/archify/schemas/dataflow.schema.json +243 -0
- package/skills/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/archify/schemas/sequence.schema.json +223 -0
- package/skills/archify/schemas/workflow.schema.json +428 -0
- package/skills/archify/scripts/check-render-output.mjs +835 -0
- package/skills/archify/scripts/check-update.mjs +1667 -0
- package/skills/archify/scripts/render-examples.mjs +26 -0
- package/skills/archify/scripts/update-contract.mjs +182 -0
- package/skills/archify/skill-release.json +10 -0
- package/skills/artifacts-builder/SKILL.md +120 -0
- package/skills/artifacts-builder/reference/diagramas.md +123 -0
- package/skills/artifacts-builder/reference/estilo.md +123 -0
- package/skills/artifacts-builder/reference/exportar-pdf.md +56 -0
- package/skills/artifacts-builder/reference/graficas.md +22 -0
- package/skills/artifacts-builder/scripts/bundle_artifact.py +109 -0
- package/skills/artifacts-builder/scripts/validate_artifact.py +345 -0
- package/skills/xone-hotswap/SKILL.md +185 -0
- package/skills/xone-hotswap/references/comandos.md +706 -0
- package/skills/xone-hotswap/references/conexion-y-despliegue.md +226 -0
package/dist/cli/main.js
CHANGED
|
@@ -14,17 +14,34 @@ import { cmdDoctor } from "./doctor.js";
|
|
|
14
14
|
import { cmdVerify } from "./verify.js";
|
|
15
15
|
import { ModeloMalEscrito, parsear, resolver } from "../core/modelos.js";
|
|
16
16
|
import { topeResuelto } from "../core/contextos.js";
|
|
17
|
-
import { cargar } from "../agent/configEnDisco.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
17
|
+
import { aplicarAuth, cargar, cloudstudioDelProyecto, aplicarCredencialAlProceso, guardarCloudStudioDeProyecto, guardarEntornoDeProyecto, guardarModeloGlobal, guardarModelosDeProyecto, proveedoresPersonalizados, guardarModoDeProyecto, guardarProyectoCloudStudioDeProyecto, guardarRamaDeProyecto, guardarTemaDeProyecto, } from "../agent/configEnDisco.js";
|
|
18
|
+
import { crearCheckpointerDeProyecto } from "../agent/checkpointer.js";
|
|
19
|
+
import { crearTareasEnDisco } from "../agent/tareasEnDisco.js";
|
|
20
|
+
import { carpetaDeArtefactosDeSesion } from "../core/artefactos.js";
|
|
21
|
+
import { seAplicaSinAprobacion } from "../core/settings.js";
|
|
22
|
+
import { conectarCloudStudio, sesionCloudStudio, PUERTO_CALLBACK } from "../agent/cloudstudioMcp.js";
|
|
23
|
+
import { clienteCloudStudio } from "../agent/cloudstudioClient.js";
|
|
24
|
+
import { cargarSettings } from "../agent/settingsEnDisco.js";
|
|
25
|
+
import { descargarProyecto } from "../agent/descarga.js";
|
|
26
|
+
import { arbolLimpio, cambiosPendientes, prepararRepo, sinCommitear } from "../agent/gitSync.js";
|
|
27
|
+
import { subir } from "../agent/subida.js";
|
|
28
|
+
import { guardarCredencial } from "../agent/authEnDisco.js";
|
|
29
|
+
import { asistenteDeModelo } from "./wizardInicial.js";
|
|
30
|
+
import { COMANDOS, configurarModoInicial, correrConsola, crearCompleter, hayCredencial, hayEstadoDeProyecto, MENSAJE_BIENVENIDA, MENSAJE_REANUDANDO, PETICION_REANUDAR_PROYECTO, } from "./consola.js";
|
|
31
|
+
import { crearDetectorDeEof, crearLeerSecreto, crearPreguntar, crearPielStdio, escribirEnStdout } from "./stdio.js";
|
|
32
|
+
import { crearTema, esTema, seleccionarTema } from "./tema.js";
|
|
21
33
|
import { pedirDecisiones } from "./aprobar.js";
|
|
34
|
+
import { modeloDeAcuse } from "./acuseDeModelo.js";
|
|
22
35
|
import { inspeccionar } from "../agent/entorno.js";
|
|
36
|
+
import { versionEnMarcha } from "../agent/versionEnDisco.js";
|
|
23
37
|
import { SkillsEnDisco } from "../agent/skills.js";
|
|
24
38
|
import { Modelos } from "../agent/modelos.js";
|
|
39
|
+
import { CatalogoModelos } from "../agent/catalogoModelos.js";
|
|
25
40
|
import { abrirSesionReal, ficherosDelProyecto } from "../agent/turnoReal.js";
|
|
41
|
+
import { SimuladorVerifier } from "../agent/verificador.js";
|
|
26
42
|
import { crearProyecto } from "../agent/crearProyecto.js";
|
|
27
43
|
import { createTokenTracker } from "../vendor/tokenTracking.js";
|
|
44
|
+
import { compacto, formatearTokens, formatearTope } from "./tokens.js";
|
|
28
45
|
/**
|
|
29
46
|
* Estilo, y solo con TTY detrás.
|
|
30
47
|
*
|
|
@@ -40,6 +57,10 @@ const BOLD = tema.negrita;
|
|
|
40
57
|
const RESET = tema.reset;
|
|
41
58
|
/** El prompt. Se ve dónde acaba la respuesta y empieza lo que uno escribe. */
|
|
42
59
|
const PROMPT = CON_COLOR ? `${tema.prompt}❯${tema.reset} ` : "> ";
|
|
60
|
+
/** El arranque automático falla como un turno normal: informa y deja la consola viva. */
|
|
61
|
+
function describirError(e) {
|
|
62
|
+
return e instanceof Error ? `${e.constructor.name}: ${e.message}` : String(e);
|
|
63
|
+
}
|
|
43
64
|
/**
|
|
44
65
|
* Envuelve las líneas de `readline` para pintar la barra y el prompt ANTES de cada una.
|
|
45
66
|
*
|
|
@@ -68,6 +89,12 @@ async function* conPrompt(rl, escribir, barra) {
|
|
|
68
89
|
}
|
|
69
90
|
const AYUDA = `xonecode — harness de XOne
|
|
70
91
|
|
|
92
|
+
xonecode abre la consola WEB en el navegador (por defecto)
|
|
93
|
+
xonecode --no-abrir la web, pero sin abrir el navegador (imprime la URL)
|
|
94
|
+
xonecode --puerto <n> la web en otro puerto (por omisión 4173)
|
|
95
|
+
xonecode --anfitrion <host> acepta ese Host además del loopback, para un túnel
|
|
96
|
+
xonecode --cli la consola de terminal (TUI)
|
|
97
|
+
xonecode --cli --no-tui consola clásica (stdio)
|
|
71
98
|
xonecode run "<peticion>" un turno, de un disparo (pipeable)
|
|
72
99
|
xonecode run --real "<peticion>" el agente real sobre el proyecto del cwd
|
|
73
100
|
xonecode describe qué hay montado y qué es de pega (sin red)
|
|
@@ -76,6 +103,11 @@ const AYUDA = `xonecode — harness de XOne
|
|
|
76
103
|
xonecode doctor ¿hay un proyecto aquí? ¿responde el simulador?
|
|
77
104
|
xonecode verify [ruta] valida el proyecto con xone-simulator (por omisión, aquí)
|
|
78
105
|
xonecode --guion la consola con el agente de pega, sin gastar
|
|
106
|
+
xonecode --guion --web lo mismo, y si el cwd es un proyecto offline lo abre
|
|
107
|
+
solo: la maqueta completa sin alta ni CloudStudio
|
|
108
|
+
xonecode --web fuerza la web aunque no haya terminal interactivo
|
|
109
|
+
xonecode --cli --tui fuerza la interfaz de terminal (TUI)
|
|
110
|
+
xonecode --cli --sin-raton TUI sin capturar el ratón (la rueda vuelve al terminal)
|
|
79
111
|
xonecode --help esto
|
|
80
112
|
|
|
81
113
|
Modelo:
|
|
@@ -131,20 +163,6 @@ export function extraerBanderasDeModelo(argv) {
|
|
|
131
163
|
resto,
|
|
132
164
|
};
|
|
133
165
|
}
|
|
134
|
-
/**
|
|
135
|
-
* Token compacto para la línea de estado: `0 tokens` hasta 999, `12.8K tokens` a partir
|
|
136
|
-
* de 1000. Un decimal basta para un indicador de cabecera; la cifra exacta la da quien
|
|
137
|
-
* la necesita (printTokenUsage al final de los experimentos).
|
|
138
|
-
*/
|
|
139
|
-
function formatearTokens(total) {
|
|
140
|
-
return total < 1000 ? `${total} tokens` : `${(total / 1000).toFixed(1)}K tokens`;
|
|
141
|
-
}
|
|
142
|
-
/** El tope de contexto, compacto: `131K`, `200K`, `1M`. Sin decimales de cortesía. */
|
|
143
|
-
function formatearTope(tope) {
|
|
144
|
-
if (tope >= 1_000_000)
|
|
145
|
-
return `${tope / 1_000_000}M`;
|
|
146
|
-
return `${Math.round(tope / 1000)}K`;
|
|
147
|
-
}
|
|
148
166
|
/**
|
|
149
167
|
* La barra de estado como función PURA: la compone quien tiene los valores y
|
|
150
168
|
* la pinta quien tiene el color. Así la barra se puede probar sin TTY — y es
|
|
@@ -156,13 +174,151 @@ function formatearTope(tope) {
|
|
|
156
174
|
*/
|
|
157
175
|
export function formatearBarra(p) {
|
|
158
176
|
const ctx = p.contexto > 0
|
|
159
|
-
? ` · ctx ${
|
|
177
|
+
? ` · ctx ${compacto(p.contexto)}` +
|
|
160
178
|
(p.tope !== undefined
|
|
161
179
|
? `/${formatearTope(p.tope)} (${Math.round((p.contexto / p.tope) * 100)}%)`
|
|
162
180
|
: "")
|
|
163
181
|
: "";
|
|
164
182
|
return `─ ${p.proyecto} (${p.colecciones} colls) · ${p.modelo} · ${formatearTokens(p.tokens)}${ctx} · /ayuda`;
|
|
165
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* El tope de la ventana del modelo ACTUAL: tabla de `core/contextos.ts` + lo que el
|
|
186
|
+
* usuario haya fijado en `config.json` (lo de proyecto gana a lo global, como los
|
|
187
|
+
* modelos). Fábrica PORQUE la barra lo lee en cada llamada y no en el arranque:
|
|
188
|
+
* /modelo cambia el modelo en caliente, y un tope del modelo equivocado es un
|
|
189
|
+
* porcentaje que miente. Si el id no parsea —no debería, viene de `resolver`—, no hay
|
|
190
|
+
* tope y no pasa nada.
|
|
191
|
+
*
|
|
192
|
+
* Exportada para que la TUI calcule su `ctx` con LA MISMA función que usa la barra de
|
|
193
|
+
* stdio y la que /config declara: dos pieles no pueden divergir en el tope.
|
|
194
|
+
*/
|
|
195
|
+
export function crearTopeDelModelo(raiz) {
|
|
196
|
+
return (id) => {
|
|
197
|
+
try {
|
|
198
|
+
const { proveedor, modelo } = parsear(id);
|
|
199
|
+
const { config } = cargar(raiz);
|
|
200
|
+
// Misma resolución que /config (`topeResuelto`): proyecto gana a global, y la
|
|
201
|
+
// tabla es el último recurso. El origen aquí no se mira, pero así no puede
|
|
202
|
+
// divergir entre lo que la barra calcula y lo que /config declara.
|
|
203
|
+
return topeResuelto(proveedor, modelo, {
|
|
204
|
+
proyecto: config.proyecto?.contextos,
|
|
205
|
+
global: config.global?.contextos,
|
|
206
|
+
})?.tope;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* ¿TUI o stdio? `--no-tui` gana siempre (el usuario pidió explícitamente la consola
|
|
215
|
+
* clásica); después `--tui` fuerza la TUI aunque no haya TTY (quien la pide, la tiene:
|
|
216
|
+
* sin TTY el raw mode no funciona y el resultado es suyo); por omisión, AMBOS lados
|
|
217
|
+
* TTY → TUI (solo mirar stdout monta la TUI con stdin de tubería — «echo /salir |
|
|
218
|
+
* xonecode» en un terminal real — y los bytes del pipe se consumen como teclas mientras
|
|
219
|
+
* el EOF del pipe no termina el lazo) y cualquier tubería → stdio, que es lo que
|
|
220
|
+
* mantiene el e2e de pipe byte-idéntico.
|
|
221
|
+
*/
|
|
222
|
+
/**
|
|
223
|
+
* ¿Captura la TUI el ratón? Con él, la rueda mueve el transcript y no el scrollback del
|
|
224
|
+
* terminal, a cambio de que seleccionar texto sea Alt/Shift + arrastre. `--sin-raton` lo
|
|
225
|
+
* apaga. Pura, para probarla.
|
|
226
|
+
*/
|
|
227
|
+
export function quiereRaton(argv = []) {
|
|
228
|
+
return !argv.includes("--sin-raton");
|
|
229
|
+
}
|
|
230
|
+
export function decidirTui(argv = []) {
|
|
231
|
+
if (argv.includes("--no-tui"))
|
|
232
|
+
return false;
|
|
233
|
+
if (argv.includes("--tui"))
|
|
234
|
+
return true;
|
|
235
|
+
return process.stdout.isTTY === true && process.stdin.isTTY === true;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Error de USO: una bandera imposible de obedecer, nunca un fallo del harness. Mismo
|
|
239
|
+
* tratamiento que `ModeloMalEscrito` en el `catch` de `main` — mensaje a stderr, sin
|
|
240
|
+
* traza, código 64 — pero con su propia clase: reusar `ModeloMalEscrito` aquí mentiría
|
|
241
|
+
* sobre la causa, y el contrato de códigos de salida lo lee CI.
|
|
242
|
+
*/
|
|
243
|
+
export class ErrorDeUso extends Error {
|
|
244
|
+
}
|
|
245
|
+
const PUERTO_WEB_POR_OMISION = 4173;
|
|
246
|
+
/**
|
|
247
|
+
* Qué piel arranca. Hermana pura de `decidirTui`, y por la misma razón: la decisión se
|
|
248
|
+
* prueba sin TTY, y la rama que la usa solo la obedece.
|
|
249
|
+
*
|
|
250
|
+
* La omisión ya es la web. `--cli` gana siempre —es la puerta a la consola de terminal
|
|
251
|
+
* entera, TUI incluida— y `--web` fuerza la web incluso sin terminal, que es lo que hace
|
|
252
|
+
* falta en una máquina remota sin TTY.
|
|
253
|
+
*
|
|
254
|
+
* El TTY entra por parámetro y no se lee aquí dentro para poder probar los dos lados sin
|
|
255
|
+
* terminal; la omisión sigue siendo el `process.stdin.isTTY` de verdad.
|
|
256
|
+
*/
|
|
257
|
+
/**
|
|
258
|
+
* Las banderas que solo tienen sentido en la consola de TERMINAL. Pedir cualquiera de ellas
|
|
259
|
+
* es pedir la terminal, así que implican `--cli`.
|
|
260
|
+
*
|
|
261
|
+
* Medido con un pty: sin esto, `xonecode --tui` imprimía «consola web en …» y abría el
|
|
262
|
+
* navegador — o sea, la bandera documentada como «fuerza la interfaz de terminal» hacía
|
|
263
|
+
* exactamente lo contrario y sin decir nada; `--no-tui`, que ERA la vía a la stdio, se
|
|
264
|
+
* ignoraba igual. Una bandera que se obedece al revés en silencio es el bug mudo que este
|
|
265
|
+
* repo persigue en todas partes.
|
|
266
|
+
*/
|
|
267
|
+
const BANDERAS_DE_TERMINAL = ["--tui", "--no-tui", "--sin-raton"];
|
|
268
|
+
export function decidirPiel(argv = [], entorno = {}) {
|
|
269
|
+
// `--cli` gana SIEMPRE, también frente a la combinación imposible de abajo: es la orden
|
|
270
|
+
// más explícita que hay y no puede quedar en un error.
|
|
271
|
+
if (argv.includes("--cli"))
|
|
272
|
+
return "consola";
|
|
273
|
+
const deTerminal = BANDERAS_DE_TERMINAL.filter((b) => argv.includes(b));
|
|
274
|
+
if (argv.includes("--web")) {
|
|
275
|
+
// Obedecer una y callar la otra es lo que no se puede hacer: la bandera perdedora se
|
|
276
|
+
// habría ignorado en silencio. Error de USO (64), como `--tui` sin terminal.
|
|
277
|
+
if (deTerminal.length > 0) {
|
|
278
|
+
throw new ErrorDeUso(`«${deTerminal.join("», «")}» son de la consola de terminal y --web pide la web: elige una`);
|
|
279
|
+
}
|
|
280
|
+
return "web";
|
|
281
|
+
}
|
|
282
|
+
if (deTerminal.length > 0)
|
|
283
|
+
return "consola";
|
|
284
|
+
// Sin stdin TTY la omisión NO es la web: `echo "…" | xonecode` intentaría abrir un
|
|
285
|
+
// navegador y se llevaría por delante el e2e de tubería byte-idéntica, que es lo que
|
|
286
|
+
// sostiene que `npm test` no necesite terminal.
|
|
287
|
+
const tty = entorno.stdinTTY ?? process.stdin.isTTY === true;
|
|
288
|
+
return tty ? "web" : "consola";
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* El anfitrión de un túnel: un nombre de máquina, y nada más.
|
|
292
|
+
*
|
|
293
|
+
* Se valida aquí y no en el servidor porque es un error de USO (64) y no de ejecución. Sin
|
|
294
|
+
* comodines, sin esquema, sin ruta y sin puerto: lo que llega en la cabecera `Host` de un
|
|
295
|
+
* túnel HTTPS es el nombre pelado, y aceptar `https://x/` o `*.ngrok-free.app` sería aceptar
|
|
296
|
+
* algo que nunca va a casar —o, peor, casar de más—.
|
|
297
|
+
*/
|
|
298
|
+
function anfitrionDeTunel(bruto) {
|
|
299
|
+
const valor = (bruto ?? "").trim();
|
|
300
|
+
if (!/^[a-zA-Z0-9.-]+$/.test(valor) || valor.includes("*")) {
|
|
301
|
+
throw new ErrorDeUso(`--anfitrion espera un nombre de máquina (por ejemplo «algo.ngrok-free.app»), y recibió «${bruto ?? ""}»`);
|
|
302
|
+
}
|
|
303
|
+
return valor;
|
|
304
|
+
}
|
|
305
|
+
export function parsearOpcionesWeb(argv) {
|
|
306
|
+
const abrir = !argv.includes("--no-abrir");
|
|
307
|
+
const j = argv.indexOf("--anfitrion");
|
|
308
|
+
const anfitrion = j === -1 ? {} : { anfitrion: anfitrionDeTunel(argv[j + 1]) };
|
|
309
|
+
const i = argv.indexOf("--puerto");
|
|
310
|
+
if (i === -1)
|
|
311
|
+
return { puerto: PUERTO_WEB_POR_OMISION, abrir, ...anfitrion };
|
|
312
|
+
const bruto = argv[i + 1];
|
|
313
|
+
const puerto = Number(bruto);
|
|
314
|
+
if (!Number.isInteger(puerto) || puerto < 1 || puerto > 65535) {
|
|
315
|
+
throw new ErrorDeUso(`--puerto espera un número entre 1 y 65535, y recibió «${bruto ?? ""}»`);
|
|
316
|
+
}
|
|
317
|
+
if (puerto === PUERTO_CALLBACK) {
|
|
318
|
+
throw new ErrorDeUso(`el puerto ${PUERTO_CALLBACK} está reservado al callback de OAuth de CloudStudio: elige otro`);
|
|
319
|
+
}
|
|
320
|
+
return { puerto, abrir, ...anfitrion };
|
|
321
|
+
}
|
|
166
322
|
/**
|
|
167
323
|
* El ejecutor de turno REAL de la consola: cada línea de prosa corre sobre una `SesionReal`
|
|
168
324
|
* que sobrevive entre turnos (mismo agente, mismo hilo).
|
|
@@ -179,8 +335,31 @@ export function formatearBarra(p) {
|
|
|
179
335
|
* `alAbrirSesion` se llama UNA vez, justo tras `abrirSesionReal`, y es la costura por la
|
|
180
336
|
* que `entrarEnConsola` apunta la barra de estado al tracker de la sesión (que alimenta
|
|
181
337
|
* el agente; el de `createTokenTracker()` de cabecera se descarta al reasignarse).
|
|
338
|
+
*
|
|
339
|
+
* Exportada porque la consola web la monta tal cual: es exactamente lo que espera la
|
|
340
|
+
* opción `crearEjecutor` del vestíbulo (`web/servidor/vestibulo.ts`), que usa esa misma
|
|
341
|
+
* costura para poder CERRAR la sesión al cambiar de proyecto. Una segunda fábrica para la
|
|
342
|
+
* web habría sido un segundo sitio donde arreglar cada bug del ejecutor real.
|
|
343
|
+
*/
|
|
344
|
+
/**
|
|
345
|
+
* `checkpointerDeProyecto` entra por parámetro y no se importa aquí, y no es purismo: la
|
|
346
|
+
* consola de TERMINAL no lo pasa. Su hilo es un uuid nuevo en cada arranque y no hay índice
|
|
347
|
+
* de sesiones donde reanudarlo, así que persistirlo solo dejaría hilos que nadie puede
|
|
348
|
+
* volver a abrir engordando el fichero del proyecto. Persistir es de quien tiene identidad
|
|
349
|
+
* que reanudar — hoy, la consola web.
|
|
182
350
|
*/
|
|
183
|
-
function crearEjecutorReal(alAbrirSesion
|
|
351
|
+
export function crearEjecutorReal(alAbrirSesion, checkpointerDeProyecto, carpetaDeArtefactos,
|
|
352
|
+
/**
|
|
353
|
+
* La carpeta de los ADJUNTOS de una tarea — `/adjuntos/` para el agente, de solo lectura.
|
|
354
|
+
*
|
|
355
|
+
* Es un valor y no una función de `(raiz, hilo)` como las dos de arriba, y esa diferencia
|
|
356
|
+
* es el dato: los artefactos se derivan de la sesión, pero los adjuntos son de la TAREA, y
|
|
357
|
+
* la tarea solo la conoce quien abrió esta consola (el corredor). Por eso llega a la
|
|
358
|
+
* FÁBRICA: es una propiedad de la consola, igual que su checkpointer.
|
|
359
|
+
*
|
|
360
|
+
* Ausente en toda consola de persona y en toda tarea sin adjuntos.
|
|
361
|
+
*/
|
|
362
|
+
carpetaDeAdjuntos) {
|
|
184
363
|
let sesion;
|
|
185
364
|
let fuentesVistas;
|
|
186
365
|
let hiloVisto;
|
|
@@ -194,18 +373,81 @@ function crearEjecutorReal(alAbrirSesion) {
|
|
|
194
373
|
consolaReal.escribir("✗ falta algo imprescindible: xonecode no puede trabajar aquí.\n");
|
|
195
374
|
return;
|
|
196
375
|
}
|
|
376
|
+
const persistente = checkpointerDeProyecto?.(estado.raiz);
|
|
377
|
+
// Y si había fábrica pero no dio nada, el fichero no se pudo abrir: se DICE. Sin esto
|
|
378
|
+
// la sesión funcionaba igual y en silencio dejaba de recordar entre arranques, que es
|
|
379
|
+
// justo la clase de degradación muda que este repo evita. El motivo no viaja —puede
|
|
380
|
+
// llevar la ruta absoluta del home—, así que se dice QUÉ pasa, no dónde.
|
|
381
|
+
if (checkpointerDeProyecto !== undefined && persistente === undefined) {
|
|
382
|
+
consolaReal.escribir("aviso: no se pudo abrir la memoria del proyecto (.xonecode/checkpoint.sqlite); " +
|
|
383
|
+
"esta conversación no se recordará al reabrirla.\n");
|
|
384
|
+
}
|
|
197
385
|
sesion = await abrirSesionReal({
|
|
198
386
|
raiz: estado.raiz,
|
|
199
|
-
modelos: new Modelos(estado.fuentes),
|
|
387
|
+
modelos: new Modelos(estado.fuentes, proveedoresPersonalizados),
|
|
200
388
|
skills: new SkillsEnDisco(),
|
|
201
389
|
entorno,
|
|
390
|
+
// El hilo de la CONSOLA, no uno propio. Había dos ids para lo mismo —el que
|
|
391
|
+
// `/hilo` enseña y el que se generaba dentro— y solo coincidían tras un `/nuevo`.
|
|
392
|
+
// Y en la web ese hilo es el id de la sesión, que es lo que la hace reanudable.
|
|
393
|
+
hilo: estado.hilo,
|
|
394
|
+
// La memoria del agente entre arranques. Ausente —porque nadie pasó fábrica, o
|
|
395
|
+
// porque abrir el fichero falló— y `abrirSesionReal` cae en el `MemorySaver` de
|
|
396
|
+
// siempre: se pierde la memoria entre arranques, que es lo que ya pasaba.
|
|
397
|
+
...(persistente === undefined ? {} : { checkpointer: persistente }),
|
|
398
|
+
// Dónde caen los diagramas y demás salidas del agente. Con sesión (la web) van a la
|
|
399
|
+
// carpeta de ESA sesión; sin ella, `abrirSesionReal` cae a `.xonecode/artefactos`
|
|
400
|
+
// del proyecto — el hilo del terminal es un uuid nuevo por arranque, y una carpeta
|
|
401
|
+
// por hilo dejaría basura que nadie puede volver a abrir.
|
|
402
|
+
...(carpetaDeArtefactos === undefined
|
|
403
|
+
? {}
|
|
404
|
+
: { artefactos: carpetaDeArtefactos(estado.raiz, estado.hilo) }),
|
|
405
|
+
// Los adjuntos de la tarea, si esta consola es de una tarea con alguno. Ausente es
|
|
406
|
+
// «no hay», y el campo NO se pone: una cadena vacía montaría el cwd del proceso.
|
|
407
|
+
...(carpetaDeAdjuntos === undefined ? {} : { adjuntos: carpetaDeAdjuntos }),
|
|
408
|
+
// Si las escrituras de ESTE proyecto se aplican sin preguntar. Se pregunta en cada
|
|
409
|
+
// ronda —de ahí la función— porque `/aprobacion` lo cambia sin cerrar la sesión, y
|
|
410
|
+
// porque `interactivo` es de la consola que esté delante. Los settings se releen
|
|
411
|
+
// cada vez por lo mismo: la alternativa es que el cambio no surta efecto hasta
|
|
412
|
+
// reabrir, y la mitad de las veces eso sería en la dirección peligrosa.
|
|
413
|
+
// Cuántas rondas de aprobación admite un turno de ESTA consola. La pone quien la
|
|
414
|
+
// monta porque es quien sabe quién está detrás (ver `Consola.topeDeAprobaciones`);
|
|
415
|
+
// ausente y `abrirSesionReal` usa el `MAX_APPROVAL_ROUNDS` de siempre.
|
|
416
|
+
...(consolaReal.topeDeAprobaciones === undefined
|
|
417
|
+
? {}
|
|
418
|
+
: { topeDeRondas: consolaReal.topeDeAprobaciones }),
|
|
419
|
+
sinAprobacion: () => seAplicaSinAprobacion({
|
|
420
|
+
raiz: estado.raiz,
|
|
421
|
+
sinAprobacion: cargarSettings().settings.sinAprobacion,
|
|
422
|
+
// Del DISCO y por la raíz, no de `estado.fuentes.proyecto`: en la web ese
|
|
423
|
+
// campo no se rellena nunca, y la guarda habría dado «offline» para todos.
|
|
424
|
+
cloudstudio: cloudstudioDelProyecto(estado.raiz),
|
|
425
|
+
// `interactivo` SOLO no basta, y esto no es celo: `consolaWeb.ts` lo declara
|
|
426
|
+
// `true` a fuego, así que una pestaña que se cierra a mitad de turno seguiría
|
|
427
|
+
// auto-aprobando. «Hay alguien delante» ya está definido en este repo —
|
|
428
|
+
// `pedirDecisiones` lo calcula como `interactive && !eof()` (`cli/aprobar.ts`)—
|
|
429
|
+
// y esa es la misma cuenta. Con ella, al caerse el cable la escritura vuelve al
|
|
430
|
+
// camino de aprobación, donde el eof la rechaza.
|
|
431
|
+
interactivo: consolaReal.interactivo && !(consolaReal.eof?.() ?? false),
|
|
432
|
+
}),
|
|
433
|
+
// El simulador de verdad. Su ausencia en la máquina no se descubre aquí sino al
|
|
434
|
+
// verificar, y entonces se dice en el turno — sin tumbar nada.
|
|
435
|
+
verifier: new SimuladorVerifier(),
|
|
202
436
|
// Las aprobaciones entran por el `preguntar` de la propia consola: el turno para
|
|
203
437
|
// y pregunta DENTRO de la sesión, sin salir de ella ni montar otro lector de stdin.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
438
|
+
// Si la consola aporta su propio puerto (`aprobacionesTui`, el modal de la TUI),
|
|
439
|
+
// manda: mismo contrato que este `pedirDecisiones` por readline.
|
|
440
|
+
pedirAprobacion: async (lista, ficheros, diffs) => consolaReal.aprobacionesTui !== undefined
|
|
441
|
+
? consolaReal.aprobacionesTui(lista, ficheros, diffs)
|
|
442
|
+
: pedirDecisiones(lista, consolaReal.preguntar, consolaReal.escribir, {
|
|
443
|
+
interactive: consolaReal.interactivo,
|
|
444
|
+
// Sin esto, el Ctrl-D de un TTY llega como cadena vacía y `interactive:
|
|
445
|
+
// true` la aprueba: un EOF haciéndose pasar por el Enter que aprueba
|
|
446
|
+
// escrituras. `eof` lo distingue (`cli/aprobar.ts` lo explica entero).
|
|
447
|
+
eof: consolaReal.eof,
|
|
448
|
+
fichero: (id) => ficheros.get(id),
|
|
449
|
+
diff: (id) => diffs.get(id),
|
|
450
|
+
}),
|
|
209
451
|
});
|
|
210
452
|
alAbrirSesion(sesion);
|
|
211
453
|
// Recién construida: no hay cambio que detectar en esta primera vuelta.
|
|
@@ -216,7 +458,7 @@ function crearEjecutorReal(alAbrirSesion) {
|
|
|
216
458
|
// `/modelo` solo actualizó `estado.fuentes`; aquí es donde deja de ser un cambio
|
|
217
459
|
// cosmético: el agente se reconstruye con el modelo nuevo y el hilo SE CONSERVA.
|
|
218
460
|
if (JSON.stringify(estado.fuentes) !== JSON.stringify(fuentesVistas)) {
|
|
219
|
-
await sesion.cambiarModelos(new Modelos(estado.fuentes));
|
|
461
|
+
await sesion.cambiarModelos(new Modelos(estado.fuentes, proveedoresPersonalizados));
|
|
220
462
|
consolaReal.escribir(" (agente reconstruido con el modelo nuevo — el hilo se conserva)\n");
|
|
221
463
|
fuentesVistas = estado.fuentes;
|
|
222
464
|
}
|
|
@@ -231,7 +473,59 @@ function crearEjecutorReal(alAbrirSesion) {
|
|
|
231
473
|
hiloVisto = estado.hilo;
|
|
232
474
|
}
|
|
233
475
|
}
|
|
234
|
-
|
|
476
|
+
// La piel del turno: la de la consola si la aporta (la TUI), y el render stdio de
|
|
477
|
+
// siempre si no — mismo reparto que `ejecutarTurnoGuionizado`.
|
|
478
|
+
const resultado = await sesion.turno(peticion, consolaReal.piel?.() ?? crearPielStdio(consolaReal.escribir));
|
|
479
|
+
/**
|
|
480
|
+
* **Y se DEVUELVE lo que el turno sabe de sí mismo**, que antes se tiraba aquí.
|
|
481
|
+
*
|
|
482
|
+
* Era una deuda medida: `EjecutorDeTurno` devolvía `void`, así que el `cortadoPorTope`
|
|
483
|
+
* —y con él el hecho de que quedaron escrituras sin aplicar y el veredicto del
|
|
484
|
+
* verificador— morían en esta línea. Con las tareas de fondo aplicando escrituras
|
|
485
|
+
* solas, eso se veía en el kanban: cuatro ficheros escritos, una escritura abandonada,
|
|
486
|
+
* el verificador sin correr ni una vez, y la tarea diciendo «terminada». Quien decide
|
|
487
|
+
* si eso es entregable es `core/entrega.ts`; aquí solo se abre el canal.
|
|
488
|
+
*
|
|
489
|
+
* Se reenvían los campos de `ResultadoDeTurno` y NO el objeto entero: la bitácora y
|
|
490
|
+
* los `Cambio` son piezas de `agent/` que no tienen por qué cruzar hasta el corredor
|
|
491
|
+
* de tareas, y `cambios` lleva rutas del proyecto que nadie de ahí necesita.
|
|
492
|
+
*/
|
|
493
|
+
return {
|
|
494
|
+
verificador: resultado.verificador,
|
|
495
|
+
pendientes: resultado.pendientes,
|
|
496
|
+
...(resultado.hallazgos === undefined ? {} : { hallazgos: resultado.hallazgos }),
|
|
497
|
+
...(resultado.motivoSinVerificar === undefined
|
|
498
|
+
? {}
|
|
499
|
+
: { motivoSinVerificar: resultado.motivoSinVerificar }),
|
|
500
|
+
};
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Los adaptadores de disco y de red que dependen SOLO de la raíz del proyecto.
|
|
505
|
+
*
|
|
506
|
+
* Existe porque estaban escritos dos veces —una en la rama TUI y otra en la stdio— y eso
|
|
507
|
+
* ya divergió, medido: el arreglo del `entorno` caducado de `/connect-studio` entró en la
|
|
508
|
+
* rama TUI y no en la de stdio, así que por la consola de terminal seguía guardándose una
|
|
509
|
+
* `url` nueva encima de un `entorno` que nombraba el juego de credenciales de OTRO
|
|
510
|
+
* servidor. Es el mismo principio que ya rige aquí para los comandos («la consola y la
|
|
511
|
+
* shell no divergen porque comparten función»), aplicado a los adaptadores: con una sola
|
|
512
|
+
* definición, «las dos ramas hacen lo mismo» deja de ser algo que haya que recordar.
|
|
513
|
+
*/
|
|
514
|
+
export function adaptadoresDeProyecto(raiz,
|
|
515
|
+
/** Misma costura que `guardarEndpointYEntorno`: los tests no pueden leer el
|
|
516
|
+
* `~/.xonecode/settings.json` de quien corre la suite. */
|
|
517
|
+
leerSettings = cargarSettings) {
|
|
518
|
+
return {
|
|
519
|
+
guardarTemaDeProyecto: (tema) => guardarTemaDeProyecto(raiz, tema),
|
|
520
|
+
// `guardarEndpointYEntorno` y NUNCA `guardarCloudStudioDeProyecto` a pelo: el endpoint
|
|
521
|
+
// y el entorno tienen que escribirse juntos o la referencia se queda caducada.
|
|
522
|
+
guardarCloudStudioDeProyecto: (url, scopes) => guardarEndpointYEntorno(raiz, url, scopes, leerSettings),
|
|
523
|
+
guardarModoDeProyecto: (modo) => guardarModoDeProyecto(raiz, modo),
|
|
524
|
+
guardarProyectoCloudStudioDeProyecto: (proyecto) => guardarProyectoCloudStudioDeProyecto(raiz, proyecto),
|
|
525
|
+
ramasDeCloudStudio: crearListaDeRamas(raiz),
|
|
526
|
+
guardarRamaDeProyecto: (rama) => guardarRamaDeProyecto(raiz, rama),
|
|
527
|
+
guardarModelosDeProyecto: (papel, id) => guardarModelosDeProyecto(raiz, papel, id),
|
|
528
|
+
sincronizar: crearSincronizador(),
|
|
235
529
|
};
|
|
236
530
|
}
|
|
237
531
|
/**
|
|
@@ -309,6 +603,188 @@ async function preguntarDatos(consola) {
|
|
|
309
603
|
export function crearCompleterDelProyecto(raiz, escribir = escribirEnStdout) {
|
|
310
604
|
return crearCompleter(escribir, () => ficherosDelProyecto(raiz));
|
|
311
605
|
}
|
|
606
|
+
const PIEZAS_DE_SINCRONIZACION_REALES = {
|
|
607
|
+
leerConfig: cargar,
|
|
608
|
+
leerSettings: cargarSettings,
|
|
609
|
+
sesion: sesionCloudStudio,
|
|
610
|
+
cliente: clienteCloudStudio,
|
|
611
|
+
descargar: descargarProyecto,
|
|
612
|
+
preparar: prepararRepo,
|
|
613
|
+
pendientes: cambiosPendientes,
|
|
614
|
+
limpio: arbolLimpio,
|
|
615
|
+
sinCommitear,
|
|
616
|
+
subirProyecto: subir,
|
|
617
|
+
};
|
|
618
|
+
/**
|
|
619
|
+
* Qué entorno de `settings.json` sirve esta URL.
|
|
620
|
+
*
|
|
621
|
+
* Existe porque el `entorno` del `config.json` puede FALTAR o quedarse viejo, y las dos
|
|
622
|
+
* cosas mandaban el token al hueco equivocado:
|
|
623
|
+
*
|
|
624
|
+
* - Falta en todo proyecto dado de alta desde la terminal, que no elige entorno. Esos
|
|
625
|
+
* proyectos viven en `legado` hasta que alguien registra el oficial en el vestíbulo, y
|
|
626
|
+
* entonces `adoptarLegadoSiProcede` MUEVE ese juego a `webstudio` y los deja sin tokens:
|
|
627
|
+
* reautenticaban en silencio. Casando la URL leen el hueco adoptado y no se enteran.
|
|
628
|
+
* - Se queda viejo cuando `/connect-studio` reescribe la `url` del proyecto: sin volver a
|
|
629
|
+
* resolver, el `entorno` anterior seguiría nombrando el juego de OTRO servidor.
|
|
630
|
+
*
|
|
631
|
+
* La comparación normaliza con `URL` y cae a la comparación literal si alguna cadena no
|
|
632
|
+
* parsea. Qué absorbe esa normalización, MEDIDO y no supuesto: el case del host y el puerto
|
|
633
|
+
* por omisión explícito (`:443`) sí casan; una barra final de más, un `?` vacío, otro puerto,
|
|
634
|
+
* `http` frente a `https` y otro case en la RUTA no casan. Que no casen es benigno y no
|
|
635
|
+
* accidental: el fallo devuelve `undefined` —y cae en `legado`, donde ese proyecto ya
|
|
636
|
+
* estaba— en vez de resolver al id de otro entorno. Ninguna variante cruza a un id ajeno, y
|
|
637
|
+
* ESA es la propiedad de la que depende no mandarle a un servidor el token de otro.
|
|
638
|
+
*/
|
|
639
|
+
export function entornoDeUrl(url, entornos) {
|
|
640
|
+
const canonica = (valor) => {
|
|
641
|
+
try {
|
|
642
|
+
return new URL(valor).toString();
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
return valor;
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
const buscada = canonica(url);
|
|
649
|
+
return entornos.find((entorno) => canonica(entorno.url) === buscada)?.id;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* El adaptador de `/connect-studio`: guarda el endpoint Y vuelve a resolver el `entorno`.
|
|
653
|
+
*
|
|
654
|
+
* Las dos escrituras van juntas a propósito. Separarlas es lo que dejaba el `entorno`
|
|
655
|
+
* caducado apuntando al juego de credenciales de otro servidor — el fallo peor de todo
|
|
656
|
+
* este arreglo, porque manda un token válido a un endpoint que no lo emitió.
|
|
657
|
+
*/
|
|
658
|
+
export function guardarEndpointYEntorno(raiz, url, scopes, leerSettings = cargarSettings) {
|
|
659
|
+
const guardado = guardarCloudStudioDeProyecto(raiz, url, scopes);
|
|
660
|
+
// Sin entorno registrado para esa URL se BORRA la clave: el OAuth de esa URL cae en
|
|
661
|
+
// `legado`, y decir «webstudio» sería nombrar un juego que no es el suyo.
|
|
662
|
+
guardarEntornoDeProyecto(raiz, entornoDeUrl(guardado.url, leerSettings().settings.entornos));
|
|
663
|
+
return guardado;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* El endpoint de CloudStudio del proyecto YA guardado, o `undefined` si `/sync` no puede
|
|
667
|
+
* seguir: ni «no hay `cloudstudio`» ni «falta el proyecto o la rama» (un `/connect-studio`
|
|
668
|
+
* sin completar el alta) se distinguen hoy — las dos caen en el mismo texto genérico de
|
|
669
|
+
* `crearSincronizador`. Si algún día hace falta un mensaje más fino, es aquí donde separar
|
|
670
|
+
* los dos casos, sin tocar a quien llama.
|
|
671
|
+
*/
|
|
672
|
+
function cloudStudioDeProyecto(leerConfig, leerSettings, raiz) {
|
|
673
|
+
const proyectoConfig = leerConfig(raiz).config.proyecto;
|
|
674
|
+
const cloudstudio = proyectoConfig?.cloudstudio;
|
|
675
|
+
if (cloudstudio === undefined)
|
|
676
|
+
return undefined;
|
|
677
|
+
if (cloudstudio.proyecto === undefined || cloudstudio.rama === undefined)
|
|
678
|
+
return undefined;
|
|
679
|
+
// `entorno` viaja junto a la URL porque decide de QUÉ juego de credenciales se lee
|
|
680
|
+
// (`porEntorno[id]`, `agent/cloudstudioMcp.ts`). Si al `config.json` le falta —todo
|
|
681
|
+
// proyecto dado de alta desde la terminal— se resuelve casando la URL contra los
|
|
682
|
+
// entornos registrados: ver `entornoDeUrl`. Si tampoco así aparece, `undefined` cae en
|
|
683
|
+
// `legado`, que es donde ese proyecto tiene sus tokens.
|
|
684
|
+
return {
|
|
685
|
+
url: cloudstudio.url,
|
|
686
|
+
scopes: cloudstudio.scopes,
|
|
687
|
+
proyecto: cloudstudio.proyecto,
|
|
688
|
+
rama: cloudstudio.rama,
|
|
689
|
+
entorno: proyectoConfig?.entorno ?? entornoDeUrl(cloudstudio.url, leerSettings().settings.entornos),
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* El adaptador real de `consola.sincronizar`: abre una sesión MCP VIVA con
|
|
694
|
+
* `sesionCloudStudio` —`conectarCloudStudio` cierra su transporte al terminar y sirve
|
|
695
|
+
* para el alta, pero no para operar— y la cierra siempre en un `finally`, tanto si la
|
|
696
|
+
* operación termina bien como si revienta.
|
|
697
|
+
*
|
|
698
|
+
* El orden dentro de «bajar» es crítico: `descargarProyecto` (que ya retira las vistas
|
|
699
|
+
* aplanadas) y DESPUÉS `prepararRepo`. Al revés, git vería esos `.xml` como borrados y la
|
|
700
|
+
* primera subida los borraría en Studio.
|
|
701
|
+
*
|
|
702
|
+
* Las dos direcciones se niegan con el árbol sucio ANTES de abrir sesión ni tocar
|
|
703
|
+
* CloudStudio: «subir» porque se sube el estado de un commit y no un borrador (para que
|
|
704
|
+
* mover la ref signifique algo), y «bajar» porque la descarga SOBRESCRIBE el disco y el
|
|
705
|
+
* baseline se construye después — sin un commit debajo, el trabajo local no se recupera.
|
|
706
|
+
*/
|
|
707
|
+
export function crearSincronizador(piezas = PIEZAS_DE_SINCRONIZACION_REALES) {
|
|
708
|
+
return async (accion, raiz, politicaDeAprobacion, informar = () => { }) => {
|
|
709
|
+
const config = cloudStudioDeProyecto(piezas.leerConfig, piezas.leerSettings, raiz);
|
|
710
|
+
if (config === undefined) {
|
|
711
|
+
return { tipo: "texto", texto: "este proyecto no es cloud (o le falta el proyecto/rama del alta)\n" };
|
|
712
|
+
}
|
|
713
|
+
// Las DOS direcciones exigen árbol limpio, y «bajar» no menos que «subir»: la
|
|
714
|
+
// descarga SOBRESCRIBE el disco (`extraerZipBase64` escribe encima, no fusiona) y el
|
|
715
|
+
// baseline se construye DESPUÉS, así que sin un commit debajo el trabajo local no se
|
|
716
|
+
// recupera de ninguna forma. Es alcanzable sin escribir `/sync`: el alta llama a
|
|
717
|
+
// `sincronizar("bajar", …)`, así que quien arranca xonecode en una carpeta con
|
|
718
|
+
// trabajo y elige modo cloud lo perdería. La guarda va ANTES de abrir sesión MCP.
|
|
719
|
+
if (accion !== "estado" && !(await piezas.limpio(raiz))) {
|
|
720
|
+
// `sinCommitear` y no `pendientes`: la pregunta aquí es «qué falta por COMMITEAR»,
|
|
721
|
+
// y además `pendientes` compara contra la ref de seguimiento, que en el alta de una
|
|
722
|
+
// carpeta que todavía no es repo no existe siquiera.
|
|
723
|
+
return { tipo: "arbol-sucio", accion, pendientes: await piezas.sinCommitear(raiz) };
|
|
724
|
+
}
|
|
725
|
+
const sesion = await piezas.sesion(config.url, { scopes: config.scopes, entornoId: config.entorno });
|
|
726
|
+
try {
|
|
727
|
+
const puerto = piezas.cliente(sesion.invocar, config.proyecto.nombre);
|
|
728
|
+
if (accion === "bajar") {
|
|
729
|
+
// `ramaOrigen` fija explícitamente de qué rama se baja (`descargarProyecto` la
|
|
730
|
+
// deja posicionada y la restaura al terminar): sin esto se traería lo que
|
|
731
|
+
// estuviera ACTIVO en la sesión de Studio, y una subida posterior firmaría ese
|
|
732
|
+
// contenido como si fuera de `config.rama`, en silencio.
|
|
733
|
+
const bajada = await piezas.descargar({ puerto, raiz, proyecto: config.proyecto, ramaOrigen: config.rama, informar });
|
|
734
|
+
await piezas.preparar(raiz, bajada.rama, informar);
|
|
735
|
+
return { tipo: "texto", texto: `bajados ${bajada.descargados.length} ficheros (${bajada.via})\n` };
|
|
736
|
+
}
|
|
737
|
+
if (accion === "subir") {
|
|
738
|
+
const informe = await piezas.subirProyecto({
|
|
739
|
+
puerto,
|
|
740
|
+
raiz,
|
|
741
|
+
// La MISMA rama de la que se bajó: lo que se pide es aplicar cambios y subirlos a
|
|
742
|
+
// la rama del proyecto, no a una rama de trabajo aparte que nadie mira en Studio.
|
|
743
|
+
ramaOrigen: config.rama,
|
|
744
|
+
proyecto: config.proyecto,
|
|
745
|
+
// Fail-closed si alguien llamara a `sincronizar("subir", ...)` sin política (el
|
|
746
|
+
// comando `/sync` siempre construye una): sin ella, no autoriza NADA en vez de
|
|
747
|
+
// asumir que sí. El hueco lo documenta `core/cloudstudio.ts#PoliticaDeAprobacion`.
|
|
748
|
+
politicaDeAprobacion: politicaDeAprobacion ?? (async () => false),
|
|
749
|
+
informar,
|
|
750
|
+
});
|
|
751
|
+
return { tipo: "texto", texto: `subidos ${informe.ok.length}, fallaron ${informe.fallos.length}\n` };
|
|
752
|
+
}
|
|
753
|
+
const pendientes = await piezas.pendientes(raiz, config.rama);
|
|
754
|
+
return { tipo: "texto", texto: `rama ${config.rama}: ${pendientes.length} ficheros por subir\n` };
|
|
755
|
+
}
|
|
756
|
+
finally {
|
|
757
|
+
await sesion.cerrar();
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* El adaptador real de `consola.ramasDeCloudStudio`: mismo patrón de sesión VIVA +
|
|
763
|
+
* `finally`. Se usa durante el alta, con `proyecto` y `url` ya guardados pero la rama
|
|
764
|
+
* todavía sin elegir — por eso lee el config en cada llamada en vez de recibirlo cerrado.
|
|
765
|
+
*/
|
|
766
|
+
export function crearListaDeRamas(raiz, piezas = PIEZAS_DE_SINCRONIZACION_REALES) {
|
|
767
|
+
return async (proyectoNombre) => {
|
|
768
|
+
const proyectoConfig = piezas.leerConfig(raiz).config.proyecto;
|
|
769
|
+
const cloudstudio = proyectoConfig?.cloudstudio;
|
|
770
|
+
if (cloudstudio?.url === undefined)
|
|
771
|
+
return [];
|
|
772
|
+
// Mismo motivo, y misma resolución por URL, que en `cloudStudioDeProyecto`: sin el id
|
|
773
|
+
// del entorno un proyecto ya migrado a `webstudio` volvería a autenticar bajo `legado`.
|
|
774
|
+
const sesion = await piezas.sesion(cloudstudio.url, {
|
|
775
|
+
scopes: cloudstudio.scopes,
|
|
776
|
+
entornoId: proyectoConfig?.entorno ?? entornoDeUrl(cloudstudio.url, piezas.leerSettings().settings.entornos),
|
|
777
|
+
});
|
|
778
|
+
try {
|
|
779
|
+
const puerto = piezas.cliente(sesion.invocar, proyectoNombre);
|
|
780
|
+
await puerto.abrir(proyectoNombre);
|
|
781
|
+
return await puerto.ramas();
|
|
782
|
+
}
|
|
783
|
+
finally {
|
|
784
|
+
await sesion.cerrar();
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
}
|
|
312
788
|
export async function entrarEnConsola(fuentes, raiz = process.cwd(), escribir = escribirEnStdout, inspeccionarProyecto = inspeccionar, crearRl = () => readline.createInterface({
|
|
313
789
|
input: process.stdin,
|
|
314
790
|
output: process.stdout,
|
|
@@ -320,7 +796,70 @@ export async function entrarEnConsola(fuentes, raiz = process.cwd(), escribir =
|
|
|
320
796
|
prompt: PROMPT,
|
|
321
797
|
}), guion = false,
|
|
322
798
|
/** Costura de test del «¿hay alguien al otro lado?»: sin TTY no se pregunta nada. */
|
|
323
|
-
interactivo = process.stdin.isTTY === true
|
|
799
|
+
interactivo = process.stdin.isTTY === true,
|
|
800
|
+
/**
|
|
801
|
+
* ¿Arranca la TUI? Costura de test: los tests corren sin TTY y pasan `false` (o no
|
|
802
|
+
* la pasan, y `decidirTui` sin banderas cae en el `isTTY` del proceso). En la rama
|
|
803
|
+
* TUI este parámetro de `interactivo` no se usa: la TUI es interactiva por diseño.
|
|
804
|
+
*/
|
|
805
|
+
usarTui = decidirTui(),
|
|
806
|
+
/** ¿Ratón en la TUI? Solo la rama TUI lo mira. */
|
|
807
|
+
raton = true,
|
|
808
|
+
/** Adaptadores compartidos por stdio y TUI; `main` construye una sola instancia real. */
|
|
809
|
+
dependencias = {
|
|
810
|
+
catalogoModelos: new CatalogoModelos(undefined, undefined, proveedoresPersonalizados),
|
|
811
|
+
guardarModeloGlobal,
|
|
812
|
+
conectarCloudStudio: (url, scopes, informar) =>
|
|
813
|
+
// El entorno se resuelve por URL, no se omite: sin esto, un `/connect-studio` sobre
|
|
814
|
+
// un proyecto del vestíbulo abría OAuth en `legado` —un hueco que `/sync` no lee— y
|
|
815
|
+
// dejaba al proyecto reautenticando en cada sincronización.
|
|
816
|
+
conectarCloudStudio(url, { informar, scopes, entornoId: entornoDeUrl(url, cargarSettings().settings.entornos) }),
|
|
817
|
+
}) {
|
|
818
|
+
// La elección de modelo y las credenciales se hidratan ANTES de decidir la piel:
|
|
819
|
+
// stdio y TUI reciben exactamente las mismas fuentes y el catálogo leerá la clave
|
|
820
|
+
// ya aplicada solo cuando `/modelos` provoque su primera consulta.
|
|
821
|
+
const cargado = cargar(raiz);
|
|
822
|
+
aplicarAuth(cargado.auth);
|
|
823
|
+
// El tema es una preferencia del proyecto: un config global no puede cambiar cómo se
|
|
824
|
+
// presenta otro repositorio. Un valor manual desconocido conserva XOne por omisión.
|
|
825
|
+
seleccionarTema("xone");
|
|
826
|
+
const temaDeProyecto = cargado.config.proyecto?.tema;
|
|
827
|
+
if (temaDeProyecto !== undefined && esTema(temaDeProyecto))
|
|
828
|
+
seleccionarTema(temaDeProyecto);
|
|
829
|
+
// `let`: el asistente de cuenta y el alta de proyecto (más abajo, ambos solo con TTY)
|
|
830
|
+
// pueden escribir modelo global o de proyecto, y la cabecera no puede quedarse leyendo
|
|
831
|
+
// las fuentes de ANTES de esa escritura.
|
|
832
|
+
let fuentesHidratadas = {
|
|
833
|
+
...fuentes,
|
|
834
|
+
proyecto: cargado.config.proyecto,
|
|
835
|
+
global: cargado.config.global,
|
|
836
|
+
};
|
|
837
|
+
if (usarTui) {
|
|
838
|
+
// La TUI es la MISMA consola: `entrarEnConsola` no la duplica, le entrega las
|
|
839
|
+
// piezas que son de esta capa (la inspección, el asistente de creación, el
|
|
840
|
+
// ejecutor real, el tope compartido) y el montaje vive en `tui/correrTui.ts`,
|
|
841
|
+
// que no puede importar de aquí porque sería un ciclo.
|
|
842
|
+
//
|
|
843
|
+
// Import DINÁMICO: `correrTui.ts` arrastra ink y react, y un import estático los
|
|
844
|
+
// cargaría también en `run`, `describe`, `config` y en cada pipe — que no los
|
|
845
|
+
// necesitan ni los van a pintar. Solo la rama TUI paga el módulo.
|
|
846
|
+
const { correrConsolaTui } = await import("./tui/correrTui.js");
|
|
847
|
+
return correrConsolaTui({
|
|
848
|
+
fuentes: fuentesHidratadas,
|
|
849
|
+
raiz,
|
|
850
|
+
guion,
|
|
851
|
+
...dependencias,
|
|
852
|
+
...adaptadoresDeProyecto(raiz),
|
|
853
|
+
inspeccionarProyecto,
|
|
854
|
+
ofrecer: ofrecerCrearProyecto,
|
|
855
|
+
crearEjecutor: guion ? undefined : crearEjecutorReal,
|
|
856
|
+
topeDe: crearTopeDelModelo(raiz),
|
|
857
|
+
raton,
|
|
858
|
+
// Una TUI ya implica interacción real. No se vuelve a consultar isTTY dentro de
|
|
859
|
+
// Ink: algunos emuladores integrados lo exponen falso tras capturar stdin.
|
|
860
|
+
asistenteInicial: true,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
324
863
|
// `let` porque el asistente de creación puede cambiarlo TODO: si el usuario acepta,
|
|
325
864
|
// la carpeta pasa de «0 colls, sin app.xml» a proyecto de verdad, y la cabecera se
|
|
326
865
|
// re-inspecciona para no mentir en la primera línea que se ve.
|
|
@@ -330,7 +869,7 @@ interactivo = process.stdin.isTTY === true) {
|
|
|
330
869
|
// con /describe. `let` porque /modelo <p>/<m> la cambia EN CALIENTE dentro de la sesión.
|
|
331
870
|
let modeloTrabajo;
|
|
332
871
|
{
|
|
333
|
-
const { proveedor, modelo } = resolver(
|
|
872
|
+
const { proveedor, modelo } = resolver(fuentesHidratadas).trabajo;
|
|
334
873
|
modeloTrabajo = `${proveedor}/${modelo}`;
|
|
335
874
|
}
|
|
336
875
|
/**
|
|
@@ -347,28 +886,11 @@ interactivo = process.stdin.isTTY === true) {
|
|
|
347
886
|
// al arrancar y tras cada /nuevo o /modelo. La lista de comandos se GENERA recorriendo
|
|
348
887
|
// el registro — una lista escrita a mano se queda vieja en cuanto alguien añade uno.
|
|
349
888
|
/**
|
|
350
|
-
* El tope de la ventana del modelo ACTUAL
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
* en caliente con /modelo, y un tope del modelo equivocado es un porcentaje que miente.
|
|
354
|
-
* Si el id no parsea —no debería, viene de `resolver`—, no hay tope y no pasa nada.
|
|
889
|
+
* El tope de la ventana del modelo ACTUAL. La lógica vive en `crearTopeDelModelo`
|
|
890
|
+
* (módulo, y compartida con la TUI): la barra la llama en cada emisión porque
|
|
891
|
+
* /modelo cambia el modelo en caliente.
|
|
355
892
|
*/
|
|
356
|
-
const topeDelModelo = (
|
|
357
|
-
try {
|
|
358
|
-
const { proveedor, modelo } = parsear(id);
|
|
359
|
-
const { config } = cargar(raiz);
|
|
360
|
-
// Misma resolución que /config (`topeResuelto`): proyecto gana a global, y la
|
|
361
|
-
// tabla es el último recurso. El origen aquí no se mira, pero así no puede
|
|
362
|
-
// divergir entre lo que la barra calcula y lo que /config declara.
|
|
363
|
-
return topeResuelto(proveedor, modelo, {
|
|
364
|
-
proyecto: config.proyecto?.contextos,
|
|
365
|
-
global: config.global?.contextos,
|
|
366
|
-
})?.tope;
|
|
367
|
-
}
|
|
368
|
-
catch {
|
|
369
|
-
return undefined;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
893
|
+
const topeDelModelo = crearTopeDelModelo(raiz);
|
|
372
894
|
/**
|
|
373
895
|
* La barra de estado, al estilo de la de opencode: lo que uno necesita tener delante sin
|
|
374
896
|
* pedirlo. En stdio no se puede clavar al fondo de la pantalla —eso pide control de
|
|
@@ -408,15 +930,11 @@ interactivo = process.stdin.isTTY === true) {
|
|
|
408
930
|
lineas: conPrompt(rl, escribir, barraDeEstado),
|
|
409
931
|
escribir: (t) => {
|
|
410
932
|
escribir(t);
|
|
411
|
-
// La
|
|
412
|
-
//
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
// "hilo nuevo: <id>\n" → /nuevo
|
|
417
|
-
const comoModelo = /^modelo \(los tres papeles\): (.+)\n$/.exec(t);
|
|
418
|
-
if (comoModelo) {
|
|
419
|
-
modeloTrabajo = comoModelo[1];
|
|
933
|
+
// La TUI usa el mismo parser: un acuse de los tres papeles, o el del papel
|
|
934
|
+
// `trabajo` individual de `/modelos`, actualiza la cabecera sin duplicar regex.
|
|
935
|
+
const acuse = modeloDeAcuse(t);
|
|
936
|
+
if (acuse !== undefined && (acuse.papel === undefined || acuse.papel === "trabajo")) {
|
|
937
|
+
modeloTrabajo = acuse.modelo;
|
|
420
938
|
escribir(lineaDeEstado());
|
|
421
939
|
}
|
|
422
940
|
else if (/^hilo nuevo: /.test(t)) {
|
|
@@ -425,8 +943,39 @@ interactivo = process.stdin.isTTY === true) {
|
|
|
425
943
|
},
|
|
426
944
|
preguntar: crearPreguntar(rl),
|
|
427
945
|
interactivo,
|
|
946
|
+
eof: crearDetectorDeEof(rl),
|
|
428
947
|
leerSecreto: crearLeerSecreto(rl),
|
|
948
|
+
catalogoModelos: dependencias.catalogoModelos,
|
|
949
|
+
guardarModeloGlobal: dependencias.guardarModeloGlobal,
|
|
950
|
+
conectarCloudStudio: dependencias.conectarCloudStudio,
|
|
951
|
+
...adaptadoresDeProyecto(raiz),
|
|
429
952
|
};
|
|
953
|
+
// Los dobles de readline de tests (y una tubería) pueden declararse interactivos para
|
|
954
|
+
// probar otro diálogo, pero no son un terminal en el que abrir OAuth. El asistente de
|
|
955
|
+
// arranque es exclusivamente de una sesión TTY real.
|
|
956
|
+
if (process.stdin.isTTY === true) {
|
|
957
|
+
// 1. Cuenta: proveedor y modelo, SOLO si nadie eligió nunca (mira `origen`, no un
|
|
958
|
+
// flag de «primer arranque» aparte). Antes que el proyecto: es configuración de la
|
|
959
|
+
// persona, no de la carpeta.
|
|
960
|
+
await asistenteDeModelo(consola, {
|
|
961
|
+
origenDeTrabajo: resolver(fuentesHidratadas).trabajo.origen,
|
|
962
|
+
hayCredencial: (proveedor) => hayCredencial(proveedor, raiz),
|
|
963
|
+
guardarCredencial: (proveedor, clave) => guardarCredencial(proveedor, clave),
|
|
964
|
+
// La clave se PRUEBA antes de escribirse: esto la pone en el proceso (que es de
|
|
965
|
+
// donde la lee `CatalogoModelos`) sin tocar `auth.json`.
|
|
966
|
+
aplicarCredencial: aplicarCredencialAlProceso,
|
|
967
|
+
});
|
|
968
|
+
// 2-3-4. Modo, proyecto de CloudStudio + rama + descarga, y modelo propio opcional.
|
|
969
|
+
await configurarModoInicial(raiz, consola);
|
|
970
|
+
// El asistente y el alta pueden haber escrito modelo global o de proyecto (y, con
|
|
971
|
+
// cloud, ficheros nuevos): se relee TODO antes de fijar la cabecera, o mentiría sobre
|
|
972
|
+
// lo que se acaba de elegir.
|
|
973
|
+
const recargado = cargar(raiz);
|
|
974
|
+
fuentesHidratadas = { ...fuentesHidratadas, proyecto: recargado.config.proyecto, global: recargado.config.global };
|
|
975
|
+
entorno = await inspeccionarProyecto(raiz);
|
|
976
|
+
const elegidoTrasAlta = resolver(fuentesHidratadas).trabajo;
|
|
977
|
+
modeloTrabajo = `${elegidoTrasAlta.proveedor}/${elegidoTrasAlta.modelo}`;
|
|
978
|
+
}
|
|
430
979
|
// El asistente de creación: la única escritura fuera de un turno del agente, y
|
|
431
980
|
// por eso la única que pregunta ANTES de escribir. Si se crea, la carpeta ya ES
|
|
432
981
|
// un proyecto XOne y `entorno` se re-inspecciona; si no, la consola sigue viva
|
|
@@ -440,7 +989,11 @@ interactivo = process.stdin.isTTY === true) {
|
|
|
440
989
|
}
|
|
441
990
|
escribir(cabecera());
|
|
442
991
|
// Mismo patrón de prefijo que run.ts y el manejador /nuevo de consola.ts.
|
|
443
|
-
const estado = {
|
|
992
|
+
const estado = {
|
|
993
|
+
hilo: `xonecode-${randomUUID()}`,
|
|
994
|
+
raiz,
|
|
995
|
+
fuentes: fuentesHidratadas,
|
|
996
|
+
};
|
|
444
997
|
// `--guion` conserva el agente de pega (el valor por omisión de `correrConsola`): es lo
|
|
445
998
|
// que permite ver la consola correr sin gastar. Sin ella, el ejecutor real corre cada
|
|
446
999
|
// línea de prosa sobre una sesión que se abre en el PRIMER turno y sobrevive a los demás.
|
|
@@ -449,6 +1002,21 @@ interactivo = process.stdin.isTTY === true) {
|
|
|
449
1002
|
: crearEjecutorReal((sesion) => {
|
|
450
1003
|
tracker = sesion.tracker;
|
|
451
1004
|
});
|
|
1005
|
+
// Una memoria .xonecode pertenece al PROYECTO y significa que hay trabajo que
|
|
1006
|
+
// retomar. El saludo de primera visita no debe ocultar ese contexto. `--guion` no
|
|
1007
|
+
// simula esta lectura: no hay modelo real que pueda analizarlo.
|
|
1008
|
+
if (hayEstadoDeProyecto(raiz) && !guion) {
|
|
1009
|
+
consola.escribir(MENSAJE_REANUDANDO);
|
|
1010
|
+
try {
|
|
1011
|
+
await ejecutar(PETICION_REANUDAR_PROYECTO, estado, consola);
|
|
1012
|
+
}
|
|
1013
|
+
catch (e) {
|
|
1014
|
+
consola.escribir(`${describirError(e)}\n`);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
consola.escribir(MENSAJE_BIENVENIDA);
|
|
1019
|
+
}
|
|
452
1020
|
const codigo = await correrConsola(consola, estado, ejecutar);
|
|
453
1021
|
// Siempre, tanto si se sale por /salir como por EOF: sin rl.close() el proceso queda
|
|
454
1022
|
// escuchando stdin y `xonecode` no vuelve nunca a la shell.
|
|
@@ -457,26 +1025,117 @@ interactivo = process.stdin.isTTY === true) {
|
|
|
457
1025
|
}
|
|
458
1026
|
export async function main(argv) {
|
|
459
1027
|
const [comando, ...resto] = argv;
|
|
460
|
-
// Las banderas de la consola (`--guion` y las de modelo) no son subcomandos:
|
|
461
|
-
// `xonecode --guion` entra en la consola igual que `xonecode` a secas, pero con el
|
|
462
|
-
// agente de pega. `--help`/`-h` NO entran aquí: son el subcomando de ayuda de abajo.
|
|
463
|
-
// El resto de banderas (`--real`, `--json`, …) pertenecen a otros subcomandos y siguen
|
|
464
|
-
// cayendo en «no conozco el comando».
|
|
465
|
-
// `--guion` se quita del argv ANTES de extraerBanderasDeModelo: no es una bandera de
|
|
466
|
-
// modelo y no debe llegar como `resto` a ninguna parte.
|
|
467
|
-
if (!comando || comando === "--guion" || comando.startsWith("--modelo")) {
|
|
468
|
-
const guion = argv.includes("--guion");
|
|
469
|
-
// extraerBanderasDeModelo también con argv vacío, para que fuentes.entorno
|
|
470
|
-
// .XONECODE_MODELO se rellene igual que en todos los demás subcomandos: la consola
|
|
471
|
-
// no puede ser la única vía que ignora esa variable.
|
|
472
|
-
const { fuentes } = extraerBanderasDeModelo(argv.filter((a) => a !== "--guion"));
|
|
473
|
-
return entrarEnConsola(fuentes, undefined, undefined, undefined, undefined, guion);
|
|
474
|
-
}
|
|
475
|
-
if (comando === "--help" || comando === "-h") {
|
|
476
|
-
process.stdout.write(AYUDA);
|
|
477
|
-
return 0;
|
|
478
|
-
}
|
|
479
1028
|
try {
|
|
1029
|
+
// Las banderas de la consola (`--guion` y las de modelo) no son subcomandos:
|
|
1030
|
+
// `xonecode --guion` entra en la consola igual que `xonecode` a secas, pero con el
|
|
1031
|
+
// agente de pega. `--help`/`-h` NO entran aquí: son el subcomando de ayuda de abajo.
|
|
1032
|
+
// El resto de banderas (`--real`, `--json`, …) pertenecen a otros subcomandos y siguen
|
|
1033
|
+
// cayendo en «no conozco el comando».
|
|
1034
|
+
// `--guion` se quita del argv ANTES de extraerBanderasDeModelo: no es una bandera de
|
|
1035
|
+
// modelo y no debe llegar como `resto` a ninguna parte.
|
|
1036
|
+
if (!comando ||
|
|
1037
|
+
comando === "--guion" ||
|
|
1038
|
+
comando === "--tui" ||
|
|
1039
|
+
comando === "--no-tui" ||
|
|
1040
|
+
comando === "--sin-raton" ||
|
|
1041
|
+
// Las cuatro de la consola web. Sin ellas, `xonecode --web` cae en «no conozco el
|
|
1042
|
+
// comando» y sale 64 antes de que `decidirPiel` llegue a decidir nada — medido.
|
|
1043
|
+
comando === "--web" ||
|
|
1044
|
+
comando === "--cli" ||
|
|
1045
|
+
comando === "--no-abrir" ||
|
|
1046
|
+
comando === "--puerto" ||
|
|
1047
|
+
comando.startsWith("--modelo")) {
|
|
1048
|
+
// La bifurcación va ANTES del alta de cuatro pasos de `entrarEnConsola`: si no,
|
|
1049
|
+
// `xonecode` con TTY preguntaría el proveedor en el terminal y DESPUÉS abriría un
|
|
1050
|
+
// navegador para volver a preguntarlo.
|
|
1051
|
+
if (decidirPiel(argv) === "web") {
|
|
1052
|
+
const { puerto, abrir, anfitrion } = parsearOpcionesWeb(argv);
|
|
1053
|
+
// Import DINÁMICO, por el mismo motivo que el de la TUI: esta rama arrastra el
|
|
1054
|
+
// vestíbulo entero (grafo del agente, MCP, sesiones) y `run`, `describe` y
|
|
1055
|
+
// cualquier tubería no lo necesitan ni lo van a levantar.
|
|
1056
|
+
const { arrancarConsolaWeb } = await import("../web/servidor/arranque.js");
|
|
1057
|
+
return await arrancarConsolaWeb({
|
|
1058
|
+
puerto,
|
|
1059
|
+
...(anfitrion === undefined ? {} : { anfitrion }),
|
|
1060
|
+
abrir,
|
|
1061
|
+
cwd: process.cwd(),
|
|
1062
|
+
guion: argv.includes("--guion"),
|
|
1063
|
+
// Las dos piezas de ESTA capa que el vestíbulo necesita, pasadas y no importadas
|
|
1064
|
+
// desde allí: `arranque.ts` importando `main.ts` sería un ciclo entre el
|
|
1065
|
+
// despachador y la piel que monta. Y no son opcionales de verdad — sin
|
|
1066
|
+
// `crearEjecutor` el vestíbulo cae en el turno GUIONIZADO, y sin
|
|
1067
|
+
// `dependenciasDeProyecto` no hay `/sync`, o sea que la descarga del alta no
|
|
1068
|
+
// bajaría nada. Las dos cosas serían mudas.
|
|
1069
|
+
// Con checkpointer PERSISTENTE, al revés que la consola de terminal: aquí cada
|
|
1070
|
+
// conversación tiene id en el índice, así que su hilo se puede reanudar — y esa
|
|
1071
|
+
// es la diferencia entre reabrir y releer.
|
|
1072
|
+
// Y la carpeta de ADJUNTOS cuando la apertura es de una tarea con alguno: el
|
|
1073
|
+
// vestíbulo la reenvía desde `abrirParaTarea`, que es quien la recibe del
|
|
1074
|
+
// corredor — el único que sabe de qué tarea se trata.
|
|
1075
|
+
crearEjecutor: (alAbrir, opcionesDeConsola) => crearEjecutorReal(alAbrir, crearCheckpointerDeProyecto, carpetaDeArtefactosDeSesion, opcionesDeConsola?.adjuntos),
|
|
1076
|
+
// El tope de la ventana para el contador de contexto de la web, con LA MISMA
|
|
1077
|
+
// función que la barra de stdio y la de la TUI. Dos resoluciones del tope serían
|
|
1078
|
+
// dos porcentajes distintos para el mismo modelo, y esa función ya respeta la
|
|
1079
|
+
// precedencia que `/config` declara (proyecto > global > tabla).
|
|
1080
|
+
topeDeContexto: (raiz, modelo) => crearTopeDelModelo(raiz)(modelo),
|
|
1081
|
+
// Con qué código corre este proceso. Se lee aquí y no allí porque toca disco y
|
|
1082
|
+
// lanza `git`, y los tests de `arranque.ts` llaman a esa función entera.
|
|
1083
|
+
version: versionEnMarcha,
|
|
1084
|
+
// La cola de tareas de la MÁQUINA, y con ella el corredor. Se pasa desde aquí y
|
|
1085
|
+
// no se construye allí por la misma razón que las dos de arriba, más una: la
|
|
1086
|
+
// omisión de `arrancarConsolaWeb` tiene que ser NO ejecutar tareas, porque sus
|
|
1087
|
+
// propios tests lo llaman entero y tomarían el cerrojo de quien los corre.
|
|
1088
|
+
tareas: (informar) => crearTareasEnDisco({ informar }),
|
|
1089
|
+
dependenciasDeProyecto: (raiz) => ({
|
|
1090
|
+
...adaptadoresDeProyecto(raiz),
|
|
1091
|
+
catalogoModelos: new CatalogoModelos(undefined, undefined, proveedoresPersonalizados),
|
|
1092
|
+
guardarModeloGlobal,
|
|
1093
|
+
conectarCloudStudio: (url, scopes, informar) => conectarCloudStudio(url, {
|
|
1094
|
+
informar,
|
|
1095
|
+
scopes,
|
|
1096
|
+
entornoId: entornoDeUrl(url, cargarSettings().settings.entornos),
|
|
1097
|
+
}),
|
|
1098
|
+
}),
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
const guion = argv.includes("--guion");
|
|
1102
|
+
const pedirTui = argv.includes("--tui");
|
|
1103
|
+
// Por defecto, TUI siempre. `--no-tui` fuerza stdio; sin banderas, TUI.
|
|
1104
|
+
const usarTui = !argv.includes("--no-tui");
|
|
1105
|
+
// `--tui` fuerza la TUI, pero no puede fabricar un terminal: sin stdin interactivo
|
|
1106
|
+
// no hay teclado que leer, y sin stdout TTY ink pintaría códigos de escape en la
|
|
1107
|
+
// tubería. Error de USO (64), no un crash — la bandera fue imposible, no el entorno.
|
|
1108
|
+
if (pedirTui && usarTui && (process.stdin.isTTY !== true || process.stdout.isTTY !== true)) {
|
|
1109
|
+
process.stderr.write("la TUI necesita un terminal interactivo: usa --no-tui o corre sin tubería\n");
|
|
1110
|
+
return 64;
|
|
1111
|
+
}
|
|
1112
|
+
// extraerBanderasDeModelo también con argv vacío, para que fuentes.entorno
|
|
1113
|
+
// .XONECODE_MODELO se rellene igual que en todos los demás subcomandos: la consola
|
|
1114
|
+
// no puede ser la única vía que ignora esa variable.
|
|
1115
|
+
// `--puerto` se quita CON su valor: un `4300` suelto no es una bandera de modelo,
|
|
1116
|
+
// pero tampoco tiene por qué llegar a `resto` como si fuera un argumento del usuario.
|
|
1117
|
+
const sinBanderasDeConsola = argv.filter((a, i) => {
|
|
1118
|
+
if (["--guion", "--sin-raton", "--web", "--cli", "--no-abrir", "--puerto"].includes(a))
|
|
1119
|
+
return false;
|
|
1120
|
+
return argv[i - 1] !== "--puerto";
|
|
1121
|
+
});
|
|
1122
|
+
const { fuentes } = extraerBanderasDeModelo(sinBanderasDeConsola);
|
|
1123
|
+
const catalogoModelos = new CatalogoModelos(undefined, undefined, proveedoresPersonalizados);
|
|
1124
|
+
// Por defecto, TUI siempre. `--no-tui` fuerza stdio.
|
|
1125
|
+
return await entrarEnConsola(fuentes, undefined, undefined, undefined, undefined, guion, undefined, !argv.includes("--no-tui"), quiereRaton(argv), {
|
|
1126
|
+
catalogoModelos,
|
|
1127
|
+
guardarModeloGlobal,
|
|
1128
|
+
conectarCloudStudio: (url, scopes, informar) =>
|
|
1129
|
+
// El entorno se resuelve por URL, no se omite: sin esto, un `/connect-studio` sobre
|
|
1130
|
+
// un proyecto del vestíbulo abría OAuth en `legado` —un hueco que `/sync` no lee— y
|
|
1131
|
+
// dejaba al proyecto reautenticando en cada sincronización.
|
|
1132
|
+
conectarCloudStudio(url, { informar, scopes, entornoId: entornoDeUrl(url, cargarSettings().settings.entornos) }),
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
if (comando === "--help" || comando === "-h") {
|
|
1136
|
+
process.stdout.write(AYUDA);
|
|
1137
|
+
return 0;
|
|
1138
|
+
}
|
|
480
1139
|
if (comando === "run") {
|
|
481
1140
|
const { fuentes, resto: sinModelo } = extraerBanderasDeModelo(resto);
|
|
482
1141
|
const peticion = sinModelo.filter((a) => !a.startsWith("--")).join(" ");
|
|
@@ -508,7 +1167,7 @@ export async function main(argv) {
|
|
|
508
1167
|
catch (e) {
|
|
509
1168
|
// Un modelo mal escrito es un error del USUARIO, no del harness: su mensaje, sin
|
|
510
1169
|
// traza, y código de uso (64). No se propaga como un fallo de software.
|
|
511
|
-
if (e instanceof ModeloMalEscrito) {
|
|
1170
|
+
if (e instanceof ModeloMalEscrito || e instanceof ErrorDeUso) {
|
|
512
1171
|
process.stderr.write(`${e.message}\n`);
|
|
513
1172
|
return 64; // EX_USAGE
|
|
514
1173
|
}
|