machineconfig 8.14__py3-none-any.whl → 8.50__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of machineconfig might be problematic. Click here for more details.
- machineconfig/cluster/remote/run_cluster.py +1 -1
- machineconfig/cluster/remote/run_remote.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +10 -8
- machineconfig/cluster/sessions_managers/wt_local.py +1 -1
- machineconfig/cluster/sessions_managers/wt_local_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
- machineconfig/jobs/installer/checks/check_installations.py +133 -0
- machineconfig/jobs/installer/checks/install_utils.py +132 -0
- machineconfig/jobs/installer/checks/report_utils.py +39 -0
- machineconfig/jobs/installer/checks/vt_utils.py +89 -0
- machineconfig/jobs/installer/installer_data.json +225 -140
- machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
- machineconfig/jobs/installer/package_groups.py +10 -9
- machineconfig/jobs/installer/python_scripts/boxes.py +1 -2
- machineconfig/jobs/installer/python_scripts/code.py +10 -8
- machineconfig/jobs/installer/python_scripts/hx.py +30 -13
- machineconfig/jobs/installer/python_scripts/nerfont_windows_helper.py +6 -5
- machineconfig/jobs/installer/python_scripts/sysabc.py +25 -19
- machineconfig/jobs/installer/python_scripts/yazi.py +33 -17
- machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
- machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +1 -1
- machineconfig/jobs/scripts_dynamic/a.py +413 -10
- machineconfig/profile/create_links.py +77 -20
- machineconfig/profile/create_links_export.py +63 -58
- machineconfig/profile/mapper_data.toml +30 -0
- machineconfig/profile/mapper_dotfiles.toml +253 -0
- machineconfig/scripts/python/agents.py +70 -172
- machineconfig/scripts/python/ai/initai.py +3 -1
- machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +7 -5
- machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
- machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +29 -0
- machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
- machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
- machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
- machineconfig/scripts/python/ai/utils/vscode_tasks.py +6 -3
- machineconfig/scripts/python/cloud.py +58 -11
- machineconfig/scripts/python/croshell.py +4 -156
- machineconfig/scripts/python/devops.py +57 -40
- machineconfig/scripts/python/devops_navigator.py +17 -3
- machineconfig/scripts/python/fire_jobs.py +8 -207
- machineconfig/scripts/python/ftpx.py +5 -225
- machineconfig/scripts/python/graph/cli_graph.json +8743 -0
- machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
- machineconfig/scripts/python/{env_manager → helpers/helper_env}/env_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_gemini.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_qwen.py +1 -1
- machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_launch.py +5 -5
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +6 -6
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +10 -5
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +3 -3
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +225 -0
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +7 -6
- machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +267 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +98 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +76 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_nw.py +52 -72
- machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +274 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_self.py +40 -23
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_file.py +44 -30
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_server.py +26 -43
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_terminal.py +12 -6
- machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +12 -6
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
- machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +68 -52
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/run_script.py +75 -58
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +3 -3
- machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +3 -3
- machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_linux/fzfg +4 -3
- machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_windows/fzfg.ps1 +1 -1
- machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
- machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
- machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
- machineconfig/scripts/python/helpers/helpers_network/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address.py +15 -17
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address_switch.py +1 -1
- machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_ssh.py +2 -2
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_identity.py +73 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_ssh_key.py +175 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_linux.py +319 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_windows.py +275 -0
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +117 -33
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +3 -2
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -13
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_2.py +63 -19
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
- machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
- machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +186 -0
- machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +1 -1
- machineconfig/scripts/python/helpers/helpers_terminal/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_terminal/terminal_impl.py +96 -0
- machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/download.py +1 -1
- machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/python.py +47 -26
- machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
- machineconfig/scripts/python/mcfg_entry.py +133 -48
- machineconfig/scripts/python/msearch.py +15 -61
- machineconfig/scripts/python/sessions.py +59 -194
- machineconfig/scripts/python/terminal.py +18 -96
- machineconfig/scripts/python/utils.py +101 -20
- machineconfig/settings/atuin/config.toml +294 -0
- machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
- machineconfig/settings/linters/.ruff.toml +1 -0
- machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
- machineconfig/settings/shells/bash/init.sh +6 -3
- machineconfig/settings/shells/pwsh/init.ps1 +69 -1
- machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
- machineconfig/settings/shells/wezterm/wezterm.lua +4 -1
- machineconfig/settings/shells/wt/settings.json +20 -7
- machineconfig/settings/shells/zsh/init.sh +25 -4
- machineconfig/settings/television/cable_unix/bash-history.toml +1 -1
- machineconfig/settings/television/cable_windows/pwsh-history.toml +1 -1
- machineconfig/settings/tv/config.toml +234 -0
- machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
- machineconfig/settings/wsl/.wslconfig +5 -30
- machineconfig/settings/yazi/yazi_linux.toml +18 -8
- machineconfig/settings/zellij/layouts/st.kdl +2 -2
- machineconfig/settings/zellij/layouts/st2.kdl +1 -1
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_linux/web_shortcuts/live_from_github.sh +3 -0
- machineconfig/setup_mac/__init__.py +0 -2
- machineconfig/setup_windows/__init__.py +0 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +14 -13
- machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +4 -3
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -3
- machineconfig/type_hinting/sql/__init__.py +1 -0
- machineconfig/type_hinting/sql/base.py +216 -0
- machineconfig/type_hinting/sql/core_schema.py +64 -0
- machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
- machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
- machineconfig/type_hinting/typedict/__init__.py +1 -0
- machineconfig/type_hinting/typedict/ast_utils.py +130 -0
- machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
- machineconfig/type_hinting/typedict/generators.py +231 -0
- machineconfig/type_hinting/typedict/polars_schema.py +24 -0
- machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
- machineconfig/utils/accessories.py +24 -0
- machineconfig/utils/code.py +41 -13
- machineconfig/utils/files/ascii_art.py +10 -14
- machineconfig/utils/files/headers.py +3 -5
- machineconfig/utils/files/read.py +8 -1
- machineconfig/utils/installer_utils/github_release_bulk.py +11 -91
- machineconfig/utils/installer_utils/github_release_scraper.py +99 -0
- machineconfig/utils/installer_utils/install_from_url.py +1 -1
- machineconfig/utils/installer_utils/installer_class.py +12 -4
- machineconfig/utils/installer_utils/installer_cli.py +1 -15
- machineconfig/utils/installer_utils/installer_helper.py +2 -2
- machineconfig/utils/installer_utils/installer_locator_utils.py +13 -13
- machineconfig/utils/installer_utils/installer_runner.py +4 -4
- machineconfig/utils/io.py +25 -8
- machineconfig/utils/meta.py +6 -4
- machineconfig/utils/options.py +49 -19
- machineconfig/utils/options_utils/__init__.py +0 -0
- machineconfig/utils/options_utils/options_tv_linux.py +211 -0
- machineconfig/utils/options_utils/options_tv_windows.py +88 -0
- machineconfig/utils/options_utils/tv_options.py +37 -0
- machineconfig/utils/path_extended.py +6 -6
- machineconfig/utils/scheduler.py +8 -2
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
- machineconfig/utils/source_of_truth.py +6 -1
- machineconfig/utils/ssh.py +69 -18
- machineconfig/utils/ssh_utils/abc.py +1 -1
- machineconfig/utils/ssh_utils/copy_from_here.py +17 -12
- machineconfig/utils/ssh_utils/utils.py +21 -5
- machineconfig/utils/ssh_utils/wsl.py +107 -170
- machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
- machineconfig/utils/upgrade_packages.py +4 -8
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/METADATA +29 -22
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/RECORD +251 -211
- machineconfig/jobs/installer/check_installations.py +0 -248
- machineconfig/profile/backup.toml +0 -49
- machineconfig/profile/mapper.toml +0 -263
- machineconfig/scripts/python/helpers_devops/cli_config.py +0 -105
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -89
- machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
- machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -208
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
- machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
- machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
- machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -620
- machineconfig/scripts/python/helpers_network/ssh_add_identity.py +0 -116
- machineconfig/scripts/python/helpers_network/ssh_add_ssh_key.py +0 -153
- machineconfig/scripts/python/helpers_network/ssh_debug_linux.py +0 -391
- machineconfig/scripts/python/helpers_network/ssh_debug_windows.py +0 -338
- machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -77
- machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
- machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
- machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
- machineconfig/utils/options_tv.py +0 -119
- machineconfig/utils/tst.py +0 -20
- /machineconfig/{scripts/python/helpers_agents → jobs/installer/checks}/__init__.py +0 -0
- /machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
- /machineconfig/scripts/python/{helpers_agents/agentic_frameworks → graph}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_agents}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_agents/agentic_frameworks}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_helper_types.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aichat/config.yaml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aider/.aider.conf.yml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/copilot/config.yml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/crush/crush.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/gemini/settings.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/privacy.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/prompt.txt +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.sh +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes → helpers/helpers_cloud}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_croshell}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_devops}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_devops/themes}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_fire_command/__init__.py} +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/f.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/file_wrangler.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_streamlit_helper.py +0 -0
- /machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nfs.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nw_drive.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/onetimeshare.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/wifi_conn.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_1.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
- /machineconfig/scripts/python/helpers/{ast_search.py → helpers_search/ast_search.py} +0 -0
- /machineconfig/scripts/python/helpers/{qr_code.py → helpers_search/qr_code.py} +0 -0
- /machineconfig/scripts/python/helpers/{repo_rag.py → helpers_search/repo_rag.py} +0 -0
- /machineconfig/scripts/python/helpers/{symantic_search.py → helpers_search/symantic_search.py} +0 -0
- /machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/pdf.py +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/WHEEL +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/entry_points.txt +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/top_level.txt +0 -0
|
@@ -75,18 +75,18 @@ def get_installed_cli_apps():
|
|
|
75
75
|
print("🔍 LISTING INSTALLED CLI APPS 🔍")
|
|
76
76
|
if platform.system() == "Windows":
|
|
77
77
|
print("🪟 Searching for Windows executables...")
|
|
78
|
-
apps = PathExtended.home().joinpath("AppData/Local/Microsoft/WindowsApps").
|
|
78
|
+
apps = [p for p in PathExtended.home().joinpath("AppData/Local/Microsoft/WindowsApps").glob("*.exe") if "notepad" not in str(p)]
|
|
79
79
|
elif platform.system() in ["Linux", "Darwin"]:
|
|
80
80
|
print(f"🐧 Searching for {platform.system()} executables...")
|
|
81
81
|
if platform.system() == "Linux":
|
|
82
|
-
apps = PathExtended(LINUX_INSTALL_PATH).
|
|
82
|
+
apps = list(PathExtended(LINUX_INSTALL_PATH).glob("*")) + list(PathExtended("/usr/local/bin").glob("*"))
|
|
83
83
|
else: # Darwin/macOS
|
|
84
|
-
apps = PathExtended("/usr/local/bin").
|
|
84
|
+
apps = list(PathExtended("/usr/local/bin").glob("*")) + list(PathExtended("/opt/homebrew/bin").glob("*"))
|
|
85
85
|
else:
|
|
86
86
|
error_msg = f"❌ ERROR: System {platform.system()} not supported"
|
|
87
87
|
print(error_msg)
|
|
88
88
|
raise NotImplementedError(error_msg)
|
|
89
|
-
apps = [app for app in apps if app.
|
|
89
|
+
apps = [app for app in apps if (app.stat().st_size / 1024) > 0.1 and not app.is_symlink()] # no symlinks like paint and wsl and bash
|
|
90
90
|
print(f"✅ Found {len(apps)} installed applications")
|
|
91
91
|
return apps
|
|
92
92
|
|
machineconfig/utils/io.py
CHANGED
|
@@ -55,19 +55,36 @@ def read_ini(path: "Path", encoding: Optional[str] = None):
|
|
|
55
55
|
return res
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
def remove_c_style_comments(text: str) -> str:
|
|
59
|
+
import re
|
|
60
|
+
# Step 1: Escape URLs (https:// or any URLs you want to protect)
|
|
61
|
+
url_pattern = r'https?://[^\s]*'
|
|
62
|
+
urls = re.findall(url_pattern, text)
|
|
63
|
+
url_map = {url: f"__URL{index}__" for index, url in enumerate(urls)}
|
|
64
|
+
|
|
65
|
+
# Temporarily replace URLs with placeholders
|
|
66
|
+
for url, placeholder in url_map.items():
|
|
67
|
+
text = text.replace(url, placeholder)
|
|
68
|
+
|
|
69
|
+
# Step 2: Remove C-style comments
|
|
70
|
+
# Remove all // single-line comments
|
|
71
|
+
text = re.sub(r'//.*', '', text)
|
|
72
|
+
# Remove all /* … */ block comments (non-greedy)
|
|
73
|
+
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
|
|
74
|
+
|
|
75
|
+
# Step 3: Restore URLs
|
|
76
|
+
for url, placeholder in url_map.items():
|
|
77
|
+
text = text.replace(placeholder, url)
|
|
78
|
+
|
|
79
|
+
return text
|
|
80
|
+
|
|
81
|
+
|
|
58
82
|
def read_json(path: "Path", r: bool = False, **kwargs: Any) -> Any: # return could be list or dict etc
|
|
59
83
|
import json
|
|
60
84
|
try:
|
|
61
85
|
mydict = json.loads(Path(path).read_text(encoding="utf-8"), **kwargs)
|
|
62
86
|
except Exception:
|
|
63
|
-
|
|
64
|
-
def remove_comments(text: str) -> str:
|
|
65
|
-
# remove all // single-line comments
|
|
66
|
-
text = re.sub(r'//.*', '', text)
|
|
67
|
-
# remove all /* … */ block comments (non-greedy)
|
|
68
|
-
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
|
|
69
|
-
return text
|
|
70
|
-
mydict = json.loads(remove_comments(Path(path).read_text(encoding="utf-8")), **kwargs)
|
|
87
|
+
mydict = json.loads(remove_c_style_comments(Path(path).read_text(encoding="utf-8")), **kwargs)
|
|
71
88
|
_ = r
|
|
72
89
|
return mydict
|
|
73
90
|
|
machineconfig/utils/meta.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
|
|
7
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
|
|
8
|
+
from machineconfig.scripts.python.helpers.helpers_fire_command.file_wrangler import get_import_module_code
|
|
9
9
|
from machineconfig.utils.accessories import get_repo_root
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
repo_root = get_repo_root(Path(py_file))
|
|
@@ -68,7 +68,7 @@ def lambda_to_python_script(lmb: Callable[[], Any],
|
|
|
68
68
|
|
|
69
69
|
# sanity checks
|
|
70
70
|
if not (callable(lmb) and isinstance(lmb, _types.LambdaType)):
|
|
71
|
-
raise TypeError("Expected a lambda function object")
|
|
71
|
+
raise TypeError(f"Expected a lambda function object, got {type(lmb)}")
|
|
72
72
|
|
|
73
73
|
src = _inspect.getsource(lmb)
|
|
74
74
|
src = _textwrap.dedent(src)
|
|
@@ -81,7 +81,7 @@ def lambda_to_python_script(lmb: Callable[[], Any],
|
|
|
81
81
|
lambda_node = n
|
|
82
82
|
break
|
|
83
83
|
if lambda_node is None:
|
|
84
|
-
raise ValueError("Could not find a lambda expression in source")
|
|
84
|
+
raise ValueError(f"""Could not find a lambda expression in source, got:\n{lmb}\nwhich is not a lambda, its a {src}""")
|
|
85
85
|
|
|
86
86
|
body = lambda_node.body
|
|
87
87
|
if not isinstance(body, _ast.Call):
|
|
@@ -113,7 +113,7 @@ def lambda_to_python_script(lmb: Callable[[], Any],
|
|
|
113
113
|
if not callable(func_obj):
|
|
114
114
|
raise TypeError("Resolved object is not callable")
|
|
115
115
|
|
|
116
|
-
func_name = getattr(func_obj, "__name__", "<unknown>")
|
|
116
|
+
func_name = getattr(getattr(func_obj, "__code__", None), "co_name", None) or getattr(func_obj, "__name__", "<unknown>")
|
|
117
117
|
|
|
118
118
|
import_prefix: str = ""
|
|
119
119
|
if import_module:
|
|
@@ -249,8 +249,10 @@ def lambda_to_python_script(lmb: Callable[[], Any],
|
|
|
249
249
|
if __name__ == "__main__":
|
|
250
250
|
from machineconfig.utils.code import print_code
|
|
251
251
|
import_code_robust = "<import_code_robust>"
|
|
252
|
+
print_code.__name__ = "blah"
|
|
252
253
|
res = lambda_to_python_script(
|
|
253
254
|
lambda: print_code(code=import_code_robust, lexer="python", desc="import as module code"),
|
|
254
255
|
in_global=True, import_module=False
|
|
255
256
|
)
|
|
257
|
+
|
|
256
258
|
print(res)
|
machineconfig/utils/options.py
CHANGED
|
@@ -7,10 +7,10 @@ import subprocess
|
|
|
7
7
|
from typing import Optional, Union, Iterable, overload, Literal, cast
|
|
8
8
|
|
|
9
9
|
@overload
|
|
10
|
-
def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False) -> T: ...
|
|
10
|
+
def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, preview: Optional[Literal["bat"]]=None) -> T: ...
|
|
11
11
|
@overload
|
|
12
|
-
def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[True], custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, ) -> list[T]: ...
|
|
13
|
-
def choose_from_options[T](options: Iterable[T], msg: str, multi: bool, custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, ) -> Union[T, list[T]]:
|
|
12
|
+
def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[True], custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, preview: Optional[Literal["bat"]]=None) -> list[T]: ...
|
|
13
|
+
def choose_from_options[T](options: Iterable[T], msg: str, multi: bool, custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, preview: Optional[Literal["bat"]]=None, ) -> Union[T, list[T]]:
|
|
14
14
|
# TODO: replace with https://github.com/tmbo/questionary
|
|
15
15
|
# # also see https://github.com/charmbracelet/gum
|
|
16
16
|
options_strings: list[str] = [str(x) for x in options]
|
|
@@ -20,11 +20,6 @@ def choose_from_options[T](options: Iterable[T], msg: str, multi: bool, custom_i
|
|
|
20
20
|
# from machineconfig.utils.installer_utils.installer_cli import check_tool_exists
|
|
21
21
|
# print("ch1")
|
|
22
22
|
if tv and check_tool_exists("tv"):
|
|
23
|
-
# from pyfzf.pyfzf import FzfPrompt
|
|
24
|
-
# fzf_prompt = FzfPrompt()
|
|
25
|
-
# nl = "\n"
|
|
26
|
-
# 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, ' ')}")
|
|
27
|
-
# print("ch2")
|
|
28
23
|
from machineconfig.utils.accessories import randstr
|
|
29
24
|
options_txt_path = Path.home().joinpath("tmp_results/tmp_files/choices_" + randstr(6) + ".txt")
|
|
30
25
|
options_txt_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -35,34 +30,70 @@ def choose_from_options[T](options: Iterable[T], msg: str, multi: bool, custom_i
|
|
|
35
30
|
# the interactive session completes. Do not capture_output or redirect
|
|
36
31
|
# stdin/stderr here so `tv` stays attached to the terminal.
|
|
37
32
|
tv_out_path = options_txt_path.with_name(options_txt_path.stem + "_out.txt")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
if preview is None:
|
|
34
|
+
preview_line = ""
|
|
35
|
+
elif preview == "bat":
|
|
36
|
+
preview_line = r"""--preview-command "bat -n --color=always {}" --preview-size 70 """
|
|
37
|
+
|
|
38
|
+
import platform
|
|
39
|
+
if platform.system() == "Windows":
|
|
40
|
+
# PowerShell + TUI apps can be finicky when stdin is a pipe. Avoid piping into `tv` and
|
|
41
|
+
# instead provide a `--source-command` so `tv` can keep stdin attached to the console.
|
|
42
|
+
# Also: `tv --ansi` is a flag (no value). Passing `true` makes it a positional argument
|
|
43
|
+
# (channel/path/command), which can lead to confusing behavior.
|
|
44
|
+
source_cmd = f"cmd /C type \"{options_txt_path}\""
|
|
45
|
+
tv_cmd = f"""
|
|
46
|
+
$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
|
47
|
+
tv {preview_line} --ansi --source-command '{source_cmd}' --source-output "{{}}" | Out-File -Encoding utf8 -FilePath "{tv_out_path}" """
|
|
48
|
+
else:
|
|
49
|
+
source_cmd = f'cat "{options_txt_path}"'
|
|
50
|
+
tv_cmd = f"""tv {preview_line} --ansi --source-command "{source_cmd}" --source-output "{{}}" > "{tv_out_path}" """
|
|
51
|
+
|
|
52
|
+
# print(f"Running tv command: {tv_cmd}")
|
|
53
|
+
# print(f"Options file: {options_txt_path}")
|
|
54
|
+
# print(f"Content:\n{options_txt_path.read_text(encoding='utf-8')}")
|
|
55
|
+
# print(f"tv output file: {tv_out_path}")
|
|
56
|
+
from machineconfig.utils.code import run_shell_script
|
|
57
|
+
res = run_shell_script(tv_cmd, display_script=False, clean_env=False)
|
|
58
|
+
|
|
41
59
|
# If tv returned a non-zero code and there is no output file, treat it as an error.
|
|
42
60
|
if res.returncode != 0 and not tv_out_path.exists():
|
|
43
61
|
raise RuntimeError(f"Got error running tv command: {tv_cmd}\nreturncode: {res.returncode}")
|
|
44
62
|
|
|
45
63
|
# Read selections (if any) from the output file created by tv.
|
|
46
|
-
|
|
64
|
+
print(f"Reading tv output from: {tv_out_path}")
|
|
65
|
+
out_text = tv_out_path.read_text(encoding="utf-8-sig")
|
|
47
66
|
choice_string_multi = [x for x in out_text.splitlines() if x.strip() != ""]
|
|
48
67
|
|
|
68
|
+
# if len(choice_string_multi) == 0: # e.g. user pressed escape
|
|
69
|
+
# console.print(Panel("❓ No option selected!", title="Error", expand=False))
|
|
70
|
+
# return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
|
|
71
|
+
|
|
49
72
|
# Cleanup temporary files
|
|
50
73
|
options_txt_path.unlink(missing_ok=True)
|
|
51
74
|
tv_out_path.unlink(missing_ok=True)
|
|
52
75
|
if not multi:
|
|
53
76
|
try:
|
|
54
77
|
choice_one_string = choice_string_multi[0]
|
|
55
|
-
if isinstance(list(options)[0], str):
|
|
78
|
+
if isinstance(list(options)[0], str):
|
|
79
|
+
print(f"✅ Selected option: {choice_one_string}")
|
|
80
|
+
return cast(T, choice_one_string)
|
|
56
81
|
choice_idx = options_strings.index(choice_one_string)
|
|
57
|
-
|
|
82
|
+
choice_made = list(options)[choice_idx]
|
|
83
|
+
print(f"✅ Selected option: {choice_made}")
|
|
84
|
+
return choice_made
|
|
58
85
|
except IndexError as ie:
|
|
59
|
-
print(f"❌ Error: {options=}, {choice_string_multi=}")
|
|
60
|
-
print(f"🔍 Available choices: {choice_string_multi}")
|
|
86
|
+
# print(f"❌ Error: {options=}, {choice_string_multi=}")
|
|
87
|
+
print(f"🔍 Available choices: {len(choice_string_multi)}")
|
|
61
88
|
raise ie
|
|
62
89
|
if isinstance(list(options)[0], str):
|
|
63
|
-
|
|
90
|
+
result2 = cast(list[T], choice_string_multi)
|
|
91
|
+
print(f"✅ Selected options: {result2}")
|
|
92
|
+
return result2
|
|
64
93
|
choice_idx_s = [options_strings.index(x) for x in choice_string_multi]
|
|
65
|
-
|
|
94
|
+
result = [list(options)[x] for x in choice_idx_s]
|
|
95
|
+
print(f"✅ Selected options: {result}")
|
|
96
|
+
return result
|
|
66
97
|
else:
|
|
67
98
|
if default is not None:
|
|
68
99
|
assert default in options, f"Default `{default}` option not in options `{list(options)}`"
|
|
@@ -138,7 +169,6 @@ def choose_cloud_interactively() -> str:
|
|
|
138
169
|
|
|
139
170
|
def get_ssh_hosts() -> list[str]:
|
|
140
171
|
from paramiko import SSHConfig
|
|
141
|
-
|
|
142
172
|
c = SSHConfig()
|
|
143
173
|
c.parse(open(Path.home().joinpath(".ssh/config"), encoding="utf-8"))
|
|
144
174
|
return list(c.get_hostnames())
|
|
File without changes
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
|
|
2
|
+
#!/usr/bin/env python3
|
|
3
|
+
import base64
|
|
4
|
+
import pathlib
|
|
5
|
+
import pprint
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import tempfile
|
|
9
|
+
import os
|
|
10
|
+
from typing import Any, overload, Literal, Union
|
|
11
|
+
|
|
12
|
+
from git import Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _format_preview_value(value: Any) -> str:
|
|
16
|
+
if isinstance(value, str):
|
|
17
|
+
return value
|
|
18
|
+
return pprint.pformat(value, width=88, sort_dicts=True)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _toml_inline_table(values: dict[str, str]) -> str:
|
|
22
|
+
if not values:
|
|
23
|
+
return ""
|
|
24
|
+
parts: list[str] = []
|
|
25
|
+
for key in sorted(values.keys()):
|
|
26
|
+
raw_value = values[key]
|
|
27
|
+
escaped = raw_value.replace("\\", "\\\\").replace('"', '\\"')
|
|
28
|
+
parts.append(f'{key} = "{escaped}"')
|
|
29
|
+
return "env = { " + ", ".join(parts) + " }\n"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _normalize_extension(extension: str | None) -> str | None:
|
|
33
|
+
if extension is None:
|
|
34
|
+
return None
|
|
35
|
+
trimmed = extension.strip()
|
|
36
|
+
if trimmed.startswith("."):
|
|
37
|
+
trimmed = trimmed[1:]
|
|
38
|
+
if not trimmed:
|
|
39
|
+
return None
|
|
40
|
+
return trimmed
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _infer_extension_from_key(key: Optional[str]) -> str | None:
|
|
44
|
+
if not isinstance(key, str):
|
|
45
|
+
return None
|
|
46
|
+
candidate = key.strip()
|
|
47
|
+
if not candidate or any(char.isspace() for char in candidate):
|
|
48
|
+
return None
|
|
49
|
+
suffix = pathlib.Path(candidate).suffix
|
|
50
|
+
if not suffix:
|
|
51
|
+
return None
|
|
52
|
+
return _normalize_extension(suffix)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@overload
|
|
56
|
+
def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[True], preview_size_percent: float) -> list[str]: ...
|
|
57
|
+
@overload
|
|
58
|
+
def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[False], preview_size_percent: float) -> Union[str, None]: ...
|
|
59
|
+
def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: bool, preview_size_percent: float) -> Union[Union[str, None], list[str]]:
|
|
60
|
+
keys = list(options_to_preview_mapping.keys())
|
|
61
|
+
if not keys:
|
|
62
|
+
return [] if multi else None
|
|
63
|
+
normalized_extension = _normalize_extension(extension)
|
|
64
|
+
preview_panel_size = max(10, min(90, int(preview_size_percent)))
|
|
65
|
+
terminal_width = shutil.get_terminal_size(fallback=(120, 40)).columns
|
|
66
|
+
preview_width = max(20, int(terminal_width * preview_panel_size / 100) - 4)
|
|
67
|
+
with tempfile.TemporaryDirectory(prefix="tv_channel_") as tmpdir:
|
|
68
|
+
tempdir = pathlib.Path(tmpdir)
|
|
69
|
+
entries: list[str] = []
|
|
70
|
+
index_map: dict[int, str] = {}
|
|
71
|
+
preview_map_path = tempdir / "previews.tsv"
|
|
72
|
+
preview_rows: list[str] = []
|
|
73
|
+
for idx, key in enumerate(keys):
|
|
74
|
+
display_key = key.replace("\t", " ").replace("\n", " ")
|
|
75
|
+
entries.append(f"{idx}\t{display_key}")
|
|
76
|
+
index_map[idx] = key
|
|
77
|
+
preview_value = _format_preview_value(options_to_preview_mapping[key])
|
|
78
|
+
encoded_preview = base64.b64encode(preview_value.encode("utf-8")).decode("ascii")
|
|
79
|
+
entry_extension = normalized_extension or _infer_extension_from_key(key) or ""
|
|
80
|
+
preview_rows.append(f"{idx}\t{encoded_preview}\t{entry_extension}")
|
|
81
|
+
preview_map_path.write_text("\n".join(preview_rows), encoding="utf-8")
|
|
82
|
+
entries_path = tempdir / "entries.tsv"
|
|
83
|
+
entries_path.write_text("\n".join(entries), encoding="utf-8")
|
|
84
|
+
preview_script = tempdir / "preview.sh"
|
|
85
|
+
preview_script.write_text(
|
|
86
|
+
"""#!/usr/bin/env bash
|
|
87
|
+
set -euo pipefail
|
|
88
|
+
|
|
89
|
+
idx="$1"
|
|
90
|
+
script_dir="$(cd -- "$(dirname -- "$0")" && pwd)"
|
|
91
|
+
previews_file="${script_dir}/previews.tsv"
|
|
92
|
+
|
|
93
|
+
if [[ ! -f "${previews_file}" ]]; then
|
|
94
|
+
exit 0
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
encoded_preview="$(awk -F '\t' -v idx="${idx}" '($1==idx){print $2; exit}' "${previews_file}" || true)"
|
|
98
|
+
|
|
99
|
+
if [[ -z "${encoded_preview}" ]]; then
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
preview_content="$(printf '%s' "${encoded_preview}" | base64 --decode)"
|
|
104
|
+
|
|
105
|
+
preview_ext_from_row="$(awk -F '\t' -v idx="${idx}" '($1==idx){print $3; exit}' "${previews_file}" || true)"
|
|
106
|
+
preview_ext="${MCFG_PREVIEW_EXT:-${preview_ext_from_row}}"
|
|
107
|
+
preview_width="${MCFG_PREVIEW_WIDTH:-}"
|
|
108
|
+
preview_size_pct="${MCFG_PREVIEW_SIZE_PCT:-}"
|
|
109
|
+
|
|
110
|
+
if [[ -z "${preview_width}" && -n "${COLUMNS:-}" ]]; then
|
|
111
|
+
if [[ "${preview_size_pct}" =~ ^[0-9]+$ ]]; then
|
|
112
|
+
preview_width=$((COLUMNS * preview_size_pct / 100))
|
|
113
|
+
else
|
|
114
|
+
preview_width="${COLUMNS}"
|
|
115
|
+
fi
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
if command -v bat >/dev/null 2>&1; then
|
|
119
|
+
bat_args=(--force-colorization --style=plain --paging=never --wrap=character)
|
|
120
|
+
if [[ -n "${preview_ext}" ]]; then
|
|
121
|
+
bat_args+=(--language "${preview_ext}")
|
|
122
|
+
fi
|
|
123
|
+
if [[ "${preview_width}" =~ ^[0-9]+$ ]]; then
|
|
124
|
+
bat_args+=(--terminal-width "${preview_width}")
|
|
125
|
+
fi
|
|
126
|
+
printf '%s' "${preview_content}" | bat "${bat_args[@]}"
|
|
127
|
+
elif command -v glow >/dev/null 2>&1; then
|
|
128
|
+
printf '%s' "${preview_content}" | glow -
|
|
129
|
+
elif command -v fold >/dev/null 2>&1 && [[ "${preview_width}" =~ ^[0-9]+$ ]]; then
|
|
130
|
+
printf '%s' "${preview_content}" | fold -s -w "${preview_width}"
|
|
131
|
+
else
|
|
132
|
+
printf '%s' "${preview_content}"
|
|
133
|
+
fi
|
|
134
|
+
""",
|
|
135
|
+
encoding="utf-8"
|
|
136
|
+
)
|
|
137
|
+
preview_script.chmod(0o755)
|
|
138
|
+
preview_env: dict[str, str] = {
|
|
139
|
+
"BAT_THEME": "ansi",
|
|
140
|
+
"MCFG_PREVIEW_SIZE_PCT": str(preview_panel_size),
|
|
141
|
+
}
|
|
142
|
+
if normalized_extension is not None:
|
|
143
|
+
preview_env["MCFG_PREVIEW_EXT"] = normalized_extension
|
|
144
|
+
if preview_width > 0:
|
|
145
|
+
preview_env["MCFG_PREVIEW_WIDTH"] = str(preview_width)
|
|
146
|
+
preview_env_line = _toml_inline_table(preview_env)
|
|
147
|
+
channel_config = f"""[metadata]
|
|
148
|
+
name = "temp_options"
|
|
149
|
+
description = "Temporary channel for selecting options"
|
|
150
|
+
|
|
151
|
+
[source]
|
|
152
|
+
command = "bat '{entries_path}'"
|
|
153
|
+
display = "{{split:\\t:1}}"
|
|
154
|
+
output = "{{split:\\t:0}}"
|
|
155
|
+
|
|
156
|
+
[preview]
|
|
157
|
+
command = "{preview_script} {{split:\\t:0}}"
|
|
158
|
+
{preview_env_line}
|
|
159
|
+
|
|
160
|
+
[ui.preview_panel]
|
|
161
|
+
size = {preview_panel_size}
|
|
162
|
+
"""
|
|
163
|
+
channel_path = tempdir / "temp_options.toml"
|
|
164
|
+
channel_path.write_text(channel_config, encoding="utf-8")
|
|
165
|
+
env = os.environ.copy()
|
|
166
|
+
tv_config_dir = pathlib.Path.home() / ".config" / "television"
|
|
167
|
+
if not tv_config_dir.exists():
|
|
168
|
+
tv_config_dir = pathlib.Path(os.getenv("XDG_CONFIG_HOME", str(pathlib.Path.home() / ".config"))) / "television"
|
|
169
|
+
cable_dir = tv_config_dir / "cable"
|
|
170
|
+
cable_dir.mkdir(parents=True, exist_ok=True)
|
|
171
|
+
temp_channel_link = cable_dir / "temp_options.toml"
|
|
172
|
+
if temp_channel_link.exists() or temp_channel_link.is_symlink():
|
|
173
|
+
temp_channel_link.unlink()
|
|
174
|
+
temp_channel_link.symlink_to(channel_path)
|
|
175
|
+
output_file = tempdir / "selection.txt"
|
|
176
|
+
try:
|
|
177
|
+
result = subprocess.run(["tv", "temp_options"], check=False, stdout=output_file.open("w"), text=True, env=env)
|
|
178
|
+
finally:
|
|
179
|
+
if temp_channel_link.exists() or temp_channel_link.is_symlink():
|
|
180
|
+
temp_channel_link.unlink()
|
|
181
|
+
if result.returncode not in (0, 130):
|
|
182
|
+
raise SystemExit(result.returncode)
|
|
183
|
+
if result.returncode == 130:
|
|
184
|
+
return [] if multi else None
|
|
185
|
+
if not output_file.exists():
|
|
186
|
+
return [] if multi else None
|
|
187
|
+
selected_lines = [line.strip() for line in output_file.read_text().splitlines() if line.strip()]
|
|
188
|
+
if not selected_lines:
|
|
189
|
+
return [] if multi else None
|
|
190
|
+
selected_keys: list[str] = []
|
|
191
|
+
for line in selected_lines:
|
|
192
|
+
try:
|
|
193
|
+
index = int(line)
|
|
194
|
+
key = index_map.get(index)
|
|
195
|
+
if key is not None:
|
|
196
|
+
selected_keys.append(key)
|
|
197
|
+
except ValueError:
|
|
198
|
+
continue
|
|
199
|
+
if multi:
|
|
200
|
+
return selected_keys
|
|
201
|
+
return selected_keys[0] if selected_keys else None
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
demo_mapping: dict[str, str] = {
|
|
206
|
+
"Option 1": "# Option 1\nThis is the preview for option 1.",
|
|
207
|
+
"Option 2": "# Option 2\nThis is the preview for option 2.",
|
|
208
|
+
"Option 3": "# Option 3\nThis is the preview for option 3."
|
|
209
|
+
}
|
|
210
|
+
result = select_from_options(demo_mapping, multi=True, extension="md", preview_size_percent=50)
|
|
211
|
+
print(f"Selected: {result}")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import pathlib
|
|
3
|
+
import pprint
|
|
4
|
+
import shutil
|
|
5
|
+
from typing import Any, Literal, Union, overload
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _format_preview_value(value: Any) -> str:
|
|
9
|
+
if isinstance(value, str):
|
|
10
|
+
return value
|
|
11
|
+
return pprint.pformat(value, width=88, sort_dicts=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _normalize_extension(extension: str | None) -> str | None:
|
|
15
|
+
if extension is None:
|
|
16
|
+
return None
|
|
17
|
+
trimmed = extension.strip()
|
|
18
|
+
if trimmed.startswith("."):
|
|
19
|
+
trimmed = trimmed[1:]
|
|
20
|
+
if not trimmed:
|
|
21
|
+
return None
|
|
22
|
+
return trimmed
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@overload
|
|
26
|
+
def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[True], preview_size_percent: float) -> list[str]: ...
|
|
27
|
+
@overload
|
|
28
|
+
def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[False], preview_size_percent: float) -> str | None: ...
|
|
29
|
+
def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: bool, preview_size_percent: float) -> Union[list[str], Union[str, None]]:
|
|
30
|
+
keys = list(options_to_preview_mapping.keys())
|
|
31
|
+
if not keys:
|
|
32
|
+
return [] if multi else None
|
|
33
|
+
normalized_extension = _normalize_extension(extension)
|
|
34
|
+
preview_panel_size = max(10, min(90, int(preview_size_percent)))
|
|
35
|
+
from machineconfig.utils.accessories import randstr
|
|
36
|
+
tempdir = pathlib.Path.home() / "tmp_results" / "tmp_files" / f"tv_channel_{randstr(6)}"
|
|
37
|
+
tempdir.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
try:
|
|
39
|
+
index_map: dict[str, str] = {}
|
|
40
|
+
ext_for_preview = normalized_extension or "txt"
|
|
41
|
+
entries_lines: list[str] = []
|
|
42
|
+
for idx, key in enumerate(keys):
|
|
43
|
+
display_key = key.replace("\n", " ")
|
|
44
|
+
entries_lines.append(f"{idx}|{display_key}")
|
|
45
|
+
index_map[str(idx)] = key
|
|
46
|
+
preview_value = _format_preview_value(options_to_preview_mapping[key])
|
|
47
|
+
preview_file = tempdir / f"{idx}.{ext_for_preview}"
|
|
48
|
+
preview_file.write_text(preview_value, encoding="utf-8")
|
|
49
|
+
entries_path = tempdir / "entries.txt"
|
|
50
|
+
entries_path.write_text("\n".join(entries_lines), encoding="utf-8")
|
|
51
|
+
output_file = tempdir / "selection.txt"
|
|
52
|
+
tempdir_fwd = str(tempdir).replace("\\", "/")
|
|
53
|
+
source_cmd = f"cmd /C type \"{entries_path}\""
|
|
54
|
+
preview_cmd = f"bat --force-colorization --style=plain --paging=never {tempdir_fwd}/{{split:|:0}}.{ext_for_preview}"
|
|
55
|
+
tv_cmd = f'''$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
|
|
56
|
+
tv --ansi --source-command '{source_cmd}' --source-display '{{split:|:1}}' --source-output '{{split:|:0}}' --preview-command '{preview_cmd}' --preview-size {preview_panel_size} --no-remote | Out-File -Encoding utf8 -FilePath "{output_file}"
|
|
57
|
+
'''
|
|
58
|
+
from machineconfig.utils.code import run_shell_script
|
|
59
|
+
result = run_shell_script(tv_cmd, display_script=False, clean_env=False)
|
|
60
|
+
if result.returncode not in (0, 130) and not output_file.exists():
|
|
61
|
+
raise SystemExit(result.returncode)
|
|
62
|
+
if result.returncode == 130:
|
|
63
|
+
return [] if multi else None
|
|
64
|
+
if not output_file.exists():
|
|
65
|
+
return [] if multi else None
|
|
66
|
+
selected_lines = [line.strip() for line in output_file.read_text(encoding="utf-8-sig").splitlines() if line.strip()]
|
|
67
|
+
if not selected_lines:
|
|
68
|
+
return [] if multi else None
|
|
69
|
+
selected_keys: list[str] = []
|
|
70
|
+
for line in selected_lines:
|
|
71
|
+
key = index_map.get(line)
|
|
72
|
+
if key is not None:
|
|
73
|
+
selected_keys.append(key)
|
|
74
|
+
if multi:
|
|
75
|
+
return selected_keys
|
|
76
|
+
return selected_keys[0] if selected_keys else None
|
|
77
|
+
finally:
|
|
78
|
+
shutil.rmtree(tempdir, ignore_errors=True)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
demo_mapping: dict[str, str] = {
|
|
83
|
+
"Option 1": "# Option 1\nThis is the preview for option 1.",
|
|
84
|
+
"Option 2": "# Option 2\nThis is the preview for option 2.",
|
|
85
|
+
"Option 3": "# Option 3\nThis is the preview for option 3."
|
|
86
|
+
}
|
|
87
|
+
result = select_from_options(demo_mapping, multi=True, extension="md", preview_size_percent=50)
|
|
88
|
+
print(f"Selected: {result}")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Any, Literal, overload
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@overload
|
|
6
|
+
def choose_from_dict_with_preview(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[False], preview_size_percent: float) -> str | None: ...
|
|
7
|
+
@overload
|
|
8
|
+
def choose_from_dict_with_preview(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[True], preview_size_percent: float) -> list[str]: ...
|
|
9
|
+
def choose_from_dict_with_preview(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: bool, preview_size_percent: float) -> str | list[str] | None:
|
|
10
|
+
if not options_to_preview_mapping:
|
|
11
|
+
return [] if multi else None
|
|
12
|
+
system = platform.system()
|
|
13
|
+
if system == "Windows":
|
|
14
|
+
from machineconfig.utils.options_utils.options_tv_windows import select_from_options
|
|
15
|
+
return select_from_options(options_to_preview_mapping, extension=extension, multi=multi, preview_size_percent=preview_size_percent)
|
|
16
|
+
else:
|
|
17
|
+
from machineconfig.utils.options_utils.options_tv_linux import select_from_options
|
|
18
|
+
return select_from_options(options_to_preview_mapping, extension=extension, multi=multi, preview_size_percent=preview_size_percent)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
demo_mapping: dict[str, str] = {
|
|
23
|
+
"config.py": """from pathlib import Path
|
|
24
|
+
|
|
25
|
+
CONFIG_DIR = Path.home() / ".config"
|
|
26
|
+
DEBUG = True
|
|
27
|
+
""",
|
|
28
|
+
"utils.py": """def greet(name: str) -> str:
|
|
29
|
+
return f"Hello, {name}!"
|
|
30
|
+
""",
|
|
31
|
+
"main.rs": """fn main() {
|
|
32
|
+
println!("Hello, world!");
|
|
33
|
+
}
|
|
34
|
+
""",
|
|
35
|
+
}
|
|
36
|
+
result = choose_from_dict_with_preview(demo_mapping, extension="py", multi=True, preview_size_percent=50)
|
|
37
|
+
print(f"Selected: {result}")
|
|
@@ -103,7 +103,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
103
103
|
slf = self.expanduser().resolve()
|
|
104
104
|
if content:
|
|
105
105
|
assert self.is_dir(), NotADirectoryError(f"💥 When `content` flag is set to True, path must be a directory. It is not: `{repr(self)}`")
|
|
106
|
-
[x.move(folder=path.parent, content=False, overwrite=overwrite) for x in self.
|
|
106
|
+
[x.move(folder=path.parent, content=False, overwrite=overwrite) for x in self.glob("*")]
|
|
107
107
|
return path # contents live within this directory.
|
|
108
108
|
if overwrite:
|
|
109
109
|
tmp_path = slf.rename(path.parent.absolute() / randstr())
|
|
@@ -294,7 +294,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
294
294
|
target = "BROKEN LINK " + str(self) # avoid infinite recursions for broken links.
|
|
295
295
|
return "🔗 Symlink '" + str(self) + "' ==> " + (str(target) if target == self else str(target))
|
|
296
296
|
elif self.is_absolute():
|
|
297
|
-
return self._type() + " '" + str(self.clickable()) + "'" + (" | " + datetime.fromtimestamp(self.stat().st_ctime).isoformat()[:-7].replace("T", " ") if self.exists() else "") + (f" | {self.
|
|
297
|
+
return self._type() + " '" + str(self.clickable()) + "'" + (" | " + datetime.fromtimestamp(self.stat().st_ctime).isoformat()[:-7].replace("T", " ") if self.exists() else "") + (f" | {round(self.stat().st_size / (1024**2), 1)} Mb" if self.is_file() else "")
|
|
298
298
|
elif "http" in str(self):
|
|
299
299
|
return "🕸️ URL " + str(self.as_url_str())
|
|
300
300
|
else:
|
|
@@ -476,7 +476,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
476
476
|
verbose: bool = True,
|
|
477
477
|
content: bool = False,
|
|
478
478
|
orig: bool = False,
|
|
479
|
-
pwd: Optional[str] = None,
|
|
480
479
|
mode: FILE_MODE = "w",
|
|
481
480
|
**kwargs: Any,
|
|
482
481
|
) -> "PathExtended":
|
|
@@ -493,7 +492,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
493
492
|
path_resolved = PathExtended(op_zip)
|
|
494
493
|
else:
|
|
495
494
|
import shutil
|
|
496
|
-
|
|
497
495
|
if content:
|
|
498
496
|
root_dir, base_dir = slf, "."
|
|
499
497
|
else:
|
|
@@ -501,7 +499,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
501
499
|
base_name = str(path_resolved)[:-4] if str(path_resolved).endswith(".zip") else str(path_resolved)
|
|
502
500
|
op_zip = shutil.make_archive(base_name=base_name, format="zip", root_dir=str(root_dir), base_dir=str(base_dir), verbose=False, **kwargs)
|
|
503
501
|
path_resolved = PathExtended(op_zip)
|
|
504
|
-
msg = f"ZIPPED {repr(slf)} ==>
|
|
502
|
+
msg = f"ZIPPED {repr(slf)} ==> {repr(path_resolved)}"
|
|
505
503
|
res_out = PathExtended(path_resolved)
|
|
506
504
|
ret = self if orig else res_out
|
|
507
505
|
delayed_msg = ""
|
|
@@ -538,7 +536,9 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
538
536
|
assert merge is False, "I have not implemented this yet"
|
|
539
537
|
assert path is None, "I have not implemented this yet"
|
|
540
538
|
if tmp:
|
|
541
|
-
|
|
539
|
+
tmp_root = PathExtended("~/tmp_results").expanduser()
|
|
540
|
+
tmp_root.mkdir(parents=True, exist_ok=True)
|
|
541
|
+
return self.unzip(folder=tmp_root.joinpath("tmp_unzips").joinpath(randstr()), content=True).joinpath(self.stem)
|
|
542
542
|
slf = zipfile__ = self.expanduser().resolve()
|
|
543
543
|
if any(ztype in str(slf.parent) for ztype in (".zip", ".7z")): # path include a zip archive in the middle.
|
|
544
544
|
tmp__ = [item for item in (".zip", ".7z", "") if item in str(slf)]
|
machineconfig/utils/scheduler.py
CHANGED
|
@@ -166,7 +166,10 @@ class CacheMemory[T]():
|
|
|
166
166
|
else:
|
|
167
167
|
age = datetime.now() - self.time_produced
|
|
168
168
|
if (age > self.expire) or (fresh and (age.total_seconds() > tolerance_seconds)):
|
|
169
|
-
|
|
169
|
+
if not fresh:
|
|
170
|
+
self.logger.warning(f"""🔄 CACHE STALE 📦 {self.name} cache: Populating fresh cache from source func. """ + f"""⏱️ Age = {age} > Expiry {self.expire} """ )
|
|
171
|
+
else:
|
|
172
|
+
self.logger.warning(f"""⚠️ Fresh flag raised, age = {age} > {tolerance_seconds} seconds of tolerance. Updating {self.name} cache from source func.""")
|
|
170
173
|
t0 = time.time()
|
|
171
174
|
self.cache = self.source_func()
|
|
172
175
|
self.logger.warning(f"⏱️ Cache population took {time.time() - t0:.2f} seconds.")
|
|
@@ -202,7 +205,10 @@ class Cache[T](): # This class helps to accelrate access to latest data coming
|
|
|
202
205
|
if self.path.exists(): # prefer to read from disk over source func as a default source of cache.
|
|
203
206
|
age = datetime.now() - datetime.fromtimestamp(self.path.stat().st_mtime)
|
|
204
207
|
if (age > self.expire) or (fresh and (age.total_seconds() > tolerance_seconds)): # cache is old or if fresh flag is raised
|
|
205
|
-
|
|
208
|
+
if not fresh:
|
|
209
|
+
self.logger.warning(f"""🔄 CACHE STALE 📦 {self.name} cache: Populating fresh cache from source func. """ + f"""⏱️ Age = {age} > Expiry {self.expire} """ )
|
|
210
|
+
else:
|
|
211
|
+
self.logger.warning(f"""⚠️ Fresh flag raised, age = {age} > {tolerance_seconds} seconds of tolerance. Updating {self.name} cache from source func.""")
|
|
206
212
|
t0 = time.time()
|
|
207
213
|
self.cache = self.source_func() # fresh data.
|
|
208
214
|
self.logger.warning(f"⏱️ Cache population took {time.time() - t0:.2f} seconds.")
|