> Este texto traducido está en revisión lingüística. Si un pasaje no está claro, consulte el [original en inglés](https://www.sm.ch-j.eu/docs/i18n/en/remote-editor-latency.md). # Guardando editor remoto y monitorizando latencia ## Causas raíz El editor anterior subió su archivo de trabajo en el directorio padre del destino, que a menudo no puede ser escrito por la cuenta SSH bajo `/etc` o `/var`. SFTP renombrado no proporcionó ninguna ruta de sudo autorizada, verificación de conflictos de línea base, verificación de metadatos de seguridad o sobreescritura semántica confiable. Su controlador de errores borró la copia temporal, eliminando el material de recuperación incluso cuando la finalización era incierta. Estado de la sesión previamente expuesto sólo el estado de conexión. No hubo medición de la respuesta ICMP o SSH autenticada que alimentara el indicador terminal. ## Guardar protocolo 1. Un ayudante SSH fijo abre un archivo regular de un solo enlace usando descriptor-relativo operaciones y `O_NOFOLLOW` para cada componente de ruta. Captura el contenido, SHA-256 hash, dispositivo/inodo, UID, GID, bits de permiso completos, tiempos de modificación/cambio nanosegundo y todos los atributos extendidos legibles. El núcleo verifica el hash y decodifica el estricto UTF-8, preservando un BOM. Cada documento obtiene un `editId` opaco, visorizado a su plugin, la sesión de terminal y la identidad del servidor. 2. Una operación de preparación sin privilegios resuelve la cuenta autenticada inicio a través de `pwd.getpwuid(os.getuid())`, crea `$HOME/ch-j-sm/` con el modo `0700` y crea un diario de recuperación privado y exclusivo. Un hogar inseguro, un espacio de trabajo de enlaces simbólicos o un espacio de espacio de trabajo propiedad de otra cuenta es rechazado. 3. El núcleo carga un archivo de trabajo oculto y aleatorio 128-bit `.tmp` a través de SFTP con creación exclusiva (`wx`), modo `0600` y un tiempo de espera de sesenta segundos de canal/carga. La revista contiene el objetivo, la base de referencia original y el hash de trabajo esperado; no contiene credenciales. 4. **One SSH exec realiza toda la finalización**, opcionalmente a través de la executor de sudo existente. El ayudante abre y agiliza con seguridad la copia de trabajo privada y la descarga. Bloquea el inodo original, comprueba su línea de base, crea un archivo de destino exclusivo junto al objetivo, escribe todo el contenido, aplica y verifica metadatos, revisa el original nuevamente y descarga el reemplazo. 5. Un mismo directorio `os.replace` reemplaza atómicamente al objetivo. El padre el directorio se borra y la ruta final, el hash, el inodo y los metadatos se verifican. La ruta original permanece disponible hasta el reemplazo. El archivo de trabajo nunca se mueve a través de sistemas de archivos y nunca se elimina al finalizar. 6. Después de Core recibe un éxito verificado, una limpieza separada vuelve a comprobar el final contenido y metadatos de seguridad antes de eliminar el archivo de trabajo y el diario. El fallo de limpieza sigue reportando un ahorro confirmado con `recoveryAvailable: true`. Una carga fallida, sudo fallido, conflicto, error de metadatos o sustitución fallida mantiene el material original y cualquier material escenificado. Las cargas incompletas se identifican como tales y no pueden ser recuperadas como un documento editado completo. La comunicación perdida de SSH, el estado de salida, los tiempos de espera y los resultados inesperados de finalización se reportan como **unknown**. El núcleo bloquea otro salvo en un documento incierto; no hay reintento de reemplazo automático. ## Metadatos, privilegios y límites de seguridad El ayudante preserva y verifica UID, GID y todos los bits de permiso POSIX, incluidos los bits ejecutables, setuid, setgid y pegajosos. Reproduce atributos extendidos Linux byte para byte, incluyendo ACL POSIX, capacidades y etiquetas SELinux. La propiedad y el contenido del archivo se establecen antes de los atributos de seguridad, porque escribe/cocina puede borrar las capacidades o bits de permiso especiales. Se eliminan los atributos de destino heredados que están ausentes en el original. El tiempo de modificación refleja la nueva escritura y no se copia del original. Un atributo ilegible o irrepresentable aborta la operación; los metadatos no se eliminan silenciosamente. Las firmas IMA/EMV son rechazadas explícitamente porque copiar las firmas de integridad con contenido/inodo los invalidaría. Se rechazan los enlaces en la ruta de destino o inicio, los archivos enlazados de forma dura y los archivos no regulares. No se utiliza ningún retroceso de sobreescritura SFTP en su lugar o más débil. Los archivos de escritura ordinarios se ejecutan como la cuenta SSH. Los guardados protegidos requieren `{ sudo: true }` explícito; una contraseña de sudo es opcional para sudo sin contraseña. Las contraseñas son validadas y pasadas sólo en canal stdin, nunca dentro del comando, diario, archivo de trabajo, registrador o error remoto. Las sesiones de Root SSH utilizan el ejecutor raíz directo existente. La autorización fallida deja intacto el original y la copia de trabajo. Todos los salvas ejecutados como root (root directo SSH o sudo) también rechazan directorios de destino/antecedentes que poseen o pueden escribir otras cuentas, o que llevan un acceso ACL, en lugar de confiar en una ruta privilegiada controlada por otra cuenta. El ayudante fijo se ejecuta con `python3 -I -B`: ignora las rutas del usuario Python, el directorio de trabajo y los paquetes del sitio del usuario, y no escribe ningún código de byte. Las rutas y los argumentos JSON se citan en Core. Los complementos nunca reciben mangos SSH exec o SFTP arbitrarios. El sandbox existente, CSP, los permisos de manifiesto, la verificación de la clave del host y la verificación de actualización permanecen en uso. ## Plugin API y la integración restante de Mónaco **La interfaz de usuario de Mónaco/File Manager distribuida por separado no está presente en esta compra. Su integración de interfaz de usuario, sudo prompt, manejo de buffer sucio y traducciones no se pudo implementar o probar aquí.** La fuente del plugin incluido es el complemento Hash & Checksum. No se inventó ninguna fuente de administrador de archivos sustituto. Los métodos existentes siguen estando disponibles. Los métodos/opciones aditivos se exponen por el tiempo de ejecución precargado con caja de arena y comprobado con permiso: ```js const opened = await chjPlugin.files.readText(sessionId, path); // For a protected read, explicitly pass { sudo: true, sudoPassword }. const result = await chjPlugin.files.saveText(sessionId, path, editor.getValue(), { editId: opened.editId, sudo: true, // Include only after the user authorizes elevation. sudoPassword // Omit for passwordless sudo; never persist this value. }); if (result.ok && result.value.status === "saved") { // Mark this exact submitted editor version as saved. Later edits stay dirty. } else { // Keep the Monaco model dirty. Translate result.error.code/status. // result.error.recoveryId/recoveryPath identify retained working material. } await chjPlugin.files.closeText(opened.editId); const recovery = await chjPlugin.files.listRecovery(sessionId); const copy = await chjPlugin.files.readRecovery(sessionId, recoveryId); // Review copy.status and copy.text; open a fresh document before another save. await chjPlugin.files.cleanupRecovery(sessionId, recoveryId); // Cleanup rejects anything whose target is not currently confirmed. ``` Los métodos de recuperación aceptan un argumento final opcional `{ sudo: true, sudoPassword }` cuando se inspecciona un objetivo requiere elevación. Retienen el espacio de trabajo del usuario autenticado incluso cuando se ejecuta a través de sudo. Los diarios sobreviven al reinicio de la aplicación y a la reconexión de SSH; la recuperación nunca se repite automáticamente. Revise un búfer recuperado contra un objetivo recién abierto antes de guardarlo explícitamente. Los guardias `files.read` leen, inspeccionan y manejan la liberación. `files.write` guarda y limpia la recuperación. `writeText` sigue rechazando el fallo de los plugins existentes; `saveText` devuelve un sobre `{ ok, value/error }` estructurado porque Electron deja caer propiedades personalizadas en objetos de error rechazados. Un argumento heredado `writeText` puede utilizar la línea de base de un único documento abierto. Múltiples documentos en vivo para la misma ruta requieren `editId`; las llamadas ambiguas del legado fallan de forma segura en lugar de elegir la línea de base de otro documento. Los complementos deben liberar los mangos no utilizados. La interfaz de usuario externa debe mostrar el ahorro inmediatamente, mantener los buffers sucios en caso de fallo, ofrecer sudo sólo después de fallos de permiso/autenticación, mostrar conflictos sin sobreescritura y ofrecer recuperación después de la pérdida de conexión o un resultado desconocido. Debe traducir estos estados en checo, alemán e inglés. Copia de estado sugerida: | State | Czech | German | English | | --- | --- | --- | --- | | Saving | Ukládání… | Wird gespeichert… | Saving… | | Confirmed | Uloženo | Gespeichert | Saved | | Failure | Uložení selhalo | Speichern fehlgeschlagen | Save failed | | Sudo required | Vyžadováno oprávnění sudo | sudo-Autorisierung erforderlich | Sudo authorization required | | Conflict | Původní soubor byl změněn | Originaldatei wurde geändert | Original file changed | | Disconnected | Spojení bylo přerušeno | Verbindung unterbrochen | Connection lost | | Unknown | Výsledek uložení není znám | Speicherergebnis unbekannt | Save result unknown | | Recovery | Obnova je dostupná | Wiederherstellung verfügbar | Recovery available | Fallo de | | Uložení selhalo | Speichern fehlgeschlagen | Guardar fallida | | Sudo requiere | Vyžadováno oprávnění sudo | sudo-Autorisierung erforderlich | Se necesita autorización de Sudo | | Conflicto | Pđvodní soubor byl změněn | Originaldatei wurde geändert | Archivo original cambiado | | Desconectado | Spojení bylo přerušeno | Verbindung unterbrochen | Connection perdido | | Desconocido | Výsledek uložení není znám | Speicherergebnis unbekannt | Guardar resultado desconocido | | Recuperación | Obnova je dostupná | Wiederherstellung verfügbar | recuperación disponible | ## Definiciones de latencia y ciclo de vida **Ping** es la latencia reportada por una respuesta de eco ICMP del lado del cliente a la IP resuelta utilizada por SSH. Core invoca el ejecutable ping de la plataforma asíncronamente con un array de argumento fijo, sin necesidad de iniciar shell o administrador. Los argumentos Linux, macOS (incluyendo `ping6`) y Windows se seleccionan por separado. Bloqueado ICMP, un ejecutable que falta, un timeout, salida inválida o una respuesta Windows de sólo-último límite como `<1ms` produce un valor no disponible, no un número inventado. **SSH RTT** es la duración monotónica de `performance.now()` de una solicitud exec `true` fija sobre el cliente SSH autenticado existente. Incluye configuración de canales, procesamiento de servidores y entrega de respuestas; no es pura red RTT o SSH duración de inicio de sesión. La sugerencia de indicador traducida explica esta distinción. El monitoreo comienza tan pronto como el terminal se conecta. Las sondas se ejecutan simultáneamente y de manera asíncrona, con un tiempo de espera de cinco segundos para la solicitud/proceso. La siguiente muestra está programada doce segundos después de que la muestra anterior se complete, evitando solapamientos. Desconecte aborta ICMP, borra los temporizadores, cancela el trabajo pendiente de SSH y elimina los valores. Los controles de identidad del registro impiden que una conexión previa se publique después de reconectarse. El fallo de ICMP nunca cambia el estado de conexión SSH. Latencia viaja dentro del evento `state` existente / carga útil IPC `ssh:state`: ```js latency: { pingMs: 24.1, sshRttMs: 31.25, measuredAt: "…" } ``` Los números no disponibles son `null`. El indicador de terminal compacto utiliza milisegundos, texto no disponible localizado y etiquetas Czech/German/English. Se esconde desconectado. No se necesitó ninguna capacidad adicional de ejecución del renderizador ni solicitud de IPC. ## Validación y límites operativos - Ejecutar desde `app/`: `npm ci`, luego `npm test`. - Resultado local final: pruebas **179; 163 pasado, 0 fallado, 16 skipped**. Todos los saltos son los casos del sistema de archivos Linux en este host macOS. `npm ci`, compilación de sintaxis de Python y `git diff --check` completadas con éxito. - Las pruebas cubren Core salva/recuperación, protocolo de sudo y redacción de contraseña, real POSIX shell citando caminos hostiles, conflictos, cargas, limpieza fallida, resultados inciertos, documentos concurrentes, permisos IPC y ciclo de vida de latencia. - Las pruebas del sistema de archivos Linux ejercitan el punto de entrada de Python enviado, usando un sistema privado búsqueda de cuentas-hogar de prueba: propiedad/mode/xattrs, ACLs POSIX, simlinks/hardlinks, FIFOs, fallos de carga y metadatos, fallos de reemplazo, carreras de destino, resultados post-remplazo desconocidos y recuperación. La puesta en escena del sistema de archivos cruzados utiliza `/dev/shm` y otro sistema de archivos cuando está disponible. Estas pruebas se saltan en hosts no-Linux; las instalaciones ACL/separate-filesystem que faltan se omiten explícitamente. - El trabajo añadido Linux GitHub Actions ejecuta el sistema de archivos completo y repite casos como raíz. Este flujo de trabajo no ha sido enviado desde este árbol de trabajo. - La validación local fue en macOS. La sintaxis de origen de Python fue verificada con el Herramientas de línea de comandos ejecutable en Python. Un verdadero macOS loopback ICMP control de humo devolvió `0.071 ms`. No se probó localmente ningún servidor SSH, tiempo de ejecución Windows ni tiempo de ejecución de Linux en vivo, ni despliegue SELinux ni interfaz de usuario externa de Mónaco. - La edición de texto remoto ahora requiere Linux y Python 3 con descriptor-relativo soporte para sistema de archivos/xattr. Un intérprete que falta falla explícitamente. Este Core rechaza intencionadamente metadatos, enlaces y configuraciones de directorios inseguros. - El bloqueo es un consejo: los procesos que no cooperan todavía pueden competir en el estrecho intervalo entre la última comprobación de referencia y el cambio atómico. Linux no suministra ninguna operación general de comparación y sustitución de contenido atómico. Las comprobaciones detectan cambios antes de ese intervalo; las copias de trabajo retenidas soportan la recuperación después. - Los datos de recuperación permanecen en la cuenta remota hasta una limpieza confirmada. Incompleta, revistas conflictivas e ininspectivas nunca se descartan silenciosamente. No hay una expiración automática del archivo de recuperación y ninguna opción de sobreescritura forzada. - `npm ci` informó de las conclusiones de la auditoría de dependencia de 17 (9 moderada, 8 alta). Este cambio no cambia las dependencias o el modelo de confianza de actualización de la aplicación. El usuario solicitó que se retuvieran los cambios en el árbol de trabajo existente, actualmente `feature/hash-checksum-plugin`; no se realizó ningún cambio de rama o limpieza no relacionada. ## Archivos cambiados para esta tarea - `src/main/files/remoteFileService.js` - `src/main/files/remoteEditor.py` (nuevo) - `src/main/sessions/sessionManager.js` - `src/main/sessions/latencyMonitor.js` (nuevo) - `src/main/plugins/pluginRuntime.js` - `src/preload/pluginPreload.js` - `src/renderer/app.js` - `src/renderer/index.html` - `src/renderer/styles.css` - `src/renderer/i18n.js` - `test/remoteFileService.test.js` - `test/sessionManager.test.js` - `test/pluginRuntime.test.js` - `test/remoteEditor.test.js` (nuevo) - `test/remoteEditorProtocol.test.js` (nuevo) - `test/remoteEditorLinux.test.js` (nuevo) - `test/fixtures/remoteEditorLinux.py` (nuevo) - `test/latencyMonitor.test.js` (nuevo) - `README.md` - `docs/remote-editor-latency.md` (este archivo) - `../.github/workflows/remote-editor-tests.yml` (nuevo)