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

@@ -0,0 +1,168 @@
1
+
2
+ from machineconfig.utils.schemas.layouts.layout_types import TabConfig, LayoutConfig
3
+ from machineconfig.utils.accessories import split_list
4
+ from typing import Literal
5
+
6
+
7
+
8
+ def split_tabs_by_weight(tabs: list[TabConfig], max_weight: int) -> list[list[TabConfig]]:
9
+ """Split tabs into chunks where each chunk's total weight <= max_weight."""
10
+ chunks: list[list[TabConfig]] = []
11
+ current_chunk: list[TabConfig] = []
12
+ current_weight = 0
13
+ for tab in tabs:
14
+ tab_weight = tab.get("tabWeight", 1)
15
+ if current_weight + tab_weight > max_weight and current_chunk:
16
+ chunks.append(current_chunk)
17
+ current_chunk = [tab]
18
+ current_weight = tab_weight
19
+ else:
20
+ current_chunk.append(tab)
21
+ current_weight += tab_weight
22
+
23
+ if current_chunk:
24
+ chunks.append(current_chunk)
25
+
26
+ return chunks
27
+
28
+
29
+ def combine_tabs_into_super_tabs(tabs: list[TabConfig], num_super_tabs: int) -> list[TabConfig]:
30
+ """Combine tabs into num_super_tabs super tabs with combined commands."""
31
+ if len(tabs) <= num_super_tabs:
32
+ return tabs # No need to combine
33
+
34
+ tab_groups = split_list(tabs, to=num_super_tabs)
35
+ super_tabs: list[TabConfig] = []
36
+ for idx, group in enumerate(tab_groups):
37
+ if len(group) == 1:
38
+ super_tabs.append(group[0])
39
+ else:
40
+ combined_command = "; ".join(tab["command"] for tab in group)
41
+ combined_name = f"super_tab_{idx+1}"
42
+ # Use startDir of the first tab
43
+ start_dir = group[0]["startDir"]
44
+ # Sum weights
45
+ total_weight = sum(tab.get("tabWeight", 1) for tab in group)
46
+ super_tabs.append({
47
+ "tabName": combined_name,
48
+ "startDir": start_dir,
49
+ "command": combined_command,
50
+ "tabWeight": total_weight
51
+ })
52
+ return super_tabs
53
+
54
+
55
+ def combine_tabs_by_weight_into_super_tabs(tabs: list[TabConfig], max_weight: int) -> list[TabConfig]:
56
+ """Combine tabs into super tabs where each super tab has weight <= max_weight."""
57
+ tab_groups = split_tabs_by_weight(tabs, max_weight=max_weight)
58
+ super_tabs: list[TabConfig] = []
59
+ for idx, group in enumerate(tab_groups):
60
+ if len(group) == 1:
61
+ super_tabs.append(group[0])
62
+ else:
63
+ combined_command = "; ".join(tab["command"] for tab in group)
64
+ combined_name = f"super_tab_{idx+1}"
65
+ start_dir = group[0]["startDir"]
66
+ total_weight = sum(tab.get("tabWeight", 1) for tab in group)
67
+ super_tabs.append({
68
+ "tabName": combined_name,
69
+ "startDir": start_dir,
70
+ "command": combined_command,
71
+ "tabWeight": total_weight
72
+ })
73
+ return super_tabs
74
+
75
+
76
+ def restrict_num_tabs(layout_configs: list[LayoutConfig], max_thresh: int, threshold_type: Literal["number", "weight"], breaking_method: Literal["moreLayouts", "combineTabs"]) -> list[LayoutConfig]:
77
+ match threshold_type, breaking_method:
78
+ case "number", "moreLayouts":
79
+ return _restrict_num_tabs_helper1(layout_configs=layout_configs, max_thresh=max_thresh, threshold_type=threshold_type, breaking_method=breaking_method)
80
+ case "number", "combineTabs":
81
+ return _restrict_num_tabs_helper2(layout_configs=layout_configs, max_thresh=max_thresh, threshold_type=threshold_type, breaking_method=breaking_method)
82
+ case "weight", "moreLayouts":
83
+ return _restrict_num_tabs_helper3(layout_configs=layout_configs, max_thresh=max_thresh, threshold_type=threshold_type, breaking_method=breaking_method)
84
+ case "weight", "combineTabs":
85
+ return _restrict_num_tabs_helper4(layout_configs=layout_configs, max_thresh=max_thresh, threshold_type=threshold_type, breaking_method=breaking_method)
86
+ case _:
87
+ raise NotImplementedError(f"The combination {threshold_type}, {breaking_method} is not implemented")
88
+
89
+
90
+ def _restrict_num_tabs_helper1(layout_configs: list[LayoutConfig], max_thresh: int, threshold_type: Literal["number"], breaking_method: Literal["moreLayouts"]) -> list[LayoutConfig]:
91
+ """When threshold is exceeded, create more layouts with max_thresh tabs each."""
92
+ new_layout_configs: list[LayoutConfig] = []
93
+ for a_layout_config in layout_configs:
94
+ if len(a_layout_config["layoutTabs"]) > max_thresh:
95
+ print(f"Layout '{a_layout_config['layoutName']}' has too many tabs ({len(a_layout_config['layoutTabs'])} > {max_thresh}). Splitting into multiple layouts.")
96
+ tab_chunks = split_list(a_layout_config["layoutTabs"], every=max_thresh)
97
+ for idx, tab_chunk in enumerate(tab_chunks):
98
+ new_layout_configs.append({
99
+ "layoutName": f"{a_layout_config['layoutName']}_part{idx+1}",
100
+ "layoutTabs": tab_chunk
101
+ })
102
+ else:
103
+ print(f"Layout '{a_layout_config['layoutName']}' has acceptable number of tabs ({len(a_layout_config['layoutTabs'])} <= {max_thresh}). Keeping as is.")
104
+ new_layout_configs.append(a_layout_config)
105
+ return new_layout_configs
106
+
107
+
108
+ def _restrict_num_tabs_helper2(layout_configs: list[LayoutConfig], max_thresh: int, threshold_type: Literal["number"], breaking_method: Literal["combineTabs"]) -> list[LayoutConfig]:
109
+ """When threshold is exceeded, combine tabs into super tabs to reduce count to max_thresh."""
110
+ new_layout_configs: list[LayoutConfig] = []
111
+ for a_layout_config in layout_configs:
112
+ num_tabs = len(a_layout_config["layoutTabs"])
113
+ if num_tabs > max_thresh:
114
+ print(f"Layout '{a_layout_config['layoutName']}' has too many tabs ({num_tabs} > {max_thresh}). Combining into {max_thresh} super tabs.")
115
+ super_tabs = combine_tabs_into_super_tabs(a_layout_config["layoutTabs"], num_super_tabs=max_thresh)
116
+ new_layout_configs.append({
117
+ "layoutName": a_layout_config["layoutName"],
118
+ "layoutTabs": super_tabs
119
+ })
120
+ else:
121
+ print(f"Layout '{a_layout_config['layoutName']}' has acceptable number of tabs ({num_tabs} <= {max_thresh}). Keeping as is.")
122
+ new_layout_configs.append(a_layout_config)
123
+ return new_layout_configs
124
+
125
+
126
+ def _restrict_num_tabs_helper3(layout_configs: list[LayoutConfig], max_thresh: int, threshold_type: Literal["weight"], breaking_method: Literal["moreLayouts"]) -> list[LayoutConfig]:
127
+ """When threshold is exceeded, create more layouts with max_thresh total weight each."""
128
+ new_layout_configs: list[LayoutConfig] = []
129
+ for a_layout_config in layout_configs:
130
+ layout_weight = sum(tab.get("tabWeight", 1) for tab in a_layout_config["layoutTabs"])
131
+ if layout_weight > max_thresh:
132
+ print(f"Layout '{a_layout_config['layoutName']}' has too much weight ({layout_weight} > {max_thresh}). Splitting into multiple layouts.")
133
+ tab_chunks = split_tabs_by_weight(a_layout_config["layoutTabs"], max_weight=max_thresh)
134
+ for idx, tab_chunk in enumerate(tab_chunks):
135
+ new_layout_configs.append({
136
+ "layoutName": f"{a_layout_config['layoutName']}_part{idx+1}",
137
+ "layoutTabs": tab_chunk
138
+ })
139
+ else:
140
+ print(f"Layout '{a_layout_config['layoutName']}' has acceptable total weight ({layout_weight} <= {max_thresh}). Keeping as is.")
141
+ new_layout_configs.append(a_layout_config)
142
+ return new_layout_configs
143
+
144
+
145
+ def _restrict_num_tabs_helper4(layout_configs: list[LayoutConfig], max_thresh: int, threshold_type: Literal["weight"], breaking_method: Literal["combineTabs"]) -> list[LayoutConfig]:
146
+ """When threshold is exceeded, combine tabs into super tabs with weight <= max_thresh."""
147
+ new_layout_configs: list[LayoutConfig] = []
148
+ for a_layout_config in layout_configs:
149
+ layout_weight = sum(tab.get("tabWeight", 1) for tab in a_layout_config["layoutTabs"])
150
+ if layout_weight > max_thresh:
151
+ print(f"Layout '{a_layout_config['layoutName']}' has too much weight ({layout_weight} > {max_thresh}). Combining into super tabs with weight <= {max_thresh}.")
152
+ super_tabs = combine_tabs_by_weight_into_super_tabs(a_layout_config["layoutTabs"], max_weight=max_thresh)
153
+ new_layout_configs.append({
154
+ "layoutName": a_layout_config["layoutName"],
155
+ "layoutTabs": super_tabs
156
+ })
157
+ else:
158
+ print(f"Layout '{a_layout_config['layoutName']}' has acceptable total weight ({layout_weight} <= {max_thresh}). Keeping as is.")
159
+ new_layout_configs.append(a_layout_config)
160
+ return new_layout_configs
161
+
162
+
163
+ def run(layouts: list[LayoutConfig]):
164
+ from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
165
+ manager = ZellijLocalManager(session_layouts=layouts)
166
+ manager.start_all_sessions(poll_interval=2, poll_seconds=2)
167
+ manager.run_monitoring_routine(wait_ms=2000)
168
+ manager.kill_all_sessions()
@@ -456,7 +456,7 @@ def run_zellij_layout(layout_config: LayoutConfig):
456
456
  layout_path = created_zellij_layout(layout_config, None)
