railmux 0.4.0.dev30__tar.gz → 0.4.0.dev31__tar.gz
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.
- {railmux-0.4.0.dev30/src/railmux.egg-info → railmux-0.4.0.dev31}/PKG-INFO +1 -1
- railmux-0.4.0.dev31/src/railmux/__init__.py +1 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/cli.py +27 -1
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_install_log.py +7 -1
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_pacman.py +24 -3
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31/src/railmux.egg-info}/PKG-INFO +1 -1
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_cli.py +75 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_install_log.py +1 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_pacman.py +35 -0
- railmux-0.4.0.dev30/src/railmux/__init__.py +0 -1
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/LICENSE +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/MANIFEST.in +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/README.md +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/pyproject.toml +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/setup.cfg +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/__main__.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/atomic_file.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/background_index.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/codex_index.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/config.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/config_cli.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/diagnostics.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/discovery.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/display_transport.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/entrypoint.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/fast_display_client.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/fast_display_history.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/fast_display_input.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/fast_display_protocol.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/fast_display_server.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/favorites.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/fuzzy.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/help_workspace.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/launcher.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/legacy_sessions.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/local_clipboard.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/local_open.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/models.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/modes.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/mouse_manager.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/orphan_marker.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/pane_surface.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/path_codec.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/provider_paths.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/remote_config.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/renames.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/restart_state.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/runtime_config.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/scroll_agent.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/scroll_manager.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/selection_isolation.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/self_update.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/session_cache.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/session_index.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/session_lease.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/setting_contracts.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/settings.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ssh_args.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ssh_compat.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ssh_display_diagnostics.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ssh_doctor.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/system_deps.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/terminal_status.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/tmux_binding_manager.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/tmux_ctl.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/tmux_health.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/tmux_server.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/tool_panes.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/transcript.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/__init__.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/_widgets.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/app.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/keymap.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/modals.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/projects_pane.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/running_pane.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/sessions_pane.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/sidebar.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/statusbar.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/ui/workspace.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_attach_relay.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_bootstrap.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_msys2.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_paths.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_tmux_lifecycle.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux/windows_ui_transition.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux.egg-info/SOURCES.txt +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux.egg-info/dependency_links.txt +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux.egg-info/entry_points.txt +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux.egg-info/requires.txt +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/src/railmux.egg-info/top_level.txt +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_agent_workspace.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_atomic_file.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_background_index.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_codex_delete.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_codex_index.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_codex_lineage.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_codex_transcript.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_config.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_config_cli.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_denested_transport_benchmark.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_diagnostics.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_discovery.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_display_transport.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_fast_full_window.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_focus_highlight.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_fullscreen_binding.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_fuzzy.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_help_agent.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_help_workspace.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_history_cleanup.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_keymap.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_launcher.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_layout_profile.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_legacy_sessions.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_local_clipboard.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_local_open.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_modals.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_mode_selection.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_modes.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_mouse_manager.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_new_project.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_options.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_orphan_recovery.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_pane_surface.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_path_codec.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_projects_pane.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_provider_paths.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_release_notes.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_release_workflow.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_remote_config.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_renames.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_restart_state.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_running_pane.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_running_sort.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_runtime_config.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_scroll_agent.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_scroll_manager.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_selection_isolation.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_self_update.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_session_cache.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_session_index.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_session_lease.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_sessions_pane.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_setting_contracts.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_settings.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_sidebar.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_soft_quit.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_ssh_args.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_ssh_compat.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_ssh_display_benchmark.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_ssh_display_diagnostics.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_ssh_doctor.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_status.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_statusbar.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_system_deps.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_terminal_size.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_terminal_status.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_termux_local_touch.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_tmux_binding_manager.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_tmux_ctl.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_tmux_health.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_tmux_integration.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_tmux_server.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_tmux_status.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_tool_panes.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_transcript.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_utf8_state.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_web_demo_recorder.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_wheel_smoke.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_widgets.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_attach_relay.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_bootstrap.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_msys2.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_paths.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_tmux_lifecycle.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_ui_transition.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_windows_wrapper_contract.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tests/test_yolo_prompt.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tools/denested_transport_benchmark.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tools/release_notes.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tools/wheel_smoke.py +0 -0
- {railmux-0.4.0.dev30 → railmux-0.4.0.dev31}/tools/windows_archive_smoke.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0.dev31"
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import os
|
|
5
|
+
import signal
|
|
5
6
|
import shutil
|
|
6
7
|
import subprocess
|
|
7
8
|
import sys
|
|
@@ -109,6 +110,9 @@ def _run_tmux_client_with_watchdog(
|
|
|
109
110
|
attributes = termios.tcgetattr(sys.stdin.fileno())
|
|
110
111
|
except (OSError, termios.error):
|
|
111
112
|
pass
|
|
113
|
+
direct_terminal_size = (
|
|
114
|
+
_interactive_terminal_size() if managed_windows else None
|
|
115
|
+
)
|
|
112
116
|
started_at = time.monotonic()
|
|
113
117
|
try:
|
|
114
118
|
popen_kwargs: dict[str, object] = {"env": env}
|
|
@@ -127,7 +131,7 @@ def _run_tmux_client_with_watchdog(
|
|
|
127
131
|
*,
|
|
128
132
|
asynchronous_probe: bool = False,
|
|
129
133
|
) -> tuple[int, bool]:
|
|
130
|
-
nonlocal expected_target, expected_session_id
|
|
134
|
+
nonlocal direct_terminal_size, expected_target, expected_session_id
|
|
131
135
|
watchdog = tmux_health.FailureWatchdog.starting(
|
|
132
136
|
(
|
|
133
137
|
time.monotonic()
|
|
@@ -151,6 +155,28 @@ def _run_tmux_client_with_watchdog(
|
|
|
151
155
|
pump = getattr(process, "pump", None)
|
|
152
156
|
if pump is None:
|
|
153
157
|
time.sleep(0.25)
|
|
158
|
+
if managed_windows:
|
|
159
|
+
current_size = _interactive_terminal_size()
|
|
160
|
+
if (
|
|
161
|
+
current_size is not None
|
|
162
|
+
and current_size != direct_terminal_size
|
|
163
|
+
):
|
|
164
|
+
direct_terminal_size = current_size
|
|
165
|
+
pid = getattr(process, "pid", None)
|
|
166
|
+
if (
|
|
167
|
+
isinstance(pid, int)
|
|
168
|
+
and not isinstance(pid, bool)
|
|
169
|
+
and pid > 0
|
|
170
|
+
):
|
|
171
|
+
try:
|
|
172
|
+
# MSYS2 occasionally misses the native
|
|
173
|
+
# console's resize notification. Signal only
|
|
174
|
+
# this launcher's direct tmux client so it
|
|
175
|
+
# re-reads both dimensions; tmux remains the
|
|
176
|
+
# authority for shared-window sizing.
|
|
177
|
+
os.kill(pid, signal.SIGWINCH)
|
|
178
|
+
except (OSError, ValueError):
|
|
179
|
+
pass
|
|
154
180
|
else:
|
|
155
181
|
pump(0.25)
|
|
156
182
|
now = time.monotonic()
|
|
@@ -324,7 +324,13 @@ class InstallReporter:
|
|
|
324
324
|
if package_count is not None:
|
|
325
325
|
self._package_total = int(package_count.group(1))
|
|
326
326
|
self._package_step = max(1, self._package_total // 10)
|
|
327
|
-
self._progress_detail =
|
|
327
|
+
self._progress_detail = (
|
|
328
|
+
f"waiting for the first transfer among "
|
|
329
|
+
f"{self._package_total} packages"
|
|
330
|
+
)
|
|
331
|
+
self.note(
|
|
332
|
+
f"Package transaction resolved: {self._package_total} packages."
|
|
333
|
+
)
|
|
328
334
|
return
|
|
329
335
|
total_download = _TOTAL_DOWNLOAD_RE.match(line)
|
|
330
336
|
if total_download is not None:
|
|
@@ -30,7 +30,9 @@ _MIRRORLIST_LIMIT = 256 * 1024
|
|
|
30
30
|
_PACMAN_CONF_LIMIT = 256 * 1024
|
|
31
31
|
_PROBE_BYTES = 256 * 1024
|
|
32
32
|
_PROBE_TIMEOUT = 8.0
|
|
33
|
+
_PACKAGE_PROBE_BYTES = 256 * 1024
|
|
33
34
|
_PACKAGE_PROBE_TIMEOUT = 5.0
|
|
35
|
+
_PACKAGE_PROBE_MIN_RATE = 128 * 1024
|
|
34
36
|
_SWITCH_RATIO = 1.25
|
|
35
37
|
_FRESHNESS_WINDOW_SECONDS = 6 * 60 * 60
|
|
36
38
|
_ZSTD_MAGIC = b"\x28\xb5\x2f\xfd"
|
|
@@ -91,18 +93,21 @@ def _probe_package_url(
|
|
|
91
93
|
url: str,
|
|
92
94
|
*,
|
|
93
95
|
opener: Callable[..., object] = urllib.request.urlopen,
|
|
96
|
+
clock: Callable[[], float] = time.monotonic,
|
|
94
97
|
) -> None:
|
|
95
|
-
"""Require
|
|
98
|
+
"""Require a responsive real package path, not only database access."""
|
|
96
99
|
if urllib.parse.urlsplit(url).scheme.lower() != "https":
|
|
97
100
|
raise PacmanMirrorError("package URL is not HTTPS")
|
|
98
101
|
request = urllib.request.Request(
|
|
99
102
|
url,
|
|
100
103
|
headers={
|
|
101
104
|
"Accept-Encoding": "identity",
|
|
102
|
-
"Range": "bytes=0-
|
|
105
|
+
"Range": f"bytes=0-{_PACKAGE_PROBE_BYTES - 1}",
|
|
103
106
|
"User-Agent": "Railmux-pacman-package-probe/1",
|
|
104
107
|
},
|
|
105
108
|
)
|
|
109
|
+
started = clock()
|
|
110
|
+
data = bytearray()
|
|
106
111
|
try:
|
|
107
112
|
with opener(request, timeout=_PACKAGE_PROBE_TIMEOUT) as response:
|
|
108
113
|
final_url = getattr(response, "geturl", lambda: request.full_url)()
|
|
@@ -111,15 +116,31 @@ def _probe_package_url(
|
|
|
111
116
|
status = getattr(response, "status", 200)
|
|
112
117
|
if status not in (200, 206):
|
|
113
118
|
raise PacmanMirrorError(f"package returned HTTP {status}")
|
|
114
|
-
data
|
|
119
|
+
while len(data) < _PACKAGE_PROBE_BYTES:
|
|
120
|
+
chunk = response.read(min(
|
|
121
|
+
64 * 1024, _PACKAGE_PROBE_BYTES - len(data)
|
|
122
|
+
))
|
|
123
|
+
if not chunk:
|
|
124
|
+
break
|
|
125
|
+
data.extend(chunk)
|
|
126
|
+
if clock() - started >= _PACKAGE_PROBE_TIMEOUT:
|
|
127
|
+
break
|
|
115
128
|
except PacmanMirrorError:
|
|
116
129
|
raise
|
|
117
130
|
except urllib.error.HTTPError as exc:
|
|
118
131
|
raise PacmanMirrorError(f"package returned HTTP {exc.code}") from exc
|
|
119
132
|
except (OSError, urllib.error.URLError, http.client.HTTPException) as exc:
|
|
120
133
|
raise PacmanMirrorError("package probe failed") from exc
|
|
134
|
+
elapsed = max(clock() - started, 0.001)
|
|
135
|
+
if len(data) < 1024:
|
|
136
|
+
raise PacmanMirrorError("mirror returned too little package data")
|
|
121
137
|
if not data.startswith(_ZSTD_MAGIC):
|
|
122
138
|
raise PacmanMirrorError("mirror did not return an MSYS package")
|
|
139
|
+
if (
|
|
140
|
+
elapsed >= _PACKAGE_PROBE_TIMEOUT
|
|
141
|
+
or len(data) / elapsed < _PACKAGE_PROBE_MIN_RATE
|
|
142
|
+
):
|
|
143
|
+
raise PacmanMirrorError("package transfer was too slow")
|
|
123
144
|
|
|
124
145
|
|
|
125
146
|
def probe_pacman_mirror(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import signal
|
|
2
3
|
import subprocess
|
|
3
4
|
import sys
|
|
4
5
|
from unittest.mock import MagicMock, patch
|
|
@@ -632,6 +633,80 @@ def test_local_tmux_watchdog_exits_and_records_after_consecutive_failures(
|
|
|
632
633
|
assert "stopped responding" in capsys.readouterr().err
|
|
633
634
|
|
|
634
635
|
|
|
636
|
+
def test_managed_windows_direct_attach_notifies_height_only_resize(monkeypatch):
|
|
637
|
+
target = TmuxServerTarget("/tmp/railmux", 77)
|
|
638
|
+
|
|
639
|
+
class DirectClient:
|
|
640
|
+
pid = 321
|
|
641
|
+
returncode = None
|
|
642
|
+
|
|
643
|
+
def poll(self):
|
|
644
|
+
return self.returncode
|
|
645
|
+
|
|
646
|
+
client = DirectClient()
|
|
647
|
+
monkeypatch.setattr(
|
|
648
|
+
"railmux.cli.subprocess.Popen", lambda *_args, **_kwargs: client)
|
|
649
|
+
monkeypatch.setattr("railmux.cli.sys.stdin.isatty", lambda: True)
|
|
650
|
+
monkeypatch.setattr("railmux.cli.sys.stdout.isatty", lambda: True)
|
|
651
|
+
monkeypatch.setattr("railmux.cli.termios.tcgetattr", lambda _fd: ["saved"])
|
|
652
|
+
sizes = [os.terminal_size((120, 40)), os.terminal_size((120, 55))]
|
|
653
|
+
monkeypatch.setattr(
|
|
654
|
+
"railmux.cli.os.get_terminal_size",
|
|
655
|
+
lambda _fd: sizes.pop(0) if len(sizes) > 1 else sizes[0],
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
def finish_after_poll(_seconds):
|
|
659
|
+
client.returncode = 0
|
|
660
|
+
|
|
661
|
+
monkeypatch.setattr("railmux.cli.time.sleep", finish_after_poll)
|
|
662
|
+
monkeypatch.setattr(
|
|
663
|
+
"railmux.provider_paths.running_in_managed_windows_wrapper",
|
|
664
|
+
lambda: True,
|
|
665
|
+
)
|
|
666
|
+
notify = MagicMock()
|
|
667
|
+
monkeypatch.setattr("railmux.cli.os.kill", notify)
|
|
668
|
+
|
|
669
|
+
assert _run_tmux_client_with_watchdog(
|
|
670
|
+
["tmux", "-L", "railmux"], {}, expected_target=target,
|
|
671
|
+
expected_session_id="$7",
|
|
672
|
+
) == 0
|
|
673
|
+
notify.assert_called_once_with(321, signal.SIGWINCH)
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def test_posix_direct_attach_does_not_poll_or_signal_resize(monkeypatch):
|
|
677
|
+
class DirectClient:
|
|
678
|
+
pid = 321
|
|
679
|
+
returncode = None
|
|
680
|
+
|
|
681
|
+
def poll(self):
|
|
682
|
+
return self.returncode
|
|
683
|
+
|
|
684
|
+
client = DirectClient()
|
|
685
|
+
monkeypatch.setattr(
|
|
686
|
+
"railmux.cli.subprocess.Popen", lambda *_args, **_kwargs: client)
|
|
687
|
+
monkeypatch.setattr("railmux.cli.sys.stdin.isatty", lambda: True)
|
|
688
|
+
monkeypatch.setattr("railmux.cli.termios.tcgetattr", lambda _fd: ["saved"])
|
|
689
|
+
|
|
690
|
+
def finish_after_poll(_seconds):
|
|
691
|
+
client.returncode = 0
|
|
692
|
+
|
|
693
|
+
monkeypatch.setattr("railmux.cli.time.sleep", finish_after_poll)
|
|
694
|
+
monkeypatch.setattr(
|
|
695
|
+
"railmux.provider_paths.running_in_managed_windows_wrapper",
|
|
696
|
+
lambda: False,
|
|
697
|
+
)
|
|
698
|
+
get_size = MagicMock()
|
|
699
|
+
notify = MagicMock()
|
|
700
|
+
monkeypatch.setattr("railmux.cli.os.get_terminal_size", get_size)
|
|
701
|
+
monkeypatch.setattr("railmux.cli.os.kill", notify)
|
|
702
|
+
|
|
703
|
+
assert _run_tmux_client_with_watchdog(
|
|
704
|
+
["tmux", "-L", "railmux"], {},
|
|
705
|
+
) == 0
|
|
706
|
+
get_size.assert_not_called()
|
|
707
|
+
notify.assert_not_called()
|
|
708
|
+
|
|
709
|
+
|
|
635
710
|
def test_abrupt_tmux_client_exit_records_disappeared_server(monkeypatch):
|
|
636
711
|
target = TmuxServerTarget("/tmp/railmux", 77)
|
|
637
712
|
|
|
@@ -310,6 +310,7 @@ def test_compact_reporter_surfaces_extraction_and_package_progress(tmp_path):
|
|
|
310
310
|
assert "Extracting private runtime: 42%" in rendered
|
|
311
311
|
assert "Extracting private runtime: 100%" in rendered
|
|
312
312
|
assert "Package transaction: 48 packages, 57.40 MiB download." in rendered
|
|
313
|
+
assert "Package transaction resolved: 48 packages." in rendered
|
|
313
314
|
assert "Downloading packages: 1/48" in rendered
|
|
314
315
|
assert "Verifying downloaded package signatures" in rendered
|
|
315
316
|
assert "Installing packages: 1/48" in rendered
|
|
@@ -10,6 +10,7 @@ from railmux.windows_pacman import (
|
|
|
10
10
|
PACMAN_MIRROR_SOURCES,
|
|
11
11
|
PacmanMirrorError,
|
|
12
12
|
PacmanMirrorProbe,
|
|
13
|
+
_probe_package_url,
|
|
13
14
|
deactivate_pacman_hosts,
|
|
14
15
|
optimize_pacman_mirror,
|
|
15
16
|
probe_pacman_mirror,
|
|
@@ -231,6 +232,40 @@ def test_network_probe_is_bounded_and_rejects_an_https_downgrade():
|
|
|
231
232
|
assert requests[0][1] == 8.0
|
|
232
233
|
|
|
233
234
|
|
|
235
|
+
def test_package_probe_measures_a_real_bounded_transfer():
|
|
236
|
+
requests = []
|
|
237
|
+
|
|
238
|
+
def opener(request, *, timeout):
|
|
239
|
+
requests.append((request, timeout))
|
|
240
|
+
return FakeResponse(
|
|
241
|
+
b"\x28\xb5\x2f\xfd" + b"x" * (256 * 1024),
|
|
242
|
+
final_url="https://mirror.invalid/python.pkg.tar.zst",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
_probe_package_url(
|
|
246
|
+
"https://mirror.invalid/python.pkg.tar.zst",
|
|
247
|
+
opener=opener,
|
|
248
|
+
clock=lambda: 1.0,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
assert requests[0][0].get_header("Range") == "bytes=0-262143"
|
|
252
|
+
assert requests[0][1] == 5.0
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_package_probe_rejects_a_working_but_slow_source():
|
|
256
|
+
times = [0.0, 3.0]
|
|
257
|
+
|
|
258
|
+
with pytest.raises(PacmanMirrorError, match="too slow"):
|
|
259
|
+
_probe_package_url(
|
|
260
|
+
"https://mirror.invalid/python.pkg.tar.zst",
|
|
261
|
+
opener=lambda _request, **_kwargs: FakeResponse(
|
|
262
|
+
b"\x28\xb5\x2f\xfd" + b"x" * (256 * 1024),
|
|
263
|
+
final_url="https://mirror.invalid/python.pkg.tar.zst",
|
|
264
|
+
),
|
|
265
|
+
clock=lambda: times.pop(0) if len(times) > 1 else times[0],
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
234
269
|
def test_optimizer_limits_active_pool_but_reprobes_inactive_candidates(tmp_path):
|
|
235
270
|
mirrorlist = write_mirrorlist(tmp_path)
|
|
236
271
|
nju = PACMAN_MIRROR_SOURCES[-1][1]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.0.dev30"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|