machineconfig 6.82__py3-none-any.whl → 8.51__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.
- machineconfig/cluster/remote/cloud_manager.py +1 -1
- machineconfig/cluster/remote/run_cluster.py +1 -1
- machineconfig/cluster/remote/run_remote.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +29 -15
- machineconfig/cluster/sessions_managers/wt_local.py +17 -222
- machineconfig/cluster/sessions_managers/wt_local_manager.py +56 -194
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +42 -198
- 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_reporting.py +76 -0
- machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
- machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +4 -2
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +3 -2
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +2 -2
- machineconfig/jobs/installer/checks/check_installations.py +133 -0
- machineconfig/jobs/installer/checks/install_utils.py +132 -0
- machineconfig/jobs/installer/checks/report_utils.py +39 -0
- machineconfig/jobs/installer/checks/vt_utils.py +89 -0
- machineconfig/jobs/installer/installer_data.json +1500 -310
- machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
- machineconfig/jobs/installer/linux_scripts/q.sh +10 -7
- machineconfig/jobs/installer/linux_scripts/redis.sh +1 -0
- machineconfig/jobs/installer/package_groups.py +62 -91
- machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
- machineconfig/jobs/installer/{custom → python_scripts}/boxes.py +2 -3
- machineconfig/jobs/installer/{custom_dev → python_scripts}/brave.py +5 -3
- machineconfig/jobs/installer/python_scripts/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/code.py +14 -9
- machineconfig/jobs/installer/{custom_dev → python_scripts}/dubdb_adbc.py +1 -1
- machineconfig/jobs/installer/python_scripts/hx.py +214 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/nerdfont.py +2 -2
- machineconfig/jobs/installer/{custom_dev → python_scripts}/nerfont_windows_helper.py +32 -26
- machineconfig/jobs/installer/python_scripts/sysabc.py +145 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/wezterm.py +2 -19
- machineconfig/jobs/installer/{custom_dev → python_scripts}/winget.py +10 -14
- machineconfig/jobs/installer/python_scripts/yazi.py +139 -0
- machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nfs +0 -1
- machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
- machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +13 -0
- machineconfig/jobs/scripts/powershell_scripts/obs.ps1 +4 -0
- machineconfig/jobs/scripts_dynamic/a.py +428 -0
- machineconfig/logger.py +1 -2
- machineconfig/profile/create_helper.py +56 -18
- machineconfig/profile/create_links.py +79 -21
- machineconfig/profile/create_links_export.py +87 -36
- machineconfig/profile/create_shell_profile.py +92 -127
- machineconfig/profile/mapper_data.toml +45 -0
- machineconfig/profile/mapper_dotfiles.toml +249 -0
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/wrap_mcfg +46 -0
- machineconfig/scripts/nu/wrap_mcfg.nu +69 -0
- machineconfig/scripts/python/agents.py +85 -165
- machineconfig/scripts/python/ai/initai.py +4 -2
- machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
- machineconfig/scripts/python/ai/scripts/command_runner.ps1 +33 -0
- machineconfig/scripts/python/ai/{command_runner → scripts}/command_runner.sh +1 -1
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +8 -6
- machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
- machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Thinking-Beast-Mode.chatmode.md → agents/Thinking-Beast-Mode.agent.md} +0 -1
- machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md → agents/Ultimate-Transparent-Thinking-Beast-Mode.agent.md} +0 -1
- machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/deepResearch.chatmode.md → agents/deepResearch.agent.md} +2 -2
- machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +6 -6
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +33 -0
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/watch_exec.prompt.md +20 -0
- machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
- machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
- machineconfig/scripts/python/ai/{generate_files.py → utils/generate_files.py} +2 -2
- machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
- machineconfig/scripts/python/ai/{vscode_tasks.py → utils/vscode_tasks.py} +13 -5
- machineconfig/scripts/python/cloud.py +58 -11
- machineconfig/scripts/python/croshell.py +10 -162
- machineconfig/scripts/python/devops.py +73 -36
- machineconfig/scripts/python/devops_navigator.py +16 -6
- machineconfig/scripts/python/fire_jobs.py +8 -222
- machineconfig/scripts/python/ftpx.py +7 -200
- machineconfig/scripts/python/graph/cli_graph.json +8743 -0
- machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
- machineconfig/scripts/python/helpers/helper_env/env_manager_tui.py +204 -0
- machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +1 -1
- machineconfig/scripts/python/helpers/helpers_agents/agentic_frameworks/fire_crush.py +39 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +3 -4
- machineconfig/scripts/python/helpers/helpers_agents/agentic_frameworks/fire_gemini.py +55 -0
- machineconfig/scripts/python/helpers/helpers_agents/agentic_frameworks/fire_qwen.py +30 -0
- machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_help_launch.py +38 -16
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_helper_types.py +11 -14
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/aichat/config.yaml +5 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/aider/.aider.conf.yml +2 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/copilot/config.yml +1 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/crush/crush.json +10 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/gemini/settings.json +12 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/privacy.py +109 -0
- machineconfig/scripts/python/helpers/helpers_agents/templates/prompt.txt +10 -0
- machineconfig/scripts/python/helpers/helpers_agents/templates/template.sh +34 -0
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +32 -25
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +29 -22
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +9 -8
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +3 -3
- machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +225 -0
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +12 -12
- machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +262 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +98 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +67 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_nw.py +201 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +274 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_self.py +197 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_share_file.py +151 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_share_server.py +125 -0
- machineconfig/scripts/python/{helpers_devops/cli_terminal.py → helpers/helpers_devops/cli_share_terminal.py} +26 -22
- machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +17 -23
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
- machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +78 -71
- machineconfig/scripts/python/helpers/helpers_devops/run_script.py +197 -0
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +4 -4
- machineconfig/scripts/python/{helpers_fire/helpers4.py → helpers/helpers_fire_command/file_wrangler.py} +57 -20
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +1 -0
- machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +26 -16
- machineconfig/scripts/python/helpers/helpers_msearch/__init__.py +5 -0
- machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
- machineconfig/scripts/{linux → python/helpers/helpers_msearch/scripts_linux}/fzfg +6 -5
- machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
- machineconfig/scripts/python/helpers/helpers_msearch/scripts_windows/fzfg.ps1 +59 -0
- machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
- machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
- machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
- machineconfig/scripts/python/helpers/helpers_network/address.py +174 -0
- machineconfig/scripts/python/helpers/helpers_network/address_switch.py +78 -0
- machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
- machineconfig/scripts/python/{nw → helpers/helpers_network}/mount_nfs.py +2 -2
- machineconfig/scripts/python/{nw → helpers/helpers_network}/mount_ssh.py +3 -3
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_identity.py +73 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_ssh_key.py +175 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_linux.py +319 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_windows.py +275 -0
- machineconfig/scripts/python/{nw → helpers/helpers_network}/wifi_conn.py +1 -53
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -1
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +159 -48
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +4 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -12
- machineconfig/scripts/python/helpers/helpers_repos/repo_analyzer_1.py +160 -0
- machineconfig/scripts/python/{helpers_repos/count_lines.py → helpers/helpers_repos/repo_analyzer_2.py} +156 -191
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
- machineconfig/scripts/python/helpers/helpers_search/ast_search.py +74 -0
- machineconfig/scripts/python/helpers/helpers_search/qr_code.py +166 -0
- machineconfig/scripts/python/helpers/helpers_search/repo_rag.py +325 -0
- machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
- machineconfig/scripts/python/helpers/helpers_search/symantic_search.py +25 -0
- machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +186 -0
- machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +20 -14
- machineconfig/scripts/python/helpers/helpers_terminal/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_terminal/terminal_impl.py +96 -0
- machineconfig/scripts/python/helpers/helpers_utils/download.py +150 -0
- machineconfig/scripts/python/helpers/helpers_utils/pdf.py +96 -0
- machineconfig/scripts/python/helpers/helpers_utils/python.py +210 -0
- machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
- machineconfig/scripts/python/mcfg_entry.py +143 -0
- machineconfig/scripts/python/msearch.py +26 -0
- machineconfig/scripts/python/sessions.py +69 -135
- machineconfig/scripts/python/terminal.py +58 -0
- machineconfig/scripts/python/utils.py +115 -38
- machineconfig/scripts/windows/wrap_mcfg.ps1 +63 -0
- machineconfig/settings/atuin/config.toml +294 -0
- machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
- 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 +3 -2
- machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
- machineconfig/settings/lf/windows/lfrc +14 -16
- machineconfig/settings/linters/.ruff.toml +2 -1
- machineconfig/settings/marimo/marimo.toml +1 -1
- machineconfig/settings/marimo/snippets/globalize.py +34 -0
- machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
- machineconfig/settings/shells/bash/init.sh +47 -12
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +1 -1
- machineconfig/settings/shells/nushell/config.nu +25 -33
- machineconfig/settings/shells/nushell/env.nu +21 -8
- machineconfig/settings/shells/nushell/init.nu +138 -0
- machineconfig/settings/shells/pwsh/init.ps1 +111 -17
- machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
- machineconfig/settings/shells/starship/starship.toml +16 -0
- machineconfig/settings/shells/wezterm/wezterm.lua +6 -1
- machineconfig/settings/shells/wt/settings.json +27 -18
- machineconfig/settings/shells/zsh/init.sh +42 -23
- machineconfig/settings/television/cable_unix/alias.toml +8 -0
- machineconfig/settings/television/cable_unix/aws-buckets.toml +14 -0
- machineconfig/settings/television/cable_unix/aws-instances.toml +13 -0
- machineconfig/settings/television/cable_unix/bash-history.toml +8 -0
- machineconfig/settings/television/cable_unix/channels.toml +19 -0
- machineconfig/settings/television/cable_unix/dirs.toml +13 -0
- machineconfig/settings/television/cable_unix/distrobox-list.toml +42 -0
- machineconfig/settings/television/cable_unix/docker-images.toml +13 -0
- machineconfig/settings/television/cable_unix/dotfiles.toml +11 -0
- machineconfig/settings/television/cable_unix/env.toml +17 -0
- machineconfig/settings/television/cable_unix/files.toml +11 -0
- machineconfig/settings/television/cable_unix/fish-history.toml +8 -0
- machineconfig/settings/television/cable_unix/git-branch.toml +11 -0
- machineconfig/settings/television/cable_unix/git-diff.toml +10 -0
- machineconfig/settings/television/cable_unix/git-log.toml +12 -0
- machineconfig/settings/television/cable_unix/git-reflog.toml +12 -0
- machineconfig/settings/television/cable_unix/git-repos.toml +16 -0
- machineconfig/settings/television/cable_unix/guix.toml +20 -0
- machineconfig/settings/television/cable_unix/just-recipes.toml +18 -0
- machineconfig/settings/television/cable_unix/k8s-deployments.toml +36 -0
- machineconfig/settings/television/cable_unix/k8s-pods.toml +50 -0
- machineconfig/settings/television/cable_unix/k8s-services.toml +36 -0
- machineconfig/settings/television/cable_unix/man-pages.toml +24 -0
- machineconfig/settings/television/cable_unix/nu-history.toml +7 -0
- machineconfig/settings/television/cable_unix/procs.toml +20 -0
- machineconfig/settings/television/cable_unix/text.toml +17 -0
- machineconfig/settings/television/cable_unix/tldr.toml +18 -0
- machineconfig/settings/television/cable_unix/zsh-history.toml +9 -0
- machineconfig/settings/television/cable_windows/alias.toml +7 -0
- machineconfig/settings/television/cable_windows/dirs.toml +13 -0
- machineconfig/settings/television/cable_windows/docker-images.toml +13 -0
- machineconfig/settings/television/cable_windows/dotfiles.toml +11 -0
- machineconfig/settings/television/cable_windows/env.toml +17 -0
- machineconfig/settings/television/cable_windows/files.toml +14 -0
- machineconfig/settings/television/cable_windows/git-branch.toml +11 -0
- machineconfig/settings/television/cable_windows/git-diff.toml +10 -0
- machineconfig/settings/television/cable_windows/git-log.toml +11 -0
- machineconfig/settings/television/cable_windows/git-reflog.toml +11 -0
- machineconfig/settings/television/cable_windows/git-repos.toml +15 -0
- machineconfig/settings/television/cable_windows/nu-history.toml +7 -0
- machineconfig/settings/television/cable_windows/pwsh-history.toml +6 -0
- machineconfig/settings/television/cable_windows/text.toml +17 -0
- machineconfig/settings/tv/config.toml +234 -0
- machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
- machineconfig/settings/wsl/.wslconfig +5 -30
- machineconfig/settings/wt/__init__.py +0 -0
- machineconfig/settings/yazi/init.lua +61 -0
- machineconfig/settings/yazi/keymap_linux.toml +94 -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/theme.toml +4 -0
- machineconfig/settings/yazi/yazi_linux.toml +94 -0
- machineconfig/settings/yazi/yazi_windows.toml +58 -0
- machineconfig/settings/zellij/layouts/st.kdl +40 -9
- machineconfig/settings/zellij/layouts/st2.kdl +1 -1
- machineconfig/setup_linux/__init__.py +2 -2
- machineconfig/setup_linux/apps_desktop.sh +8 -27
- machineconfig/setup_linux/web_shortcuts/interactive.sh +27 -12
- machineconfig/setup_linux/web_shortcuts/live_from_github.sh +34 -0
- machineconfig/setup_mac/__init__.py +1 -4
- machineconfig/setup_mac/apps_gui.sh +248 -0
- machineconfig/setup_windows/__init__.py +2 -5
- machineconfig/setup_windows/uv.ps1 +8 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +28 -12
- machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +31 -0
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +17 -0
- machineconfig/type_hinting/sql/__init__.py +1 -0
- machineconfig/type_hinting/sql/base.py +216 -0
- machineconfig/type_hinting/sql/core_schema.py +64 -0
- machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
- machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
- machineconfig/type_hinting/typedict/__init__.py +1 -0
- machineconfig/type_hinting/typedict/ast_utils.py +130 -0
- machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
- machineconfig/type_hinting/typedict/generators.py +231 -0
- machineconfig/type_hinting/typedict/polars_schema.py +24 -0
- machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
- machineconfig/utils/accessories.py +31 -4
- machineconfig/utils/code.py +163 -51
- machineconfig/utils/files/ascii_art.py +11 -15
- machineconfig/utils/files/headers.py +6 -7
- machineconfig/utils/files/read.py +8 -1
- machineconfig/utils/installer_utils/github_release_bulk.py +95 -138
- machineconfig/utils/installer_utils/github_release_scraper.py +99 -0
- machineconfig/utils/installer_utils/install_from_url.py +183 -0
- machineconfig/utils/installer_utils/installer_class.py +53 -102
- machineconfig/utils/installer_utils/installer_cli.py +161 -0
- machineconfig/utils/installer_utils/installer_helper.py +129 -0
- machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +42 -91
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +20 -65
- machineconfig/utils/io.py +94 -9
- machineconfig/utils/links.py +56 -38
- machineconfig/utils/meta.py +38 -21
- machineconfig/utils/options.py +81 -23
- machineconfig/utils/options_utils/__init__.py +0 -0
- machineconfig/utils/options_utils/options_tv_linux.py +211 -0
- machineconfig/utils/options_utils/options_tv_windows.py +88 -0
- machineconfig/utils/options_utils/tv_options.py +37 -0
- machineconfig/utils/path_extended.py +52 -102
- machineconfig/utils/path_helper.py +76 -23
- machineconfig/utils/procs.py +1 -1
- machineconfig/utils/scheduler.py +26 -53
- machineconfig/utils/scheduling.py +0 -2
- 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 +6 -1
- machineconfig/utils/ssh.py +216 -419
- machineconfig/utils/ssh_utils/abc.py +5 -0
- machineconfig/utils/ssh_utils/copy_from_here.py +116 -0
- machineconfig/utils/ssh_utils/copy_to_here.py +303 -0
- machineconfig/utils/ssh_utils/utils.py +158 -0
- machineconfig/utils/ssh_utils/wsl.py +147 -0
- machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
- machineconfig/utils/terminal.py +1 -0
- machineconfig/utils/upgrade_packages.py +107 -35
- machineconfig/utils/ve.py +12 -4
- machineconfig-8.51.dist-info/METADATA +140 -0
- machineconfig-8.51.dist-info/RECORD +543 -0
- {machineconfig-6.82.dist-info → machineconfig-8.51.dist-info}/entry_points.txt +4 -1
- machineconfig/jobs/installer/check_installations.py +0 -248
- machineconfig/jobs/installer/custom/hx.py +0 -140
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -71
- machineconfig/jobs/installer/powershell_scripts/archive_pygraphviz.ps1 +0 -12
- machineconfig/jobs/installer/powershell_scripts/openssh-server_add_key.ps1 +0 -7
- machineconfig/jobs/installer/powershell_scripts/openssh-server_copy-ssh-id.ps1 +0 -14
- machineconfig/profile/backup.toml +0 -49
- machineconfig/profile/mapper.toml +0 -256
- machineconfig/scripts/linux/fzf2g +0 -21
- machineconfig/scripts/linux/fzfag +0 -17
- machineconfig/scripts/linux/fzffg +0 -25
- machineconfig/scripts/linux/fzfrga +0 -21
- machineconfig/scripts/linux/mcfgs +0 -38
- machineconfig/scripts/linux/other/share_smb +0 -1
- machineconfig/scripts/linux/other/switch_ip +0 -20
- machineconfig/scripts/linux/skrg +0 -4
- machineconfig/scripts/linux/warp-cli.sh +0 -122
- machineconfig/scripts/linux/z_ls +0 -104
- machineconfig/scripts/python/ai/command_runner/prompt.txt +0 -9
- machineconfig/scripts/python/helpers_devops/cli_config.py +0 -120
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -77
- machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
- machineconfig/scripts/python/helpers_devops/cli_nw.py +0 -73
- machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -181
- machineconfig/scripts/python/helpers_devops/cli_self.py +0 -122
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +0 -104
- machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -221
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
- machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_crush.py +0 -37
- machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_gemini.py +0 -44
- machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_qwen.py +0 -43
- machineconfig/scripts/python/helpers_fire/prompt.txt +0 -2
- machineconfig/scripts/python/helpers_fire/template.sh +0 -15
- machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
- machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
- machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -588
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +0 -17
- machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -76
- machineconfig/scripts/python/helpers_repos/secure_repo.py +0 -15
- machineconfig/scripts/python/mcfg.py +0 -48
- machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
- machineconfig/scripts/python/nw/devops_add_identity.py +0 -82
- machineconfig/scripts/python/nw/devops_add_ssh_key.py +0 -134
- machineconfig/scripts/python/nw/ssh_debug_linux.py +0 -391
- machineconfig/scripts/python/nw/ssh_debug_windows.py +0 -338
- machineconfig/scripts/python/nw/wsl_windows_transfer.py +0 -66
- machineconfig/scripts/windows/fzfb.ps1 +0 -3
- machineconfig/scripts/windows/fzfg.ps1 +0 -2
- machineconfig/scripts/windows/fzfrga.bat +0 -20
- machineconfig/scripts/windows/mcfgs.ps1 +0 -17
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +0 -13
- machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
- machineconfig/settings/lf/windows/fzf_edit.ps1 +0 -6
- machineconfig/settings/lf/windows/tst.ps1 +0 -1
- machineconfig/settings/yazi/yazi.toml +0 -4
- machineconfig/setup_linux/apps.sh +0 -66
- machineconfig/setup_linux/others/cli_installation.sh +0 -137
- machineconfig/setup_linux/others/mint_keyboard_shortcuts.sh +0 -30
- machineconfig/setup_linux/ssh/openssh_all.sh +0 -25
- machineconfig/setup_linux/ssh/openssh_wsl.sh +0 -38
- machineconfig/setup_mac/apps.sh +0 -73
- machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
- machineconfig/setup_windows/apps.ps1 +0 -62
- machineconfig/setup_windows/others/obs.ps1 +0 -4
- machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
- machineconfig/setup_windows/ssh/add_identity.ps1 +0 -11
- machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
- machineconfig/utils/installer_utils/installer.py +0 -225
- machineconfig/utils/tst.py +0 -20
- machineconfig-6.82.dist-info/METADATA +0 -82
- machineconfig-6.82.dist-info/RECORD +0 -441
- machineconfig/jobs/installer/{custom_dev → checks}/__init__.py +0 -0
- machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- machineconfig/{scripts/python/helpers_cloud → jobs/installer/python_scripts}/__init__.py +0 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/alacritty.py +0 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/bypass_paywall.py +0 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/cursor.py +0 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/espanso.py +0 -0
- machineconfig/jobs/installer/{custom → python_scripts}/gh.py +0 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/goes.py +0 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/lvim.py +0 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/redis.py +0 -0
- machineconfig/{setup_linux/others → jobs/scripts/bash_scripts}/android.sh +0 -0
- machineconfig/jobs/{installer/linux_scripts → scripts/bash_scripts}/lid.sh +0 -0
- machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_drive +0 -0
- machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nw_drive +0 -0
- machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_smb +0 -0
- machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_cloud.sh +0 -0
- machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_nfs +0 -0
- machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/start_docker +0 -0
- machineconfig/{scripts → jobs/scripts/powershell_scripts}/Restore-ThunderbirdProfile.ps1 +0 -0
- machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/docker.ps1 +0 -0
- machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nfs.ps1 +0 -0
- machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nw.ps1 +0 -0
- machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_smb.ps1 +0 -0
- machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/power_options.ps1 +0 -0
- machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_cloud.cmd +0 -0
- machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_smb.ps1 +0 -0
- machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/unlock_bitlocker.ps1 +0 -0
- machineconfig/scripts/python/{helpers_croshell → ai/utils}/__init__.py +0 -0
- machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
- machineconfig/scripts/python/{helpers_devops → graph}/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_devops/themes → helpers}/__init__.py +0 -0
- machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
- machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/agentic_frameworks/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents/templates}/template.ps1 +0 -0
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_cloud}/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +1 -1
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
- /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_croshell}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
- /machineconfig/scripts/python/{nw → helpers/helpers_devops}/__init__.py +0 -0
- /machineconfig/{setup_windows/wt_and_pwsh → scripts/python/helpers/helpers_devops/themes}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_fire_command/__init__.py} +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command/fire_jobs_streamlit_helper.py → helpers/helpers_fire_command/f.py} +0 -0
- /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers/helpers_fire_command/fire_jobs_streamlit_helper.py} +0 -0
- /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
- /machineconfig/{settings/yazi/keymap.toml → scripts/python/helpers/helpers_network/__init__.py} +0 -0
- /machineconfig/scripts/python/{nw → helpers/helpers_network}/mount_nw_drive.py +0 -0
- /machineconfig/scripts/python/{nw → helpers/helpers_network}/onetimeshare.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
- /machineconfig/{setup_windows/wt_and_pwsh → settings/wt}/set_wt_settings.py +0 -0
- {machineconfig-6.82.dist-info → machineconfig-8.51.dist-info}/WHEEL +0 -0
- {machineconfig-6.82.dist-info → machineconfig-8.51.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import random
|
|
3
|
+
import string
|
|
4
|
+
import json
|
|
5
|
+
import shlex
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Any
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
POWERSHELL_CMD = "powershell" if __import__("platform").system().lower() == "windows" else "pwsh"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_random_suffix(length: int) -> str:
|
|
18
|
+
"""Generate a random string suffix for unique PowerShell script names."""
|
|
19
|
+
return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse_command(command: str) -> tuple[str, list[str]]:
|
|
23
|
+
try:
|
|
24
|
+
parts = shlex.split(command)
|
|
25
|
+
if not parts:
|
|
26
|
+
raise ValueError("Empty command provided")
|
|
27
|
+
return parts[0], parts[1:] if len(parts) > 1 else []
|
|
28
|
+
except ValueError as e:
|
|
29
|
+
logger.error(f"Error parsing command '{command}': {e}")
|
|
30
|
+
parts = command.split()
|
|
31
|
+
return parts[0] if parts else "", parts[1:] if len(parts) > 1 else []
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def escape_for_wt(text: str) -> str:
|
|
35
|
+
"""Escape text for use in Windows Terminal commands."""
|
|
36
|
+
text = text.replace('"', '""')
|
|
37
|
+
if " " in text or ";" in text or "&" in text or "|" in text:
|
|
38
|
+
return f'"{text}"'
|
|
39
|
+
return text
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def validate_layout_config(layout_config: LayoutConfig) -> None:
|
|
43
|
+
"""Validate layout configuration format and content."""
|
|
44
|
+
if not layout_config["layoutTabs"]:
|
|
45
|
+
raise ValueError("Layout must contain at least one tab")
|
|
46
|
+
for tab in layout_config["layoutTabs"]:
|
|
47
|
+
if not tab["tabName"].strip():
|
|
48
|
+
raise ValueError(f"Invalid tab name: {tab['tabName']}")
|
|
49
|
+
if not tab["command"].strip():
|
|
50
|
+
raise ValueError(f"Invalid command for tab '{tab['tabName']}': {tab['command']}")
|
|
51
|
+
if not tab["startDir"].strip():
|
|
52
|
+
raise ValueError(f"Invalid startDir for tab '{tab['tabName']}': {tab['startDir']}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def generate_wt_command_string(layout_config: LayoutConfig, window_name: str) -> str:
|
|
56
|
+
"""Generate complete Windows Terminal command string."""
|
|
57
|
+
command_parts = []
|
|
58
|
+
|
|
59
|
+
for i, tab in enumerate(layout_config["layoutTabs"]):
|
|
60
|
+
is_first = i == 0
|
|
61
|
+
|
|
62
|
+
if is_first:
|
|
63
|
+
tab_parts = ["wt", "-w", escape_for_wt(window_name)]
|
|
64
|
+
else:
|
|
65
|
+
tab_parts = ["new-tab"]
|
|
66
|
+
|
|
67
|
+
tab_name = tab["tabName"]
|
|
68
|
+
cwd = tab["startDir"]
|
|
69
|
+
command = tab["command"]
|
|
70
|
+
|
|
71
|
+
if cwd.startswith("~/"):
|
|
72
|
+
cwd = cwd.replace("~/", f"{Path.home()}/")
|
|
73
|
+
elif cwd == "~":
|
|
74
|
+
cwd = str(Path.home())
|
|
75
|
+
|
|
76
|
+
tab_parts.extend(["-d", escape_for_wt(cwd)])
|
|
77
|
+
tab_parts.extend(["--title", escape_for_wt(tab_name)])
|
|
78
|
+
tab_parts.append("--")
|
|
79
|
+
|
|
80
|
+
# Split the command into arguments
|
|
81
|
+
command_args = shlex.split(command)
|
|
82
|
+
tab_parts.extend(command_args)
|
|
83
|
+
|
|
84
|
+
command_parts.append(" ".join(tab_parts))
|
|
85
|
+
|
|
86
|
+
return " `; ".join(command_parts)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def check_wt_session_status(session_name: str) -> dict[str, Any]:
|
|
90
|
+
try:
|
|
91
|
+
ps_script = """
|
|
92
|
+
try {
|
|
93
|
+
$wtProcesses = Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue
|
|
94
|
+
if ($wtProcesses) {
|
|
95
|
+
$processInfo = @()
|
|
96
|
+
$wtProcesses | ForEach-Object {
|
|
97
|
+
$info = @{
|
|
98
|
+
"Id" = $_.Id
|
|
99
|
+
"ProcessName" = $_.ProcessName
|
|
100
|
+
"StartTime" = $_.StartTime.ToString()
|
|
101
|
+
}
|
|
102
|
+
$processInfo += $info
|
|
103
|
+
}
|
|
104
|
+
$processInfo | ConvertTo-Json -Depth 2
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
# No Windows Terminal processes found
|
|
108
|
+
}
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
result = subprocess.run([POWERSHELL_CMD, "-Command", ps_script], capture_output=True, text=True, timeout=5)
|
|
112
|
+
|
|
113
|
+
if result.returncode == 0:
|
|
114
|
+
output = result.stdout.strip()
|
|
115
|
+
if output and output != "":
|
|
116
|
+
try:
|
|
117
|
+
processes = json.loads(output)
|
|
118
|
+
if not isinstance(processes, list):
|
|
119
|
+
processes = [processes]
|
|
120
|
+
|
|
121
|
+
return {"wt_running": True, "session_exists": len(processes) > 0, "session_name": session_name, "all_windows": processes, "session_windows": processes}
|
|
122
|
+
except Exception as e:
|
|
123
|
+
return {"wt_running": True, "session_exists": False, "error": f"Failed to parse process info: {e}", "session_name": session_name}
|
|
124
|
+
else:
|
|
125
|
+
return {"wt_running": False, "session_exists": False, "session_name": session_name, "all_windows": []}
|
|
126
|
+
else:
|
|
127
|
+
return {"wt_running": False, "error": result.stderr, "session_name": session_name}
|
|
128
|
+
|
|
129
|
+
except subprocess.TimeoutExpired:
|
|
130
|
+
return {"wt_running": False, "error": "Timeout while checking Windows Terminal processes", "session_name": session_name}
|
|
131
|
+
except FileNotFoundError:
|
|
132
|
+
return {"wt_running": False, "error": f"PowerShell ({POWERSHELL_CMD}) not found in PATH", "session_name": session_name}
|
|
133
|
+
except Exception as e:
|
|
134
|
+
return {"wt_running": False, "error": str(e), "session_name": session_name}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def check_command_status(tab_name: str, layout_config: LayoutConfig) -> dict[str, Any]:
|
|
138
|
+
"""Check if a command is running by looking for processes."""
|
|
139
|
+
tab_config = None
|
|
140
|
+
for tab in layout_config["layoutTabs"]:
|
|
141
|
+
if tab["tabName"] == tab_name:
|
|
142
|
+
tab_config = tab
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
if tab_config is None:
|
|
146
|
+
return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "pid": None, "command": None}
|
|
147
|
+
|
|
148
|
+
command = tab_config["command"]
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
primary_cmd = command.split()[0] if command.strip() else ""
|
|
152
|
+
if not primary_cmd:
|
|
153
|
+
return {"status": "error", "error": "Empty command", "running": False, "command": command, "tab_name": tab_name}
|
|
154
|
+
|
|
155
|
+
ps_script = f"""
|
|
156
|
+
try {{
|
|
157
|
+
$processes = Get-Process -Name '{primary_cmd}' -ErrorAction SilentlyContinue
|
|
158
|
+
if ($processes) {{
|
|
159
|
+
$processes | ForEach-Object {{
|
|
160
|
+
$procInfo = @{{
|
|
161
|
+
"pid" = $_.Id
|
|
162
|
+
"name" = $_.ProcessName
|
|
163
|
+
"start_time" = $_.StartTime.ToString()
|
|
164
|
+
}}
|
|
165
|
+
Write-Output ($procInfo | ConvertTo-Json -Compress)
|
|
166
|
+
}}
|
|
167
|
+
}}
|
|
168
|
+
}} catch {{
|
|
169
|
+
# No processes found or other error
|
|
170
|
+
}}
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
result = subprocess.run([POWERSHELL_CMD, "-Command", ps_script], capture_output=True, text=True, timeout=5)
|
|
174
|
+
|
|
175
|
+
if result.returncode == 0:
|
|
176
|
+
output_lines = [line.strip() for line in result.stdout.strip().split("\n") if line.strip()]
|
|
177
|
+
matching_processes = []
|
|
178
|
+
|
|
179
|
+
for line in output_lines:
|
|
180
|
+
if line.startswith("{") and line.endswith("}"):
|
|
181
|
+
try:
|
|
182
|
+
proc_info = json.loads(line)
|
|
183
|
+
matching_processes.append(proc_info)
|
|
184
|
+
except json.JSONDecodeError:
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
if matching_processes:
|
|
188
|
+
return {"status": "running", "running": True, "processes": matching_processes, "command": command, "tab_name": tab_name}
|
|
189
|
+
else:
|
|
190
|
+
return {"status": "not_running", "running": False, "processes": [], "command": command, "tab_name": tab_name}
|
|
191
|
+
else:
|
|
192
|
+
return {"status": "error", "error": f"Command failed: {result.stderr}", "running": False, "command": command, "tab_name": tab_name}
|
|
193
|
+
|
|
194
|
+
except subprocess.TimeoutExpired:
|
|
195
|
+
logger.error(f"Timeout checking command status for tab '{tab_name}'")
|
|
196
|
+
return {"status": "timeout", "error": "Timeout checking process status", "running": False, "command": command, "tab_name": tab_name}
|
|
197
|
+
except Exception as e:
|
|
198
|
+
logger.error(f"Error checking command status for tab '{tab_name}': {e}")
|
|
199
|
+
return {"status": "error", "error": str(e), "running": False, "command": command, "tab_name": tab_name}
|
|
@@ -206,7 +206,7 @@ if __name__ == "__main__":
|
|
|
206
206
|
# Example usage with new schema
|
|
207
207
|
sample_layout: LayoutConfig = {
|
|
208
208
|
"layoutName": "SampleBots",
|
|
209
|
-
"layoutTabs": [{"tabName": "Explorer", "startDir": "~/code", "command": "
|
|
209
|
+
"layoutTabs": [{"tabName": "Explorer", "startDir": "~/code", "command": "yazi"}, {"tabName": "🤖Bot2", "startDir": "~", "command": "cmatrix"}, {"tabName": "📊Monitor", "startDir": "~", "command": "htop"}, {"tabName": "📝Logs", "startDir": "/var/log", "command": "tail -f /var/log/app.log"}],
|
|
210
210
|
}
|
|
211
211
|
try:
|
|
212
212
|
# Create layout using the generator with new design
|
|
@@ -276,7 +276,9 @@ class ZellijLocalManager:
|
|
|
276
276
|
print(f"📊 Quick Summary: {global_summary['running_commands']}/{global_summary['total_commands']} commands running across {global_summary['healthy_sessions']}/{global_summary['total_sessions']} sessions")
|
|
277
277
|
|
|
278
278
|
logger.info(f"Starting monitoring routine with {wait_ms}ms intervals")
|
|
279
|
-
|
|
279
|
+
from machineconfig.utils.scheduler import LoggerTemplate
|
|
280
|
+
from typing import cast
|
|
281
|
+
sched = Scheduler(routine=routine, wait_ms=wait_ms, logger=cast(LoggerTemplate, logger))
|
|
280
282
|
sched.run()
|
|
281
283
|
|
|
282
284
|
def save(self, session_id: Optional[str]) -> str:
|
|
@@ -312,7 +314,7 @@ if __name__ == "__main__":
|
|
|
312
314
|
{
|
|
313
315
|
"layoutName": "Development",
|
|
314
316
|
"layoutTabs": [
|
|
315
|
-
{"tabName": "🚀Frontend", "startDir": "~/code/myapp/frontend", "command": "
|
|
317
|
+
{"tabName": "🚀Frontend", "startDir": "~/code/myapp/frontend", "command": "bun run dev"},
|
|
316
318
|
{"tabName": "⚙️Backend", "startDir": "~/code/myapp/backend", "command": "python manage.py runserver"},
|
|
317
319
|
{"tabName": "📊Monitor", "startDir": "~", "command": "htop"},
|
|
318
320
|
],
|
|
@@ -88,8 +88,9 @@ class ZellijSessionManager:
|
|
|
88
88
|
# Print statuses
|
|
89
89
|
for i, status in enumerate(statuses):
|
|
90
90
|
print(f"Manager {i}: {status}")
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
from machineconfig.utils.scheduler import LoggerTemplate
|
|
92
|
+
from typing import cast
|
|
93
|
+
sched = Scheduler(routine=routine, wait_ms=60_000, logger=cast(LoggerTemplate, logger))
|
|
93
94
|
sched.run()
|
|
94
95
|
|
|
95
96
|
def save(self, session_id: Optional[str]) -> str:
|
|
@@ -53,7 +53,7 @@ class ProcessMonitor:
|
|
|
53
53
|
command = tab_config["command"]
|
|
54
54
|
try:
|
|
55
55
|
check_script = self._create_process_check_script(command)
|
|
56
|
-
remote_cmd = f"$HOME/.local/bin devops self
|
|
56
|
+
remote_cmd = f"$HOME/.local/bin devops self python -c {shlex.quote(check_script)}"
|
|
57
57
|
result = self.remote_executor.run_command(remote_cmd, timeout=15)
|
|
58
58
|
if result.returncode == 0:
|
|
59
59
|
try:
|
|
@@ -139,7 +139,7 @@ if __name__ == "__main__":
|
|
|
139
139
|
check_timestamp = timestamp_result.stdout.strip() if timestamp_result.returncode == 0 else "unknown"
|
|
140
140
|
|
|
141
141
|
check_script = self._create_fresh_check_script(command)
|
|
142
|
-
remote_cmd = f"$HOME/.local/bin/devops self
|
|
142
|
+
remote_cmd = f"$HOME/.local/bin/devops self python -c {shlex.quote(check_script)}"
|
|
143
143
|
result = self.remote_executor.run_command(remote_cmd, timeout=15)
|
|
144
144
|
|
|
145
145
|
if result.returncode == 0:
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check Installations
|
|
3
|
+
===================
|
|
4
|
+
|
|
5
|
+
This module scans installed applications using VirusTotal and generates a safety report.
|
|
6
|
+
It also provides functionality to download and install pre-checked applications.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import csv
|
|
10
|
+
import platform
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
16
|
+
|
|
17
|
+
from machineconfig.jobs.installer.checks.install_utils import upload_app
|
|
18
|
+
from machineconfig.jobs.installer.checks.report_utils import AppData, generate_markdown_report
|
|
19
|
+
from machineconfig.jobs.installer.checks.vt_utils import get_vt_client, scan_file
|
|
20
|
+
from machineconfig.utils.installer_utils.installer_runner import get_installed_cli_apps
|
|
21
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
22
|
+
from machineconfig.utils.source_of_truth import CONFIG_ROOT, INSTALL_VERSION_ROOT
|
|
23
|
+
|
|
24
|
+
# Constants
|
|
25
|
+
APP_SUMMARY_PATH = CONFIG_ROOT.joinpath(f"profile/records/{platform.system().lower()}/apps_summary_report.csv")
|
|
26
|
+
|
|
27
|
+
console = Console()
|
|
28
|
+
|
|
29
|
+
def main() -> None:
|
|
30
|
+
"""Main execution function."""
|
|
31
|
+
console.rule("[bold blue]MachineConfig Installation Checker[/bold blue]")
|
|
32
|
+
|
|
33
|
+
# 1. Gather Installed Apps
|
|
34
|
+
with console.status("[bold green]Gathering installed applications...[/bold green]"):
|
|
35
|
+
apps_paths = get_installed_cli_apps()
|
|
36
|
+
# Filter and find versions
|
|
37
|
+
# This part mimics the original logic but simplifies it
|
|
38
|
+
# We assume apps_paths contains PathExtended objects
|
|
39
|
+
|
|
40
|
+
# Find version files
|
|
41
|
+
install_version_root_ext = PathExtended(INSTALL_VERSION_ROOT)
|
|
42
|
+
version_files = list(install_version_root_ext.search()) if install_version_root_ext.exists() else []
|
|
43
|
+
|
|
44
|
+
apps_to_scan: list[tuple[PathExtended, Optional[str]]] = []
|
|
45
|
+
|
|
46
|
+
for app_path in apps_paths:
|
|
47
|
+
# Try to find version
|
|
48
|
+
version = None
|
|
49
|
+
# Simple matching logic: check if any version file matches the app name
|
|
50
|
+
# This is a heuristic from the original code
|
|
51
|
+
matching_versions = [v for v in version_files if v.stem == app_path.stem]
|
|
52
|
+
if matching_versions:
|
|
53
|
+
version = matching_versions[0].read_text(encoding="utf-8").strip()
|
|
54
|
+
|
|
55
|
+
apps_to_scan.append((app_path, version))
|
|
56
|
+
|
|
57
|
+
console.print(f"[green]Found {len(apps_to_scan)} applications to check.[/green]")
|
|
58
|
+
|
|
59
|
+
# 2. Scan Apps with VirusTotal
|
|
60
|
+
app_data_list: list[AppData] = []
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
client = get_vt_client()
|
|
64
|
+
|
|
65
|
+
with Progress(
|
|
66
|
+
SpinnerColumn(),
|
|
67
|
+
TextColumn("[progress.description]{task.description}"),
|
|
68
|
+
BarColumn(),
|
|
69
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
70
|
+
console=console
|
|
71
|
+
) as progress:
|
|
72
|
+
scan_task = progress.add_task("[cyan]Scanning apps...", total=len(apps_to_scan))
|
|
73
|
+
|
|
74
|
+
for app_path, version in apps_to_scan:
|
|
75
|
+
progress.update(scan_task, description=f"Scanning {app_path.name}...")
|
|
76
|
+
|
|
77
|
+
positive_pct, _ = scan_file(app_path, client, progress, scan_task)
|
|
78
|
+
|
|
79
|
+
# Upload if safe (optional, based on original code logic which uploaded everything?)
|
|
80
|
+
# Original code uploaded everything.
|
|
81
|
+
progress.update(scan_task, description=f"Uploading {app_path.name}...")
|
|
82
|
+
app_url = upload_app(app_path) or ""
|
|
83
|
+
|
|
84
|
+
app_data_list.append({
|
|
85
|
+
"app_name": app_path.stem,
|
|
86
|
+
"version": version,
|
|
87
|
+
"positive_pct": positive_pct,
|
|
88
|
+
"scan_time": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
|
89
|
+
"app_path": app_path.collapseuser(strict=False).as_posix(),
|
|
90
|
+
"app_url": app_url
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
progress.advance(scan_task)
|
|
94
|
+
|
|
95
|
+
except FileNotFoundError as e:
|
|
96
|
+
console.print(f"[bold red]{e}[/bold red]")
|
|
97
|
+
console.print("[yellow]Skipping scanning due to missing credentials.[/yellow]")
|
|
98
|
+
# Fallback: just list apps without scan results
|
|
99
|
+
for app_path, version in apps_to_scan:
|
|
100
|
+
app_data_list.append({
|
|
101
|
+
"app_name": app_path.stem,
|
|
102
|
+
"version": version,
|
|
103
|
+
"positive_pct": None,
|
|
104
|
+
"scan_time": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
|
105
|
+
"app_path": app_path.collapseuser(strict=False).as_posix(),
|
|
106
|
+
"app_url": ""
|
|
107
|
+
})
|
|
108
|
+
except Exception as e:
|
|
109
|
+
console.print(f"[bold red]An unexpected error occurred during scanning: {e}[/bold red]")
|
|
110
|
+
|
|
111
|
+
# 3. Generate Reports
|
|
112
|
+
if app_data_list:
|
|
113
|
+
# CSV Report
|
|
114
|
+
APP_SUMMARY_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
115
|
+
csv_path = APP_SUMMARY_PATH.with_suffix(".csv")
|
|
116
|
+
|
|
117
|
+
with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
|
|
118
|
+
fieldnames = list(app_data_list[0].keys())
|
|
119
|
+
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
|
120
|
+
writer.writeheader()
|
|
121
|
+
writer.writerows(app_data_list)
|
|
122
|
+
|
|
123
|
+
console.print(f"[green]CSV report saved to: {csv_path}[/green]")
|
|
124
|
+
|
|
125
|
+
# Markdown Report
|
|
126
|
+
md_path = APP_SUMMARY_PATH.with_suffix(".md")
|
|
127
|
+
generate_markdown_report(app_data_list, md_path)
|
|
128
|
+
console.print(f"[green]Markdown report saved to: {md_path}[/green]")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == '__main__':
|
|
133
|
+
main()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Installation Utilities
|
|
3
|
+
======================
|
|
4
|
+
|
|
5
|
+
This module provides functionality to download and install pre-checked applications.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import csv
|
|
9
|
+
import platform
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Optional
|
|
13
|
+
|
|
14
|
+
import gdown
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
18
|
+
from machineconfig.utils.source_of_truth import CONFIG_ROOT
|
|
19
|
+
|
|
20
|
+
# Constants
|
|
21
|
+
APP_SUMMARY_PATH = CONFIG_ROOT.joinpath(f"profile/records/{platform.system().lower()}/apps_summary_report.csv")
|
|
22
|
+
CLOUD_STORAGE_NAME = "gdw" # Default cloud storage name for rclone
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
def upload_app(path: PathExtended) -> Optional[str]:
|
|
27
|
+
"""Uploads the app to cloud storage and returns the shareable link."""
|
|
28
|
+
try:
|
|
29
|
+
# Using PathExtended.to_cloud
|
|
30
|
+
# Assuming 'gdw' is the configured remote in rclone
|
|
31
|
+
link_path = path.to_cloud(CLOUD_STORAGE_NAME, rel2home=True, share=True, os_specific=True, verbose=False)
|
|
32
|
+
if link_path:
|
|
33
|
+
return str(link_path)
|
|
34
|
+
return None
|
|
35
|
+
except Exception as e:
|
|
36
|
+
console.print(f"[red]Failed to upload {path}: {e}[/red]")
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def download_google_drive_file(url: str) -> PathExtended:
|
|
40
|
+
"""Downloads a file from Google Drive using gdown."""
|
|
41
|
+
try:
|
|
42
|
+
# Extract ID from URL
|
|
43
|
+
# Assuming URL format like https://drive.google.com/file/d/FILE_ID/view or similar
|
|
44
|
+
# or https://drive.google.com/uc?id=FILE_ID
|
|
45
|
+
if "id=" in url:
|
|
46
|
+
file_id = url.split("id=")[1].split("&")[0]
|
|
47
|
+
elif "/d/" in url:
|
|
48
|
+
file_id = url.split("/d/")[1].split("/")[0]
|
|
49
|
+
else:
|
|
50
|
+
# Fallback to gdown's auto detection or just pass URL
|
|
51
|
+
file_id = url
|
|
52
|
+
|
|
53
|
+
# Create a temporary directory for download
|
|
54
|
+
output_dir = PathExtended.tmpdir(prefix="gdown_")
|
|
55
|
+
# gdown.download returns the output filename
|
|
56
|
+
output_file = gdown.download(id=file_id, output=str(output_dir) + "/", quiet=False, fuzzy=True)
|
|
57
|
+
|
|
58
|
+
if not output_file:
|
|
59
|
+
raise ValueError(f"Download failed for {url}")
|
|
60
|
+
|
|
61
|
+
return PathExtended(output_file)
|
|
62
|
+
except Exception as e:
|
|
63
|
+
raise RuntimeError(f"Failed to download from Google Drive: {e}") from e
|
|
64
|
+
|
|
65
|
+
def install_cli_app(app_url: str) -> bool:
|
|
66
|
+
"""Downloads and installs a CLI app."""
|
|
67
|
+
try:
|
|
68
|
+
if not app_url:
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
exe_path = download_google_drive_file(app_url)
|
|
72
|
+
console.print(f"[green]Downloaded {exe_path.name}[/green]")
|
|
73
|
+
|
|
74
|
+
system = platform.system().lower()
|
|
75
|
+
if system in ["linux", "darwin"]:
|
|
76
|
+
exe_path.chmod(0o755) # Make executable
|
|
77
|
+
# Move to local bin (requires user to have write access or sudo, which we can't easily do here without interaction)
|
|
78
|
+
# Using ~/.local/bin is safer
|
|
79
|
+
install_path = Path.home() / ".local/bin"
|
|
80
|
+
install_path.mkdir(parents=True, exist_ok=True)
|
|
81
|
+
target = install_path / exe_path.name
|
|
82
|
+
exe_path.rename(target)
|
|
83
|
+
console.print(f"[green]Installed to {target}[/green]")
|
|
84
|
+
elif system == "windows":
|
|
85
|
+
install_path = Path.home() / "AppData/Local/Microsoft/WindowsApps"
|
|
86
|
+
install_path.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
target = install_path / exe_path.name
|
|
88
|
+
# Use shutil.move or PathExtended.move
|
|
89
|
+
exe_path.move(path=target, overwrite=True)
|
|
90
|
+
console.print(f"[green]Installed to {target}[/green]")
|
|
91
|
+
|
|
92
|
+
return True
|
|
93
|
+
except Exception as e:
|
|
94
|
+
console.print(f"[red]Failed to install app from {app_url}: {e}[/red]")
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
def load_summary_report() -> list[dict[str, Any]]:
|
|
98
|
+
"""Loads the summary report from CSV."""
|
|
99
|
+
if APP_SUMMARY_PATH.exists():
|
|
100
|
+
with open(APP_SUMMARY_PATH, 'r', encoding='utf-8') as csvfile:
|
|
101
|
+
reader = csv.DictReader(csvfile)
|
|
102
|
+
return list(reader)
|
|
103
|
+
else:
|
|
104
|
+
console.print(f"[yellow]Warning: Summary report not found at {APP_SUMMARY_PATH}[/yellow]")
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
def download_safe_apps(name: str = "essentials") -> None:
|
|
108
|
+
"""Downloads and installs safe apps."""
|
|
109
|
+
data = load_summary_report()
|
|
110
|
+
if not data:
|
|
111
|
+
console.print("[red]No app data available to install.[/red]")
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
apps_to_install = []
|
|
115
|
+
if name == "essentials":
|
|
116
|
+
# Install all available apps in the report? Or filter?
|
|
117
|
+
# Original code installed all if name="essentials" (implied by logic)
|
|
118
|
+
apps_to_install = [item['app_url'] for item in data if item.get('app_url')]
|
|
119
|
+
else:
|
|
120
|
+
apps_to_install = [item['app_url'] for item in data if item.get('app_name') == name and item.get('app_url')]
|
|
121
|
+
|
|
122
|
+
if not apps_to_install:
|
|
123
|
+
console.print(f"[yellow]No apps found to install for '{name}'.[/yellow]")
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
console.print(f"[bold]Installing {len(apps_to_install)} apps...[/bold]")
|
|
127
|
+
|
|
128
|
+
with ThreadPoolExecutor(max_workers=5) as executor:
|
|
129
|
+
results = list(executor.map(install_cli_app, apps_to_install))
|
|
130
|
+
|
|
131
|
+
success_count = sum(results)
|
|
132
|
+
console.print(f"[bold green]Successfully installed {success_count}/{len(apps_to_install)} apps.[/bold green]")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Report Utilities
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
This module provides functionality to generate reports for installed applications.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional, TypedDict
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
class AppData(TypedDict):
|
|
16
|
+
app_name: str
|
|
17
|
+
version: Optional[str]
|
|
18
|
+
positive_pct: Optional[float]
|
|
19
|
+
scan_time: str
|
|
20
|
+
app_path: str
|
|
21
|
+
app_url: str
|
|
22
|
+
|
|
23
|
+
def generate_markdown_report(data: list[AppData], output_path: Path) -> None:
|
|
24
|
+
"""Generates a Markdown table from the app data."""
|
|
25
|
+
if not data:
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
keys = list(data[0].keys())
|
|
29
|
+
header = "| " + " | ".join(key.replace("_", " ").title() for key in keys) + " |"
|
|
30
|
+
separator = "| " + " | ".join("---" for _ in keys) + " |"
|
|
31
|
+
rows = []
|
|
32
|
+
for row in data:
|
|
33
|
+
# Fix: row is a dict, keys are strings.
|
|
34
|
+
row_values = [str(row.get(key, '')) for key in keys]
|
|
35
|
+
rows.append("| " + " | ".join(row_values) + " |")
|
|
36
|
+
|
|
37
|
+
content = "\n".join([header, separator] + rows)
|
|
38
|
+
output_path.write_text(content, encoding="utf-8")
|
|
39
|
+
console.print(Panel(content, title="Safety Report Summary", expand=False))
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VirusTotal Utilities
|
|
3
|
+
====================
|
|
4
|
+
|
|
5
|
+
This module provides functionality to interact with VirusTotal API.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Optional, TypedDict
|
|
11
|
+
|
|
12
|
+
import vt
|
|
13
|
+
from rich.progress import Progress, TaskID
|
|
14
|
+
|
|
15
|
+
# Constants
|
|
16
|
+
VT_TOKEN_PATH = Path.home().joinpath("dotfiles/creds/tokens/virustotal")
|
|
17
|
+
|
|
18
|
+
class ScanResult(TypedDict):
|
|
19
|
+
result: Optional[str]
|
|
20
|
+
category: str
|
|
21
|
+
|
|
22
|
+
def get_vt_client() -> vt.Client:
|
|
23
|
+
"""Retrieves the VirusTotal client using the token from the credentials file."""
|
|
24
|
+
if not VT_TOKEN_PATH.exists():
|
|
25
|
+
raise FileNotFoundError(f"VirusTotal token not found at {VT_TOKEN_PATH}")
|
|
26
|
+
|
|
27
|
+
token = VT_TOKEN_PATH.read_text(encoding="utf-8").split("\n")[0].strip()
|
|
28
|
+
return vt.Client(token)
|
|
29
|
+
|
|
30
|
+
def scan_file(path: Path, client: vt.Client, progress: Optional[Progress] = None, task_id: Optional[TaskID] = None) -> tuple[Optional[float], list[Any]]:
|
|
31
|
+
"""
|
|
32
|
+
Scans a file using VirusTotal.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
path: Path to the file to scan.
|
|
36
|
+
client: VirusTotal client instance.
|
|
37
|
+
progress: Optional Rich progress bar.
|
|
38
|
+
task_id: Optional Task ID for the progress bar.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
A tuple containing the positive percentage (0-100) and the list of result details.
|
|
42
|
+
Returns (None, []) if scanning fails or file is skipped.
|
|
43
|
+
"""
|
|
44
|
+
if path.is_dir():
|
|
45
|
+
if progress and task_id:
|
|
46
|
+
progress.console.print(f"[yellow]📁 Skipping directory: {path}[/yellow]")
|
|
47
|
+
return None, []
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
with open(path, "rb") as f:
|
|
51
|
+
analysis = client.scan_file(f)
|
|
52
|
+
|
|
53
|
+
repeat_counter = 0
|
|
54
|
+
while True:
|
|
55
|
+
try:
|
|
56
|
+
anal = client.get_object("/analyses/{}", analysis.id)
|
|
57
|
+
if anal.status == "completed":
|
|
58
|
+
break
|
|
59
|
+
except Exception as e:
|
|
60
|
+
repeat_counter += 1
|
|
61
|
+
if repeat_counter > 3:
|
|
62
|
+
raise ValueError(f"❌ Error scanning {path}: {e}") from e
|
|
63
|
+
if progress and task_id:
|
|
64
|
+
progress.console.print(f"[red]⚠️ Error scanning {path}, retrying... ({repeat_counter}/3)[/red]")
|
|
65
|
+
time.sleep(5)
|
|
66
|
+
|
|
67
|
+
time.sleep(10) # Wait before checking status again
|
|
68
|
+
|
|
69
|
+
# Process results
|
|
70
|
+
results_data = list(anal.results.values())
|
|
71
|
+
malicious = []
|
|
72
|
+
for result_item in results_data:
|
|
73
|
+
result_dict = result_item.__dict__ if hasattr(result_item, '__dict__') else {
|
|
74
|
+
'result': getattr(result_item, 'result', None),
|
|
75
|
+
'category': getattr(result_item, 'category', 'unknown')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if result_dict.get('result') is None and result_dict.get('category') in ["undetected", "type-unsupported", "failure", "timeout", "confirmed-timeout"]:
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
malicious.append(result_item)
|
|
82
|
+
|
|
83
|
+
positive_pct = round(len(malicious) / len(results_data) * 100, 1) if results_data else 0.0
|
|
84
|
+
return positive_pct, results_data
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
if progress and task_id:
|
|
88
|
+
progress.console.print(f"[bold red]❌ Failed to scan {path}: {e}[/bold red]")
|
|
89
|
+
return None, []
|