457
457
  session_name = layout_config["layoutName"]
458
458
  try:
459
- from machineconfig.cluster.sessions_managers.enhanced_command_runner import enhanced_zellij_session_start
459
+ from machineconfig.cluster.sessions_managers.utils.enhanced_command_runner import enhanced_zellij_session_start
460
460
 
461
461
  enhanced_zellij_session_start(session_name, layout_path)
462
462
  except ImportError:
@@ -0,0 +1,6 @@
1
+ curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
2
+ | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
3
+ && echo "deb https://ngrok-agent.s3.amazonaws.com bookworm main" \
4
+ | sudo tee /etc/apt/sources.list.d/ngrok.list \
5
+ && sudo apt update \
6
+ && sudo apt install ngrok
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ cd $HOME/Downloads
4
+ curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip" -o "q.zip"
5
+ unzip q.zip
6
+ ./q/install.sh # goes to ~/.local/bin.
7
+ rm q.zip
8
+ rm -rfd q
@@ -1,6 +1,24 @@
1
1
  {
2
2
  "version": "1",
3
3
  "installers": [
4
+ {
5
+ "appName": "ngrok",
6
+ "repoURL": "CMD",
7
+ "doc": "Secure introspectable tunnels to localhost",
8
+ "fileNamePattern": {
9
+ "amd64": {
10
+ "linux": "ngrok.sh",
11
+ "windows": "winget install ngrok -s msstore",
12
+ "macos": "brew install ngrok"
13
+ },
14
+ "arm64": {
15
+ "linux": "ngrok.sh",
16
+ "windows": "winget install ngrok -s msstore",
17
+ "macos": "brew install ngrok"
18
+ }
19
+ }
20
+
21
+ },
4
22
  {
5
23
  "appName": "Visual Studio Code",
6
24
  "repoURL": "CMD",
@@ -19,7 +37,7 @@
19
37
  }
20
38
  },
21
39
  {
22
- "appName": "aider-chat",
40
+ "appName": "aider",
23
41
  "repoURL": "CMD",
24
42
  "doc": "Aider Chat",
25
43
  "fileNamePattern": {
@@ -35,6 +53,142 @@
35
53
  }
36
54
  }
37
55
  },
