> A revisão linguística desta tradução está em curso. Em caso de dúvida, consulte o [original em inglês](https://www.sm.ch-j.eu/docs/i18n/en/remote-editor-latency.md). # Salvamento remoto do editor e monitoramento de latência ## Causas Raiz O editor anterior carregou seu arquivo de trabalho para o diretório pai do alvo, que muitas vezes não pode ser escrito pela conta SSH sob `/etc` ou `/var`. SFTP renomeou não forneceu nenhum caminho de sudo autorizado, verificação de conflito de linha de base, verificação de metadados de segurança ou semântica de substituição confiável. Seu manipulador de erros excluiu a cópia temporária, removendo o material de recuperação mesmo quando a conclusão era incerta. O status da sessão expôs anteriormente apenas o estado da conexão. Não houve medição de resposta ICMP ou SSH autenticada alimentando o indicador terminal. ## Salvar protocolo 1. Um auxiliar SSH fixo abre um arquivo regular de link único usando descritor-relativo operações e `O_NOFOLLOW` para cada componente de caminho. Ele captura o conteúdo, SHA-256 hash, dispositivo/inode, UID, GID, bits de permissão completos, tempos de modificação / alteração de nanosegundos e todos os atributos estendidos legíveis. O Core verifica o hash e decodifica um UTF-8 rigoroso, preservando uma BOM. Cada documento recebe um `editId` opaco, com escopo para seu plugin, sessão de terminal e identidade do servidor. 2. Uma operação de preparação sem privilégios resolve a conta autenticada home através de `pwd.getpwuid(os.getuid())`, cria `$HOME/ch-j-sm/` com o modo `0700` e cria um diário de recuperação privado e exclusivo. Uma casa insegura, um espaço de trabalho do symlink ou um espaço de trabalhos pertencente a outra conta são rejeitados. 3. Core carrega um oculto, aleatório 128-bit `.tmp` arquivo de trabalho através SFTP com criação exclusiva (`wx`), modo `0600` e um tempo limite de canal/upload de sessenta segundos. A revista contém o alvo, a linha de base original e hash de trabalho esperado; não contém credenciais. 4. **One SSH executa toda a finalização **, opcionalmente através do executor sudo existente. O ajudante abre com segurança e hash a cópia de trabalho privada e limpa-lo. Ele bloqueia o inode original, verifica sua linha de base, cria um arquivo de destino exclusivo ao lado do alvo, escreve todo o conteúdo, aplica e verifica metadados, verifica o original novamente e limpa a substituição. 5. Um mesmo diretório `os.replace` substitui atomicamente o alvo. O pai diretório é flushed e o caminho final, hash, inode e metadados são verificados. O caminho original permanece disponível até a substituição. O arquivo de trabalho nunca é movido através de sistemas de arquivos e nunca é removido por finalização. 6. Depois que o Core recebe um sucesso verificado, uma limpeza separada verifica novamente a versão final. metadados de conteúdo e segurança antes de remover o arquivo e o diário de trabalho. Falha de limpeza ainda relata um salvamento confirmado com `recoveryAvailable: true`. Um upload com falha, sudo com falha, conflito, erro de metadados ou substituição com falha mantém o material original e qualquer material encenado. Uploads incompletos são identificados como tal e não podem ser recuperados como um documento editado completo. A comunicação perdida do SSH, o status de saída ausente, os tempos limite e os resultados inesperados da finalização são relatados como **unknown**. O Core bloqueia outro salvar em um documento incerto; não há nenhuma tentativa de substituição automática. ## Metadados, privilégios e limites de segurança O ajudante preserva e verifica UID, GID e todos os bits de permissão POSIX, incluindo executáveis, setuid, setgid e sticky bits. Ele reproduz os atributos estendidos Linux byte para byte, incluindo POSIX ACLs, recursos e rótulos SELinux. A propriedade e o conteúdo do arquivo são definidos antes dos atributos de segurança, porque a gravação/corte pode limpar recursos ou bits de permissão especiais. Os atributos de destino herdados que estão ausentes no original são removidos. O tempo de modificação reflete a nova escrita e não é copiado do original. Um atributo ilegível ou irrepresentável aborta a operação; os metadados não são descartados silenciosamente. O assinatura da IMA/EVM é explicitamente rejeitado porque a cópia do conteúdo/integridade ligada ao inodo assinatura invalidaria-os. Symlinks no caminho de destino ou home, arquivos vinculados e arquivos não regulares são rejeitados. Nenhum retorno de substituição no local ou mais fraco do SFTP é usado. As salvas graváveis comuns são executadas como a conta SSH. Salvamentos protegidos exigem `{ sudo: true }` explícito; uma senha sudo é opcional para sudo sem senha. As senhas são validadas e passadas apenas no canal stdin, nunca dentro do comando, diário, arquivo de trabalho, logger ou erro remoto. As sessões de root SSH usam o executor raiz direto existente. A autorização falhada deixa a cópia original e funcional intacta. Todos os salvamentos executados como root (raiz direta SSH ou sudo) também rejeitam diretórios/ancestrais de destino pertencentes ou graváveis por outras contas, ou que carregam um ACL de acesso, em vez de confiar em um nome de caminho privilegiado controlado por outra conta. O auxiliar fixo é executado com `python3 -I -B`: ignora caminhos Python do usuário, o diretório de trabalho e os pacotes do site do usuário e não escreve nenhum bytecode. Os argumentos Paths e JSON são citados em shell no Core. Plugins nunca recebem comandos arbitrários SSH exec ou SFTP. sandbox existente, CSP, permissões de manifesto, verificação de chave de host e verificação atualização permanecem em uso. ## Plugin API e restante integração Monaco **A interface de usuário distribuída separadamente do Monaco/File Manager está ausente neste checkout. Sua integração com a interface do usuário, sudo prompt, manipulação de buffer sujo e traduções não puderam ser implementadas ou testadas aqui.** A fonte do plugin empacotado é o plug-in Hash & Checksum. Nenhuma fonte substituta do File Manager foi inventada. Os métodos existentes permanecem disponíveis. Os métodos/opções aditivos são expostos pela pré-carga com sandbox e pelo tempo de execução verificado por permissão: ```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. ``` Os métodos de recuperação aceitam um argumento final `{ sudo: true, sudoPassword }` opcional quando inspecionar um alvo requer elevação. Eles mantêm o espaço de trabalho do usuário autenticado mesmo quando executado através do sudo. Os diários sobrevivem à reinicialização do aplicativo e ao SSH se reconectar; a recuperação nunca repete automaticamente a finalização. Revise um buffer recuperado contra um alvo recém-aberto antes de salvá-lo explicitamente. `files.read` guarda leituras, inspeção e liberação de alça. `files.write` guarda a limpeza e a recuperação. `writeText` ainda rejeita a falha para plugins existentes; `saveText` retorna um envelope `{ ok, value/error }` estruturado porque Electron deixa cair propriedades personalizadas em objetos de erro rejeitados. Um `writeText` legado de três argumentos pode usar a linha de base de um único documento aberto. Vários documentos vivos para o mesmo caminho exigem `editId`; chamadas legadas ambíguas falham com segurança em vez de escolher a linha de base de outro documento. Plugins devem liberar alças não utilizadas. A interface do usuário externa deve exibir o salvamento imediatamente, manter os buffers sujos na falha, oferecer sudo somente após falhas de permissão/autenticação, mostrar conflitos sem sobrescrever e oferecer recuperação após perda de conexão ou um resultado desconhecido. Ele deve traduzir esses estados em checo, alemão e inglês. Sugestão de cópia do estado: | 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 | ## Definições de latência e ciclo de vida **Ping** é a latência relatada por um cliente real ICMP resposta eco ao IP resolvido usado por SSH. O Core invoca o executável ping da plataforma de forma assíncrona com um array de argumento fixo, sem uma necessidade de inicialização de shell ou administrador. Os argumentos Linux, macOS (incluindo `ping6`) e Windows são selecionados separadamente. Bloqueado ICMP, um executável ausente, um timeout, saída inválida ou uma resposta Windows somente superior como `<1ms` produzem um valor indisponível, não um número inventado. **SSH RTT** é o monotônico `performance.now()` Duração de um fixo `true` requisição exec sobre o autenticado existente SSH cliente. Ele inclui configuração de canal, processamento de servidor e entrega de resposta; não é pura rede RTT ou duração de login SSH. A dica de ferramenta do indicador traduzido explica essa distinção. O monitoramento começa assim que o terminal se conecta. Sondas são executadas de forma simultânea e assíncrona, com um tempo limite de solicitação/processo de cinco segundos. A próxima amostra é programada doze segundos após a amostra anterior ser concluída, evitando sobreposição. Desconecte os abortamentos ICMP, limpa os temporizadores, cancela o trabalho SSH pendente e limpa os valores. As verificações de identidade de registro impedem que uma conexão anterior seja publicada após a reconexão. A falha do ICMP nunca altera o estado da conexão SSH. A latência viaja dentro do evento `state` existente / carga IPC `ssh:state`: ```js latency: { pingMs: 24.1, sshRttMs: 31.25, measuredAt: "…" } ``` Os números indisponíveis são `null`. O indicador de terminal compacto usa milissegundos, texto indisponível localizado e etiquetas Czech/German/English. Esconde-se na desconexão. Não foi necessária nenhuma capacidade adicional de execução do renderizador ou solicitação IPC. ## Validação e limites operacionais - Execute a partir de `app/`: `npm ci`, então `npm test`. - Resultado local final: **179 Ensaios; 163 passou, 0 falhou, 16 pulou**. Todos os saltos são os casos do sistema de arquivos Linux neste host macOS. `npm ci`, compilação de sintaxe Python e `git diff --check` concluídos com sucesso. - Os testes cobrem Core saves/recuperação, protocolo sudo e senha de redação, real POSIX shell citação de caminhos hostis, conflitos, uploads, falha na limpeza, resultados incertos, documentos simultâneos, permissões IPC e ciclo de vida de latência. - Os testes do sistema de arquivos Linux exercitam o ponto de entrada Python enviado, usando um test account-home lookup: ownership/mode/xattrs, POSIX ACLs, symlinks/hardlinks, FIFOs, falhas de upload e metadados, falhas de substituição, corridas de destino, resultados pós-substituição desconhecidos e recuperação. O cross-filesystem staging usa o `/dev/shm` e outro sistema de arquivos quando disponível. Esses testes pulam em hosts que não são do Linux; as instalações ACL/separate-filesystem ausentes são explicitamente ignoradas. - O trabalho adicional do Linux GitHub Actions executa o conjunto completo e repete o sistema de arquivos casos como raiz. Este fluxo de trabalho não foi enviado a partir desta árvore de trabalho. - A validação local foi no macOS. A sintaxe da fonte do Python foi verificada com o Ferramentas de linha de comando executável Python. Um verdadeiro loopback macOS ICMP fumaça verificação devolvido `0.071 ms`. Nenhum servidor SSH remoto ao vivo, tempo de execução Windows, tempo de corrida Linux ou implantação SELinux ou interface externa do Monaco foi testado localmente. - A edição remota de texto agora requer Linux e Python 3 com descritor-relativo suporte filesystem/xattr. Um intérprete ausente falha explicitamente. Este Core intencionalmente recusa metadados inseguros, links e configurações de diretório. - O bloqueio é consultivo: os processos não cooperantes ainda podem correr no estreito intervalo entre a última verificação de linha de base e renomeação atômica. Linux não fornece nenhuma operação geral de comparação e substituição de conteúdo atômico-hash. As verificações detectam alterações antes desse intervalo; cópias de trabalho retidas suportam a recuperação depois. - Os dados de recuperação permanecem na conta remota até uma limpeza confirmada. Incompleto, revistas contraditórias e inespectáveis nunca são descartadas silenciosamente. Não há expiração automática de arquivos de recuperação e nenhuma opção de substituição forçada. - `npm ci` relatou resultados de auditoria de dependência 17 (9 moderado, 8 alto). Esta mudança não altera dependências ou o modelo de confiança atualização do aplicativo. O usuário solicitou manter as alterações na árvore de trabalho existente, atualmente `feature/hash-checksum-plugin`; nenhum interruptor de ramificação ou limpeza não relacionada foi realizada. ## Arquivos alterados para esta tarefa - `src/main/files/remoteFileService.js` - `src/main/files/remoteEditor.py` (novo) - `src/main/sessions/sessionManager.js` - `src/main/sessions/latencyMonitor.js` (novo) - `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` (novo) - `test/remoteEditorProtocol.test.js` (novo) - `test/remoteEditorLinux.test.js` (novo) - `test/fixtures/remoteEditorLinux.py` (novo) - `test/latencyMonitor.test.js` (novo) - `README.md` - `docs/remote-editor-latency.md` (este arquivo) - `../.github/workflows/remote-editor-tests.yml` (novo)