machineconfig 2.1__py3-none-any.whl → 2.2__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/sessions_managers/archive/create_zellij_template.py +2 -1
- machineconfig/cluster/templates/utils.py +0 -35
- machineconfig/jobs/python/check_installations.py +1 -1
- machineconfig/jobs/python_custom_installers/dev/code.py +0 -13
- machineconfig/jobs/python_generic_installers/config.json +1 -1
- machineconfig/profile/create.py +10 -5
- machineconfig/profile/create_hardlinks.py +3 -1
- machineconfig/profile/shell.py +8 -7
- machineconfig/scripts/__init__.py +0 -2
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/devops +6 -4
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/generate_files.py +14 -15
- machineconfig/scripts/python/ai/mcinit.py +8 -5
- machineconfig/scripts/python/archive/tmate_conn.py +5 -5
- machineconfig/scripts/python/archive/tmate_start.py +7 -7
- machineconfig/scripts/python/choose_wezterm_theme.py +35 -32
- machineconfig/scripts/python/cloud_copy.py +22 -13
- machineconfig/scripts/python/cloud_mount.py +35 -23
- machineconfig/scripts/python/cloud_repo_sync.py +38 -25
- machineconfig/scripts/python/cloud_sync.py +4 -4
- machineconfig/scripts/python/croshell.py +37 -28
- machineconfig/scripts/python/devops.py +45 -27
- machineconfig/scripts/python/devops_add_identity.py +15 -25
- machineconfig/scripts/python/devops_add_ssh_key.py +7 -7
- machineconfig/scripts/python/devops_backup_retrieve.py +17 -15
- machineconfig/scripts/python/devops_devapps_install.py +25 -20
- machineconfig/scripts/python/devops_update_repos.py +142 -57
- machineconfig/scripts/python/dotfile.py +16 -14
- machineconfig/scripts/python/fire_agents.py +24 -17
- machineconfig/scripts/python/fire_jobs.py +91 -55
- machineconfig/scripts/python/ftpx.py +24 -14
- machineconfig/scripts/python/get_zellij_cmd.py +8 -7
- machineconfig/scripts/python/helpers/cloud_helpers.py +33 -28
- machineconfig/scripts/python/helpers/helpers2.py +25 -14
- machineconfig/scripts/python/helpers/helpers4.py +44 -30
- machineconfig/scripts/python/helpers/helpers5.py +1 -1
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +31 -9
- machineconfig/scripts/python/mount_nfs.py +8 -15
- machineconfig/scripts/python/mount_nw_drive.py +10 -5
- machineconfig/scripts/python/mount_ssh.py +8 -6
- machineconfig/scripts/python/repos.py +215 -57
- machineconfig/scripts/python/snapshot.py +0 -1
- machineconfig/scripts/python/start_slidev.py +10 -5
- machineconfig/scripts/python/start_terminals.py +22 -16
- machineconfig/scripts/python/viewer_template.py +0 -1
- machineconfig/scripts/python/wifi_conn.py +49 -75
- machineconfig/scripts/python/wsl_windows_transfer.py +8 -6
- machineconfig/settings/lf/linux/lfrc +1 -0
- machineconfig/setup_linux/web_shortcuts/croshell.sh +5 -0
- machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
- machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -4
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +3 -12
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +1 -1
- machineconfig/utils/code.py +3 -3
- machineconfig/utils/installer.py +2 -2
- machineconfig/utils/installer_utils/installer_abc.py +3 -4
- machineconfig/utils/installer_utils/installer_class.py +6 -4
- machineconfig/utils/links.py +103 -33
- machineconfig/utils/notifications.py +52 -38
- machineconfig/utils/options.py +16 -23
- machineconfig/utils/path_reduced.py +239 -205
- machineconfig/utils/procs.py +1 -1
- machineconfig/utils/source_of_truth.py +27 -0
- machineconfig/utils/ssh.py +9 -29
- machineconfig/utils/terminal.py +4 -2
- machineconfig/utils/upgrade_packages.py +91 -0
- machineconfig/utils/utils2.py +1 -2
- machineconfig/utils/utils5.py +23 -11
- machineconfig/utils/ve.py +4 -1
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/METADATA +13 -13
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/RECORD +78 -86
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
- machineconfig/setup_linux/web_shortcuts/all.sh +0 -48
- machineconfig/setup_linux/web_shortcuts/update_system.sh +0 -48
- machineconfig/utils/utils.py +0 -97
- /machineconfig/setup_linux/web_shortcuts/{tmp.sh → android.sh} +0 -0
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/WHEEL +0 -0
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
"""Utility to launch multiple AI agent prompts in a Zellij session.
|
|
3
2
|
|
|
4
3
|
Improved design notes:
|
|
@@ -20,15 +19,17 @@ from machineconfig.utils.utils2 import randstr
|
|
|
20
19
|
import random
|
|
21
20
|
# import time
|
|
22
21
|
|
|
23
|
-
AGENTS: TypeAlias = Literal[
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
AGENTS: TypeAlias = Literal[
|
|
23
|
+
"cursor-agent", "gemini", "crush", "q", "onlyPrepPromptFiles"
|
|
24
|
+
# warp terminal
|
|
25
|
+
]
|
|
26
26
|
TabConfig = dict[str, tuple[str, str]] # tab name -> (cwd, command)
|
|
27
27
|
DEFAULT_AGENT_CAP = 6
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def get_gemini_api_keys() -> list[str]:
|
|
31
31
|
from machineconfig.utils.utils2 import read_ini
|
|
32
|
+
|
|
32
33
|
config = read_ini(Path.home().joinpath("dotfiles/creds/llm/gemini/api_keys.ini"))
|
|
33
34
|
res: list[str] = []
|
|
34
35
|
for a_section_name in list(config.sections()):
|
|
@@ -45,12 +46,17 @@ def get_gemini_api_keys() -> list[str]:
|
|
|
45
46
|
def _search_python_files(repo_root: Path, keyword: str) -> list[Path]:
|
|
46
47
|
"""Return all Python files under repo_root whose text contains keyword.
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
Notes:
|
|
50
|
+
- Skips any paths that reside under a directory named ".venv" at any depth.
|
|
51
|
+
- Errors reading individual files are ignored (decoded with 'ignore').
|
|
49
52
|
"""
|
|
50
53
|
py_files = list(repo_root.rglob("*.py"))
|
|
51
54
|
keyword_lower = keyword.lower()
|
|
52
55
|
matches: list[Path] = []
|
|
53
56
|
for f in py_files:
|
|
57
|
+
# Skip anything under a .venv directory anywhere in the path
|
|
58
|
+
if any(part == ".venv" for part in f.parts):
|
|
59
|
+
continue
|
|
54
60
|
try:
|
|
55
61
|
if keyword_lower in f.read_text(encoding="utf-8", errors="ignore").lower():
|
|
56
62
|
matches.append(f)
|
|
@@ -63,6 +69,8 @@ def _search_python_files(repo_root: Path, keyword: str) -> list[Path]:
|
|
|
63
69
|
def _write_list_file(target: Path, files: Iterable[Path]) -> None:
|
|
64
70
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
65
71
|
target.write_text("\n".join(str(f) for f in files), encoding="utf-8")
|
|
72
|
+
|
|
73
|
+
|
|
66
74
|
def _chunk_prompts(prompts: list[str], max_agents: int) -> list[str]:
|
|
67
75
|
prompts = [p for p in prompts if p.strip() != ""] # drop blank entries
|
|
68
76
|
if len(prompts) <= max_agents:
|
|
@@ -73,6 +81,8 @@ def _chunk_prompts(prompts: list[str], max_agents: int) -> list[str]:
|
|
|
73
81
|
for i in range(0, len(prompts), chunk_size):
|
|
74
82
|
grouped.append("\nTargeted Locations:\n".join(prompts[i : i + chunk_size]))
|
|
75
83
|
return grouped
|
|
84
|
+
|
|
85
|
+
|
|
76
86
|
def _confirm(message: str, default_no: bool = True) -> bool:
|
|
77
87
|
suffix = "[y/N]" if default_no else "[Y/n]"
|
|
78
88
|
answer = input(f"{message} {suffix} ").strip().lower()
|
|
@@ -93,11 +103,7 @@ def launch_agents(repo_root: Path, prompts: list[str], agent: AGENTS, *, max_age
|
|
|
93
103
|
raise ValueError("No prompts provided")
|
|
94
104
|
|
|
95
105
|
if len(prompts) > max_agents:
|
|
96
|
-
proceed = _confirm(
|
|
97
|
-
message=(
|
|
98
|
-
f"You are about to launch {len(prompts)} agents which exceeds the cap ({max_agents}). Proceed?"
|
|
99
|
-
)
|
|
100
|
-
)
|
|
106
|
+
proceed = _confirm(message=(f"You are about to launch {len(prompts)} agents which exceeds the cap ({max_agents}). Proceed?"))
|
|
101
107
|
if not proceed:
|
|
102
108
|
print("Aborting per user choice.")
|
|
103
109
|
return {}
|
|
@@ -145,6 +151,10 @@ crush run {prompt_path}
|
|
|
145
151
|
case "q":
|
|
146
152
|
cmd = f"""
|
|
147
153
|
q chat --no-interactive --trust-all-tools {prompt_path}
|
|
154
|
+
"""
|
|
155
|
+
case "onlyPrepPromptFiles":
|
|
156
|
+
cmd = f"""
|
|
157
|
+
echo "Prepared prompt file at {prompt_path}"
|
|
148
158
|
"""
|
|
149
159
|
case _:
|
|
150
160
|
raise ValueError(f"Unsupported agent type: {agent}")
|
|
@@ -161,7 +171,7 @@ sleep 0.1
|
|
|
161
171
|
sleep 0.1
|
|
162
172
|
echo "---------END OF AGENT OUTPUT---------"
|
|
163
173
|
"""
|
|
164
|
-
cmd_path.write_text(cmd_prefix+cmd+cmd_postfix, encoding="utf-8")
|
|
174
|
+
cmd_path.write_text(cmd_prefix + cmd + cmd_postfix, encoding="utf-8")
|
|
165
175
|
fire_cmd = f"bash {shlex.quote(str(cmd_path))}"
|
|
166
176
|
tab_config[f"Agent{idx}"] = (str(repo_root), fire_cmd)
|
|
167
177
|
|
|
@@ -208,18 +218,15 @@ def main(): # noqa: C901 - (complexity acceptable for CLI glue)
|
|
|
208
218
|
combined_prompts = [prefix + "\n" + p for p in combined_prompts]
|
|
209
219
|
|
|
210
220
|
from machineconfig.utils.options import choose_one_option
|
|
221
|
+
|
|
211
222
|
agent_selected = choose_one_option(header="Select agent type", options=get_args(AGENTS))
|
|
212
223
|
|
|
213
|
-
tab_config = launch_agents(
|
|
214
|
-
repo_root=repo_root,
|
|
215
|
-
prompts=combined_prompts,
|
|
216
|
-
agent=agent_selected,
|
|
217
|
-
max_agents=DEFAULT_AGENT_CAP,
|
|
218
|
-
)
|
|
224
|
+
tab_config = launch_agents(repo_root=repo_root, prompts=combined_prompts, agent=agent_selected, max_agents=DEFAULT_AGENT_CAP)
|
|
219
225
|
if not tab_config:
|
|
220
226
|
return
|
|
221
227
|
|
|
222
228
|
from machineconfig.utils.utils2 import randstr
|
|
229
|
+
|
|
223
230
|
random_name = randstr(length=3)
|
|
224
231
|
manager = ZellijLocalManager(session2zellij_tabs={"Agents": tab_config}, session_name_prefix=random_name)
|
|
225
232
|
manager.start_all_sessions()
|
|
@@ -7,13 +7,14 @@ fire
|
|
|
7
7
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
from machineconfig.scripts.python.helpers.helpers4 import search_for_files_of_interest
|
|
12
11
|
from machineconfig.scripts.python.helpers.helpers4 import convert_kwargs_to_fire_kwargs_str
|
|
13
12
|
from machineconfig.scripts.python.helpers.helpers4 import parse_pyfile
|
|
14
13
|
from machineconfig.scripts.python.helpers.helpers4 import get_import_module_code
|
|
15
14
|
from machineconfig.utils.ve import get_repo_root, get_ve_activate_line, get_ve_path_and_ipython_profile
|
|
16
|
-
from machineconfig.utils.
|
|
15
|
+
from machineconfig.utils.options import display_options, choose_one_option
|
|
16
|
+
from machineconfig.utils.path import match_file_name, sanitize_path
|
|
17
|
+
from machineconfig.utils.source_of_truth import PROGRAM_PATH
|
|
17
18
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
18
19
|
from machineconfig.utils.io_save import save_toml
|
|
19
20
|
from machineconfig.utils.utils2 import randstr, read_toml
|
|
@@ -28,27 +29,27 @@ str2obj = {"True": True, "False": False, "None": None}
|
|
|
28
29
|
|
|
29
30
|
def main() -> None:
|
|
30
31
|
parser = argparse.ArgumentParser()
|
|
31
|
-
parser.add_argument("path",
|
|
32
|
-
parser.add_argument("function", nargs=
|
|
33
|
-
parser.add_argument("--ve",
|
|
34
|
-
parser.add_argument("--cmd",
|
|
35
|
-
parser.add_argument("--interactive",
|
|
36
|
-
parser.add_argument("--debug",
|
|
32
|
+
parser.add_argument("path", nargs="?", type=str, help="The directory containing the jobs", default=".")
|
|
33
|
+
parser.add_argument("function", nargs="?", type=str, help="Fuction to run", default=None)
|
|
34
|
+
parser.add_argument("--ve", "-v", type=str, help="virtual enviroment name", default="")
|
|
35
|
+
parser.add_argument("--cmd", "-B", action="store_true", help="Create a cmd fire command to launch the the job asynchronously.")
|
|
36
|
+
parser.add_argument("--interactive", "-i", action="store_true", help="Whether to run the job interactively using IPython")
|
|
37
|
+
parser.add_argument("--debug", "-d", action="store_true", help="debug")
|
|
37
38
|
parser.add_argument("--choose_function", "-c", action="store_true", help="debug")
|
|
38
|
-
parser.add_argument("--loop",
|
|
39
|
-
parser.add_argument("--jupyter",
|
|
39
|
+
parser.add_argument("--loop", "-l", action="store_true", help="infinite recusion (runs again after completion/interruption)")
|
|
40
|
+
parser.add_argument("--jupyter", "-j", action="store_true", help="open in a jupyter notebook")
|
|
40
41
|
parser.add_argument("--submit_to_cloud", "-C", action="store_true", help="submit to cloud compute")
|
|
41
|
-
parser.add_argument("--remote",
|
|
42
|
-
parser.add_argument("--module",
|
|
43
|
-
parser.add_argument("--streamlit",
|
|
44
|
-
parser.add_argument("--environment",
|
|
45
|
-
parser.add_argument("--holdDirectory",
|
|
46
|
-
parser.add_argument("--PathExport",
|
|
47
|
-
parser.add_argument("--git_pull",
|
|
48
|
-
parser.add_argument("--optimized",
|
|
49
|
-
parser.add_argument("--Nprocess",
|
|
50
|
-
parser.add_argument("--zellij_tab",
|
|
51
|
-
parser.add_argument("--watch",
|
|
42
|
+
parser.add_argument("--remote", "-r", action="store_true", help="launch on a remote machine")
|
|
43
|
+
parser.add_argument("--module", "-m", action="store_true", help="launch the main file")
|
|
44
|
+
parser.add_argument("--streamlit", "-S", action="store_true", help="run as streamlit app")
|
|
45
|
+
parser.add_argument("--environment", "-E", type=str, help="Choose ip, localhost, hostname or arbitrary url", default="")
|
|
46
|
+
parser.add_argument("--holdDirectory", "-D", action="store_true", help="hold current directory and avoid cd'ing to the script directory")
|
|
47
|
+
parser.add_argument("--PathExport", "-P", action="store_true", help="augment the PYTHONPATH with repo root.")
|
|
48
|
+
parser.add_argument("--git_pull", "-g", action="store_true", help="Start by pulling the git repo")
|
|
49
|
+
parser.add_argument("--optimized", "-O", action="store_true", help="Run the optimized version of the function")
|
|
50
|
+
parser.add_argument("--Nprocess", "-p", type=int, help="Number of processes to use", default=1)
|
|
51
|
+
parser.add_argument("--zellij_tab", "-z", type=str, dest="zellij_tab", help="open in a new zellij tab")
|
|
52
|
+
parser.add_argument("--watch", "-w", action="store_true", help="watch the file for changes")
|
|
52
53
|
parser.add_argument("--kw", nargs="*", default=None, help="keyword arguments to pass to the function in the form of k1 v1 k2 v2 ... (meaning k1=v1, k2=v2, etc)")
|
|
53
54
|
try:
|
|
54
55
|
args = parser.parse_args()
|
|
@@ -60,7 +61,8 @@ def main() -> None:
|
|
|
60
61
|
# print(f"Passed path sanitied to {path_obj}")
|
|
61
62
|
if not path_obj.exists():
|
|
62
63
|
path_obj = match_file_name(sub_string=args.path, search_root=PathExtended.cwd())
|
|
63
|
-
else:
|
|
64
|
+
else:
|
|
65
|
+
pass
|
|
64
66
|
if path_obj.is_dir():
|
|
65
67
|
print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
|
|
66
68
|
files = search_for_files_of_interest(path_obj)
|
|
@@ -73,8 +75,9 @@ def main() -> None:
|
|
|
73
75
|
print(f"💾 Selected file: {choice_file}.\nRepo root: {repo_root}")
|
|
74
76
|
|
|
75
77
|
ve_root_from_file, ipy_profile = get_ve_path_and_ipython_profile(choice_file)
|
|
76
|
-
if ipy_profile is None:
|
|
77
|
-
|
|
78
|
+
if ipy_profile is None:
|
|
79
|
+
ipy_profile = "default"
|
|
80
|
+
activate_ve_line = get_ve_activate_line(ve_root=args.ve or ve_root_from_file or "$HOME/code/machineconfig/.venv")
|
|
78
81
|
|
|
79
82
|
# Convert args.kw to dictionary
|
|
80
83
|
if choice_file.suffix == ".py":
|
|
@@ -99,19 +102,23 @@ def main() -> None:
|
|
|
99
102
|
choice_function_tmp = display_options(msg="Choose a function to run", options=options, fzf=True, multi=False)
|
|
100
103
|
assert isinstance(choice_function_tmp, str), f"choice_function must be a string. Got {type(choice_function_tmp)}"
|
|
101
104
|
choice_index = options.index(choice_function_tmp)
|
|
102
|
-
choice_function = choice_function_tmp.split(
|
|
105
|
+
choice_function = choice_function_tmp.split(" -- ")[0]
|
|
103
106
|
choice_function_args = func_args[choice_index]
|
|
104
107
|
|
|
105
|
-
if choice_function == "RUN AS MAIN":
|
|
108
|
+
if choice_function == "RUN AS MAIN":
|
|
109
|
+
choice_function = None
|
|
106
110
|
if len(choice_function_args) > 0 and len(kwargs) == 0:
|
|
107
111
|
for item in choice_function_args:
|
|
108
112
|
kwargs[item.name] = input(f"Please enter a value for argument `{item.name}` (type = {item.type}) (default = {item.default}) : ") or item.default
|
|
109
113
|
elif choice_file.suffix == ".sh": # in this case, we choos lines.
|
|
110
114
|
options = []
|
|
111
115
|
for line in choice_file.read_text(encoding="utf-8").splitlines():
|
|
112
|
-
if line.startswith("#"):
|
|
113
|
-
|
|
114
|
-
if line
|
|
116
|
+
if line.startswith("#"):
|
|
117
|
+
continue
|
|
118
|
+
if line == "":
|
|
119
|
+
continue
|
|
120
|
+
if line.startswith("echo"):
|
|
121
|
+
continue
|
|
115
122
|
options.append(line)
|
|
116
123
|
chosen_lines = display_options(msg="Choose a line to run", options=options, fzf=True, multi=True)
|
|
117
124
|
choice_file = PathExtended.tmpfile(suffix=".sh")
|
|
@@ -124,9 +131,10 @@ def main() -> None:
|
|
|
124
131
|
if choice_file.suffix == ".py":
|
|
125
132
|
if args.streamlit:
|
|
126
133
|
import socket
|
|
134
|
+
|
|
127
135
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
128
136
|
try:
|
|
129
|
-
s.connect((
|
|
137
|
+
s.connect(("8.8.8.8", 1))
|
|
130
138
|
local_ip_v4 = s.getsockname()[0]
|
|
131
139
|
except Exception:
|
|
132
140
|
local_ip_v4 = socket.gethostbyname(socket.gethostname())
|
|
@@ -140,7 +148,8 @@ def main() -> None:
|
|
|
140
148
|
toml_path = toml_path_maybe
|
|
141
149
|
elif choice_file.parent.name == "pages":
|
|
142
150
|
toml_path_maybe = choice_file.parent.parent.joinpath(".streamlit/config.toml")
|
|
143
|
-
if toml_path_maybe.exists():
|
|
151
|
+
if toml_path_maybe.exists():
|
|
152
|
+
toml_path = toml_path_maybe
|
|
144
153
|
if toml_path is not None:
|
|
145
154
|
print(f"📄 Reading config.toml @ {toml_path}")
|
|
146
155
|
config = read_toml(toml_path)
|
|
@@ -152,10 +161,14 @@ def main() -> None:
|
|
|
152
161
|
secrets_template_path = PathExtended.home().joinpath(f"dotfiles/creds/streamlit/{PathExtended(repo_root).name}/{choice_file.name}/secrets.toml")
|
|
153
162
|
if args.environment != "" and not secrets_path.exists() and secrets_template_path.exists():
|
|
154
163
|
secrets_template = read_toml(secrets_template_path)
|
|
155
|
-
if args.environment == "ip":
|
|
156
|
-
|
|
157
|
-
elif args.environment == "
|
|
158
|
-
|
|
164
|
+
if args.environment == "ip":
|
|
165
|
+
host_url = f"http://{local_ip_v4}:{port}/oauth2callback"
|
|
166
|
+
elif args.environment == "localhost":
|
|
167
|
+
host_url = f"http://localhost:{port}/oauth2callback"
|
|
168
|
+
elif args.environment == "hostname":
|
|
169
|
+
host_url = f"http://{computer_name}:{port}/oauth2callback"
|
|
170
|
+
else:
|
|
171
|
+
host_url = f"http://{args.environment}:{port}/oauth2callback"
|
|
159
172
|
try:
|
|
160
173
|
secrets_template["auth"]["redirect_uri"] = host_url
|
|
161
174
|
secrets_template["auth"]["cookie_secret"] = randstr(35)
|
|
@@ -167,11 +180,14 @@ def main() -> None:
|
|
|
167
180
|
message = f"🚀 Streamlit app is running @:\n1- http://{local_ip_v4}:{port}\n2- http://{computer_name}:{port}\n3- http://localhost:{port}"
|
|
168
181
|
from rich.panel import Panel
|
|
169
182
|
from rich import print as rprint
|
|
183
|
+
|
|
170
184
|
rprint(Panel(message))
|
|
171
185
|
exe = f"streamlit run --server.address 0.0.0.0 --server.headless true --server.port {port}"
|
|
172
186
|
# exe = f"cd '{choice_file.parent}'; " + exe
|
|
173
|
-
elif args.interactive is False:
|
|
174
|
-
|
|
187
|
+
elif args.interactive is False:
|
|
188
|
+
exe = "python"
|
|
189
|
+
elif args.jupyter:
|
|
190
|
+
exe = "jupyter-lab"
|
|
175
191
|
else:
|
|
176
192
|
exe = f"ipython -i --no-banner --profile {ipy_profile} "
|
|
177
193
|
elif choice_file.suffix == ".ps1" or choice_file.suffix == ".sh":
|
|
@@ -181,7 +197,9 @@ def main() -> None:
|
|
|
181
197
|
else:
|
|
182
198
|
raise NotImplementedError(f"File type {choice_file.suffix} not supported, in the sense that I don't know how to fire it.")
|
|
183
199
|
|
|
184
|
-
if
|
|
200
|
+
if (
|
|
201
|
+
args.module or (args.debug and args.choose_function)
|
|
202
|
+
): # because debugging tools do not support choosing functions and don't interplay with fire module. So the only way to have debugging and choose function options is to import the file as a module into a new script and run the function of interest there and debug the new script.
|
|
185
203
|
assert choice_file.suffix == ".py", f"File must be a python file to be imported as a module. Got {choice_file}"
|
|
186
204
|
import_line = get_import_module_code(str(choice_file))
|
|
187
205
|
if repo_root is not None:
|
|
@@ -201,11 +219,15 @@ except (ImportError, ModuleNotFoundError) as ex:
|
|
|
201
219
|
print(fr"✅ Successfully imported `{choice_file}`")
|
|
202
220
|
"""
|
|
203
221
|
if choice_function is not None:
|
|
204
|
-
txt =
|
|
205
|
-
|
|
222
|
+
txt = (
|
|
223
|
+
txt
|
|
224
|
+
+ f"""
|
|
225
|
+
res = {choice_function}({("**" + str(kwargs)) if kwargs else ""})
|
|
206
226
|
"""
|
|
227
|
+
)
|
|
207
228
|
|
|
208
|
-
txt =
|
|
229
|
+
txt = (
|
|
230
|
+
f"""
|
|
209
231
|
try:
|
|
210
232
|
from rich.panel import Panel
|
|
211
233
|
from rich.console import Console
|
|
@@ -214,8 +236,10 @@ try:
|
|
|
214
236
|
console.print(Panel(Syntax(code=r'''{txt}''', lexer='python'), title='Import Script'), style="bold red")
|
|
215
237
|
except ImportError as _ex:
|
|
216
238
|
print(r'''{txt}''')
|
|
217
|
-
"""
|
|
218
|
-
|
|
239
|
+
"""
|
|
240
|
+
+ txt
|
|
241
|
+
)
|
|
242
|
+
choice_file = PathExtended.tmp().joinpath(f"tmp_scripts/python/{PathExtended(choice_file).parent.name}_{PathExtended(choice_file).stem}_{randstr()}.py")
|
|
219
243
|
choice_file.parent.mkdir(parents=True, exist_ok=True)
|
|
220
244
|
choice_file.write_text(txt, encoding="utf-8")
|
|
221
245
|
|
|
@@ -225,7 +249,8 @@ except ImportError as _ex:
|
|
|
225
249
|
command = f"{exe} -m ipdb {choice_file} " # pudb is not available on windows machines, use poor man's debugger instead.
|
|
226
250
|
elif platform.system() in ["Linux", "Darwin"]:
|
|
227
251
|
command = f"{exe} -m pudb {choice_file} " # TODO: functions not supported yet in debug mode.
|
|
228
|
-
else:
|
|
252
|
+
else:
|
|
253
|
+
raise NotImplementedError(f"Platform {platform.system()} not supported.")
|
|
229
254
|
elif args.module:
|
|
230
255
|
# both selected function and kwargs are mentioned in the made up script, therefore no need for fire module.
|
|
231
256
|
command = f"{exe} {choice_file} "
|
|
@@ -249,15 +274,19 @@ except ImportError as _ex:
|
|
|
249
274
|
# command = f"cd {choice_file.parent}\n{exe} {choice_file.name}\ncd {PathExtended.cwd()}"
|
|
250
275
|
command = f"{exe} {choice_file} "
|
|
251
276
|
if not args.cmd:
|
|
252
|
-
if "ipdb" in command:
|
|
253
|
-
|
|
277
|
+
if "ipdb" in command:
|
|
278
|
+
command = f"pip install ipdb\n{command}"
|
|
279
|
+
if "pudb" in command:
|
|
280
|
+
command = f"pip install pudb\n{command}"
|
|
254
281
|
command = f"{activate_ve_line}\n{command}"
|
|
255
282
|
else:
|
|
256
283
|
# CMD equivalent
|
|
257
|
-
if "ipdb" in command:
|
|
258
|
-
|
|
284
|
+
if "ipdb" in command:
|
|
285
|
+
command = f"pip install ipdb & {command}"
|
|
286
|
+
if "pudb" in command:
|
|
287
|
+
command = f"pip install pudb & {command}"
|
|
259
288
|
new_line = "\n"
|
|
260
|
-
command =
|
|
289
|
+
command = rf"""start cmd -Argument "/k {activate_ve_line.replace(".ps1", ".bat").replace(". ", "")} & {command.replace(new_line, " & ")} " """ # this works from powershell
|
|
261
290
|
|
|
262
291
|
if args.submit_to_cloud:
|
|
263
292
|
command = f"""
|
|
@@ -281,6 +310,7 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
|
|
|
281
310
|
for an_arg in range(args.Nprocess):
|
|
282
311
|
tab_config[f"tab{an_arg}"] = (str(PathExtended.cwd()), f"uv run -m fire {choice_file} {choice_function} --idx={an_arg} --idx_max={args.Nprocess}")
|
|
283
312
|
from machineconfig.cluster.sessions_managers.zellij_local import run_zellij_layout
|
|
313
|
+
|
|
284
314
|
run_zellij_layout(tab_config=tab_config, session_name=None)
|
|
285
315
|
return None
|
|
286
316
|
|
|
@@ -293,6 +323,7 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
|
|
|
293
323
|
from rich.panel import Panel
|
|
294
324
|
from rich.console import Console
|
|
295
325
|
from rich.syntax import Syntax
|
|
326
|
+
|
|
296
327
|
console = Console()
|
|
297
328
|
|
|
298
329
|
if args.zellij_tab is not None:
|
|
@@ -301,17 +332,22 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
|
|
|
301
332
|
comman_path__.write_text(command, encoding="utf-8")
|
|
302
333
|
console.print(Panel(Syntax(command, lexer="shell"), title=f"🔥 fire command @ {comman_path__}: "), style="bold red")
|
|
303
334
|
import subprocess
|
|
335
|
+
|
|
304
336
|
existing_tab_names = subprocess.run(["zellij", "action", "query-tab-names"], capture_output=True, text=True, check=True).stdout.splitlines()
|
|
305
337
|
if args.zellij_tab in existing_tab_names:
|
|
306
338
|
print(f"⚠️ Tab name `{args.zellij_tab}` already exists. Please choose a different name.")
|
|
307
339
|
# args.zellij_tab = input("Please enter a new tab name: ")
|
|
308
340
|
args.zellij_tab += f"_{randstr(3)}"
|
|
309
341
|
from machineconfig.cluster.sessions_managers.zellij_local import run_command_in_zellij_tab
|
|
342
|
+
|
|
310
343
|
command = run_command_in_zellij_tab(command=str(comman_path__), tab_name=args.zellij_tab, cwd=None)
|
|
311
|
-
if args.watch:
|
|
312
|
-
|
|
344
|
+
if args.watch:
|
|
345
|
+
command = "watchexec --restart --exts py,sh,ps1 " + command
|
|
346
|
+
if args.git_pull:
|
|
347
|
+
command = f"\ngit -C {choice_file.parent} pull\n" + command
|
|
313
348
|
if args.PathExport:
|
|
314
|
-
if platform.system() in ["Linux", "Darwin"]:
|
|
349
|
+
if platform.system() in ["Linux", "Darwin"]:
|
|
350
|
+
export_line = f"""export PYTHONPATH="{repo_root}""" + """:${PYTHONPATH}" """
|
|
315
351
|
elif platform.system() == "Windows":
|
|
316
352
|
# export_line = f"""set PYTHONPATH="{repo_root}""" + """:%PYTHONPATH%" """
|
|
317
353
|
# powershell equivalent
|
|
@@ -327,8 +363,8 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
|
|
|
327
363
|
command = command + "\nsleep 0.5"
|
|
328
364
|
elif platform.system() == "Windows":
|
|
329
365
|
# command = command + "timeout 0.5\n"
|
|
330
|
-
#pwsh equivalent
|
|
331
|
-
command ="$ErrorActionPreference = 'SilentlyContinue';\n" + command + "\nStart-Sleep -Seconds 0.5"
|
|
366
|
+
# pwsh equivalent
|
|
367
|
+
command = "$ErrorActionPreference = 'SilentlyContinue';\n" + command + "\nStart-Sleep -Seconds 0.5"
|
|
332
368
|
else:
|
|
333
369
|
raise NotImplementedError(f"Platform {platform.system()} not supported.")
|
|
334
370
|
command = command + f"\n. {program_path}"
|
|
@@ -337,6 +373,6 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
|
|
|
337
373
|
program_path.write_text(command, encoding="utf-8")
|
|
338
374
|
|
|
339
375
|
|
|
340
|
-
if __name__ ==
|
|
376
|
+
if __name__ == "__main__":
|
|
341
377
|
# options, func_args = parse_pyfile(file_path="C:/Users/aalsaf01/code/machineconfig/myresources/crocodile/core.py")
|
|
342
378
|
main()
|
|
@@ -19,7 +19,7 @@ def main():
|
|
|
19
19
|
┃ 🚀 FTP File Transfer
|
|
20
20
|
┃ 📋 Starting transfer process...
|
|
21
21
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
|
|
22
|
-
parser = argparse.ArgumentParser(description=
|
|
22
|
+
parser = argparse.ArgumentParser(description="FTP client")
|
|
23
23
|
|
|
24
24
|
parser.add_argument("source", help="source path (machine:path)")
|
|
25
25
|
parser.add_argument("target", help="target path (machine:path)")
|
|
@@ -38,36 +38,44 @@ def main():
|
|
|
38
38
|
source_parts = args.source.split(":")
|
|
39
39
|
machine = source_parts[0]
|
|
40
40
|
if len(source_parts) > 1 and source_parts[1] == ES: # the source path is to be inferred from target.
|
|
41
|
-
if args.target == ES:
|
|
41
|
+
if args.target == ES:
|
|
42
|
+
raise ValueError(f"""
|
|
42
43
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
43
44
|
┃ ❌ Configuration Error
|
|
44
45
|
┃ Cannot use expand symbol `{ES}` in both source and target
|
|
45
46
|
┃ This creates a cyclical inference dependency
|
|
46
47
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
|
|
47
|
-
else:
|
|
48
|
+
else:
|
|
49
|
+
target = PathExtended(args.target).expanduser().absolute()
|
|
48
50
|
source = target.collapseuser().as_posix()
|
|
49
51
|
else:
|
|
50
52
|
source = ":".join(args.source.split(":")[1:])
|
|
51
|
-
if args.target == ES:
|
|
52
|
-
|
|
53
|
+
if args.target == ES:
|
|
54
|
+
target = None
|
|
55
|
+
else:
|
|
56
|
+
target = PathExtended(args.target).expanduser().absolute().as_posix()
|
|
53
57
|
|
|
54
58
|
elif ":" in args.target and (args.target[1] != ":" if len(args.target) > 1 else True): # avoid the case of "C:/":
|
|
55
59
|
source_is_remote = False
|
|
56
60
|
target_parts = args.target.split(":")
|
|
57
61
|
machine = target_parts[0]
|
|
58
62
|
if len(target_parts) > 1 and target_parts[1] == ES:
|
|
59
|
-
if args.source == ES:
|
|
63
|
+
if args.source == ES:
|
|
64
|
+
raise ValueError(f"""
|
|
60
65
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
61
66
|
┃ ❌ Configuration Error
|
|
62
67
|
┃ Cannot use expand symbol `{ES}` in both source and target
|
|
63
68
|
┃ This creates a cyclical inference dependency
|
|
64
69
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
|
|
65
|
-
else:
|
|
70
|
+
else:
|
|
71
|
+
source = args.source
|
|
66
72
|
target = None
|
|
67
73
|
else:
|
|
68
74
|
target = ":".join(args.target.split(":")[1:])
|
|
69
|
-
if args.source == ES:
|
|
70
|
-
|
|
75
|
+
if args.source == ES:
|
|
76
|
+
source = None
|
|
77
|
+
else:
|
|
78
|
+
source = PathExtended(args.source).expanduser().absolute()
|
|
71
79
|
|
|
72
80
|
else:
|
|
73
81
|
raise ValueError("""
|
|
@@ -80,8 +88,9 @@ def main():
|
|
|
80
88
|
pprint({"source": str(source), "target": str(target), "machine": machine}, "CLI Resolution")
|
|
81
89
|
|
|
82
90
|
from paramiko.ssh_exception import AuthenticationException # type: ignore
|
|
91
|
+
|
|
83
92
|
try:
|
|
84
|
-
ssh = SSH(rf
|
|
93
|
+
ssh = SSH(rf"{machine}")
|
|
85
94
|
except AuthenticationException:
|
|
86
95
|
print("""
|
|
87
96
|
┌────────────────────────────────────────────────────────────────
|
|
@@ -92,8 +101,9 @@ def main():
|
|
|
92
101
|
│ This exception only handles password authentication
|
|
93
102
|
└────────────────────────────────────────────────────────────────""")
|
|
94
103
|
import getpass
|
|
104
|
+
|
|
95
105
|
pwd = getpass.getpass()
|
|
96
|
-
ssh = SSH(rf
|
|
106
|
+
ssh = SSH(rf"{machine}", pwd=pwd)
|
|
97
107
|
|
|
98
108
|
if args.cloud:
|
|
99
109
|
print("""
|
|
@@ -118,7 +128,7 @@ def main():
|
|
|
118
128
|
│ 📥 Transfer Mode: Remote → Local
|
|
119
129
|
│ Source: {source}
|
|
120
130
|
│ Target: {target}
|
|
121
|
-
│ Options: {
|
|
131
|
+
│ Options: {"ZIP compression" if args.zipFirst else "No compression"}, {"Recursive" if args.recursive else "Non-recursive"}
|
|
122
132
|
└────────────────────────────────────────────────────────────────""")
|
|
123
133
|
received_file = ssh.copy_to_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
|
|
124
134
|
else:
|
|
@@ -129,7 +139,7 @@ def main():
|
|
|
129
139
|
│ 📤 Transfer Mode: Local → Remote
|
|
130
140
|
│ Source: {source}
|
|
131
141
|
│ Target: {target}
|
|
132
|
-
│ Options: {
|
|
142
|
+
│ Options: {"ZIP compression" if args.zipFirst else "No compression"}, {"Recursive" if args.recursive else "Non-recursive"}
|
|
133
143
|
└────────────────────────────────────────────────────────────────""")
|
|
134
144
|
received_file = ssh.copy_from_here(source=source, target=target, z=args.zipFirst, r=args.recursive)
|
|
135
145
|
|
|
@@ -147,5 +157,5 @@ def main():
|
|
|
147
157
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
|
|
148
158
|
|
|
149
159
|
|
|
150
|
-
if __name__ ==
|
|
160
|
+
if __name__ == "__main__":
|
|
151
161
|
main()
|
|
@@ -3,12 +3,13 @@ from pathlib import Path
|
|
|
3
3
|
|
|
4
4
|
def get_zellij_cmd(wd1: Path, wd2: Path) -> str:
|
|
5
5
|
_ = wd1, wd2
|
|
6
|
-
lines = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
lines = [
|
|
7
|
+
""" zellij action new-tab --name gitdiff""",
|
|
8
|
+
"""zellij action new-pane --direction down --name local --cwd ./data """,
|
|
9
|
+
"""zellij action write-chars "cd '{wd1}'; git status" """,
|
|
10
|
+
"""zellij action move-focus up; zellij action close-pane """,
|
|
11
|
+
"""zellij action new-pane --direction down --name remote --cwd code """,
|
|
12
|
+
"""zellij action write-chars "cd '{wd2}' """,
|
|
13
|
+
"""git status" """,
|
|
13
14
|
]
|
|
14
15
|
return "; ".join(lines)
|