56
+ {
57
+ "appName": "github-copilot-cli",
58
+ "repoURL": "CMD",
59
+ "doc": "Terminal-based CLI agents and tools for productivity and coding.",
60
+ "fileNamePattern": {
61
+ "amd64": {
62
+ "linux": "npm install -g @github/copilot",
63
+ "windows": "npm install -g @github/copilot",
64
+ "macos": "npm install -g @github/copilot"
65
+ },
66
+ "arm64": {
67
+ "linux": "npm install -g @github/copilot",
68
+ "windows": "npm install -g @github/copilot",
69
+ "macos": "npm install -g @github/copilot"
70
+ }
71
+ }
72
+ },
73
+ {
74
+ "appName": "gemini",
75
+ "repoURL": "CMD",
76
+ "doc": "Terminal-based CLI agents and tools for productivity and coding.",
77
+ "fileNamePattern": {
78
+ "amd64": {
79
+ "linux": "npm install -g @google/gemini-cli",
80
+ "windows": "npm install -g @google/gemini-cli",
81
+ "macos": "npm install -g @google/gemini-cli"
82
+ },
83
+ "arm64": {
84
+ "linux": "npm install -g @google/gemini-cli",
85
+ "windows": "npm install -g @google/gemini-cli",
86
+ "macos": "npm install -g @google/gemini-cli"
87
+ }
88
+ }
89
+ },
90
+ {
91
+ "appName": "crush",
92
+ "repoURL": "CMD",
93
+ "doc": "Terminal-based CLI agents and tools for productivity and coding.",
94
+ "fileNamePattern": {
95
+ "amd64": {
96
+ "linux": "npm install -g @charmland/crush",
97
+ "windows": "npm install -g @charmland/crush",
98
+ "macos": "npm install -g @charmland/crush"
99
+ },
100
+ "arm64": {
101
+ "linux": "npm install -g @charmland/crush",
102
+ "windows": "npm install -g @charmland/crush",
103
+ "macos": "npm install -g @charmland/crush"
104
+ }
105
+ }
106
+ },
107
+ {
108
+ "appName": "opencode-ai",
109
+ "repoURL": "CMD",
110
+ "doc": "Terminal-based CLI agents and tools for productivity and coding.",
111
+ "fileNamePattern": {
112
+ "amd64": {
113
+ "linux": "npm install -g opencode-ai@latest",
114
+ "windows": "npm install -g opencode-ai@latest",
115
+ "macos": "npm install -g opencode-ai@latest"
116
+ },
117
+ "arm64": {
118
+ "linux": "npm install -g opencode-ai@latest",
119
+ "windows": "npm install -g opencode-ai@latest",
120
+ "macos": "npm install -g opencode-ai@latest"
121
+ }
122
+ }
123
+ },
124
+ {
125
+ "appName": "warp",
126
+ "repoURL": "CMD",
127
+ "doc": "Modern terminal with AI-powered features",
128
+ "fileNamePattern": {
129
+ "amd64": {
130
+ "linux": "https://app.warp.dev/download/cli?os=linux&package=deb&arch=x86_64",
131
+ "windows": null,
132
+ "macos": null
133
+ },
134
+ "arm64": {
135
+ "linux": "https://app.warp.dev/download/cli?os=linux&package=deb&arch=aarch64",
136
+ "windows": null,
137
+ "macos": null
138
+ }
139
+ }
140
+ },
141
+ {
142
+ "appName": "q",
143
+ "repoURL": "CMD",
144
+ "doc": "Q desktop application",
145
+ "fileNamePattern": {
146
+ "amd64": {
147
+ "linux": "q.sh",
148
+ "windows": null,
149
+ "macos": null
150
+ },
151
+ "arm64": {
152
+ "linux": null,
153
+ "windows": null,
154
+ "macos": null
155
+ }
156
+ }
157
+ },
158
+ {
159
+ "appName": "cursor-cli",
160
+ "repoURL": "CMD",
161
+ "doc": "Cursor CLI installer",
162
+ "fileNamePattern": {
163
+ "amd64": {
164
+ "linux": "curl https://cursor.com/install -fsS | bash",
165
+ "windows": null,
166
+ "macos": null
167
+ },
168
+ "arm64": {
169
+ "linux": null,
170
+ "windows": null,
171
+ "macos": null
172
+ }
173
+ }
174
+ },
175
+ {
176
+ "appName": "droid",
177
+ "repoURL": "CMD",
178
+ "doc": "Factory AI CLI installer",
179
+ "fileNamePattern": {
180
+ "amd64": {
181
+ "linux": "curl -fsSL https://app.factory.ai/cli | sh",
182
+ "windows": null,
183
+ "macos": null
184
+ },
185
+ "arm64": {
186
+ "linux": null,
187
+ "windows": null,
188
+ "macos": null
189
+ }
190
+ }
191
+ },
38
192
  {
39
193
  "appName": "Alacritty",
40
194
  "repoURL": "CMD",
@@ -245,17 +245,34 @@
245
245
  "doc": "🗃️ TUI DB Visualizer",
246
246
  "fileNamePattern": {
247
247
  "amd64": {
248
- "linux": "rainfrog-{version}-x86_64-unknown-linux-gnu.tar.gz",
248
+ "linux": "rainfrog-{version}-x86_64-unknown-linux-musl.tar.gz",
249
249
  "macos": null,
250
250
  "windows": null
251
251
  },
252
252
  "arm64": {
253
- "linux": "rainfrog-{version}-aarch64-unknown-linux-gnu.tar.gz",
253
+ "linux": "rainfrog-{version}-aarch64-unknown-linux-musl.tar.gz",
254
254
  "macos": null,
255
255
  "windows": null
256
256
  }
257
257
  }
258
258
  },
259
+ {
260
+ "appName": "duckdb",
261
+ "repoURL": "https://github.com/duckdb/duckdb",
262
+ "doc": "🗃️ An in-process SQL OLAP database management system",
263
+ "fileNamePattern": {
264
+ "amd64": {
265
+ "linux": "duckdb_cli-linux-amd64.zip",
266
+ "macos": "duckdb_cli-osx-universal.zip",
267
+ "windows": "duckdb_cli-windows-amd64.zip"
268
+ },
269
+ "arm64": {
270
+ "linux": "duckdb_cli-linux-arm64.zip",
271
+ "macos": "duckdb_cli-osx-universal.zip",
272
+ "windows": "duckdb_cli-windows-arm64.zip"
273
+ }
274
+ }
275
+ },
259
276
  {
260
277
  "appName": "cpz",
261
278
  "repoURL": "https://github.com/SUPERCILEX/fuc",
@@ -409,23 +409,6 @@
409
409
  }
410
410
  }
411
411
  },
