machineconfig 6.84__py3-none-any.whl → 6.86__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.

Files changed (58) hide show
  1. machineconfig/cluster/sessions_managers/wt_local.py +16 -221
  2. machineconfig/cluster/sessions_managers/wt_local_manager.py +33 -174
  3. machineconfig/cluster/sessions_managers/wt_remote_manager.py +39 -197
  4. machineconfig/cluster/sessions_managers/wt_utils/manager_persistence.py +52 -0
  5. machineconfig/cluster/sessions_managers/wt_utils/monitoring_helpers.py +50 -0
  6. machineconfig/cluster/sessions_managers/wt_utils/status_reporting.py +76 -0
  7. machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
  8. machineconfig/scripts/python/agents.py +18 -7
  9. machineconfig/scripts/python/ai/vscode_tasks.py +7 -2
  10. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  11. machineconfig/scripts/python/fire_jobs.py +31 -66
  12. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_crush.py +6 -5
  13. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
  14. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_gemini.py +2 -3
  15. machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_qwen.py +2 -2
  16. machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_help_launch.py +4 -4
  17. machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_helper_types.py +2 -2
  18. machineconfig/scripts/python/helpers_agents/templates/prompt.txt +6 -0
  19. machineconfig/scripts/python/helpers_agents/templates/template.sh +24 -0
  20. machineconfig/scripts/python/helpers_devops/cli_config.py +1 -1
  21. machineconfig/scripts/python/helpers_devops/cli_nw.py +50 -0
  22. machineconfig/scripts/python/helpers_devops/cli_self.py +3 -3
  23. machineconfig/scripts/python/helpers_devops/cli_utils.py +18 -1
  24. machineconfig/scripts/python/{helpers_fire/helpers4.py → helpers_fire_command/file_wrangler.py} +15 -0
  25. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +1 -1
  26. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +1 -1
  27. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +1 -1
  28. machineconfig/scripts/python/nw/mount_nfs +1 -1
  29. machineconfig/scripts/python/nw/wifi_conn.py +1 -53
  30. machineconfig/scripts/python/sessions.py +1 -1
  31. machineconfig/scripts/python/terminal.py +46 -17
  32. machineconfig/scripts/python/utils.py +10 -21
  33. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  34. machineconfig/scripts/windows/term.ps1 +48 -0
  35. machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
  36. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +1 -1
  37. machineconfig/utils/code.py +18 -13
  38. machineconfig/utils/installer.py +0 -1
  39. machineconfig/utils/meta.py +4 -3
  40. machineconfig/utils/path_helper.py +1 -1
  41. machineconfig/utils/scheduling.py +0 -2
  42. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
  43. machineconfig/utils/ssh.py +2 -2
  44. machineconfig/utils/upgrade_packages.py +28 -24
  45. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/METADATA +1 -1
  46. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/RECORD +55 -52
  47. machineconfig/scripts/linux/warp-cli.sh +0 -122
  48. machineconfig/scripts/python/helpers_fire/prompt.txt +0 -2
  49. machineconfig/scripts/python/helpers_fire/template.sh +0 -15
  50. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/__init__.py +0 -0
  51. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/__init__.py +0 -0
  52. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
  53. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_help_search.py +0 -0
  54. /machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_load_balancer.py +0 -0
  55. /machineconfig/scripts/python/{helpers_fire → helpers_agents/templates}/template.ps1 +0 -0
  56. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/WHEEL +0 -0
  57. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/entry_points.txt +0 -0
  58. {machineconfig-6.84.dist-info → machineconfig-6.86.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,199 @@
1
+ import subprocess
2
+ import random
3
+ import string
4
+ import json
5
+ import shlex
6
+ import logging
7
+ from typing import Any
8
+ from pathlib import Path
9
+
10
+ from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ POWERSHELL_CMD = "powershell" if __import__("platform").system().lower() == "windows" else "pwsh"
15
+
16
+
17
+ def generate_random_suffix(length: int) -> str:
18
+ """Generate a random string suffix for unique PowerShell script names."""
19
+ return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
20
+
21
+
22
+ def parse_command(command: str) -> tuple[str, list[str]]:
23
+ try:
24
+ parts = shlex.split(command)
25
+ if not parts:
26
+ raise ValueError("Empty command provided")
27
+ return parts[0], parts[1:] if len(parts) > 1 else []
28
+ except ValueError as e:
29
+ logger.error(f"Error parsing command '{command}': {e}")
30
+ parts = command.split()
31
+ return parts[0] if parts else "", parts[1:] if len(parts) > 1 else []
32
+
33
+
34
+ def escape_for_wt(text: str) -> str:
35
+ """Escape text for use in Windows Terminal commands."""
36
+ text = text.replace('"', '""')
37
+ if " " in text or ";" in text or "&" in text or "|" in text:
38
+ return f'"{text}"'
39
+ return text
40
+
41
+
42
+ def validate_layout_config(layout_config: LayoutConfig) -> None:
43
+ """Validate layout configuration format and content."""
44
+ if not layout_config["layoutTabs"]:
45
+ raise ValueError("Layout must contain at least one tab")
46
+ for tab in layout_config["layoutTabs"]:
47
+ if not tab["tabName"].strip():
48
+ raise ValueError(f"Invalid tab name: {tab['tabName']}")
49
+ if not tab["command"].strip():
50
+ raise ValueError(f"Invalid command for tab '{tab['tabName']}': {tab['command']}")
51
+ if not tab["startDir"].strip():
52
+ raise ValueError(f"Invalid startDir for tab '{tab['tabName']}': {tab['startDir']}")
53
+
54
+
55
+ def generate_wt_command_string(layout_config: LayoutConfig, window_name: str) -> str:
56
+ """Generate complete Windows Terminal command string."""
57
+ command_parts = []
58
+
59
+ for i, tab in enumerate(layout_config["layoutTabs"]):
60
+ is_first = i == 0
61
+
62
+ if is_first:
63
+ tab_parts = ["wt", "-w", escape_for_wt(window_name)]
64
+ else:
65
+ tab_parts = ["new-tab"]
66
+
67
+ tab_name = tab["tabName"]
68
+ cwd = tab["startDir"]
69
+ command = tab["command"]
70
+
71
+ if cwd.startswith("~/"):
72
+ cwd = cwd.replace("~/", f"{Path.home()}/")
73
+ elif cwd == "~":
74
+ cwd = str(Path.home())
75
+
76
+ tab_parts.extend(["-d", escape_for_wt(cwd)])
77
+ tab_parts.extend(["--title", escape_for_wt(tab_name)])
78
+ tab_parts.append("--")
79
+
80
+ # Split the command into arguments
81
+ command_args = shlex.split(command)
82
+ tab_parts.extend(command_args)
83
+
84
+ command_parts.append(" ".join(tab_parts))
85
+
86
+ return " `; ".join(command_parts)
87
+
88
+
89
+ def check_wt_session_status(session_name: str) -> dict[str, Any]:
90
+ try:
91
+ ps_script = """
92
+ try {
93
+ $wtProcesses = Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue
94
+ if ($wtProcesses) {
95
+ $processInfo = @()
96
+ $wtProcesses | ForEach-Object {
97
+ $info = @{
98
+ "Id" = $_.Id
99
+ "ProcessName" = $_.ProcessName
100
+ "StartTime" = $_.StartTime.ToString()
101
+ }
102
+ $processInfo += $info
103
+ }
104
+ $processInfo | ConvertTo-Json -Depth 2
105
+ }
106
+ } catch {
107
+ # No Windows Terminal processes found
108
+ }
109
+ """
110
+
111
+ result = subprocess.run([POWERSHELL_CMD, "-Command", ps_script], capture_output=True, text=True, timeout=5)
112
+
113
+ if result.returncode == 0:
114
+ output = result.stdout.strip()
115
+ if output and output != "":
116
+ try:
117
+ processes = json.loads(output)
118
+ if not isinstance(processes, list):
119
+ processes = [processes]
120
+
121
+ return {"wt_running": True, "session_exists": len(processes) > 0, "session_name": session_name, "all_windows": processes, "session_windows": processes}
122
+ except Exception as e:
123
+ return {"wt_running": True, "session_exists": False, "error": f"Failed to parse process info: {e}", "session_name": session_name}
124
+ else:
125
+ return {"wt_running": False, "session_exists": False, "session_name": session_name, "all_windows": []}
126
+ else:
127
+ return {"wt_running": False, "error": result.stderr, "session_name": session_name}
128
+
129
+ except subprocess.TimeoutExpired:
130
+ return {"wt_running": False, "error": "Timeout while checking Windows Terminal processes", "session_name": session_name}
131
+ except FileNotFoundError:
132
+ return {"wt_running": False, "error": f"PowerShell ({POWERSHELL_CMD}) not found in PATH", "session_name": session_name}
133
+ except Exception as e:
134
+ return {"wt_running": False, "error": str(e), "session_name": session_name}
135
+
136
+
137
+ def check_command_status(tab_name: str, layout_config: LayoutConfig) -> dict[str, Any]:
138
+ """Check if a command is running by looking for processes."""
139
+ tab_config = None
140
+ for tab in layout_config["layoutTabs"]:
141
+ if tab["tabName"] == tab_name:
142
+ tab_config = tab
143
+ break
144
+
145
+ if tab_config is None:
146
+ return {"status": "unknown", "error": f"Tab '{tab_name}' not found in layout config", "running": False, "pid": None, "command": None}
147
+
148
+ command = tab_config["command"]
149
+
150
+ try:
151
+ primary_cmd = command.split()[0] if command.strip() else ""
152
+ if not primary_cmd:
153
+ return {"status": "error", "error": "Empty command", "running": False, "command": command, "tab_name": tab_name}
154
+
155
+ ps_script = f"""
156
+ try {{
157
+ $processes = Get-Process -Name '{primary_cmd}' -ErrorAction SilentlyContinue
158
+ if ($processes) {{
159
+ $processes | ForEach-Object {{
160
+ $procInfo = @{{
161
+ "pid" = $_.Id
162
+ "name" = $_.ProcessName
163
+ "start_time" = $_.StartTime.ToString()
164
+ }}
165
+ Write-Output ($procInfo | ConvertTo-Json -Compress)
166
+ }}
167
+ }}
168
+ }} catch {{
169
+ # No processes found or other error
170
+ }}
171
+ """
172
+
173
+ result = subprocess.run([POWERSHELL_CMD, "-Command", ps_script], capture_output=True, text=True, timeout=5)
174
+
175
+ if result.returncode == 0:
176
+ output_lines = [line.strip() for line in result.stdout.strip().split("\n") if line.strip()]
177
+ matching_processes = []
178
+
179
+ for line in output_lines:
180
+ if line.startswith("{") and line.endswith("}"):
181
+ try:
182
+ proc_info = json.loads(line)
183
+ matching_processes.append(proc_info)
184
+ except json.JSONDecodeError:
185
+ continue
186
+
187
+ if matching_processes:
188
+ return {"status": "running", "running": True, "processes": matching_processes, "command": command, "tab_name": tab_name}
189
+ else:
190
+ return {"status": "not_running", "running": False, "processes": [], "command": command, "tab_name": tab_name}
191
+ else:
192
+ return {"status": "error", "error": f"Command failed: {result.stderr}", "running": False, "command": command, "tab_name": tab_name}
193
+
194
+ except subprocess.TimeoutExpired:
195
+ logger.error(f"Timeout checking command status for tab '{tab_name}'")
196
+ return {"status": "timeout", "error": "Timeout checking process status", "running": False, "command": command, "tab_name": tab_name}
197
+ except Exception as e:
198
+ logger.error(f"Error checking command status for tab '{tab_name}': {e}")
199
+ return {"status": "error", "error": str(e), "running": False, "command": command, "tab_name": tab_name}
@@ -5,7 +5,7 @@
5
5
  from pathlib import Path
6
6
  from typing import cast, Optional, get_args, Annotated
7
7
  import typer
8
- from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import AGENTS, HOST, MODEL, PROVIDER
8
+ from machineconfig.scripts.python.helpers_agents.fire_agents_helper_types import AGENTS, HOST, MODEL, PROVIDER
9
9
 
10
10
 
11
11
  def create(
@@ -24,11 +24,22 @@ def create(
24
24
  agents_dir: Annotated[Optional[Path], typer.Option(..., "--agents-dir", "-ad", help="Directory to store agent files. If not provided, will be constructed automatically.")] = None,
25
25
  ):
26
26
 
27
- from machineconfig.scripts.python.helpers_fire.fire_agents_help_launch import prep_agent_launch, get_agents_launch_layout
28
- from machineconfig.scripts.python.helpers_fire.fire_agents_load_balancer import chunk_prompts
27
+ from machineconfig.scripts.python.helpers_agents.fire_agents_help_launch import prep_agent_launch, get_agents_launch_layout
28
+ from machineconfig.scripts.python.helpers_agents.fire_agents_load_balancer import chunk_prompts
29
+ from machineconfig.scripts.python.helpers_agents.fire_agents_helper_types import PROVIDER2MODEL
29
30
  from machineconfig.utils.accessories import get_repo_root, randstr
30
31
  import json
31
32
 
33
+ # validate model is valid for the provider
34
+ valid_models_for_provider = PROVIDER2MODEL.get(provider, [])
35
+ if model not in valid_models_for_provider:
36
+ available_models = "\n ".join(valid_models_for_provider) if valid_models_for_provider else "(none configured)"
37
+ raise typer.BadParameter(
38
+ f"Model '{model}' is not valid for provider '{provider}'.\n"
39
+ f"Valid models for '{provider}':\n {available_models}\n"
40
+ f"All available models: {', '.join(get_args(MODEL))}"
41
+ )
42
+
32
43
  # validate mutual exclusive
33
44
  prompt_options = [prompt, prompt_path]
34
45
  provided_prompt = [opt for opt in prompt_options if opt is not None]
@@ -85,7 +96,7 @@ agents create "{context_path_resolved}" \\
85
96
  --separator "{separator}" \\
86
97
  {"--separate" if separate else ""}
87
98
  """
88
- (agents_dir / "aa_agents_relaunch.py").write_text(data=regenerate_py_code, encoding="utf-8")
99
+ (agents_dir / "aa_agents_relaunch.sh").write_text(data=regenerate_py_code, encoding="utf-8")
89
100
  layout_output_path = output_path if output_path is not None else agents_dir / "layout.json"
90
101
  layout_output_path.parent.mkdir(parents=True, exist_ok=True)
91
102
  layout_output_path.write_text(data=json.dumps(layoutfile, indent=4), encoding="utf-8")
@@ -136,11 +147,11 @@ def collect(
136
147
 
137
148
  def template():
138
149
  from platform import system
139
- import machineconfig.scripts.python.helpers_fire as module
150
+ import machineconfig.scripts.python.helpers_agents as module
140
151
  if system() == "Linux" or system() == "Darwin":
141
- template_path = Path(module.__file__).parent / "template.sh"
152
+ template_path = Path(module.__file__).parent / "templates/template.sh"
142
153
  elif system() == "Windows":
143
- template_path = Path(module.__file__).parent / "template.ps1"
154
+ template_path = Path(module.__file__).parent / "templates/template.ps1"
144
155
  else:
145
156
  raise typer.BadParameter(f"Unsupported OS: {system()}")
146
157
 
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  from pathlib import Path
3
+ from typing import Any
3
4
 
4
5
 
5
6
  def add_lint_and_type_check_task(repo_root: Path) -> None:
@@ -18,8 +19,12 @@ def add_lint_and_type_check_task(repo_root: Path) -> None:
18
19
  }
19
20
 
20
21
  if tasks_json_path.exists():
21
- with tasks_json_path.open("r") as f:
22
- tasks_config = json.load(f)
22
+ json_data = tasks_json_path.read_text(encoding="utf-8")
23
+ if not json_data.strip():
24
+ tasks_config: dict[str, Any] = {"version": "2.0.0", "tasks": []}
25
+ else:
26
+ tasks_config = json.loads(json_data)
27
+ assert isinstance(tasks_config, dict)
23
28
  if "tasks" not in tasks_config:
24
29
  tasks_config["tasks"] = []
25
30
  existing_labels = {task.get("label") for task in tasks_config.get("tasks", [])}
@@ -2,7 +2,7 @@
2
2
  # /// script
3
3
  # requires-python = ">=3.13"
4
4
  # dependencies = [
5
- # "machineconfig>=6.84",
5
+ # "machineconfig>=6.86",
6
6
  # "textual",
7
7
  # "pyperclip",
8
8
  # ]
@@ -7,7 +7,7 @@ fire
7
7
 
8
8
  """
9
9
 
10
- from machineconfig.utils.ve import get_ve_activate_line, get_ve_path_and_ipython_profile
10
+ from machineconfig.utils.ve import get_ve_path_and_ipython_profile
11
11
  from machineconfig.utils.options import choose_from_options
12
12
  from machineconfig.utils.path_helper import match_file_name, sanitize_path
13
13
  from machineconfig.utils.path_extended import PathExtended
@@ -26,7 +26,7 @@ def route(args: FireJobArgs, fire_args: str = "") -> None:
26
26
  suffixes = {".py", ".sh", ".ps1"}
27
27
  choice_file = match_file_name(sub_string=args.path, search_root=PathExtended.cwd(), suffixes=suffixes)
28
28
  elif path_obj.is_dir():
29
- from machineconfig.scripts.python.helpers_fire.helpers4 import search_for_files_of_interest
29
+ from machineconfig.scripts.python.helpers_fire_command.file_wrangler import search_for_files_of_interest
30
30
  print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
31
31
  files = search_for_files_of_interest(path_obj)
32
32
  print(f"🔍 Got #{len(files)} results.")
@@ -38,10 +38,6 @@ def route(args: FireJobArgs, fire_args: str = "") -> None:
38
38
 
39
39
  repo_root = get_repo_root(Path(choice_file))
40
40
  print(f"💾 Selected file: {choice_file}.\nRepo root: {repo_root}")
41
- ve_root_from_file, ipy_profile = get_ve_path_and_ipython_profile(choice_file)
42
- if ipy_profile is None:
43
- ipy_profile = "default"
44
-
45
41
  if args.marimo:
46
42
  tmp_dir = PathExtended.tmp().joinpath(f"tmp_scripts/marimo/{choice_file.stem}_{randstr()}")
47
43
  tmp_dir.mkdir(parents=True, exist_ok=True)
@@ -58,15 +54,9 @@ uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.
58
54
  # ========================= preparing kwargs_dict
59
55
  if choice_file.suffix == ".py":
60
56
  kwargs_dict = extract_kwargs(args) # This now returns empty dict, but kept for compatibility
61
- ve_root = args.ve or ve_root_from_file
62
- if ve_root is None:
63
- raise ValueError(f"Could not determine virtual environment for file {choice_file}. Please ensure it is within a recognized project structure or specify the `--ve` option.")
64
- activate_ve_line = get_ve_activate_line(ve_root=ve_root)
65
57
  else:
66
- activate_ve_line = ""
67
58
  kwargs_dict = {}
68
59
 
69
-
70
60
  # ========================= choosing function to run
71
61
  choice_function: Optional[str] = None # Initialize to avoid unbound variable
72
62
  if args.choose_function:
@@ -77,58 +67,35 @@ uv run --project {repo_root} --with marimo marimo edit --host 0.0.0.0 marimo_nb.
77
67
 
78
68
  if choice_file.suffix == ".py":
79
69
  from machineconfig.scripts.python.helpers_fire_command.fire_jobs_route_helper import get_command_streamlit
80
- if args.streamlit: exe = get_command_streamlit(choice_file, args.environment, repo_root)
81
- elif args.interactive is False: exe = "python"
82
- elif args.jupyter: exe = "jupyter-lab"
83
- else: exe = f"ipython -i --no-banner --profile {ipy_profile} "
70
+ if args.streamlit:
71
+ exe = get_command_streamlit(choice_file=choice_file, environment=args.environment, repo_root=repo_root)
72
+ exe = f"uv run {exe} "
73
+ elif args.jupyter: exe = "uv run jupyter-lab"
74
+ else:
75
+ if args.interactive:
76
+ _ve_root_from_file, ipy_profile = get_ve_path_and_ipython_profile(choice_file)
77
+ if ipy_profile is None:
78
+ ipy_profile = "default"
79
+ exe = f"uv run ipython -i --no-banner --profile {ipy_profile} "
80
+ else:
81
+ exe = "uv run python "
84
82
  elif choice_file.suffix == ".ps1" or choice_file.suffix == ".sh": exe = "."
85
83
  elif choice_file.suffix == "": exe = ""
86
84
  else: raise NotImplementedError(f"File type {choice_file.suffix} not supported, in the sense that I don't know how to fire it.")
87
85
 
88
86
  if args.module or (args.debug and args.choose_function): # 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.
89
87
  assert choice_file.suffix == ".py", f"File must be a python file to be imported as a module. Got {choice_file}"
90
- from machineconfig.scripts.python.helpers_fire.helpers4 import get_import_module_code
91
- import_line = get_import_module_code(str(choice_file))
92
- if repo_root is not None:
93
- repo_root_add = f"""sys.path.append(r'{repo_root}')"""
94
- else:
95
- repo_root_add = ""
96
- txt: str = f"""
97
- try:
98
- {import_line}
99
- except (ImportError, ModuleNotFoundError) as ex:
100
- print(fr"❌ Failed to import `{choice_file}` as a module: {{ex}} ")
101
- print(fr"⚠️ Attempting import with ad-hoc `$PATH` manipulation. DO NOT pickle any objects in this session as correct deserialization cannot be guaranteed.")
102
- import sys
103
- sys.path.append(r'{PathExtended(choice_file).parent}')
104
- {repo_root_add}
105
- from {PathExtended(choice_file).stem} import *
106
- print(fr"✅ Successfully imported `{choice_file}`")
107
- """
108
- if choice_function is not None:
109
- txt = (
110
- txt
111
- + f"""
112
- res = {choice_function}({("**" + str(kwargs_dict)) if kwargs_dict else ""})
113
- """
114
- )
115
-
116
- txt = (
117
- f"""
118
- try:
119
- from rich.panel import Panel
120
- from rich.console import Console
121
- from rich.syntax import Syntax
122
- console = Console()
123
- console.print(Panel(Syntax(code=r'''{txt}''', lexer='python'), title='Import Script'), style="bold red")
124
- except ImportError as _ex:
125
- print(r'''{txt}''')
126
- """
127
- + txt
128
- )
129
- choice_file = PathExtended.tmp().joinpath(f"tmp_scripts/python/{PathExtended(choice_file).parent.name}_{PathExtended(choice_file).stem}_{randstr()}.py")
88
+ from machineconfig.scripts.python.helpers_fire_command.file_wrangler import get_import_module_code, wrap_import_in_try_except
89
+ from machineconfig.utils.meta import lambda_to_python_script
90
+ from machineconfig.utils.code import print_code
91
+ import_code = get_import_module_code(str(choice_file))
92
+ import_code_robust = lambda_to_python_script(lambda: wrap_import_in_try_except(import_line=import_code, pyfile=str(choice_file), repo_root=str(repo_root) if repo_root is not None else None), in_global=True, import_module=False)
93
+ code_printing = lambda_to_python_script(lambda: print_code(code=import_code_robust, lexer="python", desc="import code"), in_global=True, import_module=False)
94
+ if choice_function is not None: calling = f"""res = {choice_function}({("**" + str(kwargs_dict)) if kwargs_dict else ""})"""
95
+ else: calling = """# No function selected to call. You can add your code here."""
96
+ choice_file = Path.home().joinpath(f"tmp_results/tmp_scripts/python/{Path(choice_file).parent.name}_{Path(choice_file).stem}_{randstr()}.py")
130
97
  choice_file.parent.mkdir(parents=True, exist_ok=True)
131
- choice_file.write_text(txt, encoding="utf-8")
98
+ choice_file.write_text(import_code_robust + "\n" + code_printing + "\n" + calling, encoding="utf-8")
132
99
 
133
100
  # ========================= determining basic command structure: putting together exe & choice_file & choice_function & pdb
134
101
  if args.debug:
@@ -149,26 +116,24 @@ except ImportError as _ex:
149
116
  command = f"{exe} {choice_file}"
150
117
  else:
151
118
  command = f"cd {choice_file.parent}\n{exe} {choice_file.name}\ncd {PathExtended.cwd()}"
152
-
153
119
  elif args.cmd:
154
120
  command = rf""" cd /d {choice_file.parent} & {exe} {choice_file.name} """
155
121
  else:
156
122
  if choice_file.suffix == "": command = f"{exe} {choice_file} {fire_args}"
157
123
  else: command = f"{exe} {choice_file} "
158
124
 
159
- if not args.cmd: command = f"{activate_ve_line}\n{command}"
125
+
126
+ if not args.cmd: pass
160
127
  else:
161
128
  new_line = "\n"
162
- command = rf"""start cmd -Argument "/k {activate_ve_line.replace(".ps1", ".bat").replace(". ", "")} & {command.replace(new_line, " & ")} " """ # this works from powershell
129
+ command = rf"""start cmd -Argument "/k {command.replace(new_line, " & ")} " """ # this works from powershell
163
130
  if args.submit_to_cloud:
164
- command = f"""
165
- {activate_ve_line}
166
- python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
131
+ command = f"""uv run python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
167
132
  if choice_function is not None:
168
133
  command += f"--function {choice_function} "
169
134
 
170
- if args.optimized:
171
- command = command.replace("python ", "python -OO ")
135
+ if args.optimized: command = command.replace("python ", "python -OO ")
136
+
172
137
  from rich.panel import Panel
173
138
  from rich.console import Console
174
139
  from rich.syntax import Syntax
@@ -207,7 +172,7 @@ python -m machineconfig.cluster.templates.cli_click --file {choice_file} """
207
172
  import os
208
173
  op_program_path = os.environ.get("OP_PROGRAM_PATH", None)
209
174
  if op_program_path is not None:
210
- op_program_path = PathExtended(op_program_path)
175
+ op_program_path = Path(op_program_path)
211
176
  op_program_path.parent.mkdir(parents=True, exist_ok=True)
212
177
  op_program_path.write_text(command, encoding="utf-8")
213
178
  else:
@@ -2,7 +2,7 @@
2
2
  from pathlib import Path
3
3
  # import shlex
4
4
  from typing import Optional
5
- from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import HOST, PROVIDER, MODEL
5
+ from machineconfig.scripts.python.helpers_agents.fire_agents_helper_types import HOST, PROVIDER, MODEL
6
6
 
7
7
 
8
8
  def fire_crush(api_key: Optional[str], model: MODEL, provider: PROVIDER, machine: HOST, prompt_path: Path, repo_root: Path) -> str:
@@ -16,21 +16,22 @@ crush run {prompt_path}
16
16
  json_path = Path(__file__).parent / "fire_crush.json"
17
17
  json_template = json_path.read_text(encoding="utf-8")
18
18
  json_filled = json_template.replace("{api_key}", api_key).replace("{model}", model).replace("{provider}", provider)
19
- import tempfile
20
- temp_config_file_local = tempfile.mkstemp(suffix=".json")[1]
19
+ from machineconfig.utils.accessories import randstr
20
+ temp_config_file_local = Path.home().joinpath("tmp_results/tmp_files/crush_" + randstr(8) + ".json")
21
+ temp_config_file_local.parent.mkdir(parents=True, exist_ok=True)
21
22
  Path(temp_config_file_local).write_text(json_filled, encoding="utf-8")
22
23
  cmd = f"""
23
24
 
24
25
  # -e "PATH_PROMPT=$PATH_PROMPT"
25
26
  # opencode --model "{provider}/{model}" run {prompt_path}
26
-
27
+
27
28
 
28
29
  echo "Running prompt @ {prompt_path.relative_to(repo_root)} using Docker with Crush..."
29
30
  docker run -it --rm \
30
31
  -v "{repo_root}:/workspace/{repo_root.name}" \
31
32
  -v "{temp_config_file_local}:/root/.local/share/crush/crush.json" \
32
33
  -w "/workspace/{repo_root.name}" \
33
- statistician/machineconfig:latest \
34
+ statistician/machineconfig-ai:latest \
34
35
  bash -i -c "source ~/.bashrc && cd /workspace/{repo_root.name} && cat /root/.local/share/crush/crush.json && crush run 'Please act on contents of this prompt ./{prompt_path.relative_to(repo_root)}'"
35
36
 
36
37
  """
@@ -2,7 +2,7 @@
2
2
 
3
3
  from pathlib import Path
4
4
  # import shlex
5
- from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import HOST
5
+ from machineconfig.scripts.python.helpers_agents.fire_agents_helper_types import HOST
6
6
  from typing import Optional
7
7
 
8
8
  def fire_cursor(api_key: Optional[str], prompt_path: Path, machine: HOST) -> str:
@@ -1,7 +1,7 @@
1
1
 
2
2
  from pathlib import Path
3
3
  import shlex
4
- from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import HOST
4
+ from machineconfig.scripts.python.helpers_agents.fire_agents_helper_types import HOST
5
5
  from typing import Optional, Literal
6
6
 
7
7
 
@@ -37,8 +37,7 @@ docker run -it --rm \
37
37
  -e GEMINI_API_KEY="{api_key}" \
38
38
  -v "{repo_root}:/workspace/{repo_root.name}" \
39
39
  -w "/workspace/{repo_root.name}" \
40
- statistician/machineconfig:latest \
40
+ statistician/machineconfig-ai:latest \
41
41
  gemini --prompt "$PATH_PROMPT"
42
42
  """
43
43
  return cmd
44
-
@@ -1,7 +1,7 @@
1
1
 
2
2
  from pathlib import Path
3
3
  import shlex
4
- from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import HOST
4
+ from machineconfig.scripts.python.helpers_agents.fire_agents_helper_types import HOST
5
5
  from typing import Optional, Literal
6
6
 
7
7
 
@@ -37,7 +37,7 @@ docker run -it --rm \
37
37
  -v {shlex.quote(str(oauth_creds))}:/root/.qwen/oauth_creds.json \
38
38
  -v {shlex.quote(str(settings))}:/root/.qwen/settings.json \
39
39
  -w "/workspace/{repo_root.name}" \
40
- statistician/machineconfig:latest \
40
+ statistician/machineconfig-ai:latest \
41
41
  qwen --prompt "$PATH_PROMPT"
42
42
  """
43
43
  return cmd
@@ -2,7 +2,7 @@
2
2
  import random
3
3
  import shlex
4
4
  from pathlib import Path
5
- from machineconfig.scripts.python.helpers_fire.fire_agents_helper_types import AGENTS, AGENT_NAME_FORMATTER, HOST, PROVIDER, MODEL
5
+ from machineconfig.scripts.python.helpers_agents.fire_agents_helper_types import AGENTS, AGENT_NAME_FORMATTER, HOST, PROVIDER, MODEL
6
6
 
7
7
 
8
8
  def get_api_keys(provider: PROVIDER) -> list[str]:
@@ -66,14 +66,14 @@ sleep 0.1
66
66
  assert provider == "google", "Gemini agent only works with google provider."
67
67
  api_keys = get_api_keys(provider="google")
68
68
  api_key = api_keys[idx % len(api_keys)] if len(api_keys) > 0 else None
69
- from machineconfig.scripts.python.helpers_fire.agentic_frameworks.fire_gemini import fire_gemini
69
+ from machineconfig.scripts.python.helpers_agents.agentic_frameworks.fire_gemini import fire_gemini
70
70
  cmd = fire_gemini(api_key=api_key, prompt_path=prompt_path, machine=machine, model="gemini-2.5-pro", provider="google", repo_root=repo_root)
71
71
  case "cursor-agent":
72
- from machineconfig.scripts.python.helpers_fire.agentic_frameworks.fire_cursor_agents import fire_cursor
72
+ from machineconfig.scripts.python.helpers_agents.agentic_frameworks.fire_cursor_agents import fire_cursor
73
73
  cmd = fire_cursor(prompt_path=prompt_path, machine=machine, api_key=None)
74
74
  raise NotImplementedError("Cursor agent is not implemented yet, api key missing")
75
75
  case "crush":
76
- from machineconfig.scripts.python.helpers_fire.agentic_frameworks.fire_crush import fire_crush
76
+ from machineconfig.scripts.python.helpers_agents.agentic_frameworks.fire_crush import fire_crush
77
77
  api_keys = get_api_keys(provider=provider)
78
78
  api_key = api_keys[idx % len(api_keys)] if len(api_keys) > 0 else None
79
79
  cmd = fire_crush(api_key=api_key, prompt_path=prompt_path, machine=machine, repo_root=repo_root, model=model, provider=provider)
@@ -20,7 +20,7 @@ AGENTS: TypeAlias = Literal["cursor-agent", "gemini", "qwen-code", "copilot", "c
20
20
  HOST: TypeAlias = Literal["local", "docker"]
21
21
  PROVIDER: TypeAlias = Literal["azure", "google", "aws", "openai", "anthropic", "openrouter", "xai"]
22
22
  MODEL: TypeAlias = Literal["zai/glm-4.6", "anthropic/sonnet-4.5", "google/gemini-2.5-pro", "openai/gpt-5-codex",
23
- "openrouter/supernova", "x-ai/grok-4-fast:free",
23
+ "openrouter/supernova", "openrouter/andromeda-alpha", "x-ai/grok-4-fast:free",
24
24
  ]
25
25
  PROVIDER2MODEL: dict[PROVIDER, list[MODEL]] = {
26
26
  "azure": ["zai/glm-4.6"],
@@ -28,7 +28,7 @@ PROVIDER2MODEL: dict[PROVIDER, list[MODEL]] = {
28
28
  "aws": [],
29
29
  "openai": ["openai/gpt-5-codex"],
30
30
  "anthropic": ["anthropic/sonnet-4.5"],
31
- "openrouter": ["openrouter/supernova"],
31
+ "openrouter": ["openrouter/supernova", "openrouter/andromeda-alpha"],
32
32
  "xai": ["x-ai/grok-4-fast:free"]
33
33
  }
34
34
 
@@ -0,0 +1,6 @@
1
+
2
+ please look if it is possible to easily relpace PathExtended with normal Path from pathlib
3
+ if its one or two lines of code, then go ahead and make it standard and stop using the extended library
4
+ you focus ont he methods used,
5
+
6
+ only look at the files below
@@ -0,0 +1,24 @@
1
+
2
+ #!/bin/bash
3
+ # set -e # Exit immediately if a command exits with a non-zero status.
4
+
5
+ JOB_NAME="outpatient_mapping"
6
+ REPO_ROOT="$HOME/code/machineconfig"
7
+ CONTEXT_PATH="$REPO_ROOT/.ai/todo/files.md"
8
+ # agents make-todo --strategy keywords from machineconfig.utils.path_extended import PathExtended
9
+ PROMPT_PATH="$REPO_ROOT/src/machineconfig/scripts/python/helpers_agents/templates/prompt.txt"
10
+ AGENTS_DIR="$REPO_ROOT/.ai/agents/$JOB_NAME"
11
+
12
+ agents create --agents crush \
13
+ --host docker \
14
+ --model x-ai/grok-4-fast:free \
15
+ --provider openrouter \
16
+ --context-path $CONTEXT_PATH \
17
+ --prompt-path $PROMPT_PATH \
18
+ --job-name $JOB_NAME \
19
+ --agents-dir $AGENTS_DIR
20
+ sessions balance-load "$AGENTS_DIR/layout.json" --max-thresh 6 --breaking-method moreLayouts --thresh-type number --output-path "$AGENTS_DIR/layout_balanced.json"
21
+ sessions run "$AGENTS_DIR/layout_balanced.json" --kill-upon-completion
22
+
23
+
24
+ # agents collect $AGENTS_DIR "$REPO_ROOT/.ai/agents/$JOB_NAME/collected.txt"
@@ -46,7 +46,7 @@ def path():
46
46
  uv_with = ["textual"]
47
47
  uv_project_dir = None
48
48
  if not Path.home().joinpath("code/machineconfig").exists():
49
- uv_with.append("machineconfig>=6.84")
49
+ uv_with.append("machineconfig>=6.86")
50
50
  else:
51
51
  uv_project_dir = str(Path.home().joinpath("code/machineconfig"))
52
52
  run_shell_script(get_uv_command_executing_python_script(python_script=path.read_text(encoding="utf-8"), uv_with=uv_with, uv_project_dir=uv_project_dir)[0])