machineconfig 3.99__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 +1 -2
- machineconfig/cluster/sessions_managers/{enhanced_command_runner.py → helpers/enhanced_command_runner.py} +4 -6
- machineconfig/cluster/sessions_managers/helpers/load_balancer_helper.py +145 -0
- machineconfig/cluster/sessions_managers/utils/load_balancer.py +53 -0
- machineconfig/cluster/sessions_managers/utils/maker.py +69 -0
- machineconfig/cluster/sessions_managers/wt_local.py +128 -330
- machineconfig/cluster/sessions_managers/wt_local_manager.py +53 -187
- 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 -172
- 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 -16
- machineconfig/jobs/installer/custom/boxes.py +61 -0
- machineconfig/jobs/installer/custom/gh.py +69 -53
- machineconfig/jobs/installer/custom/hx.py +77 -20
- machineconfig/jobs/installer/custom_dev/alacritty.py +45 -30
- machineconfig/jobs/installer/custom_dev/brave.py +43 -35
- machineconfig/jobs/installer/custom_dev/bypass_paywall.py +31 -20
- machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/custom_dev/code.py +33 -21
- machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +30 -0
- machineconfig/jobs/installer/custom_dev/espanso.py +64 -41
- machineconfig/jobs/installer/custom_dev/goes.py +41 -36
- machineconfig/jobs/installer/custom_dev/lvim.py +49 -33
- machineconfig/jobs/installer/custom_dev/nerdfont.py +71 -47
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +32 -26
- machineconfig/jobs/installer/custom_dev/redis.py +51 -33
- machineconfig/jobs/installer/custom_dev/sysabc.py +119 -0
- machineconfig/jobs/installer/custom_dev/wezterm.py +55 -39
- machineconfig/jobs/installer/custom_dev/winget.py +1 -0
- machineconfig/jobs/installer/installer_data.json +3406 -0
- 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/ngrok.sh +6 -0
- machineconfig/jobs/installer/linux_scripts/q.sh +9 -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 +255 -0
- 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 +198 -0
- 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 +117 -181
- machineconfig/scripts/python/define.py +31 -0
- machineconfig/scripts/python/devops.py +44 -124
- 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 +106 -244
- machineconfig/scripts/python/ftpx.py +125 -68
- 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/helpers_agents/fire_agents_help_launch.py +110 -0
- machineconfig/scripts/python/helpers_agents/fire_agents_helper_types.py +34 -0
- machineconfig/scripts/python/helpers_agents/fire_agents_load_balancer.py +22 -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} +11 -19
- machineconfig/scripts/python/{cloud_sync.py → helpers_cloud/cloud_sync.py} +12 -18
- machineconfig/scripts/python/{helpers → helpers_cloud}/helpers2.py +3 -3
- machineconfig/scripts/python/helpers_croshell/crosh.py +39 -0
- machineconfig/scripts/python/{start_slidev.py → helpers_croshell/start_slidev.py} +17 -7
- 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/helpers_devops/cli_terminal.py +156 -0
- 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/helpers_devops/devops_update_repos.py +269 -0
- 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} +57 -87
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_args_helper.py +145 -0
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +110 -0
- 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} +6 -7
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +218 -0
- machineconfig/scripts/python/helpers_repos/count_lines.py +348 -0
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +17 -0
- machineconfig/scripts/python/helpers_repos/entrypoint.py +77 -0
- machineconfig/scripts/python/helpers_repos/grource.py +340 -0
- machineconfig/scripts/python/{repos_helper_record.py → helpers_repos/record.py} +7 -4
- 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 +79 -160
- 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} +1 -3
- machineconfig/scripts/python/{devops_add_ssh_key.py → nw/devops_add_ssh_key.py} +74 -44
- machineconfig/scripts/{linux → python/nw}/mount_nfs +1 -1
- machineconfig/scripts/python/{mount_nfs.py → nw/mount_nfs.py} +19 -16
- machineconfig/scripts/{linux → python/nw}/mount_nw_drive +1 -2
- machineconfig/scripts/python/{mount_ssh.py → nw/mount_ssh.py} +7 -8
- 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} +6 -5
- machineconfig/scripts/python/sessions.py +167 -0
- 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 -9
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +16 -0
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +37 -23
- machineconfig/utils/accessories.py +7 -5
- machineconfig/utils/cloud/onedrive/README.md +139 -0
- machineconfig/utils/code.py +140 -93
- 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 +118 -0
- machineconfig/utils/files/dbms.py +257 -0
- machineconfig/utils/files/headers.py +68 -0
- machineconfig/utils/files/ouch/__init__.py +0 -0
- machineconfig/utils/files/ouch/decompress.py +45 -0
- machineconfig/utils/files/read.py +95 -0
- machineconfig/utils/installer_utils/github_release_bulk.py +2 -12
- machineconfig/utils/installer_utils/installer_class.py +68 -126
- machineconfig/utils/installer_utils/installer_cli.py +181 -0
- machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +38 -85
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +69 -69
- machineconfig/utils/io.py +77 -23
- machineconfig/utils/links.py +309 -100
- machineconfig/utils/meta.py +255 -0
- machineconfig/utils/notifications.py +1 -1
- machineconfig/utils/options.py +10 -25
- machineconfig/utils/path_extended.py +94 -104
- machineconfig/utils/path_helper.py +75 -22
- machineconfig/utils/procs.py +50 -74
- machineconfig/utils/scheduler.py +94 -97
- machineconfig/utils/scheduling.py +0 -3
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +5 -17
- machineconfig/utils/schemas/installer/installer_types.py +0 -1
- machineconfig/utils/schemas/layouts/layout_types.py +2 -1
- machineconfig/utils/source_of_truth.py +3 -6
- machineconfig/utils/ssh.py +742 -254
- machineconfig/utils/ssh_utils/utils.py +0 -0
- machineconfig/utils/terminal.py +3 -140
- machineconfig/utils/tst.py +20 -0
- machineconfig/utils/upgrade_packages.py +109 -28
- machineconfig/utils/ve.py +13 -5
- 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/templates/utils.py +0 -51
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -49
- machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -85
- machineconfig/jobs/installer/packages_custom_dev.json +0 -226
- machineconfig/jobs/installer/packages_custom_essential.json +0 -39
- machineconfig/jobs/installer/packages_github_dev.json +0 -1110
- machineconfig/jobs/installer/packages_github_essential.json +0 -804
- machineconfig/jobs/linux/msc/cli_agents.sh +0 -37
- machineconfig/jobs/python/create_bootable_media.py +0 -16
- machineconfig/jobs/python/python_cargo_build_share.py +0 -59
- machineconfig/jobs/python/python_ve_symlink.py +0 -29
- machineconfig/jobs/python/tasks.py +0 -3
- machineconfig/jobs/python/vscode/api.py +0 -49
- machineconfig/jobs/python/vscode/sync_code.py +0 -58
- 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 -170
- machineconfig/profile/shell.py +0 -176
- machineconfig/scripts/cloud/init.sh +0 -119
- 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/fire_agents +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/programs +0 -21
- machineconfig/scripts/linux/repos +0 -2
- machineconfig/scripts/linux/scheduler +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 -186
- machineconfig/scripts/python/devops_devapps_install.py +0 -159
- machineconfig/scripts/python/devops_update_repos.py +0 -180
- machineconfig/scripts/python/dotfile.py +0 -52
- machineconfig/scripts/python/fire_agents.py +0 -175
- machineconfig/scripts/python/fire_agents_help_launch.py +0 -143
- machineconfig/scripts/python/fire_agents_load_balancer.py +0 -50
- machineconfig/scripts/python/fire_jobs_args_helper.py +0 -75
- machineconfig/scripts/python/fire_jobs_layout_helper.py +0 -74
- 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 -114
- machineconfig/scripts/python/repos.py +0 -80
- machineconfig/scripts/python/repos_helper_action.py +0 -335
- machineconfig/scripts/python/share_terminal.py +0 -104
- 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/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/repos.ps1 +0 -1
- machineconfig/scripts/windows/scheduler.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/ascii_art.sh +0 -93
- 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-3.99.dist-info/METADATA +0 -167
- machineconfig-3.99.dist-info/RECORD +0 -409
- machineconfig-3.99.dist-info/entry_points.txt +0 -18
- machineconfig/cluster/{templates → remote}/run_cloud.py +0 -0
- machineconfig/cluster/{templates → remote}/run_cluster.py +0 -0
- machineconfig/cluster/{templates → remote}/run_remote.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/{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-3.99.dist-info → machineconfig-7.66.dist-info}/WHEEL +0 -0
- {machineconfig-3.99.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,36 +1,18 @@
|
|
|
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
8
|
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# _tmp = subprocess.check_output([cmd, tool_name], stderr=subprocess.DEVNULL)
|
|
15
|
-
# res: bool = True
|
|
16
|
-
# except (subprocess.CalledProcessError, FileNotFoundError):
|
|
17
|
-
# res = False
|
|
18
|
-
# return res
|
|
19
|
-
# return root_path.joinpath(tool_name).is_file()
|
|
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)
|
|
20
14
|
|
|
21
15
|
|
|
22
|
-
# def choose_from_options[T](options: Iterable[T], header: str = "", tail: str = "", prompt: str = "", msg: str = "", default: Optional[T] = None, fzf: bool = False, custom_input: bool = False) -> T:
|
|
23
|
-
# choice_key = choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=False, custom_input=custom_input)
|
|
24
|
-
# assert not isinstance(choice_key, list)
|
|
25
|
-
# return choice_key
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# def choose_from_options[T](options: Iterable[T], header: str = "", tail: str = "", prompt: str = "", msg: str = "", default: Optional[T] = None, custom_input: bool = False) -> list[T]:
|
|
29
|
-
# choice_key = choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=True, multi=True, custom_input=custom_input)
|
|
30
|
-
# if isinstance(choice_key, list):
|
|
31
|
-
# return choice_key
|
|
32
|
-
# return [choice_key]
|
|
33
|
-
|
|
34
16
|
@overload
|
|
35
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: ...
|
|
36
18
|
@overload
|
|
@@ -47,17 +29,20 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
|
|
|
47
29
|
from pyfzf.pyfzf import FzfPrompt
|
|
48
30
|
fzf_prompt = FzfPrompt()
|
|
49
31
|
nl = "\n"
|
|
50
|
-
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, ' ')}")
|
|
51
33
|
# --border=rounded doens't work on older versions of fzf installed at Ubuntu 20.04
|
|
52
34
|
if not multi:
|
|
53
35
|
try:
|
|
54
36
|
choice_one_string = choice_string_multi[0]
|
|
37
|
+
if isinstance(list(options)[0], str): return cast(T, choice_one_string)
|
|
55
38
|
choice_idx = options_strings.index(choice_one_string)
|
|
56
39
|
return list(options)[choice_idx]
|
|
57
40
|
except IndexError as ie:
|
|
58
41
|
print(f"❌ Error: {options=}, {choice_string_multi=}")
|
|
59
42
|
print(f"🔍 Available choices: {choice_string_multi}")
|
|
60
43
|
raise ie
|
|
44
|
+
if isinstance(list(options)[0], str):
|
|
45
|
+
return cast(list[T], choice_string_multi)
|
|
61
46
|
choice_idx_s = [options_strings.index(x) for x in choice_string_multi]
|
|
62
47
|
return [list(options)[x] for x in choice_idx_s]
|
|
63
48
|
else:
|
|
@@ -1,93 +1,60 @@
|
|
|
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
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
import sys
|
|
7
8
|
import subprocess
|
|
9
|
+
import os
|
|
8
10
|
from platform import system
|
|
9
11
|
from typing import Any, Optional, Union, Callable, TypeAlias, Literal
|
|
10
12
|
|
|
11
13
|
|
|
14
|
+
|
|
12
15
|
OPLike: TypeAlias = Union[str, "PathExtended", Path, None]
|
|
13
16
|
PLike: TypeAlias = Union[str, "PathExtended", Path]
|
|
14
17
|
FILE_MODE: TypeAlias = Literal["r", "w", "x", "a"]
|
|
15
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"]
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
def
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
print("\n" * 3, "~" * 50, """Consider Loading up your dotfiles or pass `gen_key=True` to make and save one.""", "~" * 50, "\n" * 3)
|
|
55
|
-
raise FileNotFoundError(err) from err
|
|
56
|
-
elif isinstance(key, (str, PathExtended, Path)):
|
|
57
|
-
key_resolved = Path(key).read_bytes() # a path to a key file was passed, read it:
|
|
58
|
-
elif type(key) is bytes:
|
|
59
|
-
key_resolved = key # key passed explicitly
|
|
60
|
-
else:
|
|
61
|
-
raise TypeError("❌ Key must be either a path, bytes object or None.")
|
|
62
|
-
code = Fernet(key=key_resolved).encrypt(msg)
|
|
63
|
-
if pwd is not None and salt is not None and iteration is not None:
|
|
64
|
-
return base64.urlsafe_b64encode(b"%b%b%b" % (salt, iteration.to_bytes(4, "big"), base64.urlsafe_b64decode(code)))
|
|
65
|
-
return code
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def decrypt(token: bytes, key: Optional[bytes] = None, pwd: Optional[str] = None, salted: bool = True) -> bytes:
|
|
69
|
-
import base64
|
|
70
|
-
|
|
71
|
-
if pwd is not None:
|
|
72
|
-
assert key is None, "❌ You can either pass key or pwd, or none of them, but not both."
|
|
73
|
-
if salted:
|
|
74
|
-
decoded = base64.urlsafe_b64decode(token)
|
|
75
|
-
salt, iterations, token = decoded[:16], decoded[16:20], base64.urlsafe_b64encode(decoded[20:])
|
|
76
|
-
key_resolved = pwd2key(password=pwd, salt=salt, iterations=int.from_bytes(bytes=iterations, byteorder="big"))
|
|
77
|
-
else:
|
|
78
|
-
key_resolved = pwd2key(password=pwd) # trailing `;` prevents IPython from caching the result.
|
|
79
|
-
elif type(key) is bytes:
|
|
80
|
-
assert pwd is None, "❌ You can either pass key or pwd, or none of them, but not both."
|
|
81
|
-
key_resolved = key # passsed explicitly
|
|
82
|
-
elif key is None:
|
|
83
|
-
key_resolved = Path.home().joinpath("dotfiles/creds/data/encrypted_files_key.bytes").read_bytes() # read from file
|
|
84
|
-
elif isinstance(key, (str, Path)):
|
|
85
|
-
key_resolved = Path(key).read_bytes() # passed a path to a file containing kwy
|
|
86
|
-
else:
|
|
87
|
-
raise TypeError(f"❌ Key must be either str, P, Path, bytes or None. Recieved: {type(key)}")
|
|
88
|
-
from cryptography.fernet import Fernet
|
|
89
|
-
|
|
90
|
-
return Fernet(key=key_resolved).decrypt(token)
|
|
22
|
+
def _is_user_admin() -> bool:
|
|
23
|
+
if os.name == "nt":
|
|
24
|
+
try:
|
|
25
|
+
return __import__("ctypes").windll.shell32.IsUserAnAdmin()
|
|
26
|
+
except Exception: # noqa: BLE001
|
|
27
|
+
import traceback
|
|
28
|
+
|
|
29
|
+
traceback.print_exc()
|
|
30
|
+
print("Admin check failed, assuming not an admin.")
|
|
31
|
+
return False
|
|
32
|
+
return os.getuid() == 0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _run_shell_command(
|
|
36
|
+
command: str,
|
|
37
|
+
shell_name: str,
|
|
38
|
+
*,
|
|
39
|
+
stdout: Optional[int] = subprocess.PIPE,
|
|
40
|
+
stderr: Optional[int] = subprocess.PIPE,
|
|
41
|
+
stdin: Optional[int] = None,
|
|
42
|
+
check: bool = False,
|
|
43
|
+
) -> subprocess.CompletedProcess[str]:
|
|
44
|
+
if shell_name in {"powershell", "pwsh"} and sys.platform == "win32":
|
|
45
|
+
args: list[str] = [shell_name, "-Command", command]
|
|
46
|
+
return subprocess.run(args, check=check, text=True, stdout=stdout, stderr=stderr, stdin=stdin)
|
|
47
|
+
executable = "/bin/bash" if shell_name == "bash" and sys.platform != "win32" else None
|
|
48
|
+
return subprocess.run(
|
|
49
|
+
command,
|
|
50
|
+
check=check,
|
|
51
|
+
text=True,
|
|
52
|
+
stdout=stdout,
|
|
53
|
+
stderr=stderr,
|
|
54
|
+
stdin=stdin,
|
|
55
|
+
shell=True,
|
|
56
|
+
executable=executable,
|
|
57
|
+
)
|
|
91
58
|
|
|
92
59
|
|
|
93
60
|
def validate_name(astring: str, replace: str = "_") -> str:
|
|
@@ -128,7 +95,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
128
95
|
print(f"🗑️ ❌ DELETED {repr(self)}.")
|
|
129
96
|
return self
|
|
130
97
|
|
|
131
|
-
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
|
|
132
99
|
path = self._resolve_path(folder=folder, name=name, path=path, default_name=self.absolute().name, rel2it=rel2it)
|
|
133
100
|
if parents:
|
|
134
101
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -154,9 +121,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
154
121
|
print(f"🚚 MOVED {repr(self)} ==> {repr(path)}`")
|
|
155
122
|
return path
|
|
156
123
|
|
|
157
|
-
def copy(
|
|
158
|
-
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
|
|
159
|
-
) -> "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
|
|
160
125
|
dest = self._resolve_path(folder=folder, name=name, path=path, default_name=self.name, rel2it=False)
|
|
161
126
|
dest = dest.expanduser().resolve()
|
|
162
127
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -188,7 +153,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
188
153
|
# ======================================= File Editing / Reading ===================================
|
|
189
154
|
def download(self, folder: OPLike = None, name: Optional[str] = None, allow_redirects: bool = True, timeout: Optional[int] = None, params: Any = None) -> "PathExtended":
|
|
190
155
|
import requests
|
|
191
|
-
|
|
192
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').
|
|
193
157
|
assert response.status_code == 200, f"Download failed with status code {response.status_code}\n{response.text}"
|
|
194
158
|
if name is not None:
|
|
@@ -392,9 +356,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
392
356
|
assert target_obj.exists(), f"Target path `{target}` (aka `{target_obj}`) doesn't exist. This will create a broken link."
|
|
393
357
|
if overwrite and (self.is_symlink() or self.exists()):
|
|
394
358
|
self.delete(sure=True, verbose=verbose)
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if system() == "Windows" and not Terminal.is_user_admin(): # you cannot create symlink without priviliages.
|
|
359
|
+
if system() == "Windows" and not _is_user_admin(): # you cannot create symlink without priviliages.
|
|
398
360
|
import win32com.shell.shell # type: ignore # pylint: disable=E0401
|
|
399
361
|
|
|
400
362
|
_proce_info = win32com.shell.shell.ShellExecuteEx(lpVerb="runas", lpFile=sys.executable, lpParameters=f" -c \"from pathlib import Path; Path(r'{self.expanduser()}').symlink_to(r'{str(target_obj)}')\"")
|
|
@@ -518,9 +480,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
518
480
|
**kwargs: Any,
|
|
519
481
|
) -> "PathExtended":
|
|
520
482
|
path_resolved, slf = self._resolve_path(folder, name, path, self.name).expanduser().resolve(), self.expanduser().resolve()
|
|
521
|
-
# if use_7z: # benefits over regular zip and encrypt: can handle very large files with low memory footprint
|
|
522
|
-
# path_resolved = path_resolved + '.7z' if not path_resolved.suffix == '.7z' else path_resolved
|
|
523
|
-
# with install_n_import("py7zr").SevenZipFile(file=path_resolved, mode=mode, password=pwd) as archive: archive.writeall(path=str(slf), arcname=None)
|
|
524
483
|
arcname_obj = PathExtended(arcname or slf.name)
|
|
525
484
|
if arcname_obj.name != slf.name:
|
|
526
485
|
arcname_obj /= slf.name # arcname has to start from somewhere and end with filename
|
|
@@ -593,15 +552,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
593
552
|
folder = folder if not content else folder.parent
|
|
594
553
|
if slf.suffix == ".7z":
|
|
595
554
|
raise NotImplementedError("I have not implemented this yet")
|
|
596
|
-
# if overwrite: P(folder).delete(sure=True)
|
|
597
|
-
# result = folder
|
|
598
|
-
# import py7zr
|
|
599
|
-
# with py7zr.SevenZipFile(file=slf, mode='r', password=pwd) as archive:
|
|
600
|
-
# if pattern is not None:
|
|
601
|
-
# import re
|
|
602
|
-
# pat = re.compile(pattern)
|
|
603
|
-
# archive.extract(path=folder, targets=[f for f in archive.getnames() if pat.match(f)])
|
|
604
|
-
# else: archive.extractall(path=folder)
|
|
605
555
|
else:
|
|
606
556
|
if overwrite:
|
|
607
557
|
if not content:
|
|
@@ -736,19 +686,52 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
736
686
|
return ret
|
|
737
687
|
|
|
738
688
|
def decompress(self, folder: OPLike = None, name: Optional[str] = None, path: OPLike = None, inplace: bool = False, orig: bool = False, verbose: bool = True) -> "PathExtended":
|
|
739
|
-
if ".tar.gz"
|
|
689
|
+
if str(self).endswith(".tar.gz") or str(self).endswith(".tgz"):
|
|
740
690
|
# res = self.ungz_untar(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
741
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
|
|
742
|
-
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"):
|
|
743
695
|
res = self.ungz(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
744
|
-
elif ".tar.bz"
|
|
696
|
+
elif str(self).endswith(".tar.bz") or str(self).endswith(".tbz"):
|
|
745
697
|
res = self.unbz(name=f"tmp_{randstr()}.tar", inplace=inplace)
|
|
746
698
|
return res.untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose)
|
|
747
|
-
elif ".tar.xz"
|
|
699
|
+
elif str(self).endswith(".tar.xz"):
|
|
748
700
|
# res = self.unxz_untar(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
749
701
|
res = self.unxz(inplace=inplace).untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose)
|
|
750
|
-
elif ".zip"
|
|
702
|
+
elif str(self).endswith(".zip"):
|
|
751
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)
|
|
752
735
|
else:
|
|
753
736
|
res = self
|
|
754
737
|
return res
|
|
@@ -826,7 +809,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
826
809
|
path = self
|
|
827
810
|
else:
|
|
828
811
|
try:
|
|
829
|
-
path = self.
|
|
812
|
+
path = PathExtended(self.expanduser().absolute().relative_to(Path.home()))
|
|
830
813
|
except ValueError as ve:
|
|
831
814
|
if strict:
|
|
832
815
|
raise ve
|
|
@@ -874,9 +857,10 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
874
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)
|
|
875
858
|
else:
|
|
876
859
|
rp = PathExtended(remotepath)
|
|
877
|
-
|
|
878
860
|
from rclone_python import rclone
|
|
861
|
+
print(f"⬆️ UPLOADING {repr(localpath)} TO {cloud}:{rp.as_posix()}`") if verbose else None
|
|
879
862
|
rclone.copyto(in_path=localpath.as_posix(), out_path=f"{cloud}:{rp.as_posix()}", )
|
|
863
|
+
|
|
880
864
|
_ = [item.delete(sure=True) for item in to_del]
|
|
881
865
|
if verbose:
|
|
882
866
|
print(f"{'⬆️' * 5} UPLOAD COMPLETED.")
|
|
@@ -884,8 +868,10 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
884
868
|
if verbose:
|
|
885
869
|
print("🔗 SHARING FILE")
|
|
886
870
|
shell_to_use = "powershell" if sys.platform == "win32" else "bash"
|
|
887
|
-
|
|
888
|
-
|
|
871
|
+
command = f"rclone link '{cloud}:{rp.as_posix()}'"
|
|
872
|
+
completed = _run_shell_command(command, shell_to_use)
|
|
873
|
+
from machineconfig.utils.terminal import Response
|
|
874
|
+
res = Response.from_completed_process(completed).capture()
|
|
889
875
|
tmp = res.op2path(strict_err=False, strict_returncode=False)
|
|
890
876
|
if tmp is None:
|
|
891
877
|
res.print()
|
|
@@ -955,11 +941,15 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
955
941
|
|
|
956
942
|
rclone_cmd += f" --progress --transfers={transfers} --verbose"
|
|
957
943
|
rclone_cmd += " --delete-during" if delete else ""
|
|
958
|
-
from machineconfig.utils.terminal import Terminal
|
|
959
944
|
if verbose:
|
|
960
945
|
print(rclone_cmd)
|
|
961
946
|
shell_to_use = "powershell" if sys.platform == "win32" else "bash"
|
|
962
|
-
|
|
947
|
+
stdout_target: Optional[int] = None if verbose else subprocess.PIPE
|
|
948
|
+
stderr_target: Optional[int] = None if verbose else subprocess.PIPE
|
|
949
|
+
completed = _run_shell_command(rclone_cmd, shell_to_use, stdout=stdout_target, stderr=stderr_target)
|
|
950
|
+
from machineconfig.utils.terminal import Response
|
|
951
|
+
|
|
952
|
+
res = Response.from_completed_process(completed)
|
|
963
953
|
success = res.is_successful(strict_err=False, strict_returcode=True)
|
|
964
954
|
if not success:
|
|
965
955
|
res.print(capture=False, desc="Cloud Storage Operation")
|