machineconfig 8.14__py3-none-any.whl → 8.50__py3-none-any.whl
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.
Potentially problematic release.
This version of machineconfig might be problematic. Click here for more details.
- machineconfig/cluster/remote/run_cluster.py +1 -1
- machineconfig/cluster/remote/run_remote.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +10 -8
- machineconfig/cluster/sessions_managers/wt_local.py +1 -1
- machineconfig/cluster/sessions_managers/wt_local_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
- machineconfig/jobs/installer/checks/check_installations.py +133 -0
- machineconfig/jobs/installer/checks/install_utils.py +132 -0
- machineconfig/jobs/installer/checks/report_utils.py +39 -0
- machineconfig/jobs/installer/checks/vt_utils.py +89 -0
- machineconfig/jobs/installer/installer_data.json +225 -140
- machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
- machineconfig/jobs/installer/package_groups.py +10 -9
- machineconfig/jobs/installer/python_scripts/boxes.py +1 -2
- machineconfig/jobs/installer/python_scripts/code.py +10 -8
- machineconfig/jobs/installer/python_scripts/hx.py +30 -13
- machineconfig/jobs/installer/python_scripts/nerfont_windows_helper.py +6 -5
- machineconfig/jobs/installer/python_scripts/sysabc.py +25 -19
- machineconfig/jobs/installer/python_scripts/yazi.py +33 -17
- machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
- machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +1 -1
- machineconfig/jobs/scripts_dynamic/a.py +413 -10
- machineconfig/profile/create_links.py +77 -20
- machineconfig/profile/create_links_export.py +63 -58
- machineconfig/profile/mapper_data.toml +30 -0
- machineconfig/profile/mapper_dotfiles.toml +253 -0
- machineconfig/scripts/python/agents.py +70 -172
- machineconfig/scripts/python/ai/initai.py +3 -1
- machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +7 -5
- machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
- machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +29 -0
- machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
- machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
- machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
- machineconfig/scripts/python/ai/utils/vscode_tasks.py +6 -3
- machineconfig/scripts/python/cloud.py +58 -11
- machineconfig/scripts/python/croshell.py +4 -156
- machineconfig/scripts/python/devops.py +57 -40
- machineconfig/scripts/python/devops_navigator.py +17 -3
- machineconfig/scripts/python/fire_jobs.py +8 -207
- machineconfig/scripts/python/ftpx.py +5 -225
- machineconfig/scripts/python/graph/cli_graph.json +8743 -0
- machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
- machineconfig/scripts/python/{env_manager → helpers/helper_env}/env_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_gemini.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_qwen.py +1 -1
- machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_launch.py +5 -5
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +6 -6
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +10 -5
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +3 -3
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +225 -0
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +7 -6
- machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +267 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +98 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +76 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_nw.py +52 -72
- machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +274 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_self.py +40 -23
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_file.py +44 -30
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_server.py +26 -43
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_terminal.py +12 -6
- machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +12 -6
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
- machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +68 -52
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/run_script.py +75 -58
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +3 -3
- machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +3 -3
- machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_linux/fzfg +4 -3
- machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_windows/fzfg.ps1 +1 -1
- machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
- machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
- machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
- machineconfig/scripts/python/helpers/helpers_network/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address.py +15 -17
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address_switch.py +1 -1
- machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_ssh.py +2 -2
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_identity.py +73 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_ssh_key.py +175 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_linux.py +319 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_windows.py +275 -0
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +117 -33
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +3 -2
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -13
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_2.py +63 -19
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
- machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
- machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +186 -0
- machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +1 -1
- machineconfig/scripts/python/helpers/helpers_terminal/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_terminal/terminal_impl.py +96 -0
- machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/download.py +1 -1
- machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/python.py +47 -26
- machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
- machineconfig/scripts/python/mcfg_entry.py +133 -48
- machineconfig/scripts/python/msearch.py +15 -61
- machineconfig/scripts/python/sessions.py +59 -194
- machineconfig/scripts/python/terminal.py +18 -96
- machineconfig/scripts/python/utils.py +101 -20
- machineconfig/settings/atuin/config.toml +294 -0
- machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
- machineconfig/settings/linters/.ruff.toml +1 -0
- machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
- machineconfig/settings/shells/bash/init.sh +6 -3
- machineconfig/settings/shells/pwsh/init.ps1 +69 -1
- machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
- machineconfig/settings/shells/wezterm/wezterm.lua +4 -1
- machineconfig/settings/shells/wt/settings.json +20 -7
- machineconfig/settings/shells/zsh/init.sh +25 -4
- machineconfig/settings/television/cable_unix/bash-history.toml +1 -1
- machineconfig/settings/television/cable_windows/pwsh-history.toml +1 -1
- machineconfig/settings/tv/config.toml +234 -0
- machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
- machineconfig/settings/wsl/.wslconfig +5 -30
- machineconfig/settings/yazi/yazi_linux.toml +18 -8
- machineconfig/settings/zellij/layouts/st.kdl +2 -2
- machineconfig/settings/zellij/layouts/st2.kdl +1 -1
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_linux/web_shortcuts/live_from_github.sh +3 -0
- machineconfig/setup_mac/__init__.py +0 -2
- machineconfig/setup_windows/__init__.py +0 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +14 -13
- machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +4 -3
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -3
- machineconfig/type_hinting/sql/__init__.py +1 -0
- machineconfig/type_hinting/sql/base.py +216 -0
- machineconfig/type_hinting/sql/core_schema.py +64 -0
- machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
- machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
- machineconfig/type_hinting/typedict/__init__.py +1 -0
- machineconfig/type_hinting/typedict/ast_utils.py +130 -0
- machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
- machineconfig/type_hinting/typedict/generators.py +231 -0
- machineconfig/type_hinting/typedict/polars_schema.py +24 -0
- machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
- machineconfig/utils/accessories.py +24 -0
- machineconfig/utils/code.py +41 -13
- machineconfig/utils/files/ascii_art.py +10 -14
- machineconfig/utils/files/headers.py +3 -5
- machineconfig/utils/files/read.py +8 -1
- machineconfig/utils/installer_utils/github_release_bulk.py +11 -91
- machineconfig/utils/installer_utils/github_release_scraper.py +99 -0
- machineconfig/utils/installer_utils/install_from_url.py +1 -1
- machineconfig/utils/installer_utils/installer_class.py +12 -4
- machineconfig/utils/installer_utils/installer_cli.py +1 -15
- machineconfig/utils/installer_utils/installer_helper.py +2 -2
- machineconfig/utils/installer_utils/installer_locator_utils.py +13 -13
- machineconfig/utils/installer_utils/installer_runner.py +4 -4
- machineconfig/utils/io.py +25 -8
- machineconfig/utils/meta.py +6 -4
- machineconfig/utils/options.py +49 -19
- machineconfig/utils/options_utils/__init__.py +0 -0
- machineconfig/utils/options_utils/options_tv_linux.py +211 -0
- machineconfig/utils/options_utils/options_tv_windows.py +88 -0
- machineconfig/utils/options_utils/tv_options.py +37 -0
- machineconfig/utils/path_extended.py +6 -6
- machineconfig/utils/scheduler.py +8 -2
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
- machineconfig/utils/source_of_truth.py +6 -1
- machineconfig/utils/ssh.py +69 -18
- machineconfig/utils/ssh_utils/abc.py +1 -1
- machineconfig/utils/ssh_utils/copy_from_here.py +17 -12
- machineconfig/utils/ssh_utils/utils.py +21 -5
- machineconfig/utils/ssh_utils/wsl.py +107 -170
- machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
- machineconfig/utils/upgrade_packages.py +4 -8
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/METADATA +29 -22
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/RECORD +251 -211
- machineconfig/jobs/installer/check_installations.py +0 -248
- machineconfig/profile/backup.toml +0 -49
- machineconfig/profile/mapper.toml +0 -263
- machineconfig/scripts/python/helpers_devops/cli_config.py +0 -105
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -89
- machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
- machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -208
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
- machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
- machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
- machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -620
- machineconfig/scripts/python/helpers_network/ssh_add_identity.py +0 -116
- machineconfig/scripts/python/helpers_network/ssh_add_ssh_key.py +0 -153
- machineconfig/scripts/python/helpers_network/ssh_debug_linux.py +0 -391
- machineconfig/scripts/python/helpers_network/ssh_debug_windows.py +0 -338
- machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -77
- machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
- machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
- machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
- machineconfig/utils/options_tv.py +0 -119
- machineconfig/utils/tst.py +0 -20
- /machineconfig/{scripts/python/helpers_agents → jobs/installer/checks}/__init__.py +0 -0
- /machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
- /machineconfig/scripts/python/{helpers_agents/agentic_frameworks → graph}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_agents}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_agents/agentic_frameworks}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_helper_types.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aichat/config.yaml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aider/.aider.conf.yml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/copilot/config.yml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/crush/crush.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/gemini/settings.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/privacy.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/prompt.txt +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.sh +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes → helpers/helpers_cloud}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_croshell}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_devops}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_devops/themes}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_fire_command/__init__.py} +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/f.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/file_wrangler.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_streamlit_helper.py +0 -0
- /machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nfs.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nw_drive.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/onetimeshare.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/wifi_conn.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_1.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
- /machineconfig/scripts/python/helpers/{ast_search.py → helpers_search/ast_search.py} +0 -0
- /machineconfig/scripts/python/helpers/{qr_code.py → helpers_search/qr_code.py} +0 -0
- /machineconfig/scripts/python/helpers/{repo_rag.py → helpers_search/repo_rag.py} +0 -0
- /machineconfig/scripts/python/helpers/{symantic_search.py → helpers_search/symantic_search.py} +0 -0
- /machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/pdf.py +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/WHEEL +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/entry_points.txt +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ capturing all user inputs collected during interactive execution.
|
|
|
6
6
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import TypedDict, Literal, NotRequired
|
|
9
|
-
from machineconfig.scripts.python.helpers_agents.fire_agents_help_launch import AGENTS
|
|
9
|
+
from machineconfig.scripts.python.helpers.helpers_agents.fire_agents_help_launch import AGENTS
|
|
10
10
|
|
|
11
11
|
SEARCH_STRATEGIES = Literal["file_path", "keyword_search", "filename_pattern"]
|
|
12
12
|
|
|
@@ -5,7 +5,12 @@ Utils
|
|
|
5
5
|
import machineconfig
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
EXCLUDE_DIRS = [".links", "
|
|
8
|
+
EXCLUDE_DIRS = [".links", "notebooks",
|
|
9
|
+
"GEMINI.md", "CLAUDE.md", "CRUSH.md", "AGENTS.md",
|
|
10
|
+
".cursor", ".clinerules", ".github/instructions", ".github/agents", ".github/prompts",
|
|
11
|
+
".ai",
|
|
12
|
+
".venv", ".git", ".idea", ".vscode", "node_modules", "__pycache__", ".mypy_cache"
|
|
13
|
+
]
|
|
9
14
|
LIBRARY_ROOT = Path(machineconfig.__file__).resolve().parent
|
|
10
15
|
|
|
11
16
|
CONFIG_ROOT = Path.home().joinpath(".config/machineconfig")
|
machineconfig/utils/ssh.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Callable, Optional, Any, cast, Union, Literal
|
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
import platform
|
|
5
|
-
from machineconfig.scripts.python.helpers_utils.python import MachineSpecs
|
|
5
|
+
from machineconfig.scripts.python.helpers.helpers_utils.python import MachineSpecs
|
|
6
6
|
from machineconfig.utils.code import get_uv_command
|
|
7
7
|
import rich.console
|
|
8
8
|
from machineconfig.utils.terminal import Response
|
|
@@ -14,7 +14,6 @@ from machineconfig.utils.ssh_utils.abc import DEFAULT_PICKLE_SUBDIR
|
|
|
14
14
|
class SSH:
|
|
15
15
|
@staticmethod
|
|
16
16
|
def from_config_file(host: str) -> "SSH":
|
|
17
|
-
"""Create SSH instance from SSH config file entry."""
|
|
18
17
|
return SSH(host=host, username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
|
|
19
18
|
|
|
20
19
|
def __init__(
|
|
@@ -148,8 +147,10 @@ class SSH:
|
|
|
148
147
|
def view_bar(self, transferred: int, total: int) -> None:
|
|
149
148
|
if self.progress and self.task is not None:
|
|
150
149
|
self.progress.update(self.task, completed=transferred, total=total)
|
|
150
|
+
|
|
151
151
|
self.tqdm_wrap = RichProgressWrapper
|
|
152
|
-
from machineconfig.scripts.python.helpers_utils.python import get_machine_specs
|
|
152
|
+
from machineconfig.scripts.python.helpers.helpers_utils.python import get_machine_specs
|
|
153
|
+
|
|
153
154
|
self.local_specs: MachineSpecs = get_machine_specs()
|
|
154
155
|
resp = self.run_shell_cmd_on_remote(
|
|
155
156
|
command="""~/.local/bin/utils get-machine-specs """,
|
|
@@ -160,6 +161,7 @@ class SSH:
|
|
|
160
161
|
)
|
|
161
162
|
json_str = resp.op
|
|
162
163
|
import ast
|
|
164
|
+
|
|
163
165
|
self.remote_specs: MachineSpecs = cast(MachineSpecs, ast.literal_eval(json_str))
|
|
164
166
|
self.terminal_responses: list[Response] = []
|
|
165
167
|
|
|
@@ -177,10 +179,22 @@ class SSH:
|
|
|
177
179
|
local_console = rich.console.Console(file=local_buffer, width=40)
|
|
178
180
|
remote_console = rich.console.Console(file=remote_buffer, width=40)
|
|
179
181
|
inspect(
|
|
180
|
-
type("LocalInfo", (object,), dict(self.local_specs))(),
|
|
182
|
+
type("LocalInfo", (object,), dict(self.local_specs))(),
|
|
183
|
+
value=False,
|
|
184
|
+
title="SSHing From",
|
|
185
|
+
docs=False,
|
|
186
|
+
dunder=False,
|
|
187
|
+
sort=False,
|
|
188
|
+
console=local_console,
|
|
181
189
|
)
|
|
182
190
|
inspect(
|
|
183
|
-
type("RemoteInfo", (object,), dict(self.remote_specs))(),
|
|
191
|
+
type("RemoteInfo", (object,), dict(self.remote_specs))(),
|
|
192
|
+
value=False,
|
|
193
|
+
title="SSHing To",
|
|
194
|
+
docs=False,
|
|
195
|
+
dunder=False,
|
|
196
|
+
sort=False,
|
|
197
|
+
console=remote_console,
|
|
184
198
|
)
|
|
185
199
|
local_lines = local_buffer.getvalue().split("\n")
|
|
186
200
|
remote_lines = remote_buffer.getvalue().split("\n")
|
|
@@ -215,18 +229,40 @@ class SSH:
|
|
|
215
229
|
self.copy_from_here(source_path="~/.ssh/id_rsa.pub", target_rel2home=None, compress_with_zip=False, recursive=False, overwrite_existing=False)
|
|
216
230
|
if self.remote_specs["system"] != "Windows":
|
|
217
231
|
raise RuntimeError("send_ssh_key is only supported for Windows remote machines")
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
232
|
+
python_code = '''
|
|
233
|
+
from pathlib import Path
|
|
234
|
+
import subprocess
|
|
235
|
+
sshd_dir = Path("C:/ProgramData/ssh")
|
|
236
|
+
admin_auth_keys = sshd_dir / "administrators_authorized_keys"
|
|
237
|
+
sshd_config = sshd_dir / "sshd_config"
|
|
238
|
+
pubkey_path = Path.home() / ".ssh" / "id_rsa.pub"
|
|
239
|
+
key_content = pubkey_path.read_text(encoding="utf-8").strip()
|
|
240
|
+
if admin_auth_keys.exists():
|
|
241
|
+
existing = admin_auth_keys.read_text(encoding="utf-8")
|
|
242
|
+
if not existing.endswith("\\n"):
|
|
243
|
+
existing += "\\n"
|
|
244
|
+
admin_auth_keys.write_text(existing + key_content + "\\n", encoding="utf-8")
|
|
245
|
+
else:
|
|
246
|
+
admin_auth_keys.write_text(key_content + "\\n", encoding="utf-8")
|
|
247
|
+
icacls_cmd = f'icacls "{admin_auth_keys}" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"'
|
|
248
|
+
subprocess.run(icacls_cmd, shell=True, check=True)
|
|
249
|
+
if sshd_config.exists():
|
|
250
|
+
config_text = sshd_config.read_text(encoding="utf-8")
|
|
251
|
+
config_text = config_text.replace("#PubkeyAuthentication", "PubkeyAuthentication")
|
|
252
|
+
sshd_config.write_text(config_text, encoding="utf-8")
|
|
253
|
+
subprocess.run("Restart-Service sshd -Force", shell=True, check=True)
|
|
254
|
+
print("SSH key added successfully")
|
|
255
|
+
'''
|
|
256
|
+
return self.run_py_remotely(python_code=python_code, uv_with=None, uv_project_dir=None, description="Adding SSH key to Windows remote", verbose_output=True, strict_stderr=False, strict_return_code=False)
|
|
223
257
|
|
|
224
258
|
def get_remote_repr(self, add_machine: bool = False) -> str:
|
|
225
259
|
return f"{self.username}@{self.hostname}:{self.port}" + (
|
|
226
260
|
f" [{self.remote_specs['system']}][{self.remote_specs['distro']}]" if add_machine else ""
|
|
227
261
|
)
|
|
262
|
+
|
|
228
263
|
def get_local_repr(self, add_machine: bool = False) -> str:
|
|
229
264
|
import getpass
|
|
265
|
+
|
|
230
266
|
return f"{getpass.getuser()}@{platform.node()}" + (f" [{platform.system()}][{self.local_specs['distro']}]" if add_machine else "")
|
|
231
267
|
|
|
232
268
|
def get_ssh_conn_str(self, command: str) -> str:
|
|
@@ -246,7 +282,9 @@ class SSH:
|
|
|
246
282
|
res.output.returncode = os.system(command)
|
|
247
283
|
return res
|
|
248
284
|
|
|
249
|
-
def run_shell_cmd_on_remote(
|
|
285
|
+
def run_shell_cmd_on_remote(
|
|
286
|
+
self, command: str, verbose_output: bool, description: str, strict_stderr: bool, strict_return_code: bool
|
|
287
|
+
) -> Response:
|
|
250
288
|
raw = self.ssh.exec_command(command)
|
|
251
289
|
res = Response(stdin=raw[0], stdout=raw[1], stderr=raw[2], cmd=command, desc=description) # type: ignore
|
|
252
290
|
if verbose_output:
|
|
@@ -301,8 +339,7 @@ class SSH:
|
|
|
301
339
|
)
|
|
302
340
|
|
|
303
341
|
def run_lambda_function(self, func: Callable[..., Any], import_module: bool, uv_with: Optional[list[str]], uv_project_dir: Optional[str]):
|
|
304
|
-
command = lambda_to_python_script(func,
|
|
305
|
-
in_global=True, import_module=import_module)
|
|
342
|
+
command = lambda_to_python_script(func, in_global=True, import_module=import_module)
|
|
306
343
|
# turns ou that the code below for some reason runs but zellij doesn't start, looks like things are assigned to different user.
|
|
307
344
|
# return self.run_py(python_code=command, uv_with=uv_with, uv_project_dir=uv_project_dir,
|
|
308
345
|
# description=f"run_py_func {func.__name__} on {self.get_remote_repr(add_machine=False)}",
|
|
@@ -334,25 +371,39 @@ class SSH:
|
|
|
334
371
|
|
|
335
372
|
def create_parent_dir_and_check_if_exists(self, path_rel2home: str, overwrite_existing: bool) -> None:
|
|
336
373
|
from machineconfig.utils.ssh_utils.utils import create_dir_and_check_if_exists
|
|
374
|
+
|
|
337
375
|
return create_dir_and_check_if_exists(self, path_rel2home=path_rel2home, overwrite_existing=overwrite_existing)
|
|
338
376
|
|
|
339
377
|
def check_remote_is_dir(self, source_path: Union[str, Path]) -> bool:
|
|
340
378
|
from machineconfig.utils.ssh_utils.utils import check_remote_is_dir
|
|
379
|
+
|
|
341
380
|
return check_remote_is_dir(self, source_path=source_path)
|
|
342
381
|
|
|
343
382
|
def expand_remote_path(self, source_path: Union[str, Path]) -> str:
|
|
344
383
|
from machineconfig.utils.ssh_utils.utils import expand_remote_path
|
|
384
|
+
|
|
345
385
|
return expand_remote_path(self, source_path=source_path)
|
|
346
386
|
|
|
347
|
-
def copy_from_here(
|
|
387
|
+
def copy_from_here(
|
|
388
|
+
self, source_path: str, target_rel2home: Optional[str], compress_with_zip: bool, recursive: bool, overwrite_existing: bool
|
|
389
|
+
) -> None:
|
|
348
390
|
from machineconfig.utils.ssh_utils.copy_from_here import copy_from_here
|
|
349
|
-
return copy_from_here(self, source_path=source_path, target_rel2home=target_rel2home, compress_with_zip=compress_with_zip, recursive=recursive, overwrite_existing=overwrite_existing)
|
|
350
391
|
|
|
351
|
-
|
|
392
|
+
return copy_from_here(
|
|
393
|
+
self,
|
|
394
|
+
source_path=source_path,
|
|
395
|
+
target_rel2home=target_rel2home,
|
|
396
|
+
compress_with_zip=compress_with_zip,
|
|
397
|
+
recursive=recursive,
|
|
398
|
+
overwrite_existing=overwrite_existing,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
def copy_to_here(
|
|
402
|
+
self, source: Union[str, Path], target: Optional[Union[str, Path]], compress_with_zip: bool, recursive: bool, internal_call: bool = False
|
|
403
|
+
) -> None:
|
|
352
404
|
from machineconfig.utils.ssh_utils.copy_to_here import copy_to_here
|
|
353
|
-
return copy_to_here(self, source=source, target=target, compress_with_zip=compress_with_zip, recursive=recursive, internal_call=internal_call)
|
|
354
405
|
|
|
355
|
-
|
|
406
|
+
return copy_to_here(self, source=source, target=target, compress_with_zip=compress_with_zip, recursive=recursive, internal_call=internal_call)
|
|
356
407
|
|
|
357
408
|
|
|
358
409
|
if __name__ == "__main__":
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
4
|
-
from pathlib import Path
|
|
3
|
+
from typing import Optional, TYPE_CHECKING
|
|
4
|
+
from pathlib import Path, PurePosixPath, PureWindowsPath
|
|
5
5
|
from machineconfig.utils.accessories import randstr
|
|
6
6
|
from machineconfig.utils.meta import lambda_to_python_script
|
|
7
7
|
from machineconfig.utils.ssh_utils.abc import DEFAULT_PICKLE_SUBDIR
|
|
8
8
|
from machineconfig.utils.code import get_uv_command
|
|
9
9
|
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from machineconfig.utils.ssh import SSH
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _build_remote_path(self: "SSH", home_dir: str, rel_path: str) -> str:
|
|
15
|
+
if self.remote_specs["system"] == "Windows":
|
|
16
|
+
return str(PureWindowsPath(home_dir) / rel_path)
|
|
17
|
+
return str(PurePosixPath(home_dir) / PurePosixPath(rel_path.replace("\\", "/")))
|
|
18
|
+
|
|
10
19
|
|
|
11
20
|
def copy_from_here(
|
|
12
21
|
self: "SSH", source_path: str, target_rel2home: Optional[str], compress_with_zip: bool, recursive: bool, overwrite_existing: bool
|
|
@@ -54,15 +63,14 @@ def copy_from_here(
|
|
|
54
63
|
target_rel2home = target_rel2home + ".zip"
|
|
55
64
|
if Path(target_rel2home).parent.as_posix() not in {"", "."}:
|
|
56
65
|
self.create_parent_dir_and_check_if_exists(path_rel2home=target_rel2home, overwrite_existing=overwrite_existing)
|
|
57
|
-
|
|
66
|
+
remote_target_full = _build_remote_path(self, self.remote_specs["home_dir"], target_rel2home)
|
|
67
|
+
print(f"""📤 [SFTP UPLOAD] Sending file: {repr(source_obj)} ==> Remote Path: {remote_target_full}""")
|
|
58
68
|
try:
|
|
59
69
|
with self.tqdm_wrap(ascii=True, unit="b", unit_scale=True) as pbar:
|
|
60
70
|
if self.sftp is None: # type: ignore[unreachable]
|
|
61
71
|
raise RuntimeError(f"SFTP connection lost for {self.hostname}")
|
|
62
|
-
print(f"Uploading {source_obj} to\n{
|
|
63
|
-
self.sftp.put(
|
|
64
|
-
localpath=str(source_obj), remotepath=str(Path(self.remote_specs["home_dir"]).joinpath(target_rel2home)), callback=pbar.view_bar
|
|
65
|
-
)
|
|
72
|
+
print(f"Uploading {source_obj} to\n{remote_target_full}")
|
|
73
|
+
self.sftp.put(localpath=str(source_obj), remotepath=remote_target_full, callback=pbar.view_bar)
|
|
66
74
|
except Exception:
|
|
67
75
|
if compress_with_zip and source_obj.exists() and str(source_obj).endswith("_archive.zip"):
|
|
68
76
|
source_obj.unlink()
|
|
@@ -83,12 +91,9 @@ def copy_from_here(
|
|
|
83
91
|
archive_handle.extractall(extraction_directory)
|
|
84
92
|
archive_path.unlink()
|
|
85
93
|
|
|
94
|
+
remote_zip_path = _build_remote_path(self, self.remote_specs["home_dir"], target_rel2home)
|
|
86
95
|
command = lambda_to_python_script(
|
|
87
|
-
lambda: unzip_archive(
|
|
88
|
-
zip_file_path=str(Path(self.remote_specs["home_dir"]).joinpath(target_rel2home)), overwrite_flag=overwrite_existing
|
|
89
|
-
),
|
|
90
|
-
in_global=True,
|
|
91
|
-
import_module=False,
|
|
96
|
+
lambda: unzip_archive(zip_file_path=remote_zip_path, overwrite_flag=overwrite_existing), in_global=True, import_module=False
|
|
92
97
|
)
|
|
93
98
|
tmp_py_file = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/create_target_dir_{randstr()}.py")
|
|
94
99
|
tmp_py_file.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
|
|
2
|
-
from pathlib import Path
|
|
2
|
+
from pathlib import Path, PurePosixPath, PureWindowsPath
|
|
3
3
|
from machineconfig.utils.accessories import randstr
|
|
4
4
|
from machineconfig.utils.meta import lambda_to_python_script
|
|
5
5
|
from machineconfig.utils.ssh_utils.abc import MACHINECONFIG_VERSION, DEFAULT_PICKLE_SUBDIR
|
|
6
6
|
from machineconfig.utils.code import get_uv_command
|
|
7
|
-
from typing import Union
|
|
7
|
+
from typing import Union, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from machineconfig.utils.ssh import SSH
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _build_remote_path(self: "SSH", home_dir: str, rel_path: str) -> str:
|
|
14
|
+
if self.remote_specs["system"] == "Windows":
|
|
15
|
+
return str(PureWindowsPath(home_dir) / rel_path)
|
|
16
|
+
return str(PurePosixPath(home_dir) / PurePosixPath(rel_path.replace("\\", "/")))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _normalize_rel_path_for_remote(self: "SSH", rel_path: str) -> str:
|
|
20
|
+
if self.remote_specs["system"] == "Windows":
|
|
21
|
+
return str(PureWindowsPath(rel_path))
|
|
22
|
+
return rel_path.replace("\\", "/")
|
|
8
23
|
|
|
9
24
|
|
|
10
25
|
def create_dir_and_check_if_exists(self: "SSH", path_rel2home: str, overwrite_existing: bool) -> None:
|
|
11
26
|
"""Helper to create a directory on remote machine and return its path."""
|
|
12
|
-
|
|
27
|
+
path_rel2home_normalized = _normalize_rel_path_for_remote(self, path_rel2home)
|
|
13
28
|
def create_target_dir(target_rel2home: str, overwrite: bool):
|
|
14
29
|
from pathlib import Path
|
|
15
30
|
import shutil
|
|
@@ -26,7 +41,7 @@ def create_dir_and_check_if_exists(self: "SSH", path_rel2home: str, overwrite_ex
|
|
|
26
41
|
print(f"Creating directory for path: {target_path_abs}")
|
|
27
42
|
target_path_abs.parent.mkdir(parents=True, exist_ok=True)
|
|
28
43
|
command = lambda_to_python_script(
|
|
29
|
-
lambda: create_target_dir(target_rel2home=
|
|
44
|
+
lambda: create_target_dir(target_rel2home=path_rel2home_normalized, overwrite=overwrite_existing),
|
|
30
45
|
in_global=True, import_module=False
|
|
31
46
|
)
|
|
32
47
|
tmp_py_file = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/create_target_dir_{randstr()}.py")
|
|
@@ -34,7 +49,8 @@ def create_dir_and_check_if_exists(self: "SSH", path_rel2home: str, overwrite_ex
|
|
|
34
49
|
tmp_py_file.write_text(command, encoding="utf-8")
|
|
35
50
|
assert self.sftp is not None
|
|
36
51
|
tmp_remote_path = ".tmp_pyfile.py"
|
|
37
|
-
|
|
52
|
+
remote_tmp_full = _build_remote_path(self, self.remote_specs["home_dir"], tmp_remote_path)
|
|
53
|
+
self.sftp.put(localpath=str(tmp_py_file), remotepath=remote_tmp_full)
|
|
38
54
|
resp = self.run_shell_cmd_on_remote(
|
|
39
55
|
command=f"""{get_uv_command(platform=self.remote_specs['system'])} run python {tmp_remote_path}""",
|
|
40
56
|
verbose_output=False,
|
|
@@ -1,167 +1,35 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import platform
|
|
3
|
-
import
|
|
2
|
+
import re
|
|
4
3
|
import shutil
|
|
5
4
|
import subprocess
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if os.environ.get("WSL_DISTRO_NAME"):
|
|
27
|
-
return
|
|
28
|
-
if "microsoft" in platform.release().lower():
|
|
29
|
-
return
|
|
30
|
-
raise RuntimeError("copy_when_inside_wsl must run inside WSL")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def _ensure_windows_environment() -> None:
|
|
34
|
-
if os.name != "nt":
|
|
35
|
-
raise RuntimeError("copy_when_inside_windows must run inside Windows")
|
|
36
|
-
if os.environ.get("WSL_DISTRO_NAME"):
|
|
37
|
-
raise RuntimeError("copy_when_inside_windows must run inside Windows")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _infer_windows_home_from_permissions() -> Path:
|
|
41
|
-
base_dir = Path("/mnt/c/Users")
|
|
42
|
-
try:
|
|
43
|
-
entries = list(base_dir.iterdir())
|
|
44
|
-
except FileNotFoundError as exc:
|
|
45
|
-
raise RuntimeError("unable to find /mnt/c/Users") from exc
|
|
46
|
-
candidates: list[Path] = []
|
|
47
|
-
for entry in entries:
|
|
48
|
-
if not entry.is_dir():
|
|
49
|
-
continue
|
|
50
|
-
if entry.name.lower() == "public" or entry.name.lower() == "all users":
|
|
51
|
-
continue
|
|
52
|
-
try:
|
|
53
|
-
mode = stat.S_IMODE(entry.stat().st_mode)
|
|
54
|
-
except OSError:
|
|
55
|
-
continue
|
|
56
|
-
if mode == 0o777:
|
|
57
|
-
candidates.append(entry)
|
|
58
|
-
if len(candidates) != 1:
|
|
59
|
-
options = ", ".join(sorted(candidate.name for candidate in candidates)) or "none"
|
|
60
|
-
raise RuntimeError(f"unable to infer Windows home directory (candidates: {options})")
|
|
61
|
-
return candidates[0]
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _resolve_windows_home_from_wsl() -> Path:
|
|
65
|
-
user_profile = os.environ.get("USERPROFILE")
|
|
66
|
-
if user_profile:
|
|
67
|
-
windows_path = PureWindowsPath(user_profile)
|
|
68
|
-
drive = windows_path.drive
|
|
69
|
-
if drive:
|
|
70
|
-
drive_letter = drive.rstrip(":").lower()
|
|
71
|
-
tail = Path(*windows_path.parts[1:])
|
|
72
|
-
candidate = Path("/mnt") / drive_letter / tail
|
|
73
|
-
if candidate.exists():
|
|
74
|
-
return candidate
|
|
75
|
-
return _infer_windows_home_from_permissions()
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def _decode_wsl_output(raw_bytes: bytes) -> str:
|
|
79
|
-
try:
|
|
80
|
-
return raw_bytes.decode("utf-16-le")
|
|
81
|
-
except UnicodeDecodeError:
|
|
82
|
-
return raw_bytes.decode()
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _get_single_wsl_distribution() -> str:
|
|
86
|
-
process = subprocess.run(["wsl.exe", "-l"], capture_output=True, text=False, check=True)
|
|
87
|
-
stdout = _decode_wsl_output(process.stdout).replace("\ufeff", "")
|
|
88
|
-
distributions: list[str] = []
|
|
89
|
-
for raw_line in stdout.splitlines():
|
|
90
|
-
line = raw_line.strip()
|
|
91
|
-
if not line or line.lower().startswith("windows subsystem for linux"):
|
|
92
|
-
continue
|
|
93
|
-
normalized = line.lstrip("* ").replace("(Default)", "").strip()
|
|
94
|
-
if normalized:
|
|
95
|
-
distributions.append(normalized)
|
|
96
|
-
if len(distributions) != 1:
|
|
97
|
-
raise RuntimeError("unable to pick a single WSL distribution")
|
|
98
|
-
return distributions[0]
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def _resolve_wsl_home_on_windows() -> Path:
|
|
102
|
-
distribution = _get_single_wsl_distribution()
|
|
103
|
-
home_root = Path(rf"\\wsl$\{distribution}\home")
|
|
104
|
-
try:
|
|
105
|
-
entries = list(home_root.iterdir())
|
|
106
|
-
except FileNotFoundError as exc:
|
|
107
|
-
raise RuntimeError(f"unable to locate WSL home directories for {distribution}") from exc
|
|
108
|
-
except OSError as exc:
|
|
109
|
-
raise RuntimeError(f"unable to inspect WSL home directories for {distribution}") from exc
|
|
110
|
-
user_dirs = [entry for entry in entries if entry.is_dir()]
|
|
111
|
-
if len(user_dirs) != 1:
|
|
112
|
-
options = ", ".join(sorted(entry.name for entry in user_dirs)) or "none"
|
|
113
|
-
raise RuntimeError(f"unable to infer WSL user directory (candidates: {options})")
|
|
114
|
-
return user_dirs[0]
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def _quote_for_powershell(path: Path) -> str:
|
|
118
|
-
return "'" + str(path).replace("'", "''") + "'"
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def _run_windows_copy_command(source_path: Path, target_path: Path) -> None:
|
|
122
|
-
source_is_dir = source_path.is_dir()
|
|
123
|
-
parent_literal = _quote_for_powershell(target_path.parent)
|
|
124
|
-
source_literal = _quote_for_powershell(source_path)
|
|
125
|
-
target_literal = _quote_for_powershell(target_path)
|
|
126
|
-
script = (
|
|
127
|
-
"$ErrorActionPreference = 'Stop'; "
|
|
128
|
-
f"New-Item -ItemType Directory -Path {parent_literal} -Force | Out-Null; "
|
|
129
|
-
f"Copy-Item -LiteralPath {source_literal} -Destination {target_literal}"
|
|
130
|
-
f"{' -Recurse' if source_is_dir else ''} -Force"
|
|
131
|
-
)
|
|
132
|
-
print(f"Copying {source_path} to {target_path}")
|
|
133
|
-
subprocess.run(
|
|
134
|
-
["powershell.exe", "-NoLogo", "-NoProfile", "-Command", script],
|
|
135
|
-
check=True,
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def _ensure_symlink(link_path: Path, target_path: Path) -> None:
|
|
140
|
-
if not target_path.exists():
|
|
141
|
-
raise FileNotFoundError(target_path)
|
|
142
|
-
if link_path.is_symlink():
|
|
143
|
-
existing_target = Path(os.path.realpath(link_path))
|
|
144
|
-
desired_target = Path(os.path.realpath(target_path))
|
|
145
|
-
if os.path.normcase(str(existing_target)) == os.path.normcase(str(desired_target)):
|
|
146
|
-
return
|
|
147
|
-
link_path.unlink()
|
|
148
|
-
elif link_path.exists():
|
|
149
|
-
raise FileExistsError(link_path)
|
|
150
|
-
link_path.symlink_to(target_path, target_is_directory=True)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def copy_when_inside_wsl(source: Path | str, target: Path | str, overwrite: bool) -> None:
|
|
154
|
-
_ensure_wsl_environment()
|
|
155
|
-
source_relative = _ensure_relative_path(source)
|
|
156
|
-
target_relative = _ensure_relative_path(target)
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from machineconfig.utils.ssh_utils.wsl_helper import (
|
|
8
|
+
ensure_relative_path,
|
|
9
|
+
remove_path,
|
|
10
|
+
ensure_wsl_environment,
|
|
11
|
+
ensure_windows_environment,
|
|
12
|
+
ensure_linux_environment,
|
|
13
|
+
resolve_windows_home_from_wsl,
|
|
14
|
+
resolve_wsl_home_on_windows,
|
|
15
|
+
run_windows_copy_command,
|
|
16
|
+
ensure_symlink,
|
|
17
|
+
normalize_port_spec_for_firewall,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def copy_when_inside_wsl(source: Path | str, target: Path | str, overwrite: bool, windows_username: str | None) -> None:
|
|
22
|
+
ensure_wsl_environment()
|
|
23
|
+
source_relative = ensure_relative_path(source)
|
|
24
|
+
target_relative = ensure_relative_path(target)
|
|
157
25
|
source_path = Path.home() / source_relative
|
|
158
|
-
target_path =
|
|
26
|
+
target_path = resolve_windows_home_from_wsl(windows_username) / target_relative
|
|
159
27
|
if not source_path.exists():
|
|
160
28
|
raise FileNotFoundError(source_path)
|
|
161
29
|
if target_path.exists():
|
|
162
30
|
if not overwrite:
|
|
163
31
|
raise FileExistsError(target_path)
|
|
164
|
-
|
|
32
|
+
remove_path(target_path)
|
|
165
33
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
166
34
|
if source_path.is_dir():
|
|
167
35
|
shutil.copytree(source_path, target_path, dirs_exist_ok=False)
|
|
@@ -171,40 +39,109 @@ def copy_when_inside_wsl(source: Path | str, target: Path | str, overwrite: bool
|
|
|
171
39
|
|
|
172
40
|
|
|
173
41
|
def copy_when_inside_windows(source: Path | str, target: Path | str, overwrite: bool) -> None:
|
|
174
|
-
|
|
175
|
-
source_relative =
|
|
176
|
-
target_relative =
|
|
42
|
+
ensure_windows_environment()
|
|
43
|
+
source_relative = ensure_relative_path(source)
|
|
44
|
+
target_relative = ensure_relative_path(target)
|
|
177
45
|
source_path = Path.home() / source_relative
|
|
178
|
-
target_path =
|
|
46
|
+
target_path = resolve_wsl_home_on_windows() / target_relative
|
|
179
47
|
if not source_path.exists():
|
|
180
48
|
raise FileNotFoundError(source_path)
|
|
181
49
|
if target_path.exists():
|
|
182
50
|
if not overwrite:
|
|
183
51
|
raise FileExistsError(target_path)
|
|
184
|
-
|
|
185
|
-
|
|
52
|
+
remove_path(target_path)
|
|
53
|
+
run_windows_copy_command(source_path, target_path)
|
|
186
54
|
|
|
187
55
|
|
|
188
|
-
def link_wsl_and_windows() -> None:
|
|
56
|
+
def link_wsl_and_windows(windows_username: str | None) -> None:
|
|
189
57
|
system = platform.system()
|
|
190
58
|
if system == "Darwin":
|
|
191
59
|
raise RuntimeError("link_wsl_and_windows is not designed for macOS")
|
|
192
60
|
try:
|
|
193
|
-
|
|
61
|
+
ensure_wsl_environment()
|
|
194
62
|
except RuntimeError:
|
|
195
63
|
try:
|
|
196
|
-
|
|
64
|
+
ensure_windows_environment()
|
|
197
65
|
except RuntimeError as exc:
|
|
198
66
|
raise RuntimeError("link_wsl_and_windows must run inside Windows or WSL") from exc
|
|
199
|
-
|
|
67
|
+
print("🔗 Running inside Windows, linking to WSL home...")
|
|
68
|
+
target_path = resolve_wsl_home_on_windows()
|
|
200
69
|
link_path = Path.home() / "wsl"
|
|
201
|
-
|
|
70
|
+
created = ensure_symlink(link_path, target_path)
|
|
71
|
+
if created:
|
|
72
|
+
print(f"✅ Created symlink: {link_path} -> {target_path}")
|
|
73
|
+
else:
|
|
74
|
+
print(f"✅ Symlink already exists: {link_path} -> {target_path}")
|
|
202
75
|
return
|
|
203
|
-
|
|
76
|
+
print("🔗 Running inside WSL, linking to Windows home...")
|
|
77
|
+
target_path = resolve_windows_home_from_wsl(windows_username)
|
|
204
78
|
link_path = Path.home() / "win"
|
|
205
|
-
|
|
79
|
+
created = ensure_symlink(link_path, target_path)
|
|
80
|
+
if created:
|
|
81
|
+
print(f"✅ Created symlink: {link_path} -> {target_path}")
|
|
82
|
+
else:
|
|
83
|
+
print(f"✅ Symlink already exists: {link_path} -> {target_path}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def open_wsl_port(ports_spec: str) -> None:
|
|
87
|
+
ensure_windows_environment()
|
|
88
|
+
normalized_ports, description = normalize_port_spec_for_firewall(ports_spec)
|
|
89
|
+
rule_name = f"WSL Ports {description}"
|
|
90
|
+
# Build PowerShell array syntax for -LocalPort parameter (e.g., @('3000-4000','8080'))
|
|
91
|
+
port_parts = normalized_ports.split(",")
|
|
92
|
+
ps_array = "@(" + ",".join(f"'{p}'" for p in port_parts) + ")"
|
|
93
|
+
script = f"New-NetFirewallRule -DisplayName '{rule_name}' -Direction Inbound -LocalPort {ps_array} -Protocol TCP -Action Allow"
|
|
94
|
+
print(f"🔥 Opening firewall for ports: {description}...")
|
|
95
|
+
result = subprocess.run(["powershell.exe", "-NoLogo", "-NoProfile", "-Command", script], capture_output=True, text=True)
|
|
96
|
+
if result.returncode == 0:
|
|
97
|
+
print(f"✅ Firewall rule created for ports: {description}")
|
|
98
|
+
else:
|
|
99
|
+
print(f"❌ Failed to create firewall rule: {result.stderr.strip()}")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def change_ssh_port(port: int) -> None:
|
|
103
|
+
ensure_linux_environment()
|
|
104
|
+
if port < 1 or port > 65535:
|
|
105
|
+
raise ValueError(f"Invalid port number: {port}")
|
|
106
|
+
|
|
107
|
+
sshd_config = Path("/etc/ssh/sshd_config")
|
|
108
|
+
if not sshd_config.exists():
|
|
109
|
+
raise FileNotFoundError(f"SSH config file not found: {sshd_config}")
|
|
110
|
+
|
|
111
|
+
print(f"🔧 Changing SSH port to {port}...")
|
|
112
|
+
|
|
113
|
+
content = sshd_config.read_text()
|
|
114
|
+
new_content = re.sub(r"^#?\s*Port\s+\d+", f"Port {port}", content, flags=re.MULTILINE)
|
|
115
|
+
if f"Port {port}" not in new_content:
|
|
116
|
+
new_content = f"Port {port}\n" + new_content
|
|
117
|
+
|
|
118
|
+
print(f"📝 Updating {sshd_config}...")
|
|
119
|
+
result = subprocess.run(["sudo", "tee", str(sshd_config)], input=new_content.encode(), capture_output=True)
|
|
120
|
+
if result.returncode != 0:
|
|
121
|
+
raise RuntimeError(f"Failed to update sshd_config: {result.stderr.decode()}")
|
|
122
|
+
print(f"✅ Updated {sshd_config}")
|
|
123
|
+
|
|
124
|
+
override_dir = Path("/etc/systemd/system/ssh.socket.d")
|
|
125
|
+
override_file = override_dir / "override.conf"
|
|
126
|
+
override_content = f"""[Socket]
|
|
127
|
+
ListenStream=
|
|
128
|
+
ListenStream={port}
|
|
129
|
+
"""
|
|
130
|
+
print(f"📝 Creating systemd socket override at {override_file}...")
|
|
131
|
+
subprocess.run(["sudo", "mkdir", "-p", str(override_dir)], check=True)
|
|
132
|
+
result = subprocess.run(["sudo", "tee", str(override_file)], input=override_content.encode(), capture_output=True)
|
|
133
|
+
if result.returncode != 0:
|
|
134
|
+
raise RuntimeError(f"Failed to create override file: {result.stderr.decode()}")
|
|
135
|
+
print("✅ Created systemd socket override")
|
|
136
|
+
|
|
137
|
+
print("🔄 Restarting SSH services...")
|
|
138
|
+
subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True)
|
|
139
|
+
subprocess.run(["sudo", "systemctl", "restart", "ssh.socket"], check=False)
|
|
140
|
+
subprocess.run(["sudo", "service", "ssh", "restart"], check=False)
|
|
141
|
+
print(f"✅ SSH port changed to {port}")
|
|
142
|
+
print(f"⚠️ Remember to open port {port} in Windows Firewall if running in WSL")
|
|
206
143
|
|
|
207
144
|
|
|
208
145
|
if __name__ == "__main__":
|
|
209
|
-
copy_when_inside_wsl(Path("projects/source.txt"), Path("windows_projects/source.txt"), True)
|
|
210
|
-
copy_when_inside_windows(Path("documents/example.txt"), Path("linux_documents/example.txt"), True)
|
|
146
|
+
copy_when_inside_wsl(Path("projects/source.txt"), Path("windows_projects/source.txt"), overwrite=True, windows_username=None)
|
|
147
|
+
copy_when_inside_windows(Path("documents/example.txt"), Path("linux_documents/example.txt"), overwrite=True)
|