412
- {
413
- "appName": "gemini-cli",
414
- "repoURL": "CMD",
415
- "doc": "🤖 Gemini CLI is a command line interface for Google Gemini AI.",
416
- "fileNamePattern": {
417
- "amd64": {
418
- "linux": "npm install -g @google/gemini-cli",
419
- "macos": "npm install -g @google/gemini-cli",
420
- "windows": "npm install -g @google/gemini-cli"
421
- },
422
- "arm64": {
423
- "linux": "npm install -g @google/gemini-cli",
424
- "macos": "npm install -g @google/gemini-cli",
425
- "windows": "npm install -g @google/gemini-cli"
426
- }
427
- }
428
- },
429
412
  {
430
413
  "appName": "gitui",
431
414
  "repoURL": "https://github.com/gitui-org/gitui",
@@ -1,26 +1,6 @@
1
1
  #!/bin/bash
2
2
 
3
3
 
4
- # Terminal-based CLI agents and tools for productivity and coding.
5
- npm install -g @github/copilot
6
- npm install -g @google/gemini-cli
7
- npm install -g @charmland/crush
8
- npm install -g opencode-ai@latest # or curl -fsSL https://opencode.ai/install | bash
9
- uv tool install --force --python python3.12 --with pip aider-chat@latest
10
- curl -fsSL https://app.factory.ai/cli | sh
11
- # WARP TERMINAL CLI
12
- # droid
13
-
14
-
15
- # cursor-cli
16
- curl https://cursor.com/install -fsS | bash
17
- cd $HOME/Downloads
18
- curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip" -o "q.zip"
19
- unzip q.zip
20
- ./q/install.sh # goes to ~/.local/bin.
21
- rm q.zip
22
- rm -rfd q
23
-
24
4
  # Vscode extensions for AI-assisted coding.
25
5
  # Github copilot
26
6
  # Roo
@@ -34,4 +14,3 @@ rm -rfd q
34
14
  # Kiro
35
15
  # Cursor
36
16
  # Warp
37
-
@@ -8,6 +8,42 @@ from typing import Optional, Literal, TypeAlias, cast, get_args, Annotated
8
8
  WHICH_CAT: TypeAlias = Literal["essentials", "essentialsDev", "systymPackages", "precheckedPackages", "ia"]
9
9
 
10
10
 
11
+ def _handle_installer_not_found(search_term: str, all_installers: list["InstallerData"]) -> None: # type: ignore
12
+ """Handle installer not found with friendly suggestions using fuzzy matching."""
13
+ from difflib import get_close_matches
14
+
15
+ # Get all possible names (both exe names and app names)
16
+ all_names = []
17
+ for inst in all_installers:
18
+ exe_name = inst["appName"]
19
+ all_names.append(exe_name)
20
+
21
+ # Find close matches using fuzzy matching
22
+ close_matches = get_close_matches(search_term, all_names, n=5, cutoff=0.4)
23
+
24
+ print(f"\n❌ '{search_term}' was not found.")
25
+
26
+ if close_matches:
27
+ print("🤔 Did you mean one of these?")
28
+ for i, match in enumerate(close_matches, 1):
29
+ print(f" {i}. {match}")
30
+ else:
31
+ print("📋 Here are some available options:")
32
+ # Show first 10 installers as examples
33
+ sample_names = []
34
+ for inst in all_installers[:10]:
35
+ exe_name = inst["appName"]
36
+ sample_names.append(exe_name)
37
+ for i, name in enumerate(sample_names, 1):
38
+ print(f" {i}. {name}")
39
+
40
+ if len(all_installers) > 10:
41
+ print(f" ... and {len(all_installers) - 10} more")
42
+
43
+ print("\n💡 Use 'ia' to interactively browse all available installers.")
44
+ print(f"💡 Use one of the categories: {list(get_args(WHICH_CAT))}")
45
+
46
+
11
47
  def main_with_parser():
12
48
  import typer
13
49
  app = typer.Typer()
@@ -20,6 +56,7 @@ def main(which: Annotated[Optional[str], typer.Argument(help=f"Choose a category
20
56
  return get_programs_by_category(program_name=which) # type: ignore
21
57
  from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
22
58
  from machineconfig.utils.installer import get_installers
59
+ from machineconfig.utils.installer_utils.installer_class import Installer
23
60
  if which != "ia" and which is not None: # install by name
24
61
  total_messages: list[str] = []
25
62
  for a_which in which.split(",") if type(which) == str else which:
@@ -28,22 +65,16 @@ def main(which: Annotated[Optional[str], typer.Argument(help=f"Choose a category
28
65
  # Find installer by exe_name or name
29
66
  selected_installer = None
30
67
  for installer in all_installers:
31
- exe_name = installer.installer_data.get("exeName", "")
32
- app_name = installer.installer_data.get("appName", "")
68
+ exe_name = installer["appName"]
69
+ app_name = installer["appName"]
33
70
  if exe_name == a_which or app_name == a_which:
34
71
  selected_installer = installer
35
72
  break
36
73
 
37
74
  if selected_installer is None:
38
- available_names = [f"{inst.installer_data.get('exeName', 'unknown')} ({inst.installer_data.get('appName', 'unknown')})" for inst in all_installers[:10]] # Show first 10
39
- raise ValueError(f"{a_which=} not found. Available installers include: {available_names}")
40
-
41
- print(f"""
42
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
43
- ┃ 🔧 Installing: {a_which}
44
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
45
- print(selected_installer)
46
- message = selected_installer.install_robust(version=None) # finish the task
75
+ _handle_installer_not_found(a_which, all_installers)
76
+ return None
77
+ message = Installer(selected_installer).install_robust(version=None) # finish the task
47
78
  total_messages.append(message)
48
79
  for a_message in total_messages:
49
80
  print(a_message)
@@ -55,58 +86,35 @@ def install_interactively():
55
86
  from machineconfig.utils.options import choose_from_options
56
87
  from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
57
88
  from machineconfig.utils.installer import get_installers
89
+ from machineconfig.utils.installer_utils.installer_class import Installer
58
90
  installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL", "GITHUB_DEV", "CUSTOM_DEV"])
59
- # Check installed programs with progress indicator
60
91
  with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}")) as progress:
61
92
  task = progress.add_task("✅ Checking installed programs...", total=len(installers))
62
93
  installer_options = []
63
94
  for x in installers:
64
- installer_options.append(x.get_description())
95
+ installer_options.append(Installer(installer_data=x).get_description())
65
96
  progress.update(task, advance=1)
66
-
67
- # Add category options at the beginning for better visibility
68
97
  category_options = [f"📦 {cat}" for cat in get_args(WHICH_CAT)]
69
- options = category_options + ["─" * 50] + installer_options
70
-
98
+ options = category_options + ["─" * 50] + installer_options
71
99
  program_names = choose_from_options(multi=True, msg="Categories are prefixed with 📦", options=options, header="🚀 CHOOSE DEV APP OR CATEGORY", default="📦 essentials", fzf=True)
72
-
73
- total_commands = ""
74
100
  installation_messages: list[str] = []
75
101
  for _an_idx, a_program_name in enumerate(program_names):
76
- # Skip separator lines
77
102
  if a_program_name.startswith("─"):
78
103
  continue
79
-
80
- print(f"""
81
- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
82
- ┃ 🔄 Processing: {a_program_name}
83
- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━""")
84
-
85
- # Handle category options (remove emoji prefix)
86
104
  if a_program_name.startswith("📦 "):
87
105
  category_name = a_program_name[2:] # Remove "📦 " prefix
88
106
  if category_name in get_args(WHICH_CAT):
89
107
  get_programs_by_category(program_name=cast(WHICH_CAT, category_name))
90
108
  else:
91
- # Handle individual installer options
92
109
  installer_idx = installer_options.index(a_program_name)
93
- an_installer = installers[installer_idx]
94
- status_message = an_installer.install_robust(version=None) # finish the task - this returns a status message, not a command
110
+ an_installer_data = installers[installer_idx]
111
+ status_message = Installer(an_installer_data).install_robust(version=None) # finish the task - this returns a status message, not a command
95
112
  installation_messages.append(status_message)
96
-
97
- # Print all installation status messages
98
113
  print("\n📊 INSTALLATION SUMMARY:")
99
114
  print("=" * 50)
100
115
  for message in installation_messages:
101
116
  print(message)
102
117
 
103
- # Only run shell commands if there are any (from category installations)
104
- if total_commands.strip():
105
- import subprocess
106
-
107
- print("\n🚀 Running additional shell commands...")
108
- subprocess.run(total_commands, shell=True, check=True)
109
-
110
118
 
111
119
  def get_programs_by_category(program_name: WHICH_CAT):
112
120
  print(f"""
@@ -121,10 +129,10 @@ def get_programs_by_category(program_name: WHICH_CAT):
121
129
  match program_name:
122
130
  case "essentials":
123
131
  installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL"])
124
- install_all(installers=installers_)
132
+ install_all(installers_data=installers_)
125
133
  case "essentialsDev":
126
134
  installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_DEV", "CUSTOM_DEV", "GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL"])
127
- install_all(installers=installers_)
135
+ install_all(installers_data=installers_)
128
136
  case "systymPackages":
129
137
  if system() == "Windows":
130
138
  options_system = parse_apps_installer_windows(LIBRARY_ROOT.joinpath("setup_windows/apps.ps1").read_text(encoding="utf-8"))
@@ -156,4 +164,7 @@ def get_programs_by_category(program_name: WHICH_CAT):
156
164
 
157
165
 
158
166
  if __name__ == "__main__":
167
+ from machineconfig.utils.schemas.installer.installer_types import InstallerData
168
+ from machineconfig.utils.installer_utils.installer_class import Installer
169
+ _ = InstallerData, Installer
159
170
  pass
@@ -16,7 +16,6 @@ from machineconfig.scripts.python.fire_agents_help_launch import prep_agent_laun
16
16
  from machineconfig.scripts.python.fire_agents_help_search import search_files_by_pattern, search_python_files
17
17
  from machineconfig.scripts.python.fire_agents_load_balancer import chunk_prompts, SPLITTING_STRATEGY, DEFAULT_AGENT_CAP
18
18
  from machineconfig.utils.options import choose_from_options
19
- from machineconfig.utils.schemas.layouts.layout_types import TabConfig, LayoutConfig
20
19
  from machineconfig.utils.accessories import get_repo_root
21
20
 
22
21
  SEARCH_STRATEGIES: TypeAlias = Literal["file_path", "keyword_search", "filename_pattern"]
@@ -150,26 +149,5 @@ manager.run_monitoring_routine()
150
149
  manager.run_monitoring_routine(wait_ms=2000)
151
150
 
152
151
 
153
- def split_too_many_tabs_to_run_in_sequential_sessions(layout_tabs: list[TabConfig], every: int):
154
- from machineconfig.utils.accessories import split_list
155
- from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
156
-
157
- for idx, layout_tabs_chunk in enumerate(split_list(layout_tabs, every=every, to=None)):
158
- a_layout_file: LayoutConfig = {"layoutName": f"split_{idx}", "layoutTabs": layout_tabs_chunk}
159
- manager = ZellijLocalManager(session_layouts=[a_layout_file])
160
- manager.start_all_sessions(poll_interval=2, poll_seconds=2)
161
- manager.run_monitoring_routine(wait_ms=2000)
162
- manager.kill_all_sessions()
163
-
164
-
165
- def split_too_many_layouts_to_run_in_sequential_sessions(layouts: list[LayoutConfig], every: int):
166
- from machineconfig.utils.accessories import split_list
167
- from machineconfig.cluster.sessions_managers.zellij_local_manager import ZellijLocalManager
168
- for _idx, layout_chunk in enumerate(split_list(layouts, every=every)):
169
- manager = ZellijLocalManager(session_layouts=layout_chunk)
170
- manager.start_all_sessions(poll_interval=2, poll_seconds=2)
171
- manager.run_monitoring_routine(wait_ms=2000)
172
-
173
-
174
152
  if __name__ == "__main__": # pragma: no cover
175
153
  main()
@@ -19,12 +19,12 @@ reference:
19
19
  """
20
20
 
21
21
 
22
- def display_terminal_url(local_ip_v4: str, port: int) -> None:
22
+ def display_terminal_url(local_ip_v4: str, port: int, protocol: str = "http") -> None:
23
23
  """Display a flashy, unmissable terminal URL announcement."""
24
24
  console = Console()
25
25
 
26
26
  # Create the main message with styling
27
- url_text = Text(f"http://{local_ip_v4}:{port}", style="bold bright_cyan underline")
27
+ url_text = Text(f"{protocol}://{local_ip_v4}:{port}", style="bold bright_cyan underline")
28
28
  message = Text.assemble(
29
29
  ("🚀 ", "bright_red"),
30
30
  ("Terminal is now accessible at: ", "bright_white bold"),
@@ -64,7 +64,11 @@ def install_ttyd():
64
64
  def main(
65
65
  port: Annotated[Optional[int], typer.Option("--port", "-p", help="Port to run the terminal server on (default: 7681)")] = None,
66
66
  username: Annotated[Optional[str], typer.Option("--username", "-u", help="Username for terminal access (default: current user)")] = None,
67
- password: Annotated[Optional[str], typer.Option("--password", "-w", help="Password for terminal access (default: from ~/dotfiles/creds/passwords/quick_password)")] = None
67
+ password: Annotated[Optional[str], typer.Option("--password", "-w", help="Password for terminal access (default: from ~/dotfiles/creds/passwords/quick_password)")] = None,
68
+ ssl: Annotated[bool, typer.Option("--ssl", "-S", help="Enable SSL")] = False,
69
+ ssl_cert: Annotated[Optional[str], typer.Option("--ssl-cert", "-C", help="SSL certificate file path")] = None,
70
+ ssl_key: Annotated[Optional[str], typer.Option("--ssl-key", "-K", help="SSL key file path")] = None,
71
+ ssl_ca: Annotated[Optional[str], typer.Option("--ssl-ca", "-A", help="SSL CA file path for client certificate verification")] = None
68
72
  ) -> None:
69
73
  install_ttyd()
70
74
  if username is None:
@@ -80,17 +84,44 @@ def main(
80
84
  if port is None:
81
85
  port = 7681 # Default port for ttyd
82
86
 
87
+ # Handle SSL certificate defaults
88
+ if ssl:
89
+ if ssl_cert is None:
90
+ ssl_cert = str(Path.home().joinpath("dotfiles/creds/passwords/ssl/origin_server/cert.pem"))
91
+ if ssl_key is None:
92
+ ssl_key = str(Path.home().joinpath("dotfiles/creds/passwords/ssl/origin_server/key.pem"))
93
+
94
+ # Verify SSL files exist
95
+ cert_path = Path(ssl_cert)
96
+ key_path = Path(ssl_key)
97
+
98
+ if not cert_path.exists():
99
+ raise FileNotFoundError(f"SSL certificate file not found: {ssl_cert}")
100
+ if not key_path.exists():
101
+ raise FileNotFoundError(f"SSL key file not found: {ssl_key}")
102
+
103
+ if ssl_ca and not Path(ssl_ca).exists():
104
+ raise FileNotFoundError(f"SSL CA file not found: {ssl_ca}")
105
+
83
106
  import socket
84
107
  s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
85
108
  s.connect(('8.8.8.8',80))
86
109
  local_ip_v4 = s.getsockname()[0]
87
110
  s.close()
88
111
 
89
- # Display the flashy terminal announcement
90
- display_terminal_url(local_ip_v4, port)
112
+ # Display the flashy terminal announcement
113
+ protocol = "https" if ssl else "http"
114
+ display_terminal_url(local_ip_v4, port, protocol)
115
+
116
+ # Build ttyd command with SSL options
117
+ ssl_args = ""
118
+ if ssl:
119
+ ssl_args = f"--ssl --ssl-cert {ssl_cert} --ssl-key {ssl_key}"
120
+ if ssl_ca:
121
+ ssl_args += f" --ssl-ca {ssl_ca}"
91
122
 
92
123
  code = f"""#!/bin/bash
93
- ttyd --writable -t enableSixel=true --port {port} --credential "{username}:{password}" -t 'theme={{"background": "black"}}' bash
124
+ ttyd --writable -t enableSixel=true {ssl_args} --port {port} --credential "{username}:{password}" -t 'theme={{"background": "black"}}' bash
94
125
  """
95
126
  import subprocess
96
127
  subprocess.run(code, shell=True, check=True)
@@ -21,8 +21,8 @@ def check_latest():
21
21
  installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["GITHUB_ESSENTIAL", "CUSTOM_ESSENTIAL"])
22
22
  installers_github = []
23
23
  for inst__ in installers:
24
- app_name = inst__.installer_data.get("appName", "unknown")
25
- repo_url = inst__.installer_data.get("repoURL", "")
24
+ app_name = inst__["appName"]
25
+ repo_url = inst__["repoURL"]
26
26
  if "ntop" in app_name:
27
27
  print(f"⏭️ Skipping {app_name} (ntop)")
28
28
  continue
@@ -91,7 +91,7 @@ def get_installed_cli_apps():
91
91
  return apps
92
92
 
93
93
 
94
- def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: list[APP_INSTALLER_CATEGORY]) -> list[Installer]:
94
+ def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: list[APP_INSTALLER_CATEGORY]) -> list[InstallerData]:
95
95
  print(f"\n{'=' * 80}\n🔍 LOADING INSTALLER CONFIGURATIONS 🔍\n{'=' * 80}")
96
96
  res_all = get_all_installer_data_files(which_cats=which_cats)
97
97
  all_installers: list[InstallerData] = []
@@ -103,7 +103,7 @@ def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: l
103
103
  suitable_installers.append(an_installer)
104
104
  all_installers.extend(suitable_installers)
105
105
  print(f"✅ Loaded {len(all_installers)} installer configurations\n{'=' * 80}")
106
- return [Installer(installer_data=installer_data) for installer_data in all_installers]
106
+ return all_installers
107
107
 
108
108
 
109
109
  def get_all_installer_data_files(which_cats: list[APP_INSTALLER_CATEGORY]) -> dict[APP_INSTALLER_CATEGORY, InstallerDataFiles]:
@@ -118,7 +118,7 @@ def get_all_installer_data_files(which_cats: list[APP_INSTALLER_CATEGORY]) -> di
118
118
  return res_final
119
119
 
120
120
 
121
- def install_all(installers: list[Installer], safe: bool = False, jobs: int = 10, fresh: bool = False):
121
+ def install_all(installers_data: list[InstallerData], safe: bool = False, jobs: int = 10, fresh: bool = False):
122
122
  print(f"\n{'=' * 80}\n🚀 BULK INSTALLATION PROCESS 🚀\n{'=' * 80}")
123
123
  if fresh:
124
124
  print("🧹 Fresh install requested - clearing version cache...")
@@ -151,14 +151,14 @@ def install_all(installers: list[Installer], safe: bool = False, jobs: int = 10,
151
151
  # print(f"✅ Safe installation completed\n{'='*80}")
152
152
  # return None
153
153
 
154
- print(f"🚀 Starting installation of {len(installers)} packages...")
154
+ print(f"🚀 Starting installation of {len(installers_data)} packages...")
155
155
  print(f"\n{'=' * 80}\n📦 INSTALLING FIRST PACKAGE 📦\n{'=' * 80}")
156
- installers[0].install(version=None)
157
- installers_remaining = installers[1:]
156
+ Installer(installers_data[0]).install(version=None)
157
+ installers_remaining = installers_data[1:]
158
158
  print(f"\n{'=' * 80}\n📦 INSTALLING REMAINING PACKAGES 📦\n{'=' * 80}")
159
159
 
160
160
  # Use joblib for parallel processing of remaining installers
161
- res = Parallel(n_jobs=jobs)(delayed(lambda x: x.install_robust(version=None))(installer) for installer in installers_remaining)
161
+ res = Parallel(n_jobs=jobs)(delayed(lambda x: Installer(x).install_robust(version=None))(installer) for installer in installers_remaining)
162
162
 
163
163
  console = Console()
164
164
 
@@ -4,7 +4,7 @@ Type definitions for the standardized layout configuration schema.
4
4
  This module defines the data structures that match the layout.json schema.
5
5
  """
6
6
 
7
- from typing import TypedDict, List, Literal
7
+ from typing import TypedDict, List, Literal, NotRequired
8
8
 
9
9
 
10
10
  class TabConfig(TypedDict):
@@ -13,6 +13,7 @@ class TabConfig(TypedDict):
13
13
  tabName: str
14
14
  startDir: str
15
15
  command: str
16
+ tabWeight: NotRequired[int] # Optional, defaults to 1 if not provided
16
17
 
17
18
 
18
19
  class LayoutConfig(TypedDict):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: machineconfig
3
- Version: 3.99
3
+ Version: 4.1
4
4
  Summary: Dotfiles management package
5
5
  Author-email: Alex Al-Saffar <programmer@usa.com>
6
6
  License: Apache 2.0
@@ -11,15 +11,16 @@ machineconfig/cluster/remote/remote_machine.py,sha256=xRuoHKNsIT0-FTFSvF1q7scnGK
11
11
  machineconfig/cluster/remote/script_execution.py,sha256=4U70FDtjOh6A6C2Ei-Xh90S888q64VhRPbExoEbdepk,9980
12
12
  machineconfig/cluster/remote/script_notify_upon_completion.py,sha256=GRxnnbnOl1-hTovTN-zI_M9wdV7x293yA77_mou9I1o,2032
13
13
  machineconfig/cluster/sessions_managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py,sha256=3vcQVg-HHa_WTxBGPtKMAdoSqJVa2EO5KAtrY8a6I3c,5264
15
14
  machineconfig/cluster/sessions_managers/wt_local.py,sha256=-e4yNxvu6ytI-u1Kihhtn_wd_qNKZMzN_WrZ9ab1qYA,18220
16
15
  machineconfig/cluster/sessions_managers/wt_local_manager.py,sha256=LqOuA9fqVDu5mWa04pxY9IUqMR4v-kDQbhZY0simvlQ,24193
17
16
  machineconfig/cluster/sessions_managers/wt_remote.py,sha256=XmZV9rLubwxND5UYAS15mAcpzDdXvm4KyubVGYkVBmo,8743
18
17
  machineconfig/cluster/sessions_managers/wt_remote_manager.py,sha256=CnnOtPiwLx0pIfs_KlcKnclLSqiDTNzfm8t8sW6nXf0,19764
19
- machineconfig/cluster/sessions_managers/zellij_local.py,sha256=NmlW-f1Tl_0vsWOtGT9Lh7dTCCUj4DVRLiizufS-VgE,26131
18
+ machineconfig/cluster/sessions_managers/zellij_local.py,sha256=T2wXkW9ug1nzdnhlD-1G2HPlBpBaNw6rdM9rk4KKeHQ,26137
20
19
  machineconfig/cluster/sessions_managers/zellij_local_manager.py,sha256=4ap0Gtd8wOybFc461OItA2kU-1jpgPQUX03s8Zjyk_4,24028
21
20
  machineconfig/cluster/sessions_managers/zellij_remote.py,sha256=3gz-wgqVW7B-4CgvJq0fiDh8691h4yeK3Xh3CfSPc2s,8749
22
21
  machineconfig/cluster/sessions_managers/zellij_remote_manager.py,sha256=T-j1KMV7mDTeGSHC5To0_JmqNtjSR_LVZT9VanP4lyI,8313
22
+ machineconfig/cluster/sessions_managers/utils/enhanced_command_runner.py,sha256=3vcQVg-HHa_WTxBGPtKMAdoSqJVa2EO5KAtrY8a6I3c,5264
23
+ machineconfig/cluster/sessions_managers/utils/load_balancer.py,sha256=ODnYCkMwpnD9ooMemJsaADAnmFSGELTW5QKJwepajL4,9051
23
24
  machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py,sha256=CFGcZPFTZQJtFf0OvMUHhadZ0qbImCP3wxvbWYVcVYo,7445
24
25
  machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py,sha256=Mitm7mKiKl5lT0OiEUHAqVg2Q21RjsKO1-hpJTHJ5lM,15196
25
26
  machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py,sha256=lApUy67_WhfaBXqt0meZSx_QvwiXjN0YLdyE3c7kP_s,6744
@@ -40,10 +41,10 @@ machineconfig/cluster/templates/run_remote.py,sha256=vCc56t8BUAUJp7tyb0PFfwy5hlm
40
41
  machineconfig/cluster/templates/utils.py,sha256=5lHgjHvodoSPBD31AwluHBBNgwimwThUsDNWGN8iH9I,1647
41
42
  machineconfig/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
43
  machineconfig/jobs/installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
- machineconfig/jobs/installer/packages_custom_dev.json,sha256=oJBj5blm8QLaMP72ZP9JE--oUiaweQSOFIAq-ySV_Wc,5646
44
+ machineconfig/jobs/installer/packages_custom_dev.json,sha256=MiHmV7hCfa4RzmO6LK3PPz0VGn3yQJgPJxrSgxKvwdc,10027
44
45
  machineconfig/jobs/installer/packages_custom_essential.json,sha256=-Mgu436rbGE7JWCy-NYxxsnkxCxPxv4Gatu0wXp5XMI,775
45
- machineconfig/jobs/installer/packages_github_dev.json,sha256=CyadNrKml42lzPwdAlpNRyEtTS12p9dbjMrugOtvLVQ,31320
46
- machineconfig/jobs/installer/packages_github_essential.json,sha256=sZZczoJ_d0gC6865NRKYATOC7GQD1mKF6s07W5tsM9s,27054
46
+ machineconfig/jobs/installer/packages_github_dev.json,sha256=11LVsvIVKMjp8i3y_TNO31IpXaZgQWGGlmhMa_wUilE,31889
47
+ machineconfig/jobs/installer/packages_github_essential.json,sha256=dCGFovFPCnEu-4wed7FJDBxPHdz8ekkAK58VTQobsdg,26472
47
48
  machineconfig/jobs/installer/custom/gh.py,sha256=hic8Z1ZdncbljY6hgHzl2rmOLOcb6imIUstMALSS3-Y,3082
48
49
  machineconfig/jobs/installer/custom/hx.py,sha256=FrUD0mlhWG2GxrnarccVIaW_sHJ5mxeF6yyzbBWyx6w,5840
49
50
  machineconfig/jobs/installer/custom_dev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -65,14 +66,16 @@ machineconfig/jobs/installer/linux_scripts/docker.sh,sha256=xP219QeQ5eLwhvOHsNYq
65
66
  machineconfig/jobs/installer/linux_scripts/docker_start.sh,sha256=TiS-hWRltw66_p_Fq8gfkA_rqUIuwxoIkFZItVF6TXA,1478
66
67
  machineconfig/jobs/installer/linux_scripts/edge.sh,sha256=2JclQOi6sBTs2VQPsQWh6dPFRu1C36OyRREI0a76yJQ,1903
67
68
  machineconfig/jobs/installer/linux_scripts/nerdfont.sh,sha256=vSJQfGYVpWF2T1vz1zZuVXdeus_PWXdP7VquRQjsLwU,2351
69
+ machineconfig/jobs/installer/linux_scripts/ngrok.sh,sha256=K-t62nhnAHxhppTmqjubIJRHozkNHfBxXGbn1Oz3w-A,287
68
70
  machineconfig/jobs/installer/linux_scripts/pgsql.sh,sha256=qe_yo4dM16B8L1VYIPqyLVvI1uns2jg7if_cokuOofY,2239
71
+ machineconfig/jobs/installer/linux_scripts/q.sh,sha256=jXokwpfwotI7X5v1dXbYKZyH5ifzSCOek-d_BociyY0,228
69
72
  machineconfig/jobs/installer/linux_scripts/redis.sh,sha256=XNsnypuP0UQDdNnLQ1rHC8XRYeqeaM213i1aga5_Q0M,2940
70
73
  machineconfig/jobs/installer/linux_scripts/timescaledb.sh,sha256=-thz4K7Eo_4IsNTQMLsmQdyMdVhW7NAMn5S2CB3-blE,3530
71
74
  machineconfig/jobs/installer/linux_scripts/vscode.sh,sha256=8S0nZpOeAdhpVHvygt6rs1sywCtjHNECfNNRhXMHWW8,5417
72
75
  machineconfig/jobs/installer/linux_scripts/warp-cli.sh,sha256=PVNLeYWdh3XEFllCVZDYIHBI42btjGlH5jbyXjJGz-Y,3033
73
76
  machineconfig/jobs/installer/linux_scripts/wezterm.sh,sha256=m697rRoIIVk-f8JdI1YQmphk-JWpMc5IYbD5YaQ3SeQ,1874
74
77
  machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1,sha256=JsQfGAMkvirhiUmBNOifMlbum2PfHSs0-Akgj-J-WZw,3177
75
- machineconfig/jobs/linux/msc/cli_agents.sh,sha256=sNT3NWyRnBPKTSsMPocLbLuyRupSR1S3yE6UF0RxI70,855
78
+ machineconfig/jobs/linux/msc/cli_agents.sh,sha256=MMa_cd4yijI69c7tztTY1b0tl9I1ECTbxqLCShElhFU,184
76
79
  machineconfig/jobs/linux/msc/lid.sh,sha256=09LeoSaXCGjCn7YxPcIFQpHroYdglJlEtFU2agarh3I,1302
77
80
  machineconfig/jobs/linux/msc/network.sh,sha256=dmISsh0hioDheinqee3qHfo2k7ClFx6G_GfGDxuflmc,1796
78
81
  machineconfig/jobs/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -148,10 +151,10 @@ machineconfig/scripts/python/devops.py,sha256=W18odC0__ZNGY_WAI-Ojr9Vf3WUTqcXk90
148
151
  machineconfig/scripts/python/devops_add_identity.py,sha256=JfN3ZrYMCgmt4ks_VCfnV9BIIHAsOYO3E0W0wZ15FR8,3791
149
152
  machineconfig/scripts/python/devops_add_ssh_key.py,sha256=KaoX83KltBsmutfKhSfZjd7nP_R1hJ2OLAWRhbswO7o,6889
150
153
  machineconfig/scripts/python/devops_backup_retrieve.py,sha256=jZe5Vki7E2GCMG8hvqUZeOONFC4cNzISoGzq_dMG4GA,5601
151
- machineconfig/scripts/python/devops_devapps_install.py,sha256=Oe8u6_EurEltEtJagcuapz-G3DyZdRyWqQ8kSEp-zHQ,8911
154
+ machineconfig/scripts/python/devops_devapps_install.py,sha256=QbRQhNdDRLLtgJwaRl2pbLmWvajb1b_Xte2ql8N3JRs,9096
152
155
  machineconfig/scripts/python/devops_update_repos.py,sha256=c5qBc9cuTGDEqDHufkjDT4d_vvJsswv3tlqk9MAulYk,8063
153
156
  machineconfig/scripts/python/dotfile.py,sha256=SRcX-9Ak1jRvF-killBTTm2IWcsNxfiLucH6ZsytAFA,2202
154
- machineconfig/scripts/python/fire_agents.py,sha256=IpglbN30zJ1jIBzHNCR2LmPULoIdFaj2M0JBsEfZEZg,9216
157
+ machineconfig/scripts/python/fire_agents.py,sha256=BgWyzac_yXzp4xpzcEOQ62UeVIiH-KR-KURAwaiAF-4,7936
155
158
  machineconfig/scripts/python/fire_agents_help_launch.py,sha256=sTdjNz2pDinDMMjUAMN7OqH-KAUeHh6Aihr_zUvtM6k,6128
156
159
  machineconfig/scripts/python/fire_agents_help_search.py,sha256=qIfSS_su2YJ1Gb0_lu4cbjlJlYMBw0v52NTGiSrGjk8,2991
157
160
  machineconfig/scripts/python/fire_agents_load_balancer.py,sha256=QPiCbQq9j5REHStPdYqQcGNkz_rp5CjotqOpMY3v5TM,2099
@@ -174,7 +177,7 @@ machineconfig/scripts/python/repos_helper_clone.py,sha256=xW5YZEoNt3k7h9NIULhUhO
174
177
  machineconfig/scripts/python/repos_helper_record.py,sha256=YEEQORfEiLddOIIgePo5eEkyQUFruFg3kc8npMvRL-o,10927
175
178
  machineconfig/scripts/python/repos_helper_update.py,sha256=AYyKIB7eQ48yoYmFjydIhRI1lV39TBv_S4_LCa-oKuQ,11042
176
179
  machineconfig/scripts/python/scheduler.py,sha256=rKhssuxkD697EY6qaV6CSdNhxpAQLDWO4fE8GMCQ9FA,3061
177
- machineconfig/scripts/python/share_terminal.py,sha256=3d0tOjAiXbonuJprHitoUmyU4zVLnexQGdTNs1-5qq0,3455
180
+ machineconfig/scripts/python/share_terminal.py,sha256=fVE0_c70EEfTNyhVov4HNtDXjR-Kx0wn0Mz1CKYEij8,4991
178
181
  machineconfig/scripts/python/snapshot.py,sha256=aDvKeoniZaeTSNv9zWBUajaj2yagAxVdfuvO1_tgq5Y,1026
179
182
  machineconfig/scripts/python/start_slidev.py,sha256=U5ujAL7R5Gd5CzFReTsnF2SThjY91aFBg0Qz_MMl6U4,4573
180
183
  machineconfig/scripts/python/start_terminals.py,sha256=DRWbMZumhPmL0DvvsCsbRNFL5AVQn1SgaziafTio3YQ,6149
@@ -375,7 +378,7 @@ machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py,sha256=rZZJamy3YxAeJh
375
378
  machineconfig/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
376
379
  machineconfig/utils/accessories.py,sha256=W_9dLzjwNTW5JQk_pe3B2ijQ1nA2-8Kdg2r7VBtzgQs,4340
377
380
  machineconfig/utils/code.py,sha256=pKPHInKgXJWeACVbxuE7sMdYeZCbNttaYCsfonGhFfc,4464
378
- machineconfig/utils/installer.py,sha256=Cfu0arQ-QE2TfDThP5Bb5HHpDA7kHlsWpAqGFrTA8c0,8954
381
+ machineconfig/utils/installer.py,sha256=AVAzuJqWkbMmfEk5_eKLGA3kgM6PCTXq7We2QoppZo0,8886
379
382
  machineconfig/utils/io.py,sha256=ZXB3aataS1IZ_0WMcCRSmoN1nbkvEO-bWYcs-TpngqU,2872
380
383
  machineconfig/utils/links.py,sha256=riNUrG8aGElRszdOPOic4M2AyOcpdcth_-y8JEiZpJ4,10253
381
384
  machineconfig/utils/notifications.py,sha256=vvdsY5IX6XEiILTnt5lNyHxhCi0ljdGX2T_67VRfrG4,9009
@@ -400,10 +403,10 @@ machineconfig/utils/installer_utils/installer_abc.py,sha256=3jjLHL1xqTPowx52r7Bf
400
403
  machineconfig/utils/installer_utils/installer_class.py,sha256=6IQswaC9mxIdeaMG-rOt-vqyKGYibBRMvC0UglZ_3mI,20268
401
404
  machineconfig/utils/schemas/fire_agents/fire_agents_input.py,sha256=CCs5ebomW1acKWZRpv9dyDzM-W6pwvVplikcutE2D8I,2339
402
405
  machineconfig/utils/schemas/installer/installer_types.py,sha256=DLagmIe0G5-xg7HZ9VrlFCDk1gIbwvX7O4gZjwq0wh0,1326
403
- machineconfig/utils/schemas/layouts/layout_types.py,sha256=M1ZFCz_kjRZPhxM19rIYUDR5lDDpwa09odR_ihtIFq0,1932
406
+ machineconfig/utils/schemas/layouts/layout_types.py,sha256=TcqlZdGVoH8htG5fHn1KWXhRdPueAcoyApppZsPAPto,2020
404
407
  machineconfig/utils/schemas/repos/repos_types.py,sha256=ECVr-3IVIo8yjmYmVXX2mnDDN1SLSwvQIhx4KDDQHBQ,405
405
- machineconfig-3.99.dist-info/METADATA,sha256=ImlRXsIaAQ9MQcpG5xYgzOi0yiWEoRsKPMj-_QfZ7Dc,7032
406
- machineconfig-3.99.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
407
- machineconfig-3.99.dist-info/entry_points.txt,sha256=c6ea0waVseT1rbfz1bw3k-eph2yVaB67x9hx64Mpvfs,1066
408
- machineconfig-3.99.dist-info/top_level.txt,sha256=porRtB8qms8fOIUJgK-tO83_FeH6Bpe12oUVC670teA,14
409
- machineconfig-3.99.dist-info/RECORD,,
408
+ machineconfig-4.1.dist-info/METADATA,sha256=b3JCS_zyxDLbFlIDEu5k4EdmkuOHwIIclgCSorz6Jdg,7031
409
+ machineconfig-4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
410
+ machineconfig-4.1.dist-info/entry_points.txt,sha256=c6ea0waVseT1rbfz1bw3k-eph2yVaB67x9hx64Mpvfs,1066
411
+ machineconfig-4.1.dist-info/top_level.txt,sha256=porRtB8qms8fOIUJgK-tO83_FeH6Bpe12oUVC670teA,14
412
+ machineconfig-4.1.dist-info/RECORD,,