xonecode 0.22.1 → 0.24.1

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 (55) hide show
  1. package/THIRD_PARTY_NOTICES.md +32 -0
  2. package/apps/web/dist/assets/EditorDeFichero-BX_LfESa.css +1 -0
  3. package/apps/web/dist/assets/EditorDeFichero-DP2FWRar.js +38 -0
  4. package/apps/web/dist/assets/index-C-U_ay7l.css +1 -0
  5. package/apps/web/dist/assets/index-DT_LY-_I.js +495 -0
  6. package/apps/web/dist/assets/{php-BTFUgUDo.js → php-fWJ9hMOH.js} +1 -1
  7. package/apps/web/dist/assets/{ruby-BKKiBtJK.js → ruby-CpEKEsYx.js} +1 -1
  8. package/apps/web/dist/index.html +2 -2
  9. package/dist/agent/cloudstudio/cloudstudioMcp.js +3 -2
  10. package/dist/agent/cloudstudio/cloudstudioMcp.js.map +1 -1
  11. package/dist/agent/conectores/conectoresEnDisco.js +3 -2
  12. package/dist/agent/conectores/conectoresEnDisco.js.map +1 -1
  13. package/dist/agent/config/configEnDisco.js +32 -2
  14. package/dist/agent/config/configEnDisco.js.map +1 -1
  15. package/dist/agent/config/settingsEnDisco.js +16 -1
  16. package/dist/agent/config/settingsEnDisco.js.map +1 -1
  17. package/dist/agent/grafo/arbolDeProyecto.js +122 -20
  18. package/dist/agent/grafo/arbolDeProyecto.js.map +1 -1
  19. package/dist/agent/grafo/proyecto.js +22 -2
  20. package/dist/agent/grafo/proyecto.js.map +1 -1
  21. package/dist/agent/motores/trueforge/capacidades.js +5 -3
  22. package/dist/agent/motores/trueforge/capacidades.js.map +1 -1
  23. package/dist/agent/motores/trueforge/memoriaTrueforge.js +2 -1
  24. package/dist/agent/motores/trueforge/memoriaTrueforge.js.map +1 -1
  25. package/dist/agent/motores/trueforge/sesionTrueforge.js +3 -1
  26. package/dist/agent/motores/trueforge/sesionTrueforge.js.map +1 -1
  27. package/dist/agent/motores/trueforge/toolsDeFichero.js +6 -3
  28. package/dist/agent/motores/trueforge/toolsDeFichero.js.map +1 -1
  29. package/dist/agent/renombrarSobre.js +72 -0
  30. package/dist/agent/renombrarSobre.js.map +1 -0
  31. package/dist/agent/sesiones/baseDeFichero.js +90 -0
  32. package/dist/agent/sesiones/baseDeFichero.js.map +1 -0
  33. package/dist/core/config.js +21 -3
  34. package/dist/core/config.js.map +1 -1
  35. package/dist/core/dispositivoDeSesion.js +21 -1
  36. package/dist/core/dispositivoDeSesion.js.map +1 -1
  37. package/dist/core/modoDeEscritura.js +21 -3
  38. package/dist/core/modoDeEscritura.js.map +1 -1
  39. package/dist/core/settings.js +28 -0
  40. package/dist/core/settings.js.map +1 -1
  41. package/dist/core/shellDeAgente.js +57 -0
  42. package/dist/core/shellDeAgente.js.map +1 -1
  43. package/dist/web/servidor/arranque.js +235 -22
  44. package/dist/web/servidor/arranque.js.map +1 -1
  45. package/dist/web/servidor/sesiones.js +5 -4
  46. package/dist/web/servidor/sesiones.js.map +1 -1
  47. package/dist/web/servidor/transporte.js.map +1 -1
  48. package/dist/web/servidor/vestibulo.js +23 -4
  49. package/dist/web/servidor/vestibulo.js.map +1 -1
  50. package/package.json +1 -1
  51. package/skills/xone-hotswap/SKILL.md +6 -5
  52. package/skills/xone-hotswap/lib/dispositivo.mjs +58 -13
  53. package/skills/xone-hotswap/scripts/xone-arrancar-android +9 -1
  54. package/apps/web/dist/assets/index-5rtynJV_.js +0 -490
  55. package/apps/web/dist/assets/index-Cq6c6WEF.css +0 -1
