diff --git a/install.ps1 b/install.ps1 index 9990805..7d6c8c8 100644 --- a/install.ps1 +++ b/install.ps1 @@ -426,6 +426,27 @@ escape_key = "^]" "@ | Set-Content -NoNewline -Encoding utf8 $clientConfig } +$hostsConfig = Join-Path $configDir "hosts.toml" +if ($ForceConfig -or -not (Test-Path $hostsConfig)) { + $defaultServer = if ($Server) { $Server } else { "user@example.com" } + $hostUdp = if ($DoshHost) { $DoshHost } else { $defaultServer } + @" +# Example: +# [server] +# ssh = "server" +# dosh_host = "server.example.com" +# port = 50000 +# default_command = "tm" +# predict = true + +[default] +ssh = "$defaultServer" +dosh_host = "$hostUdp" +port = $Port +predict = true +"@ | Set-Content -NoNewline -Encoding utf8 $hostsConfig +} + $userPath = [Environment]::GetEnvironmentVariable("Path", "User") if (-not (($userPath -split ';') -contains $bindir)) { [Environment]::SetEnvironmentVariable("Path", "$userPath;$bindir", "User") @@ -442,5 +463,6 @@ Write-Host " $bindir\dosh.exe update --check" Write-Host "" Write-Host "Client config:" Write-Host " $configDir\client.toml" +Write-Host " $configDir\hosts.toml" Write-Host "" Write-Host "Open a new terminal for PATH changes to apply." diff --git a/src/bin/dosh-server.rs b/src/bin/dosh-server.rs index 8fe21d6..d12a778 100644 --- a/src/bin/dosh-server.rs +++ b/src/bin/dosh-server.rs @@ -813,6 +813,10 @@ fn attach_snapshot(session: &Session, cols: u16, rows: u16) -> Vec { screen_snapshot(session.parser.screen()) } +fn restored_screen_is_pending_reattach(session: &Session) -> bool { + session.restored_screen.is_some() +} + fn mode_allows_terminal_updates(mode: &str) -> bool { mode != "view-only" && mode != "forward-only" } @@ -3707,7 +3711,7 @@ async fn broadcast_output( // short wall-clock age so low-throughput prompt/TUI changes persist // quickly while large bursts still avoid per-packet disk writes. The // actual file write happens after the lock is dropped. - if session.persistent { + if session.persistent && !restored_screen_is_pending_reattach(session) { session.bytes_since_persist = session .bytes_since_persist .saturating_add(output.bytes.len()); @@ -4060,7 +4064,10 @@ fn flush_persistent_screens(state: &Arc>) { let sessions_dir = locked.sessions_dir(); let mut to_write: Vec<(String, u16, u16, u64, Vec)> = Vec::new(); for (name, session) in locked.sessions.iter_mut() { - if !session.persistent || session.output_seq == session.last_persisted_seq { + if !session.persistent + || restored_screen_is_pending_reattach(session) + || session.output_seq == session.last_persisted_seq + { continue; } session.bytes_since_persist = 0; @@ -4411,6 +4418,20 @@ mod tests { let session = locked.sessions.get("work").unwrap(); assert!(session.restored_screen.is_some()); assert_eq!(attach_snapshot(session, 80, 24), restored); + assert_eq!( + session.last_persisted_seq, 7, + "pre-reattach PTY output must not overwrite the persisted restored screen" + ); + drop(locked); + + flush_persistent_screens(&state); + + let locked = state.lock().unwrap(); + let session = locked.sessions.get("work").unwrap(); + assert_eq!( + session.last_persisted_seq, 7, + "periodic flush must not replace restored screen before client-visible output" + ); } #[test] diff --git a/tests/release_scripts.rs b/tests/release_scripts.rs index df301c9..58d635a 100644 --- a/tests/release_scripts.rs +++ b/tests/release_scripts.rs @@ -157,6 +157,29 @@ fn installers_generate_same_native_auth_client_defaults() { } } +#[test] +fn windows_installer_generates_hosts_config_like_unix() { + let install = include_str!("../install.sh"); + let ps1 = include_str!("../install.ps1"); + for setting in [ + "hosts.toml", + "# default_command = \"tm\"", + "[default]", + "predict = true", + ] { + assert!( + install.contains(setting), + "Unix installer missing host config setting {setting}" + ); + assert!( + ps1.contains(setting), + "Windows installer missing host config setting {setting}" + ); + } + assert!(ps1.contains("$hostUdp = if ($DoshHost) { $DoshHost } else { $defaultServer }")); + assert!(ps1.contains("Set-Content -NoNewline -Encoding utf8 $hostsConfig")); +} + #[test] fn package_check_verifies_only_artifacts_it_builds() { let makefile = include_str!("../Makefile");