machineconfig 8.14__py3-none-any.whl → 8.45__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 +40 -51
- 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 +265 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_self.py +39 -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 +116 -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 +177 -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 +46 -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 +34 -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 +1 -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/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/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.45.dist-info}/METADATA +29 -22
- {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/RECORD +247 -208
- 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.45.dist-info}/WHEEL +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/entry_points.txt +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.45.dist-info}/top_level.txt +0 -0
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.")
|
|
@@ -6,7 +6,7 @@ capturing all user inputs collected during interactive execution.
|
|
|
6
6
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import TypedDict, Literal, NotRequired
|
|
9
|
-
from machineconfig.scripts.python.helpers_agents.fire_agents_help_launch import AGENTS
|
|
9
|
+
from machineconfig.scripts.python.helpers.helpers_agents.fire_agents_help_launch import AGENTS
|
|
10
10
|
|
|
11
11
|
SEARCH_STRATEGIES = Literal["file_path", "keyword_search", "filename_pattern"]
|
|
12
12
|
|
|
@@ -5,7 +5,12 @@ Utils
|
|
|
5
5
|
import machineconfig
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
EXCLUDE_DIRS = [".links", "
|
|
8
|
+
EXCLUDE_DIRS = [".links", "notebooks",
|
|
9
|
+
"GEMINI.md", "CLAUDE.md", "CRUSH.md", "AGENTS.md",
|
|
10
|
+
".cursor", ".clinerules", ".github/instructions", ".github/agents", ".github/prompts",
|
|
11
|
+
".ai",
|
|
12
|
+
".venv", ".git", ".idea", ".vscode", "node_modules", "__pycache__", ".mypy_cache"
|
|
13
|
+
]
|
|
9
14
|
LIBRARY_ROOT = Path(machineconfig.__file__).resolve().parent
|
|
10
15
|
|
|
11
16
|
CONFIG_ROOT = Path.home().joinpath(".config/machineconfig")
|