@@ -66,19 +66,20 @@ import { borrarCredencial, guardarCredencial } from "../../agent/config/authEnDi
66
66
  import { borrarProveedorPersonalizado, guardarProveedorPersonalizado, proveedoresPersonalizados, } from "../../agent/config/configEnDisco.js";
67
67
  import { crearCheckpointerDeProyecto, mantenimientoDelCheckpointer, } from "../../agent/sesiones/checkpointer.js";
68
68
  import { resumenDePoda } from "../../core/podaDeCheckpoint.js";
69
- import { cargarSettings, guardarConcurrenciaDeTareas, guardarDepurar, guardarWorkspace as guardarWorkspaceEnDisco, guardarDispositivos, guardarAjusteDeAvd, quitarAjustesDeAvd, guardarPuertosAsignados, guardarEntorno as guardarEntornoEnDisco, olvidarEntornoDeSettings, copiasDeEntorno, borrarCopiasDeEntorno, } from "../../agent/config/settingsEnDisco.js";
69
+ import { cargarSettings, guardarConcurrenciaDeTareas, guardarDepurar, guardarModoPorDefecto, guardarWorkspace as guardarWorkspaceEnDisco, guardarDispositivos, guardarAjusteDeAvd, quitarAjustesDeAvd, guardarPuertosAsignados, guardarEntorno as guardarEntornoEnDisco, olvidarEntornoDeSettings, copiasDeEntorno, borrarCopiasDeEntorno, } from "../../agent/config/settingsEnDisco.js";
70
70
  import { dentroDelWorkspace, depuracionActiva, expandirConCasa, mismaRuta, motivoParaNoOlvidarEntorno, motivoDeNombreDeEntornoInaceptable, motivoDeWorkspaceInaceptable, } from "../../core/settings.js";
71
71
  import { esModoDeEscritura, MODO_POR_OMISION, } from "../../core/modoDeEscritura.js";
72
72
  import { megasDeLasMudanzas, mudarWorkspaceLegado } from "../../agent/config/mudanzaEnDisco.js";
73
73
  import { elegirCarpetaEnMaquina, haySelectorDeCarpeta } from "../../agent/config/selectorEnMaquina.js";
74
- import { cloudstudioDelProyecto } from "../../agent/config/configEnDisco.js";
74
+ import { cloudstudioDelProyecto, guardarModoDeEscrituraDeProyecto, modoDeEscrituraDelProyecto } from "../../agent/config/configEnDisco.js";
75
75
  import { abrirEnSistema, olvidarEntorno as olvidarCredencialesDeEntorno, rutaAuthPorDefecto } from "../../agent/cloudstudio/cloudstudioMcp.js";
76
76
  import { nombreDePersona } from "../../agent/config/persona.js";
77
77
  import { cambiosDeSesion, fotoDeApertura, olvidarSesion, parcheDeSesion } from "../../agent/sesiones/sesionGit.js";
78
78
  import { commitDeTurno, cambiosPendientes, trabajoSinCommitear } from "../../agent/sesiones/gitSync.js";
79
79
  import { marcarTareaDeSesion, sembrarConsumosPendientes } from "./sesiones.js";
80
80
  import { RUTA_ARTEFACTOS, esRutaDeArtefacto } from "../../core/artefactos.js";
