machineconfig 5.15__py3-none-any.whl → 7.66__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/__init__.py +0 -28
- machineconfig/cluster/remote/distribute.py +0 -1
- machineconfig/cluster/remote/file_manager.py +0 -2
- machineconfig/cluster/remote/script_execution.py +0 -1
- machineconfig/cluster/sessions_managers/{utils → helpers}/enhanced_command_runner.py +4 -6
- machineconfig/cluster/sessions_managers/utils/load_balancer.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +69 -0
- machineconfig/cluster/sessions_managers/wt_local.py +114 -289
- machineconfig/cluster/sessions_managers/wt_local_manager.py +50 -193
- machineconfig/cluster/sessions_managers/wt_remote.py +51 -43
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +49 -197
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +6 -19
- machineconfig/cluster/sessions_managers/wt_utils/manager_persistence.py +52 -0
- machineconfig/cluster/sessions_managers/wt_utils/monitoring_helpers.py +50 -0
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +4 -2
- machineconfig/cluster/sessions_managers/wt_utils/status_reporting.py +76 -0
- machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
- machineconfig/cluster/sessions_managers/zellij_local.py +81 -375
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +22 -169
- machineconfig/cluster/sessions_managers/zellij_remote.py +40 -41
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +13 -10
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -8
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +5 -20
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +3 -9
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -1
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper.py +298 -0
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper_restart.py +77 -0
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper_with_panes.py +228 -0
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_manager_helper.py +165 -0
- machineconfig/jobs/{python → installer}/check_installations.py +2 -3
- machineconfig/jobs/installer/custom/boxes.py +61 -0
- machineconfig/jobs/installer/custom/hx.py +76 -19
- machineconfig/jobs/installer/custom_dev/alacritty.py +4 -4
- machineconfig/jobs/installer/custom_dev/brave.py +1 -7
- machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/custom_dev/code.py +4 -1
- machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +30 -0
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +9 -18
- machineconfig/jobs/installer/custom_dev/sysabc.py +119 -0
- machineconfig/jobs/installer/custom_dev/wezterm.py +2 -19
- machineconfig/jobs/installer/installer_data.json +1101 -115
- machineconfig/jobs/installer/linux_scripts/brave.sh +4 -14
- machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +5 -17
- machineconfig/jobs/installer/linux_scripts/docker.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/docker_start.sh +6 -14
- machineconfig/jobs/installer/linux_scripts/edge.sh +3 -11
- machineconfig/jobs/{linux/msc → installer/linux_scripts}/lid.sh +2 -8
- machineconfig/jobs/installer/linux_scripts/nerdfont.sh +5 -17
- machineconfig/jobs/{linux/msc → installer/linux_scripts}/network.sh +2 -8
- machineconfig/jobs/installer/linux_scripts/q.sh +1 -0
- machineconfig/jobs/installer/linux_scripts/redis.sh +6 -17
- machineconfig/jobs/installer/linux_scripts/vscode.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/wezterm.sh +4 -12
- machineconfig/jobs/installer/package_groups.py +108 -180
- machineconfig/logger.py +0 -1
- machineconfig/profile/backup.toml +49 -0
- machineconfig/profile/bash_shell_profiles.md +11 -0
- machineconfig/profile/create_helper.py +74 -0
- machineconfig/profile/create_links.py +288 -0
- machineconfig/profile/create_links_export.py +100 -0
- machineconfig/profile/create_shell_profile.py +136 -0
- machineconfig/profile/mapper.toml +258 -0
- machineconfig/scripts/Restore-ThunderbirdProfile.ps1 +92 -0
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/{share_cloud.sh → other/share_cloud.sh} +14 -25
- machineconfig/scripts/linux/wrap_mcfg +47 -0
- machineconfig/scripts/nu/wrap_mcfg.nu +69 -0
- machineconfig/scripts/python/agents.py +92 -103
- machineconfig/scripts/python/ai/command_runner/command_runner.sh +9 -0
- machineconfig/scripts/python/ai/command_runner/prompt.txt +9 -0
- machineconfig/scripts/python/ai/generate_files.py +307 -42
- machineconfig/scripts/python/ai/initai.py +3 -28
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +17 -18
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +17 -18
- machineconfig/scripts/python/ai/solutions/_shared.py +9 -1
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/prompts/pyright_fix.md +16 -0
- machineconfig/scripts/python/ai/solutions/generic.py +27 -4
- machineconfig/scripts/python/ai/vscode_tasks.py +37 -0
- machineconfig/scripts/python/cloud.py +29 -0
- machineconfig/scripts/python/croshell.py +111 -114
- machineconfig/scripts/python/define.py +31 -0
- machineconfig/scripts/python/devops.py +44 -103
- machineconfig/scripts/python/devops_navigator.py +10 -0
- machineconfig/scripts/python/env_manager/__init__.py +1 -0
- machineconfig/scripts/python/env_manager/path_manager_backend.py +47 -0
- machineconfig/scripts/python/env_manager/path_manager_tui.py +228 -0
- machineconfig/scripts/python/explore.py +49 -0
- machineconfig/scripts/python/fire_jobs.py +115 -152
- machineconfig/scripts/python/ftpx.py +29 -24
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.json +14 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.py +37 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_cursor_agents.py +22 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_gemini.py +42 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_qwen.py +30 -0
- machineconfig/scripts/python/{fire_agents_help_launch.py → helpers_agents/fire_agents_help_launch.py} +34 -44
- machineconfig/scripts/python/helpers_agents/fire_agents_helper_types.py +34 -0
- machineconfig/scripts/python/helpers_agents/templates/prompt.txt +6 -0
- machineconfig/scripts/python/helpers_agents/templates/template.ps1 +14 -0
- machineconfig/scripts/python/helpers_agents/templates/template.sh +24 -0
- machineconfig/scripts/python/{cloud_copy.py → helpers_cloud/cloud_copy.py} +30 -23
- machineconfig/scripts/python/{cloud_mount.py → helpers_cloud/cloud_mount.py} +10 -18
- machineconfig/scripts/python/{cloud_sync.py → helpers_cloud/cloud_sync.py} +12 -18
- machineconfig/scripts/python/{helpers → helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/helpers_croshell/crosh.py +39 -0
- machineconfig/scripts/python/{start_slidev.py → helpers_croshell/start_slidev.py} +2 -2
- machineconfig/scripts/python/helpers_devops/cli_config.py +95 -0
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +89 -0
- machineconfig/scripts/python/helpers_devops/cli_data.py +25 -0
- machineconfig/scripts/python/helpers_devops/cli_nw.py +134 -0
- machineconfig/scripts/python/helpers_devops/cli_repos.py +182 -0
- machineconfig/scripts/python/helpers_devops/cli_self.py +134 -0
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +137 -0
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +141 -0
- machineconfig/scripts/python/{share_terminal.py → helpers_devops/cli_terminal.py} +35 -23
- machineconfig/scripts/python/helpers_devops/cli_utils.py +96 -0
- machineconfig/scripts/python/{devops_backup_retrieve.py → helpers_devops/devops_backup_retrieve.py} +7 -10
- machineconfig/scripts/python/helpers_devops/devops_status.py +511 -0
- machineconfig/scripts/python/{devops_update_repos.py → helpers_devops/devops_update_repos.py} +68 -49
- machineconfig/scripts/python/helpers_devops/themes/choose_pwsh_theme.ps1 +81 -0
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +3 -0
- machineconfig/scripts/python/{choose_wezterm_theme.py → helpers_devops/themes/choose_wezterm_theme.py} +2 -2
- machineconfig/scripts/python/helpers_fire_command/__init__.py +0 -0
- machineconfig/scripts/python/{helpers/helpers4.py → helpers_fire_command/file_wrangler.py} +56 -20
- machineconfig/scripts/python/{fire_jobs_args_helper.py → helpers_fire_command/fire_jobs_args_helper.py} +5 -1
- machineconfig/scripts/python/{fire_jobs_route_helper.py → helpers_fire_command/fire_jobs_route_helper.py} +47 -2
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_streamlit_helper.py +0 -0
- machineconfig/scripts/python/helpers_msearch/__init__.py +5 -0
- machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfag +1 -1
- machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfg +1 -1
- machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfrga +1 -1
- machineconfig/scripts/python/helpers_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helpers_navigator/command_builder.py +111 -0
- machineconfig/scripts/python/helpers_navigator/command_detail.py +44 -0
- machineconfig/scripts/python/helpers_navigator/command_tree.py +588 -0
- machineconfig/scripts/python/helpers_navigator/data_models.py +28 -0
- machineconfig/scripts/python/helpers_navigator/main_app.py +272 -0
- machineconfig/scripts/python/helpers_navigator/search_bar.py +15 -0
- machineconfig/scripts/python/helpers_repos/action.py +209 -0
- machineconfig/scripts/python/helpers_repos/action_helper.py +150 -0
- machineconfig/scripts/python/{repos_helper_clone.py → helpers_repos/clone.py} +2 -3
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +218 -0
- machineconfig/scripts/python/{count_lines.py → helpers_repos/count_lines.py} +10 -10
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +17 -0
- machineconfig/scripts/python/{repos_helper.py → helpers_repos/entrypoint.py} +9 -17
- machineconfig/scripts/python/helpers_repos/grource.py +340 -0
- machineconfig/scripts/python/{repos_helper_record.py → helpers_repos/record.py} +4 -3
- machineconfig/scripts/python/helpers_repos/sync.py +66 -0
- machineconfig/scripts/python/{repos_helper_update.py → helpers_repos/update.py} +3 -3
- machineconfig/scripts/python/helpers_sessions/__init__.py +0 -0
- machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +58 -0
- machineconfig/scripts/python/helpers_utils/download.py +152 -0
- machineconfig/scripts/python/helpers_utils/path.py +108 -0
- machineconfig/scripts/python/interactive.py +64 -84
- machineconfig/scripts/python/machineconfig.py +63 -0
- machineconfig/scripts/python/msearch.py +21 -0
- machineconfig/scripts/python/nw/__init__.py +0 -0
- machineconfig/scripts/python/{devops_add_identity.py → nw/devops_add_identity.py} +0 -2
- machineconfig/scripts/python/{devops_add_ssh_key.py → nw/devops_add_ssh_key.py} +73 -43
- machineconfig/scripts/{linux → python/nw}/mount_nfs +1 -1
- machineconfig/scripts/python/{mount_nfs.py → nw/mount_nfs.py} +3 -3
- machineconfig/scripts/{linux → python/nw}/mount_nw_drive +1 -2
- machineconfig/scripts/python/{mount_ssh.py → nw/mount_ssh.py} +3 -3
- machineconfig/scripts/python/{onetimeshare.py → nw/onetimeshare.py} +0 -1
- machineconfig/scripts/python/nw/ssh_debug_linux.py +391 -0
- machineconfig/scripts/python/nw/ssh_debug_windows.py +338 -0
- machineconfig/scripts/python/{wifi_conn.py → nw/wifi_conn.py} +1 -53
- machineconfig/scripts/python/{wsl_windows_transfer.py → nw/wsl_windows_transfer.py} +5 -4
- machineconfig/scripts/python/sessions.py +64 -44
- machineconfig/scripts/python/terminal.py +127 -0
- machineconfig/scripts/python/utils.py +66 -0
- machineconfig/scripts/windows/{mount_nfs.ps1 → mounts/mount_nfs.ps1} +1 -3
- machineconfig/scripts/windows/{mount_ssh.ps1 → mounts/mount_ssh.ps1} +1 -1
- machineconfig/scripts/windows/{share_smb.ps1 → mounts/share_smb.ps1} +0 -6
- machineconfig/scripts/windows/wrap_mcfg.ps1 +60 -0
- machineconfig/settings/broot/br.sh +0 -4
- machineconfig/settings/broot/conf.toml +1 -1
- machineconfig/settings/helix/config.toml +16 -0
- machineconfig/settings/helix/languages.toml +13 -4
- machineconfig/settings/helix/yazi-picker.sh +12 -0
- machineconfig/settings/lf/linux/exe/lfcd.sh +1 -0
- machineconfig/settings/lf/linux/exe/previewer.sh +9 -3
- machineconfig/settings/lf/linux/lfrc +10 -12
- machineconfig/settings/lf/windows/fzf_edit.ps1 +2 -2
- machineconfig/settings/lf/windows/lfrc +18 -38
- machineconfig/settings/lf/windows/mkfile.ps1 +1 -1
- machineconfig/settings/linters/.ruff.toml +1 -1
- machineconfig/settings/lvim/windows/archive/config_additional.lua +0 -6
- machineconfig/settings/marimo/marimo.toml +80 -0
- machineconfig/settings/marimo/snippets/globalize.py +34 -0
- machineconfig/settings/pistol/pistol.conf +1 -1
- machineconfig/settings/shells/bash/init.sh +55 -31
- machineconfig/settings/shells/nushell/config.nu +1 -34
- machineconfig/settings/shells/nushell/init.nu +127 -0
- machineconfig/settings/shells/pwsh/init.ps1 +60 -43
- machineconfig/settings/shells/starship/starship.toml +16 -0
- machineconfig/settings/shells/wezterm/wezterm.lua +2 -0
- machineconfig/settings/shells/wt/settings.json +32 -17
- machineconfig/settings/shells/zsh/init.sh +89 -0
- machineconfig/settings/svim/linux/init.toml +0 -4
- machineconfig/settings/svim/windows/init.toml +0 -3
- machineconfig/settings/yazi/init.lua +57 -0
- machineconfig/settings/yazi/keymap_linux.toml +79 -0
- machineconfig/settings/yazi/keymap_windows.toml +78 -0
- machineconfig/settings/yazi/shell/yazi_cd.ps1 +33 -0
- machineconfig/settings/yazi/shell/yazi_cd.sh +8 -0
- machineconfig/settings/yazi/yazi.toml +13 -0
- machineconfig/setup_linux/__init__.py +10 -0
- machineconfig/setup_linux/apps_desktop.sh +89 -0
- machineconfig/setup_linux/apps_gui.sh +64 -0
- machineconfig/setup_linux/{nix → others}/cli_installation.sh +9 -29
- machineconfig/setup_linux/ssh/openssh_all.sh +25 -0
- machineconfig/setup_linux/ssh/openssh_wsl.sh +38 -0
- machineconfig/setup_linux/uv.sh +15 -0
- machineconfig/setup_linux/web_shortcuts/interactive.sh +26 -6
- machineconfig/setup_mac/__init__.py +16 -0
- machineconfig/setup_mac/apps_gui.sh +248 -0
- machineconfig/setup_mac/ssh/openssh_setup.sh +114 -0
- machineconfig/setup_mac/uv.sh +36 -0
- machineconfig/setup_windows/__init__.py +8 -0
- machineconfig/setup_windows/others/power_options.ps1 +7 -0
- machineconfig/setup_windows/ssh/add-sshkey.ps1 +29 -0
- machineconfig/setup_windows/ssh/add_identity.ps1 +11 -0
- machineconfig/setup_windows/ssh/openssh-server.ps1 +37 -0
- machineconfig/setup_windows/uv.ps1 +10 -0
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +27 -10
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +16 -0
- machineconfig/utils/accessories.py +7 -5
- machineconfig/utils/cloud/onedrive/README.md +139 -0
- machineconfig/utils/code.py +133 -106
- machineconfig/utils/files/art/fat_croco.txt +10 -0
- machineconfig/utils/files/art/halfwit_croco.txt +9 -0
- machineconfig/utils/files/art/happy_croco.txt +22 -0
- machineconfig/utils/files/art/water_croco.txt +11 -0
- machineconfig/utils/files/ascii_art.py +1 -1
- machineconfig/utils/files/dbms.py +257 -0
- machineconfig/utils/files/headers.py +11 -14
- machineconfig/utils/files/ouch/__init__.py +0 -0
- machineconfig/utils/files/ouch/decompress.py +45 -0
- machineconfig/utils/files/read.py +10 -18
- machineconfig/utils/installer_utils/installer_class.py +68 -126
- machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +109 -117
- machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +31 -81
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +44 -74
- machineconfig/utils/io.py +77 -23
- machineconfig/utils/links.py +254 -162
- machineconfig/utils/meta.py +255 -0
- machineconfig/utils/notifications.py +1 -1
- machineconfig/utils/options.py +13 -3
- machineconfig/utils/path_extended.py +46 -100
- machineconfig/utils/path_helper.py +75 -22
- machineconfig/utils/procs.py +50 -70
- machineconfig/utils/scheduler.py +94 -97
- machineconfig/utils/scheduling.py +0 -3
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
- machineconfig/utils/schemas/layouts/layout_types.py +1 -1
- machineconfig/utils/source_of_truth.py +3 -6
- machineconfig/utils/ssh.py +742 -264
- machineconfig/utils/ssh_utils/utils.py +0 -0
- machineconfig/utils/terminal.py +2 -113
- machineconfig/utils/tst.py +20 -0
- machineconfig/utils/upgrade_packages.py +109 -28
- machineconfig/utils/ve.py +11 -4
- machineconfig-7.66.dist-info/METADATA +124 -0
- machineconfig-7.66.dist-info/RECORD +451 -0
- machineconfig-7.66.dist-info/entry_points.txt +15 -0
- machineconfig/cluster/sessions_managers/ffile.py +0 -4
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -49
- machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -85
- machineconfig/jobs/linux/msc/cli_agents.sh +0 -16
- machineconfig/jobs/python/python_ve_symlink.py +0 -37
- machineconfig/jobs/python/vscode/api.py +0 -57
- machineconfig/jobs/python/vscode/sync_code.py +0 -73
- machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +0 -14
- machineconfig/jobs/windows/start_terminal.ps1 +0 -6
- machineconfig/jobs/windows/startup_file.cmd +0 -2
- machineconfig/profile/create.py +0 -303
- machineconfig/profile/shell.py +0 -176
- machineconfig/scripts/cloud/init.sh +0 -119
- machineconfig/scripts/linux/agents +0 -2
- machineconfig/scripts/linux/choose_wezterm_theme +0 -3
- machineconfig/scripts/linux/cloud_copy +0 -2
- machineconfig/scripts/linux/cloud_mount +0 -2
- machineconfig/scripts/linux/cloud_repo_sync +0 -2
- machineconfig/scripts/linux/cloud_sync +0 -2
- machineconfig/scripts/linux/croshell +0 -3
- machineconfig/scripts/linux/devops +0 -2
- machineconfig/scripts/linux/fire +0 -2
- machineconfig/scripts/linux/ftpx +0 -2
- machineconfig/scripts/linux/fzf2g +0 -21
- machineconfig/scripts/linux/fzffg +0 -25
- machineconfig/scripts/linux/gh_models +0 -2
- machineconfig/scripts/linux/initai +0 -2
- machineconfig/scripts/linux/kill_process +0 -2
- machineconfig/scripts/linux/scheduler +0 -2
- machineconfig/scripts/linux/sessions +0 -2
- machineconfig/scripts/linux/share_smb +0 -1
- machineconfig/scripts/linux/start_slidev +0 -2
- machineconfig/scripts/linux/start_terminals +0 -3
- machineconfig/scripts/linux/warp-cli.sh +0 -122
- machineconfig/scripts/linux/wifi_conn +0 -2
- machineconfig/scripts/linux/z_ls +0 -104
- machineconfig/scripts/python/ai/solutions/copilot/prompts/allLintersAndTypeCheckers.prompt.md +0 -5
- machineconfig/scripts/python/cloud_repo_sync.py +0 -190
- machineconfig/scripts/python/count_lines_frontend.py +0 -16
- machineconfig/scripts/python/dotfile.py +0 -78
- machineconfig/scripts/python/fire_agents_helper_types.py +0 -12
- machineconfig/scripts/python/get_zellij_cmd.py +0 -15
- machineconfig/scripts/python/gh_models.py +0 -104
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +0 -116
- machineconfig/scripts/python/repos.py +0 -132
- machineconfig/scripts/python/repos_helper_action.py +0 -378
- machineconfig/scripts/python/snapshot.py +0 -25
- machineconfig/scripts/python/start_terminals.py +0 -121
- machineconfig/scripts/python/t4.py +0 -17
- machineconfig/scripts/windows/agents.ps1 +0 -1
- machineconfig/scripts/windows/choose_wezterm_theme.ps1 +0 -1
- machineconfig/scripts/windows/cloud_copy.ps1 +0 -1
- machineconfig/scripts/windows/cloud_mount.ps1 +0 -1
- machineconfig/scripts/windows/cloud_repo_sync.ps1 +0 -1
- machineconfig/scripts/windows/cloud_sync.ps1 +0 -1
- machineconfig/scripts/windows/croshell.ps1 +0 -1
- machineconfig/scripts/windows/devops.ps1 +0 -1
- machineconfig/scripts/windows/dotfile.ps1 +0 -1
- machineconfig/scripts/windows/fire.ps1 +0 -1
- machineconfig/scripts/windows/ftpx.ps1 +0 -1
- machineconfig/scripts/windows/gpt.ps1 +0 -1
- machineconfig/scripts/windows/grep.ps1 +0 -2
- machineconfig/scripts/windows/initai.ps1 +0 -1
- machineconfig/scripts/windows/kill_process.ps1 +0 -1
- machineconfig/scripts/windows/nano.ps1 +0 -3
- machineconfig/scripts/windows/pomodoro.ps1 +0 -1
- machineconfig/scripts/windows/reload_path.ps1 +0 -3
- machineconfig/scripts/windows/scheduler.ps1 +0 -1
- machineconfig/scripts/windows/sessions.ps1 +0 -1
- machineconfig/scripts/windows/snapshot.ps1 +0 -1
- machineconfig/scripts/windows/start_slidev.ps1 +0 -1
- machineconfig/scripts/windows/start_terminals.ps1 +0 -1
- machineconfig/scripts/windows/wifi_conn.ps1 +0 -2
- machineconfig/scripts/windows/wsl_rdp_windows_port_forwarding.ps1 +0 -46
- machineconfig/scripts/windows/wsl_ssh_windows_port_forwarding.ps1 +0 -76
- machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
- machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +0 -57
- machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -11
- machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -52
- machineconfig/setup_windows/web_shortcuts/all.ps1 +0 -18
- machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +0 -36
- machineconfig/setup_windows/web_shortcuts/croshell.ps1 +0 -16
- machineconfig/setup_windows/web_shortcuts/ssh.ps1 +0 -11
- machineconfig/utils/ai/generate_file_checklist.py +0 -68
- machineconfig-5.15.dist-info/METADATA +0 -188
- machineconfig-5.15.dist-info/RECORD +0 -415
- machineconfig-5.15.dist-info/entry_points.txt +0 -18
- machineconfig/cluster/sessions_managers/{utils → helpers}/load_balancer_helper.py +0 -0
- machineconfig/scripts/linux/{share_nfs → other/share_nfs} +0 -0
- machineconfig/scripts/linux/{start_docker → other/start_docker} +0 -0
- machineconfig/scripts/linux/{switch_ip → other/switch_ip} +0 -0
- machineconfig/{jobs/python → scripts/python/helpers_agents}/__init__.py +0 -0
- machineconfig/scripts/python/{helpers → helpers_agents/agentic_frameworks}/__init__.py +0 -0
- machineconfig/scripts/python/{fire_agents_help_search.py → helpers_agents/fire_agents_help_search.py} +0 -0
- machineconfig/scripts/python/{fire_agents_load_balancer.py → helpers_agents/fire_agents_load_balancer.py} +0 -0
- machineconfig/{jobs/windows/msc/cli_agents.bat → scripts/python/helpers_cloud/__init__.py} +0 -0
- machineconfig/scripts/python/{helpers → helpers_cloud}/cloud_helpers.py +1 -1
- /machineconfig/scripts/python/{helpers → helpers_cloud}/helpers5.py +0 -0
- /machineconfig/{jobs/windows/msc/cli_agents.ps1 → scripts/python/helpers_croshell/__init__.py} +0 -0
- /machineconfig/scripts/python/{pomodoro.py → helpers_croshell/pomodoro.py} +0 -0
- /machineconfig/scripts/python/{scheduler.py → helpers_croshell/scheduler.py} +0 -0
- /machineconfig/scripts/python/{viewer.py → helpers_croshell/viewer.py} +0 -0
- /machineconfig/scripts/python/{viewer_template.py → helpers_croshell/viewer_template.py} +0 -0
- /machineconfig/scripts/python/{fire_jobs_streamlit_helper.py → helpers_devops/__init__.py} +0 -0
- /machineconfig/scripts/{windows/share_nfs.ps1 → python/helpers_devops/themes/__init__.py} +0 -0
- /machineconfig/{settings/yazi/keymap.toml → scripts/python/helpers_devops/themes/choose_starship_theme.ps1} +0 -0
- /machineconfig/scripts/python/{cloud_manager.py → helpers_fire_command/cloud_manager.py} +0 -0
- /machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/skrg +0 -0
- /machineconfig/scripts/{windows → python/helpers_msearch/scripts_windows}/fzfb.ps1 +0 -0
- /machineconfig/scripts/{windows → python/helpers_msearch/scripts_windows}/fzfg.ps1 +0 -0
- /machineconfig/scripts/{windows → python/helpers_msearch/scripts_windows}/fzfrga.bat +0 -0
- /machineconfig/scripts/{linux → python/nw}/mount_drive +0 -0
- /machineconfig/scripts/python/{mount_nw_drive.py → nw/mount_nw_drive.py} +0 -0
- /machineconfig/scripts/{linux → python/nw}/mount_smb +0 -0
- /machineconfig/scripts/windows/{mount_nw.ps1 → mounts/mount_nw.ps1} +0 -0
- /machineconfig/scripts/windows/{mount_smb.ps1 → mounts/mount_smb.ps1} +0 -0
- /machineconfig/scripts/windows/{share_cloud.cmd → mounts/share_cloud.cmd} +0 -0
- /machineconfig/scripts/windows/{unlock_bitlocker.ps1 → mounts/unlock_bitlocker.ps1} +0 -0
- /machineconfig/setup_linux/{web_shortcuts → others}/android.sh +0 -0
- /machineconfig/{jobs/windows/archive → setup_windows/ssh}/openssh-server_add_key.ps1 +0 -0
- /machineconfig/{jobs/windows/archive → setup_windows/ssh}/openssh-server_copy-ssh-id.ps1 +0 -0
- {machineconfig-5.15.dist-info → machineconfig-7.66.dist-info}/WHEEL +0 -0
- {machineconfig-5.15.dist-info → machineconfig-7.66.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""Metaprogramming utilities for analyzing and serializing Python functions."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_import_module_string(py_file: str) -> str:
|
|
8
|
+
from machineconfig.scripts.python.helpers_fire_command.file_wrangler import get_import_module_code
|
|
9
|
+
from machineconfig.utils.accessories import get_repo_root
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
repo_root = get_repo_root(Path(py_file))
|
|
12
|
+
import_line = get_import_module_code(py_file)
|
|
13
|
+
if repo_root is not None:
|
|
14
|
+
repo_root_add = f"""sys.path.append(r'{repo_root}')"""
|
|
15
|
+
else:
|
|
16
|
+
repo_root_add = ""
|
|
17
|
+
txt: str = f"""
|
|
18
|
+
try:
|
|
19
|
+
{import_line}
|
|
20
|
+
except (ImportError, ModuleNotFoundError) as ex:
|
|
21
|
+
print(fr"❌ Failed to import `{py_file}` as a module: {{ex}} ")
|
|
22
|
+
print(fr"⚠️ Attempting import with ad-hoc `$PATH` manipulation. DO NOT pickle any objects in this session as correct deserialization cannot be guaranteed.")
|
|
23
|
+
import sys
|
|
24
|
+
sys.path.append(r'{Path(py_file).parent}')
|
|
25
|
+
{repo_root_add}
|
|
26
|
+
from {Path(py_file).stem} import *
|
|
27
|
+
print(fr"✅ Successfully imported `{py_file}`")
|
|
28
|
+
"""
|
|
29
|
+
return txt
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_module: bool) -> str:
|
|
33
|
+
"""
|
|
34
|
+
caveats: always use keyword arguments in the lambda call for best results.
|
|
35
|
+
return statement not allowed in the wrapped function (otherwise it can be put in the global space)
|
|
36
|
+
type hint in kwargs has nothing that is not built in, e.g. Optional will not work as it requires an import.
|
|
37
|
+
|
|
38
|
+
Given a no-arg lambda like `lambda: func(a=var1, b=var2)`,
|
|
39
|
+
return a string containing the full function definition of `func`
|
|
40
|
+
but with the defaults for the parameters provided in the call replaced
|
|
41
|
+
by the *actual* values (repr) taken from the lambda's globals.
|
|
42
|
+
|
|
43
|
+
All imports are local to this function.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
lmb: A lambda function with no arguments
|
|
47
|
+
in_global: If True, return kwargs as global variable assignments followed by dedented body.
|
|
48
|
+
If False, return the full function definition with updated defaults.
|
|
49
|
+
import_module: When True, prepend module import bootstrap code for the function's source file.
|
|
50
|
+
"""
|
|
51
|
+
# local imports
|
|
52
|
+
import inspect as _inspect
|
|
53
|
+
import ast as _ast
|
|
54
|
+
import textwrap as _textwrap
|
|
55
|
+
import types as _types
|
|
56
|
+
from pathlib import Path as _Path
|
|
57
|
+
|
|
58
|
+
def _stringify_annotation(annotation: Any) -> Any:
|
|
59
|
+
if annotation is _inspect.Signature.empty or annotation is _inspect.Parameter.empty:
|
|
60
|
+
return annotation
|
|
61
|
+
if isinstance(annotation, str):
|
|
62
|
+
return annotation
|
|
63
|
+
try:
|
|
64
|
+
return _inspect.formatannotation(annotation)
|
|
65
|
+
except Exception:
|
|
66
|
+
return str(annotation)
|
|
67
|
+
|
|
68
|
+
# sanity checks
|
|
69
|
+
if not (callable(lmb) and isinstance(lmb, _types.LambdaType)):
|
|
70
|
+
raise TypeError("Expected a lambda function object")
|
|
71
|
+
|
|
72
|
+
src = _inspect.getsource(lmb)
|
|
73
|
+
src = _textwrap.dedent(src)
|
|
74
|
+
tree = _ast.parse(src)
|
|
75
|
+
|
|
76
|
+
# find first Lambda node
|
|
77
|
+
lambda_node = None
|
|
78
|
+
for n in _ast.walk(tree):
|
|
79
|
+
if isinstance(n, _ast.Lambda):
|
|
80
|
+
lambda_node = n
|
|
81
|
+
break
|
|
82
|
+
if lambda_node is None:
|
|
83
|
+
raise ValueError("Could not find a lambda expression in source")
|
|
84
|
+
|
|
85
|
+
body = lambda_node.body
|
|
86
|
+
if not isinstance(body, _ast.Call):
|
|
87
|
+
raise ValueError("Lambda body is not a call expression")
|
|
88
|
+
|
|
89
|
+
globals_dict = getattr(lmb, "__globals__", {})
|
|
90
|
+
|
|
91
|
+
# Also capture closure variables from the lambda
|
|
92
|
+
closure_dict: dict[str, Any] = {}
|
|
93
|
+
if lmb.__closure__:
|
|
94
|
+
code_obj = lmb.__code__
|
|
95
|
+
freevars = code_obj.co_freevars
|
|
96
|
+
for i, var_name in enumerate(freevars):
|
|
97
|
+
closure_dict[var_name] = lmb.__closure__[i].cell_contents
|
|
98
|
+
|
|
99
|
+
# Merge globals and closures (closures take precedence for shadowing)
|
|
100
|
+
eval_namespace = {**globals_dict, **closure_dict}
|
|
101
|
+
|
|
102
|
+
# resolve the function object being called
|
|
103
|
+
try:
|
|
104
|
+
func_ref_src = _ast.unparse(body.func)
|
|
105
|
+
except AttributeError:
|
|
106
|
+
func_ref_src = _ast.get_source_segment(src, body.func) or ""
|
|
107
|
+
try:
|
|
108
|
+
func_obj = eval(func_ref_src, eval_namespace)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
raise RuntimeError(f"Could not resolve function reference '{func_ref_src}': {e}")
|
|
111
|
+
|
|
112
|
+
if not callable(func_obj):
|
|
113
|
+
raise TypeError("Resolved object is not callable")
|
|
114
|
+
|
|
115
|
+
func_name = getattr(func_obj, "__name__", "<unknown>")
|
|
116
|
+
|
|
117
|
+
import_prefix: str = ""
|
|
118
|
+
if import_module:
|
|
119
|
+
module_file = _inspect.getsourcefile(func_obj)
|
|
120
|
+
module_path_candidate: str = module_file if module_file is not None else _inspect.getfile(func_obj)
|
|
121
|
+
import_prefix = get_import_module_string(str(_Path(module_path_candidate)))
|
|
122
|
+
|
|
123
|
+
# Evaluate each keyword argument value in the lambda's globals to get real Python objects
|
|
124
|
+
call_kwargs: dict[str, Any] = {}
|
|
125
|
+
for kw in body.keywords:
|
|
126
|
+
if kw.arg is None:
|
|
127
|
+
# **kwargs in call — evaluate to dict and merge
|
|
128
|
+
try:
|
|
129
|
+
val = eval(compile(_ast.Expression(kw.value), "<lambda_eval>", "eval"), eval_namespace)
|
|
130
|
+
if isinstance(val, dict):
|
|
131
|
+
call_kwargs.update(val)
|
|
132
|
+
else:
|
|
133
|
+
raise ValueError("Keyword expansion did not evaluate to a dict")
|
|
134
|
+
except Exception as e:
|
|
135
|
+
raise RuntimeError(f"Failed to evaluate **kwargs expression: {e}")
|
|
136
|
+
else:
|
|
137
|
+
try:
|
|
138
|
+
val = eval(compile(_ast.Expression(kw.value), "<lambda_eval>", "eval"), eval_namespace)
|
|
139
|
+
call_kwargs[kw.arg] = val
|
|
140
|
+
except Exception as e:
|
|
141
|
+
raise RuntimeError(f"Failed to evaluate value for kw '{kw.arg}': {e}")
|
|
142
|
+
|
|
143
|
+
# Try to get original source and dedent it for body extraction
|
|
144
|
+
try:
|
|
145
|
+
orig_src = _inspect.getsource(func_obj)
|
|
146
|
+
ded = _textwrap.dedent(orig_src)
|
|
147
|
+
lines = ded.splitlines()
|
|
148
|
+
def_index = None
|
|
149
|
+
for i, ln in enumerate(lines):
|
|
150
|
+
if ln.lstrip().startswith(f"def {func_name}("):
|
|
151
|
+
def_index = i
|
|
152
|
+
break
|
|
153
|
+
if def_index is None:
|
|
154
|
+
body_lines = ded.splitlines()
|
|
155
|
+
else:
|
|
156
|
+
signature_end_index = None
|
|
157
|
+
for i in range(def_index, len(lines)):
|
|
158
|
+
line_no_comment = lines[i].split("#", 1)[0].rstrip()
|
|
159
|
+
if line_no_comment.endswith(":"):
|
|
160
|
+
signature_end_index = i
|
|
161
|
+
break
|
|
162
|
+
if signature_end_index is None:
|
|
163
|
+
body_lines = lines[def_index + 1 :]
|
|
164
|
+
else:
|
|
165
|
+
body_lines = lines[signature_end_index + 1 :]
|
|
166
|
+
# ensure we have a body, otherwise use pass
|
|
167
|
+
if not any(line.strip() for line in body_lines):
|
|
168
|
+
body_text = " pass\n"
|
|
169
|
+
else:
|
|
170
|
+
joined_body = "\n".join(body_lines)
|
|
171
|
+
if not joined_body.endswith("\n"):
|
|
172
|
+
joined_body = f"{joined_body}\n"
|
|
173
|
+
body_text = joined_body
|
|
174
|
+
except (OSError, IOError, TypeError):
|
|
175
|
+
body_text = " pass\n"
|
|
176
|
+
|
|
177
|
+
# Build a replaced signature using inspect.signature
|
|
178
|
+
sig = _inspect.signature(func_obj)
|
|
179
|
+
new_params: list[_inspect.Parameter] = []
|
|
180
|
+
for name, param in sig.parameters.items():
|
|
181
|
+
# If the call provided a value for this parameter, replace default
|
|
182
|
+
if name in call_kwargs:
|
|
183
|
+
new_default = call_kwargs[name]
|
|
184
|
+
else:
|
|
185
|
+
new_default = param.default
|
|
186
|
+
|
|
187
|
+
normalized_annotation = _stringify_annotation(param.annotation)
|
|
188
|
+
|
|
189
|
+
if new_default is _inspect.Parameter.empty:
|
|
190
|
+
new_param = _inspect.Parameter(name, param.kind, annotation=normalized_annotation)
|
|
191
|
+
else:
|
|
192
|
+
new_param = _inspect.Parameter(
|
|
193
|
+
name, param.kind, default=new_default, annotation=normalized_annotation
|
|
194
|
+
)
|
|
195
|
+
new_params.append(new_param)
|
|
196
|
+
|
|
197
|
+
return_annotation = _stringify_annotation(sig.return_annotation)
|
|
198
|
+
new_sig = _inspect.Signature(parameters=new_params, return_annotation=return_annotation)
|
|
199
|
+
|
|
200
|
+
# If in_global mode, return kwargs as global assignments + dedented body
|
|
201
|
+
if in_global:
|
|
202
|
+
global_assignments: list[str] = []
|
|
203
|
+
for name, param in sig.parameters.items():
|
|
204
|
+
# Get the value from call_kwargs if provided, else use original default
|
|
205
|
+
if name in call_kwargs:
|
|
206
|
+
value = call_kwargs[name]
|
|
207
|
+
elif param.default is not _inspect.Parameter.empty:
|
|
208
|
+
value = param.default
|
|
209
|
+
else:
|
|
210
|
+
# No value provided and no default - skip this parameter
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
# Build type annotation string if available
|
|
214
|
+
if param.annotation is not _inspect.Parameter.empty:
|
|
215
|
+
annotation_literal = _stringify_annotation(param.annotation)
|
|
216
|
+
if isinstance(annotation_literal, str):
|
|
217
|
+
global_assignments.append(f"{name}: {repr(annotation_literal)} = {repr(value)}")
|
|
218
|
+
else:
|
|
219
|
+
global_assignments.append(f"{name} = {repr(value)}")
|
|
220
|
+
else:
|
|
221
|
+
global_assignments.append(f"{name} = {repr(value)}")
|
|
222
|
+
|
|
223
|
+
# Dedent the body text to remove function indentation
|
|
224
|
+
dedented_body = _textwrap.dedent(body_text).rstrip()
|
|
225
|
+
|
|
226
|
+
# Combine global assignments and body
|
|
227
|
+
if global_assignments:
|
|
228
|
+
result_parts: list[str] = ["\n".join(global_assignments), "", dedented_body]
|
|
229
|
+
result_text = "\n".join(result_parts)
|
|
230
|
+
else:
|
|
231
|
+
result_text = dedented_body
|
|
232
|
+
else:
|
|
233
|
+
header = f"def {func_name}{new_sig}:\n"
|
|
234
|
+
result_text = header + body_text
|
|
235
|
+
|
|
236
|
+
if in_global:
|
|
237
|
+
lines = result_text.splitlines()
|
|
238
|
+
if lines[-1].startswith("return "):
|
|
239
|
+
lines[-1] = lines[-1].replace("return ", "# return ", 1)
|
|
240
|
+
result_text = "\n".join(lines)
|
|
241
|
+
|
|
242
|
+
if "Optional" in result_text or "Any" in result_text or "Union" in result_text or "Literal" in result_text:
|
|
243
|
+
result_text = "from typing import Optional, Any, Union, Literal\n\n" + result_text
|
|
244
|
+
if import_prefix:
|
|
245
|
+
result_text = f"{import_prefix}{result_text}"
|
|
246
|
+
return result_text
|
|
247
|
+
|
|
248
|
+
if __name__ == "__main__":
|
|
249
|
+
from machineconfig.utils.code import print_code
|
|
250
|
+
import_code_robust = "<import_code_robust>"
|
|
251
|
+
res = lambda_to_python_script(
|
|
252
|
+
lmb=lambda: print_code(code=import_code_robust, lexer="python", desc="import as module code"),
|
|
253
|
+
in_global=True, import_module=False
|
|
254
|
+
)
|
|
255
|
+
print(res)
|
|
@@ -110,7 +110,7 @@ encryption = ssl
|
|
|
110
110
|
|
|
111
111
|
def send_message(self, to: str, subject: str, body: str, txt_to_html: bool = True, attachments: Optional[list[Any]] = None):
|
|
112
112
|
_ = attachments
|
|
113
|
-
body += "\n\nThis is an automated email sent via
|
|
113
|
+
body += "\n\nThis is an automated email sent via machineconfig.comms script."
|
|
114
114
|
# msg = message.EmailMessage()
|
|
115
115
|
msg = MIMEMultipart("alternative")
|
|
116
116
|
msg["subject"] = subject
|
machineconfig/utils/options.py
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from machineconfig.utils.installer_utils.
|
|
2
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
|
|
3
3
|
from rich.text import Text
|
|
4
4
|
from rich.panel import Panel
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
import subprocess
|
|
7
|
-
from typing import Optional, Union, Iterable, overload, Literal
|
|
7
|
+
from typing import Optional, Union, Iterable, overload, Literal, cast
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# def strip_ansi_codes(text: str) -> str:
|
|
11
|
+
# """Remove ANSI color codes from text."""
|
|
12
|
+
# import re
|
|
13
|
+
# return re.sub(r'\x1b\[[0-9;]*[a-zA-Z]', '', text)
|
|
14
|
+
|
|
8
15
|
|
|
9
16
|
@overload
|
|
10
17
|
def choose_from_options[T](msg: str, options: Iterable[T], multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False) -> T: ...
|
|
@@ -22,17 +29,20 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
|
|
|
22
29
|
from pyfzf.pyfzf import FzfPrompt
|
|
23
30
|
fzf_prompt = FzfPrompt()
|
|
24
31
|
nl = "\n"
|
|
25
|
-
choice_string_multi: list[str] = fzf_prompt.prompt(choices=options_strings, fzf_options=("--multi" if multi else "") + f' --prompt "{prompt.replace(nl, " ")}" ') # --border-label={msg.replace(nl, ' ')}")
|
|
32
|
+
choice_string_multi: list[str] = fzf_prompt.prompt(choices=options_strings, fzf_options=("--multi" if multi else "") + f' --prompt "{prompt.replace(nl, " ")}" --ansi') # --border-label={msg.replace(nl, ' ')}")
|
|
26
33
|
# --border=rounded doens't work on older versions of fzf installed at Ubuntu 20.04
|
|
27
34
|
if not multi:
|
|
28
35
|
try:
|
|
29
36
|
choice_one_string = choice_string_multi[0]
|
|
37
|
+
if isinstance(list(options)[0], str): return cast(T, choice_one_string)
|
|
30
38
|
choice_idx = options_strings.index(choice_one_string)
|
|
31
39
|
return list(options)[choice_idx]
|
|
32
40
|
except IndexError as ie:
|
|
33
41
|
print(f"❌ Error: {options=}, {choice_string_multi=}")
|
|
34
42
|
print(f"🔍 Available choices: {choice_string_multi}")
|
|
35
43
|
raise ie
|
|
44
|
+
if isinstance(list(options)[0], str):
|
|
45
|
+
return cast(list[T], choice_string_multi)
|
|
36
46
|
choice_idx_s = [options_strings.index(x) for x in choice_string_multi]
|
|
37
47
|
return [list(options)[x] for x in choice_idx_s]
|
|
38
48
|
else:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from machineconfig.utils.accessories import randstr
|
|
2
|
+
from machineconfig.utils.io import decrypt, encrypt
|
|
2
3
|
|
|
3
4
|
from datetime import datetime
|
|
4
5
|
import time
|
|
@@ -10,10 +11,12 @@ from platform import system
|
|
|
10
11
|
from typing import Any, Optional, Union, Callable, TypeAlias, Literal
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
|
|
13
15
|
OPLike: TypeAlias = Union[str, "PathExtended", Path, None]
|
|
14
16
|
PLike: TypeAlias = Union[str, "PathExtended", Path]
|
|
15
17
|
FILE_MODE: TypeAlias = Literal["r", "w", "x", "a"]
|
|
16
18
|
SHUTIL_FORMATS: TypeAlias = Literal["zip", "tar", "gztar", "bztar", "xztar"]
|
|
19
|
+
DECOMPRESS_SUPPORTED_FORMATS = [".tar.gz", ".tgz", ".tar", ".gz", ".tar.bz", ".tbz", ".tar.xz", ".zip", ".7z"]
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
def _is_user_admin() -> bool:
|
|
@@ -54,81 +57,6 @@ def _run_shell_command(
|
|
|
54
57
|
)
|
|
55
58
|
|
|
56
59
|
|
|
57
|
-
def pwd2key(password: str, salt: Optional[bytes] = None, iterations: int = 10) -> bytes: # Derive a secret key from a given password and salt"""
|
|
58
|
-
import base64
|
|
59
|
-
|
|
60
|
-
if salt is None:
|
|
61
|
-
import hashlib
|
|
62
|
-
|
|
63
|
-
m = hashlib.sha256()
|
|
64
|
-
m.update(password.encode(encoding="utf-8"))
|
|
65
|
-
return base64.urlsafe_b64encode(s=m.digest()) # make url-safe bytes required by Ferent.
|
|
66
|
-
from cryptography.hazmat.primitives import hashes
|
|
67
|
-
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
68
|
-
|
|
69
|
-
return base64.urlsafe_b64encode(PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=iterations, backend=None).derive(password.encode()))
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def encrypt(msg: bytes, key: Optional[bytes] = None, pwd: Optional[str] = None, salted: bool = True, iteration: Optional[int] = None, gen_key: bool = False) -> bytes:
|
|
73
|
-
import base64
|
|
74
|
-
from cryptography.fernet import Fernet
|
|
75
|
-
|
|
76
|
-
salt, iteration = None, None
|
|
77
|
-
if pwd is not None: # generate it from password
|
|
78
|
-
assert (key is None) and (type(pwd) is str), "❌ You can either pass key or pwd, or none of them, but not both."
|
|
79
|
-
import secrets
|
|
80
|
-
|
|
81
|
-
iteration = iteration or secrets.randbelow(exclusive_upper_bound=1_000_000)
|
|
82
|
-
salt = secrets.token_bytes(nbytes=16) if salted else None
|
|
83
|
-
key_resolved = pwd2key(password=pwd, salt=salt, iterations=iteration)
|
|
84
|
-
elif key is None:
|
|
85
|
-
if gen_key:
|
|
86
|
-
key_resolved = Fernet.generate_key()
|
|
87
|
-
Path.home().joinpath("dotfiles/creds/data/encrypted_files_key.bytes").write_bytes(key_resolved)
|
|
88
|
-
else:
|
|
89
|
-
try:
|
|
90
|
-
key_resolved = Path.home().joinpath("dotfiles/creds/data/encrypted_files_key.bytes").read_bytes()
|
|
91
|
-
print(f"⚠️ Using key from: {Path.home().joinpath('dotfiles/creds/data/encrypted_files_key.bytes')}")
|
|
92
|
-
except FileNotFoundError as err:
|
|
93
|
-
print("\n" * 3, "~" * 50, """Consider Loading up your dotfiles or pass `gen_key=True` to make and save one.""", "~" * 50, "\n" * 3)
|
|
94
|
-
raise FileNotFoundError(err) from err
|
|
95
|
-
elif isinstance(key, (str, PathExtended, Path)):
|
|
96
|
-
key_resolved = Path(key).read_bytes() # a path to a key file was passed, read it:
|
|
97
|
-
elif type(key) is bytes:
|
|
98
|
-
key_resolved = key # key passed explicitly
|
|
99
|
-
else:
|
|
100
|
-
raise TypeError("❌ Key must be either a path, bytes object or None.")
|
|
101
|
-
code = Fernet(key=key_resolved).encrypt(msg)
|
|
102
|
-
if pwd is not None and salt is not None and iteration is not None:
|
|
103
|
-
return base64.urlsafe_b64encode(b"%b%b%b" % (salt, iteration.to_bytes(4, "big"), base64.urlsafe_b64decode(code)))
|
|
104
|
-
return code
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def decrypt(token: bytes, key: Optional[bytes] = None, pwd: Optional[str] = None, salted: bool = True) -> bytes:
|
|
108
|
-
import base64
|
|
109
|
-
|
|
110
|
-
if pwd is not None:
|
|
111
|
-
assert key is None, "❌ You can either pass key or pwd, or none of them, but not both."
|
|
112
|
-
if salted:
|
|
113
|
-
decoded = base64.urlsafe_b64decode(token)
|
|
114
|
-
salt, iterations, token = decoded[:16], decoded[16:20], base64.urlsafe_b64encode(decoded[20:])
|
|
115
|
-
key_resolved = pwd2key(password=pwd, salt=salt, iterations=int.from_bytes(bytes=iterations, byteorder="big"))
|
|
116
|
-
else:
|
|
117
|
-
key_resolved = pwd2key(password=pwd) # trailing `;` prevents IPython from caching the result.
|
|
118
|
-
elif type(key) is bytes:
|
|
119
|
-
assert pwd is None, "❌ You can either pass key or pwd, or none of them, but not both."
|
|
120
|
-
key_resolved = key # passsed explicitly
|
|
121
|
-
elif key is None:
|
|
122
|
-
key_resolved = Path.home().joinpath("dotfiles/creds/data/encrypted_files_key.bytes").read_bytes() # read from file
|
|
123
|
-
elif isinstance(key, (str, Path)):
|
|
124
|
-
key_resolved = Path(key).read_bytes() # passed a path to a file containing kwy
|
|
125
|
-
else:
|
|
126
|
-
raise TypeError(f"❌ Key must be either str, P, Path, bytes or None. Recieved: {type(key)}")
|
|
127
|
-
from cryptography.fernet import Fernet
|
|
128
|
-
|
|
129
|
-
return Fernet(key=key_resolved).decrypt(token)
|
|
130
|
-
|
|
131
|
-
|
|
132
60
|
def validate_name(astring: str, replace: str = "_") -> str:
|
|
133
61
|
import re
|
|
134
62
|
|
|
@@ -167,7 +95,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
167
95
|
print(f"🗑️ ❌ DELETED {repr(self)}.")
|
|
168
96
|
return self
|
|
169
97
|
|
|
170
|
-
def move(self, folder: OPLike = None, name: Optional[str] = None, path: OPLike = None, rel2it: bool = False, overwrite: bool = False, verbose: bool = True, parents: bool = True, content: bool = False) -> "PathExtended":
|
|
98
|
+
def move(self, folder: OPLike = None, name: Optional[str] = None, path: OPLike = None, rel2it: bool = False, overwrite: bool = False, verbose: bool = True, parents: bool = True, content: bool = False) -> "PathExtended": # type: ignore
|
|
171
99
|
path = self._resolve_path(folder=folder, name=name, path=path, default_name=self.absolute().name, rel2it=rel2it)
|
|
172
100
|
if parents:
|
|
173
101
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -193,9 +121,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
193
121
|
print(f"🚚 MOVED {repr(self)} ==> {repr(path)}`")
|
|
194
122
|
return path
|
|
195
123
|
|
|
196
|
-
def copy(
|
|
197
|
-
self, folder: OPLike = None, name: Optional[str] = None, path: OPLike = None, content: bool = False, verbose: bool = True, append: Optional[str] = None, overwrite: bool = False, orig: bool = False
|
|
198
|
-
) -> "PathExtended": # tested %100 # TODO: replace `content` flag with ability to interpret "*" in resolve method.
|
|
124
|
+
def copy(self, folder: OPLike = None, name: Optional[str] = None, path: OPLike = None, content: bool = False, verbose: bool = True, append: Optional[str] = None, overwrite: bool = False, orig: bool = False) -> "PathExtended": # type: ignore
|
|
199
125
|
dest = self._resolve_path(folder=folder, name=name, path=path, default_name=self.name, rel2it=False)
|
|
200
126
|
dest = dest.expanduser().resolve()
|
|
201
127
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -227,7 +153,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
227
153
|
# ======================================= File Editing / Reading ===================================
|
|
228
154
|
def download(self, folder: OPLike = None, name: Optional[str] = None, allow_redirects: bool = True, timeout: Optional[int] = None, params: Any = None) -> "PathExtended":
|
|
229
155
|
import requests
|
|
230
|
-
|
|
231
156
|
response = requests.get(self.as_url_str(), allow_redirects=allow_redirects, timeout=timeout, params=params) # Alternative: from urllib import request; request.urlopen(url).read().decode('utf-8').
|
|
232
157
|
assert response.status_code == 200, f"Download failed with status code {response.status_code}\n{response.text}"
|
|
233
158
|
if name is not None:
|
|
@@ -555,9 +480,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
555
480
|
**kwargs: Any,
|
|
556
481
|
) -> "PathExtended":
|
|
557
482
|
path_resolved, slf = self._resolve_path(folder, name, path, self.name).expanduser().resolve(), self.expanduser().resolve()
|
|
558
|
-
# if use_7z: # benefits over regular zip and encrypt: can handle very large files with low memory footprint
|
|
559
|
-
# path_resolved = path_resolved + '.7z' if not path_resolved.suffix == '.7z' else path_resolved
|
|
560
|
-
# with install_n_import("py7zr").SevenZipFile(file=path_resolved, mode=mode, password=pwd) as archive: archive.writeall(path=str(slf), arcname=None)
|
|
561
483
|
arcname_obj = PathExtended(arcname or slf.name)
|
|
562
484
|
if arcname_obj.name != slf.name:
|
|
563
485
|
arcname_obj /= slf.name # arcname has to start from somewhere and end with filename
|
|
@@ -630,15 +552,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
630
552
|
folder = folder if not content else folder.parent
|
|
631
553
|
if slf.suffix == ".7z":
|
|
632
554
|
raise NotImplementedError("I have not implemented this yet")
|
|
633
|
-
# if overwrite: P(folder).delete(sure=True)
|
|
634
|
-
# result = folder
|
|
635
|
-
# import py7zr
|
|
636
|
-
# with py7zr.SevenZipFile(file=slf, mode='r', password=pwd) as archive:
|
|
637
|
-
# if pattern is not None:
|
|
638
|
-
# import re
|
|
639
|
-
# pat = re.compile(pattern)
|
|
640
|
-
# archive.extract(path=folder, targets=[f for f in archive.getnames() if pat.match(f)])
|
|
641
|
-
# else: archive.extractall(path=folder)
|
|
642
555
|
else:
|
|
643
556
|
if overwrite:
|
|
644
557
|
if not content:
|
|
@@ -773,19 +686,52 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
773
686
|
return ret
|
|
774
687
|
|
|
775
688
|
def decompress(self, folder: OPLike = None, name: Optional[str] = None, path: OPLike = None, inplace: bool = False, orig: bool = False, verbose: bool = True) -> "PathExtended":
|
|
776
|
-
if ".tar.gz"
|
|
689
|
+
if str(self).endswith(".tar.gz") or str(self).endswith(".tgz"):
|
|
777
690
|
# res = self.ungz_untar(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
778
691
|
return self.ungz(name=f"tmp_{randstr()}.tar", inplace=inplace).untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose) # this works for .tgz suffix as well as .tar.gz
|
|
779
|
-
elif
|
|
692
|
+
elif str(self).endswith(".tar"):
|
|
693
|
+
res = self.untar(folder=folder, name=name, path=path, inplace=inplace, orig=orig, verbose=verbose)
|
|
694
|
+
elif str(self).endswith(".gz"):
|
|
780
695
|
res = self.ungz(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
781
|
-
elif ".tar.bz"
|
|
696
|
+
elif str(self).endswith(".tar.bz") or str(self).endswith(".tbz"):
|
|
782
697
|
res = self.unbz(name=f"tmp_{randstr()}.tar", inplace=inplace)
|
|
783
698
|
return res.untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose)
|
|
784
|
-
elif ".tar.xz"
|
|
699
|
+
elif str(self).endswith(".tar.xz"):
|
|
785
700
|
# res = self.unxz_untar(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
786
701
|
res = self.unxz(inplace=inplace).untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose)
|
|
787
|
-
elif ".zip"
|
|
702
|
+
elif str(self).endswith(".zip"):
|
|
788
703
|
res = self.unzip(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
704
|
+
elif str(self).endswith(".7z"):
|
|
705
|
+
def unzip_7z(archive_path: str, dest_dir: Optional[str] = None) -> Path:
|
|
706
|
+
"""
|
|
707
|
+
Uncompresses a .7z archive to a directory and returns the Path to the extraction directory.
|
|
708
|
+
|
|
709
|
+
:param archive_path: path to the .7z archive file
|
|
710
|
+
:param dest_dir: optional path to directory to extract into; if None a temporary dir will be created
|
|
711
|
+
:return: pathlib.Path pointing to the destination directory where contents were extracted
|
|
712
|
+
:raises: FileNotFoundError if archive does not exist; py7zr.Bad7zFile or other error if extraction fails
|
|
713
|
+
"""
|
|
714
|
+
import py7zr # type: ignore
|
|
715
|
+
import tempfile
|
|
716
|
+
from pathlib import Path
|
|
717
|
+
archive_path_obj = Path(archive_path)
|
|
718
|
+
if not archive_path_obj.is_file():
|
|
719
|
+
raise FileNotFoundError(f"Archive file not found: {archive_path_obj!r}")
|
|
720
|
+
if dest_dir is None:
|
|
721
|
+
# create a temporary directory
|
|
722
|
+
dest = Path(tempfile.mkdtemp(prefix=f"unzip7z_{archive_path_obj.stem}_"))
|
|
723
|
+
else:
|
|
724
|
+
dest = Path(dest_dir)
|
|
725
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
726
|
+
# Perform extraction
|
|
727
|
+
with py7zr.SevenZipFile(str(archive_path_obj), mode='r') as archive:
|
|
728
|
+
archive.extractall(path=str(dest))
|
|
729
|
+
# Return the extraction directory path
|
|
730
|
+
return dest
|
|
731
|
+
from machineconfig.utils.code import run_lambda_function
|
|
732
|
+
destination_dir = str(self.expanduser().resolve()).replace(".7z", "")
|
|
733
|
+
run_lambda_function(lambda: unzip_7z(archive_path=str(self), dest_dir=destination_dir), uv_project_dir=None, uv_with=["py7zr"])
|
|
734
|
+
res = PathExtended(destination_dir)
|
|
789
735
|
else:
|
|
790
736
|
res = self
|
|
791
737
|
return res
|
|
@@ -863,7 +809,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
863
809
|
path = self
|
|
864
810
|
else:
|
|
865
811
|
try:
|
|
866
|
-
path = self.
|
|
812
|
+
path = PathExtended(self.expanduser().absolute().relative_to(Path.home()))
|
|
867
813
|
except ValueError as ve:
|
|
868
814
|
if strict:
|
|
869
815
|
raise ve
|
|
@@ -911,9 +857,10 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
911
857
|
rp = localpath.get_remote_path(root=root, os_specific=os_specific, rel2home=rel2home, strict=strict) # if rel2home else (P(root) / localpath if root is not None else localpath)
|
|
912
858
|
else:
|
|
913
859
|
rp = PathExtended(remotepath)
|
|
914
|
-
|
|
915
860
|
from rclone_python import rclone
|
|
861
|
+
print(f"⬆️ UPLOADING {repr(localpath)} TO {cloud}:{rp.as_posix()}`") if verbose else None
|
|
916
862
|
rclone.copyto(in_path=localpath.as_posix(), out_path=f"{cloud}:{rp.as_posix()}", )
|
|
863
|
+
|
|
917
864
|
_ = [item.delete(sure=True) for item in to_del]
|
|
918
865
|
if verbose:
|
|
919
866
|
print(f"{'⬆️' * 5} UPLOAD COMPLETED.")
|
|
@@ -924,7 +871,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
924
871
|
command = f"rclone link '{cloud}:{rp.as_posix()}'"
|
|
925
872
|
completed = _run_shell_command(command, shell_to_use)
|
|
926
873
|
from machineconfig.utils.terminal import Response
|
|
927
|
-
|
|
928
874
|
res = Response.from_completed_process(completed).capture()
|
|
929
875
|
tmp = res.op2path(strict_err=False, strict_returncode=False)
|
|
930
876
|
if tmp is None:
|