81
- import { arbolDeProyecto, leerFicheroDeProyecto, mimeDeImagen, motivoDeRutaInaceptable, } from "../../agent/grafo/arbolDeProyecto.js";
81
+ import { arbolDeProyecto, escribirFicheroDeProyecto, leerFicheroDeProyecto, mimeDeImagen, motivoDeRutaInaceptable, } from "../../agent/grafo/arbolDeProyecto.js";
82
+ import { baseDeFichero } from "../../agent/sesiones/baseDeFichero.js";
82
83
  import { modeloEnDisco } from "../../agent/navegacion/indiceEnDisco.js";
83
84
  import { ficherosDelProyecto } from "../../agent/turno/ficherosDelProyecto.js";
84
85
  import { fotoDeColecciones } from "../../core/fotoDeColecciones.js";
@@ -452,6 +453,31 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
452
453
  clearTimeout(reloj);
453
454
  }
454
455
  };
456
+ /**
457
+ * CUÁL es el proyecto abierto, deducido de su raíz: la que le tocaría a cada proyecto de la
458
+ * lista se calcula con la misma función que la creó. Guardar el id aparte al abrirlo sería una
459
+ * segunda fuente de verdad que se queda vieja el día que alguien abra por otro camino.
460
+ *
461
+ * Lo usan el alta (`proyectoActivo`) y el guardado del editor, que compara con él el proyecto
462
+ * con que se ABRIÓ la edición: tiene que ser la MISMA cuenta, o el editor y el servidor
463
+ * podrían no estar de acuerdo sobre cuál es «el abierto».
464
+ *
465
+ * El `entorno` se copia a una constante porque TypeScript no puede saber que `entornoElegido`
466
+ * —una variable del cierre, que otro mensaje puede cambiar— sigue definida dentro del callback.
467
+ */
468
+ const proyectoActivoDe = (abierto) => {
469
+ const entorno = entornoElegido;
470
+ if (abierto === undefined || entorno === undefined)
471
+ return undefined;
472
+ return proyectos.find((p) => {
473
+ try {
474
+ return vestibulo.raizDeProyecto(entorno, p.nombre) === abierto.raiz;
475
+ }
476
+ catch {
477
+ return false;
478
+ }
479
+ })?.id;
480
+ };
455
481
  /**
456
482
  * El mensaje de alta: qué falta, y con qué elegirlo.
457
483
  *
@@ -481,24 +507,11 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
481
507
  // puede escribirlo después de abrir (el alta de un proyecto cloud), y una copia
482
508
  // tomada antes se quedaría diciendo lo de antes.
483
509
  const modo = abierto === undefined ? undefined : modoDeProyecto(abierto.raiz);
484
- // CUÁL es el abierto, deducido de su raíz: la que le tocaría a cada proyecto de la
485
- // lista se calcula con la misma función que la creó. Guardar el id aparte al abrirlo
486
- // sería una segunda fuente de verdad que se queda vieja el día que alguien abra por
487
- // otro camino.
488
- // El `entorno` se copia a una constante porque TypeScript no puede saber que
489
- // `entornoElegido` —una variable del cierre, que otro mensaje puede cambiar— sigue
490
- // definida dentro del callback.
510
+ // CUÁL es el abierto, deducido de su raíz (`proyectoActivoDe`). El `entorno` se copia a una
511
+ // constante porque `entornoElegido` es una variable del cierre que otro mensaje puede cambiar,
512
+ // y TypeScript no sabría que sigue definida dentro de los callbacks de abajo.
491
513
  const entorno = entornoElegido;
492
- const activo = abierto === undefined || entorno === undefined
493
- ? undefined
494
- : proyectos.find((p) => {
495
- try {
496
- return vestibulo.raizDeProyecto(entorno, p.nombre) === abierto.raiz;
497
- }
498
- catch {
499
- return false;
500
- }
501
- })?.id;
514
+ const activo = proyectoActivoDe(abierto);
502
515
  const pendientes = proyectoAbierto ? [] : await vestibulo.pasosPendientes();
503
516
  // Lo que ya había sin commitear al ABRIR. `abierto` se capturó arriba, antes de este
504
517
  // `await`: `anunciarAlta` no va en la cola del vestíbulo, así que entre medias puede
@@ -1101,6 +1114,8 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
1101
1114
  // La casilla «Depurar», por lo mismo que el workspace: Ajustes se puede abrir en cuanto
1102
1115
  // conecta.
1103
1116
  const depuracion = mensajeDeDepuracion();
1117
+ // El modo de las sesiones nuevas (IXCODE-31), por lo mismo: está en Ajustes > General.
1118
+ const modoPorDefecto = mensajeDeModoPorDefecto();
1104
1119
  // Y los conectores MCP, tras el workspace y por la misma regla: solo si esta ejecución
1105
1120
  // los monta, y entonces la ventana de Ajustes no se abre con la sección en blanco.
1106
1121
  const conectores = mensajeDeConectores();
@@ -1127,6 +1142,8 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
1127
1142
  cliente({ clase: "mudanzaDeWorkspace", progreso: ultimoProgresoDeMudanza });
1128
1143
  if (depuracion !== undefined)
1129
1144
  cliente(depuracion);
1145
+ if (modoPorDefecto !== undefined)
1146
+ cliente(modoPorDefecto);
1130
1147
  if (conectores !== undefined)
1131
1148
  cliente(conectores);
1132
1149
  if (consumoDeLaSesion !== undefined) {
@@ -1302,6 +1319,10 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
1302
1319
  if (m !== undefined)
1303
1320
  emitir(m);
1304
1321
  };
1322
+ const mensajeDeModoPorDefecto = () => {
1323
+ const modo = opciones.modoPorDefectoGlobal?.();
1324
+ return modo === undefined ? undefined : { clase: "modoPorDefecto", modo };
1325
+ };
1305
1326
  /**
1306
1327
  * El servicio de conectores, construido UNA VEZ al montar — no en cada mensaje, porque es
1307
1328
  * quien guarda en memoria las pruebas y las autorizaciones pendientes (`servicioDeConectores.ts`).
@@ -3750,6 +3771,74 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
3750
3771
  });
3751
3772
  }
3752
3773
  };
3774
+ /**
3775
+ * Guardar lo que la persona editó. Tres cosas antes de tocar el disco, y cada una contesta en
3776
+ * vez de callar —el editor tiene un «Guardando…» encendido esperando—:
3777
+ * - que haya proyecto abierto y puerto que escriba;
3778
+ * - que nadie trabaje en esa RAÍZ (`vestibulo.motivoParaNoEditar`): un turno o una tarea que
3779
+ * escribe en la misma copia pisaría, o sería pisado, sin avisar;
3780
+ * - lo demás —guardas de ruta, huella, tope— lo decide el puerto y lo DEVUELVE.
3781
+ * El `try` es el de `atenderFichero`: un fallo inesperado contesta sin la ruta de la máquina.
3782
+ */
3783
+ const atenderGuardarFichero = async (ruta, texto, huella, id, proyecto) => {
3784
+ const abierto = vestibulo.proyectoAbierto();
3785
+ if (abierto === undefined) {
3786
+ emitir({ clase: "ficheroGuardado", ruta, id, error: "no hay ningún proyecto abierto" });
3787
+ return;
3788
+ }
3789
+ // El guardado va al proyecto del FOCO, que puede haber cambiado desde que se abrió la edición
3790
+ // (otra pestaña abre otro, se cambia de entorno). La huella no lo frena si el contenido es
3791
+ // idéntico —el mismo proyecto en dos entornos, dos proyectos del mismo esqueleto—, así que se
3792
+ // compara el proyecto con que se abrió con el de ahora, con la MISMA cuenta que el alta. Si no
3793
+ // se puede deducir, tampoco se escribe: no se sabe dónde caería.
3794
+ if (proyectoActivoDe(abierto) !== proyecto) {
3795
+ emitir({ clase: "ficheroGuardado", ruta, id, error: "el proyecto abierto ya no es el de este fichero: vuelve a abrirlo" });
3796
+ return;
3797
+ }
3798
+ if (opciones.escribirFichero === undefined) {
3799
+ emitir({ clase: "ficheroGuardado", ruta, id, error: "esta ejecución no puede escribir en el proyecto" });
3800
+ return;
3801
+ }
3802
+ const ocupado = vestibulo.motivoParaNoEditar(abierto.raiz);
3803
+ if (ocupado !== undefined) {
3804
+ emitir({ clase: "ficheroGuardado", ruta, id, error: ocupado });
3805
+ return;
3806
+ }
3807
+ try {
3808
+ // `id` lo pone la pestaña en CADA guardado y vuelve en la respuesta: esta va a todas las
3809
+ // pestañas, y cada una reconoce la suya por él. La huella no vale —dos pestañas que parten de
3810
+ // la misma versión mandan la misma—.
3811
+ emitir({ clase: "ficheroGuardado", ...(await opciones.escribirFichero(abierto.raiz, ruta, texto, huella)), id });
3812
+ }
3813
+ catch (error) {
3814
+ informar(`no se pudo guardar «${ruta}» (${codigoDe(error)})`);
3815
+ emitir({ clase: "ficheroGuardado", ruta, id, error: "no se pudo guardar el fichero" });
3816
+ }
3817
+ };
3818
+ /**
3819
+ * La base de las marcas del editor. Con la sesión ABIERTA (`abierto.sesion`, la misma que mira
3820
+ * Revisión): su ref es la foto del inicio. Sin puerto se contesta `sinBase` —el editor funciona
3821
+ * igual, sin marcas, y lo dice— en vez de dejar la barra en «Trayendo la base…».
3822
+ */
3823
+ const atenderBaseDeFichero = async (ruta, base) => {
3824
+ const abierto = vestibulo.proyectoAbierto();
3825
+ if (abierto === undefined) {
3826
+ // Contesta igual: callar dejaría la barra del editor en «Trayendo la base…».
3827
+ emitir({ clase: "baseDeFichero", ruta, base, sinBase: "no hay ningún proyecto abierto" });
3828
+ return;
3829
+ }
3830
+ if (opciones.baseDeFichero === undefined) {
3831
+ emitir({ clase: "baseDeFichero", ruta, base, sinBase: "esta ejecución no puede leer git" });
3832
+ return;
3833
+ }
3834
+ try {
3835
+ emitir({ clase: "baseDeFichero", ...(await opciones.baseDeFichero(abierto.raiz, abierto.sesion, ruta, base)) });
3836
+ }
3837
+ catch (error) {
3838
+ informar(`no se pudo leer la base de «${ruta}» (${codigoDe(error)})`);
3839
+ emitir({ clase: "baseDeFichero", ruta, base, sinBase: "no se pudo leer la base" });
3840
+ }
3841
+ };
3753
3842
  /**
3754
3843
  * El contenido de un ARTEFACTO de la sesión abierta, por su nombre.
3755
3844
  *
@@ -4918,11 +5007,59 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
4918
5007
  const { clase: _clase, ...medida } = await lecturaDeSync(raiz);
4919
5008
  sync = medida;
4920
5009
  }
4921
- const foto = { tareas, ...(sync === undefined ? {} : { sync }) };
5010
+ // El modo de las sesiones nuevas de ESTE proyecto (IXCODE-31): solo con copia, que es
5011
+ // donde vive su `config.json`; dentro, ausente es «como el global».
5012
+ const modoDeEscritura = opciones.modoDelProyecto === undefined || !esProyectoEnDisco(raiz)
5013
+ ? undefined
5014
+ : (() => {
5015
+ const proyecto = opciones.modoDelProyecto(raiz);
5016
+ return proyecto === undefined ? {} : { proyecto };
5017
+ })();
5018
+ const foto = {
5019
+ tareas,
5020
+ ...(sync === undefined ? {} : { sync }),
5021
+ ...(modoDeEscritura === undefined ? {} : { modoDeEscritura }),
5022
+ };
4922
5023
  respuesta.writeHead(200, { "content-type": "application/json" });
4923
5024
  respuesta.end(JSON.stringify(foto));
4924
5025
  return;
4925
5026
  }
5027
+ if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "copiaLocal" && mensaje.accion === "modoDeEscritura") {
5028
+ // IXCODE-31. Del cable llega el ID y, opcional, el modo; la raíz la pone el servidor,
5029
+ // como en `resumen`. Sin `modo` se QUITA: vuelve a mandar el global.
5030
+ const pedido = mensaje.modo;
5031
+ if (pedido !== undefined && !esModoDeEscritura(pedido)) {
5032
+ respuesta.writeHead(400);
5033
+ respuesta.end();
5034
+ return;
5035
+ }
5036
+ const identidad = proyectos.find((p) => p.id === mensaje.proyecto);
5037
+ const raiz = entornoElegido === undefined || identidad === undefined
5038
+ ? undefined
5039
+ : vestibulo.raizDeProyecto(entornoElegido, identidad.nombre);
5040
+ if (raiz === undefined || !esProyectoEnDisco(raiz) || opciones.guardarModoDelProyecto === undefined) {
5041
+ respuesta.writeHead(409, { "content-type": "application/json" });
5042
+ respuesta.end(JSON.stringify({
5043
+ motivo: raiz === undefined
5044
+ ? "ese proyecto no está en el listado del entorno activo"
5045
+ : "el proyecto no tiene copia local: el modo se guarda en su configuración",
5046
+ }));
5047
+ return;
5048
+ }
5049
+ try {
5050
+ opciones.guardarModoDelProyecto(raiz, pedido);
5051
+ }
5052
+ catch (error) {
5053
+ // Un `config.json` roto PARA sin escribir (`leerObjetoCrudoOAbortar`): se dice, sin la ruta.
5054
+ respuesta.writeHead(409, { "content-type": "application/json" });
5055
+ respuesta.end(JSON.stringify({ motivo: `no se pudo guardar en la configuración del proyecto (${codigoDe(error) ?? "su config.json no se puede leer"})` }));
5056
+ return;
5057
+ }
5058
+ const proyecto = opciones.modoDelProyecto?.(raiz);
5059
+ respuesta.writeHead(200, { "content-type": "application/json" });
5060
+ respuesta.end(JSON.stringify(proyecto === undefined ? {} : { proyecto }));
5061
+ return;
5062
+ }
4926
5063
  if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "soporte" && mensaje.accion === "listar") {
4927
5064
  // El listado de la pestaña Soporte, con el análisis PREVIO de cada chat y cada tarea
4928
5065
  // (`core/analisisDeSesion.ts`), contestado en la propia respuesta como `resumen`.
@@ -5134,6 +5271,29 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
5134
5271
  respuesta.end();
5135
5272
  return;
5136
5273
  }
5274
+ if (typeof mensaje === "object" &&
5275
+ mensaje !== null &&
5276
+ mensaje.clase === "guardarFichero" &&
5277
+ typeof mensaje.ruta === "string" &&
5278
+ typeof mensaje.texto === "string" &&
5279
+ typeof mensaje.huella === "string" &&
5280
+ typeof mensaje.id === "string" &&
5281
+ typeof mensaje.proyecto === "string") {
5282
+ void atenderGuardarFichero(mensaje.ruta, mensaje.texto, mensaje.huella, mensaje.id, mensaje.proyecto).catch(contar);
5283
+ respuesta.writeHead(204);
5284
+ respuesta.end();
5285
+ return;
5286
+ }
5287
+ if (typeof mensaje === "object" &&
5288
+ mensaje !== null &&
5289
+ mensaje.clase === "baseDeFichero" &&
5290
+ typeof mensaje.ruta === "string" &&
5291
+ (mensaje.base === "sesion" || mensaje.base === "commit")) {
5292
+ void atenderBaseDeFichero(mensaje.ruta, mensaje.base).catch(contar);
5293
+ respuesta.writeHead(204);
5294
+ respuesta.end();
5295
+ return;
5296
+ }
5137
5297
  if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "artefacto" && typeof mensaje.nombre === "string") {
5138
5298
  void atenderArtefacto(mensaje.nombre).catch(contar);
5139
5299
  respuesta.writeHead(204);
@@ -5372,6 +5532,18 @@ export function montarRutas(servidor, vestibulo, opciones = {}) {
5372
5532
  respuesta.end();
5373
5533
  return;
5374
5534
  }
5535
+ if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "modoPorDefecto") {
5536
+ // Un modo que no existe no se guarda: el cliente manda la intención, el servidor decide.
5537
+ if (esModoDeEscritura(mensaje.modo)) {
5538
+ opciones.guardarModoPorDefectoGlobal?.(mensaje.modo);
5539
+ const m = mensajeDeModoPorDefecto();
5540
+ if (m !== undefined)
5541
+ emitir(m);
5542
+ }
5543
+ respuesta.writeHead(204);
5544
+ respuesta.end();
5545
+ return;
5546
+ }
5375
5547
  if (typeof mensaje === "object" && mensaje !== null && mensaje.clase === "depuracion" && typeof mensaje.activa === "boolean") {
5376
5548
  opciones.guardarDepuracion?.(mensaje.activa);
5377
5549
  emitirDepuracion();
@@ -6115,6 +6287,15 @@ export function ajusteDeWorkspaceCableado(opciones) {
6115
6287
  },
6116
6288
  };
6117
6289
  }
6290
+ /**
6291
+ * Los tres puertos de la pestaña Ficheros —leer, guardar y la base de las marcas—, cableados con las
6292
+ * funciones de verdad y extraídos por el MISMO motivo que `emuladoresCableados`: dentro de
6293
+ * `arrancarConsolaWeb` serían un literal que ningún test mira, y cada uno es un campo OPCIONAL de
6294
+ * `montarRutas`, así que olvidarlo compila y pasa.
6295
+ */
6296
+ export function ficherosCableados() {
6297
+ return { leerFichero: leerFicheroDeProyecto, escribirFichero: escribirFicheroDeProyecto, baseDeFichero };
6298
+ }
6118
6299
  /**
6119
6300
  * Los siete puertos de los emuladores, cableados con las funciones de verdad — extraídos por el
6120
6301
  * MISMO motivo que `ajusteDeWorkspaceCableado`: dentro de `arrancarConsolaWeb` serían un literal
@@ -6156,6 +6337,32 @@ export function ajusteDeDepuracionCableado(opciones = {}) {
6156
6337
  guardarDepuracion: (activa) => guardar(activa),
6157
6338
  };
6158
6339
  }
6340
+ /**
6341
+ * El modo con que NACEN las sesiones nuevas (IXCODE-31), cableado: el global
6342
+ * (`settings.json#modoDeEscritura`, Ajustes > General) y el de cada proyecto
6343
+ * (`.xonecode/config.json#modoDeEscritura`, panel del proyecto). Extraída por el patrón de
6344
+ * fallo de siempre, y con una trampa concreta que el test vigila: el del proyecto se lee por la
6345
+ * RAÍZ, porque en la web `fuentes().proyecto` no se rellena nunca — leerlo de ahí daría el
6346
+ * ajuste por guardado y no se aplicaría jamás.
6347
+ */
6348
+ export function ajusteDeModoPorDefectoCableado(opciones = {}) {
6349
+ const leerGlobal = opciones.leerGlobal ?? (() => cargarSettings().settings.modoDeEscritura);
6350
+ const guardarGlobal = opciones.guardarGlobal ?? ((modo) => void guardarModoPorDefecto(undefined, modo));
6351
+ const leerProyecto = opciones.leerProyecto ?? modoDeEscrituraDelProyecto;
6352
+ const guardarProyecto = opciones.guardarProyecto ??
6353
+ ((raiz, modo) => void guardarModoDeEscrituraDeProyecto(raiz, modo));
6354
+ return {
6355
+ modoPorDefecto: (raiz) => {
6356
+ const proyecto = leerProyecto(raiz);
6357
+ const global = leerGlobal();
6358
+ return { ...(proyecto === undefined ? {} : { proyecto }), ...(global === undefined ? {} : { global }) };
6359
+ },
6360
+ modoPorDefectoGlobal: () => leerGlobal() ?? MODO_POR_OMISION,
6361
+ guardarModoPorDefectoGlobal: (modo) => guardarGlobal(modo),
6362
+ modoDelProyecto: (raiz) => leerProyecto(raiz),
6363
+ guardarModoDelProyecto: (raiz, modo) => guardarProyecto(raiz, modo),
6364
+ };
6365
+ }
6159
6366
  /**
6160
6367
  * La opción `conectores` de `montarRutas`, cableada — extraída por el MISMO motivo que
6161
6368
  * `ajusteDeWorkspaceCableado`: un literal inline dentro de `arrancarConsolaWeb` no quedaría
@@ -6553,6 +6760,9 @@ export async function arrancarConsolaWeb(opciones) {
6553
6760
  // La casilla «Depurar», con la MISMA disciplina: composición extraída y probada
6554
6761
  // (`ajusteDeDepuracionCableado`).
6555
6762
  ...ajusteDeDepuracionCableado(),
6763
+ // El modo con que nacen las sesiones nuevas (IXCODE-31), con la MISMA disciplina. El
6764
+ // lector por raíz va al vestíbulo (`vestibuloReal`); aquí, los de Ajustes y del panel.
6765
+ ...(({ modoPorDefecto: _paraElVestibulo, ...resto }) => resto)(ajusteDeModoPorDefectoCableado()),
6556
6766
  // Los conectores MCP, con la MISMA disciplina: la composición extraída y probada
6557
6767
  // (`ajusteDeConectoresCableado`), nunca un literal aquí dentro.
6558
6768
  // Y el gestor de tareas (IXCODE-11) llama por ESE mismo servicio: los dos salen de
@@ -6607,7 +6817,8 @@ export async function arrancarConsolaWeb(opciones) {
6607
6817
  modeloDelCambio,
6608
6818
  planesDelProyecto,
6609
6819
  visorOpenui: () => leerVisorOpenui(raizDelCliente),
6610
- leerFichero: leerFicheroDeProyecto,
6820
+ // Leer, guardar y la base del editor, compuestos fuera para que la composición tenga test.
6821
+ ...ficherosCableados(),
6611
6822
  leerArtefacto: leerArtefactoDeSesion,
6612
6823
  leerArtefactoCrudo,
6613
6824
  correrPasoDeReceta: (receta, paso, alSalirLinea) => correrPasoDeReceta(receta, paso, { alSalirLinea }),
@@ -6808,6 +7019,8 @@ function vestibuloReal(opciones, servidor, escribir, informar, conectores) {
6808
7019
  informar,
6809
7020
  origenDeTrabajo: resolver(fuentes()).trabajo.origen,
6810
7021
  fuentes,
7022
+ // Con qué modo nace una sesión nueva (IXCODE-31): el del proyecto de ESA raíz, si no el global.
7023
+ modoPorDefecto: ajusteDeModoPorDefectoCableado().modoPorDefecto,
6811
7024
  // Se resuelve UNA vez, aquí, y no en cada `anunciarAlta`: `git config`/`os.userInfo`
6812
7025
  // no cambian a media conexión, y repetir el subproceso en cada anuncio del alta sería
6813
7026
  // gastar sin motivo. Nunca viaja hacia CloudStudio ni hacia ningún acto —