machineconfig 7.57__py3-none-any.whl → 7.64__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 (61) hide show
  1. machineconfig/cluster/sessions_managers/utils/maker.py +19 -9
  2. machineconfig/jobs/installer/custom/boxes.py +2 -2
  3. machineconfig/jobs/installer/custom/hx.py +3 -3
  4. machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
  5. machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
  6. machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
  7. machineconfig/jobs/installer/custom_dev/sysabc.py +10 -28
  8. machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
  9. machineconfig/jobs/installer/installer_data.json +57 -23
  10. machineconfig/jobs/installer/package_groups.py +20 -13
  11. machineconfig/scripts/linux/wrap_mcfg +1 -1
  12. machineconfig/scripts/python/croshell.py +4 -4
  13. machineconfig/scripts/python/devops.py +2 -3
  14. machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
  15. machineconfig/scripts/python/fire_jobs.py +6 -5
  16. machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
  17. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
  18. machineconfig/scripts/python/helpers_devops/cli_self.py +3 -3
  19. machineconfig/scripts/python/helpers_devops/cli_share_file.py +2 -2
  20. machineconfig/scripts/python/helpers_devops/cli_share_server.py +1 -1
  21. machineconfig/scripts/python/helpers_devops/cli_terminal.py +1 -1
  22. machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -72
  23. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
  24. machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
  25. machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +3 -4
  26. machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +3 -2
  27. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
  28. machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
  29. machineconfig/scripts/python/helpers_repos/record.py +2 -1
  30. machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
  31. machineconfig/scripts/python/helpers_utils/download.py +151 -0
  32. machineconfig/scripts/python/helpers_utils/path.py +1 -1
  33. machineconfig/scripts/python/interactive.py +2 -2
  34. machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
  35. machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
  36. machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
  37. machineconfig/scripts/python/sessions.py +35 -20
  38. machineconfig/scripts/python/utils.py +2 -1
  39. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
  40. machineconfig/settings/shells/zsh/init.sh +0 -7
  41. machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
  42. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
  43. machineconfig/utils/code.py +2 -2
  44. machineconfig/utils/files/headers.py +2 -2
  45. machineconfig/utils/installer_utils/installer_class.py +39 -41
  46. machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +59 -58
  47. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
  48. machineconfig/utils/meta.py +29 -15
  49. machineconfig/utils/options.py +1 -1
  50. machineconfig/utils/path_extended.py +2 -2
  51. machineconfig/utils/path_helper.py +34 -31
  52. machineconfig/utils/schemas/layouts/layout_types.py +1 -1
  53. machineconfig/utils/ssh.py +1 -1
  54. {machineconfig-7.57.dist-info → machineconfig-7.64.dist-info}/METADATA +1 -1
  55. {machineconfig-7.57.dist-info → machineconfig-7.64.dist-info}/RECORD +60 -59
  56. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  57. /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  58. /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
  59. {machineconfig-7.57.dist-info → machineconfig-7.64.dist-info}/WHEEL +0 -0
  60. {machineconfig-7.57.dist-info → machineconfig-7.64.dist-info}/entry_points.txt +0 -0
  61. {machineconfig-7.57.dist-info → machineconfig-7.64.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  """package manager"""
2
2
 
3
- from machineconfig.utils.installer_utils.installer_abc import check_if_installed_already
3
+ from machineconfig.utils.installer_utils.installer_locator_utils import check_if_installed_already
4
4
  from machineconfig.utils.installer_utils.installer_class import Installer
5
5
  from machineconfig.utils.schemas.installer.installer_types import InstallerData, InstallerDataFiles, get_normalized_arch, get_os_name, OPERATING_SYSTEMS, CPU_ARCHITECTURES
6
6
  from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
@@ -132,30 +132,6 @@ def install_bulk(installers_data: list[InstallerData], safe: bool = False, jobs:
132
132
  print("✅ Version cache cleared")
133
133
  if safe:
134
134
  pass
135
- # print("⚠️ Safe installation mode activated...")
136
- # from machineconfig.jobs.python.check_installations import APP_SUMMARY_PATH
137
- # if platform.system().lower() == "windows":
138
- # print("🪟 Moving applications to Windows Apps folder...")
139
- # # PathExtended.get_env().WindowsPaths().WindowsApps)
140
- # folder = PathExtended.home().joinpath("AppData/Local/Microsoft/WindowsApps")
141
- # apps_dir.search("*").apply(lambda app: app.move(folder=folder))
142
- # elif platform.system().lower() in ["linux", "darwin"]:
143
- # system_name = "Linux" if platform.system().lower() == "linux" else "macOS"
144
- # print(f"🐧 Moving applications to {system_name} bin folder...")
145
- # if platform.system().lower() == "linux":
146
- # install_path = LINUX_INSTALL_PATH
147
- # else: # Darwin/macOS
148
- # install_path = "/usr/local/bin"
149
- # Terminal().run(f"sudo mv {apps_dir.as_posix()}/* {install_path}/").capture().print_if_unsuccessful(desc=f"MOVING executable to {install_path}", strict_err=True, strict_returncode=True)
150
- # else:
151
- # error_msg = f"❌ ERROR: System {platform.system()} not supported"
152
- # print(error_msg)
153
- # raise NotImplementedError(error_msg)
154
-
155
- # apps_dir.delete(sure=True)
156
- # print(f"✅ Safe installation completed\n{'='*80}")
157
- # return None
158
-
159
135
  print(f"🚀 Starting installation of {len(installers_data)} packages...")
160
136
  print("📦 INSTALLING FIRST PACKAGE 📦")
161
137
  Installer(installers_data[0]).install(version=None)
@@ -55,6 +55,16 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
55
55
  import types as _types
56
56
  from pathlib import Path as _Path
57
57
 
58
+ def _stringify_annotation(annotation: Any) -> Any:
59
+ if annotation is _inspect.Signature.empty or annotation is _inspect.Parameter.empty:
60
+ return annotation
61
+ if isinstance(annotation, str):
62
+ return annotation
63
+ try:
64
+ return _inspect.formatannotation(annotation)
65
+ except Exception:
66
+ return str(annotation)
67
+
58
68
  # sanity checks
59
69
  if not (callable(lmb) and isinstance(lmb, _types.LambdaType)):
60
70
  raise TypeError("Expected a lambda function object")
@@ -174,16 +184,18 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
174
184
  else:
175
185
  new_default = param.default
176
186
 
177
- # Recreate the Parameter (keeping annotation and kind)
187
+ normalized_annotation = _stringify_annotation(param.annotation)
188
+
178
189
  if new_default is _inspect.Parameter.empty:
179
- new_param = _inspect.Parameter(name, param.kind, annotation=param.annotation)
190
+ new_param = _inspect.Parameter(name, param.kind, annotation=normalized_annotation)
180
191
  else:
181
192
  new_param = _inspect.Parameter(
182
- name, param.kind, default=new_default, annotation=param.annotation
193
+ name, param.kind, default=new_default, annotation=normalized_annotation
183
194
  )
184
195
  new_params.append(new_param)
185
196
 
186
- new_sig = _inspect.Signature(parameters=new_params, return_annotation=sig.return_annotation)
197
+ return_annotation = _stringify_annotation(sig.return_annotation)
198
+ new_sig = _inspect.Signature(parameters=new_params, return_annotation=return_annotation)
187
199
 
188
200
  # If in_global mode, return kwargs as global assignments + dedented body
189
201
  if in_global:
@@ -200,15 +212,11 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
200
212
 
201
213
  # Build type annotation string if available
202
214
  if param.annotation is not _inspect.Parameter.empty:
203
- # Try to get a nice string representation of the annotation
204
- try:
205
- if hasattr(param.annotation, "__name__"):
206
- type_str = param.annotation.__name__
207
- else:
208
- type_str = str(param.annotation)
209
- except Exception:
210
- type_str = str(param.annotation)
211
- global_assignments.append(f"{name}: {type_str} = {repr(value)}")
215
+ annotation_literal = _stringify_annotation(param.annotation)
216
+ if isinstance(annotation_literal, str):
217
+ global_assignments.append(f"{name}: {repr(annotation_literal)} = {repr(value)}")
218
+ else:
219
+ global_assignments.append(f"{name} = {repr(value)}")
212
220
  else:
213
221
  global_assignments.append(f"{name} = {repr(value)}")
214
222
 
@@ -233,10 +241,16 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
233
241
 
234
242
  if "Optional" in result_text or "Any" in result_text or "Union" in result_text or "Literal" in result_text:
235
243
  result_text = "from typing import Optional, Any, Union, Literal\n\n" + result_text
236
-
237
244
  if import_prefix:
238
245
  result_text = f"{import_prefix}{result_text}"
239
246
  return result_text
240
247
 
241
248
  if __name__ == "__main__":
242
- pass
249
+ from machineconfig.utils.code import print_code
250
+ import_code_robust = "<import_code_robust>"
251
+ res = lambda_to_python_script(
252
+ lambda: print_code(code=import_code_robust, lexer="python", desc="import as module code"),
253
+ # in_global=True, import_module=False
254
+ in_global=True, import_module=False
255
+ )
256
+ print(res)
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from machineconfig.utils.installer_utils.installer_abc import check_tool_exists
2
+ from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
3
3
  from rich.text import Text
4
4
  from rich.panel import Panel
5
5
  from rich.console import Console
@@ -16,6 +16,7 @@ OPLike: TypeAlias = Union[str, "PathExtended", Path, None]
16
16
  PLike: TypeAlias = Union[str, "PathExtended", Path]
17
17
  FILE_MODE: TypeAlias = Literal["r", "w", "x", "a"]
18
18
  SHUTIL_FORMATS: TypeAlias = Literal["zip", "tar", "gztar", "bztar", "xztar"]
19
+ DECOMPRESS_SUPPORTED_FORMATS = [".tar.gz", ".tgz", ".tar", ".gz", ".tar.bz", ".tbz", ".tar.xz", ".zip", ".7z"]
19
20
 
20
21
 
21
22
  def _is_user_admin() -> bool:
@@ -152,7 +153,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
152
153
  # ======================================= File Editing / Reading ===================================
153
154
  def download(self, folder: OPLike = None, name: Optional[str] = None, allow_redirects: bool = True, timeout: Optional[int] = None, params: Any = None) -> "PathExtended":
154
155
  import requests
155
-
156
156
  response = requests.get(self.as_url_str(), allow_redirects=allow_redirects, timeout=timeout, params=params) # Alternative: from urllib import request; request.urlopen(url).read().decode('utf-8').
157
157
  assert response.status_code == 200, f"Download failed with status code {response.status_code}\n{response.text}"
158
158
  if name is not None:
@@ -809,7 +809,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
809
809
  path = self
810
810
  else:
811
811
  try:
812
- path = self.rel2home()
812
+ path = PathExtended(self.expanduser().absolute().relative_to(Path.home()))
813
813
  except ValueError as ve:
814
814
  if strict:
815
815
  raise ve
@@ -1,4 +1,4 @@
1
- from machineconfig.utils.path_extended import PathExtended
1
+ # from machineconfig.utils.path_extended import Path
2
2
  from machineconfig.utils.source_of_truth import EXCLUDE_DIRS
3
3
  from rich.console import Console
4
4
  from rich.panel import Panel
@@ -10,8 +10,8 @@ from typing import Optional
10
10
  console = Console()
11
11
 
12
12
 
13
- def sanitize_path(a_path: str) -> PathExtended:
14
- path = PathExtended(a_path)
13
+ def sanitize_path(a_path: str) -> Path:
14
+ path = Path(a_path)
15
15
  if Path.cwd() == Path.home() and not path.exists():
16
16
  result = input("Current working directory is home, and passed path is not full path, are you sure you want to continue, [y]/n? ") or "y"
17
17
  if result == "y":
@@ -22,13 +22,13 @@ def sanitize_path(a_path: str) -> PathExtended:
22
22
  if platform.system() == "Windows": # path copied from Linux/Mac to Windows
23
23
  # For Linux: /home/username, for Mac: /Users/username
24
24
  skip_parts = 3 if path.as_posix().startswith("/home") else 3 # Both have 3 parts to skip
25
- path = PathExtended.home().joinpath(*path.parts[skip_parts:])
25
+ path = Path.home().joinpath(*path.parts[skip_parts:])
26
26
  assert path.exists(), f"File not found: {path}"
27
27
  source_os = "Linux" if path.as_posix().startswith("/home") else "macOS"
28
28
  console.print(Panel(f"🔗 PATH MAPPING | {source_os} → Windows: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
29
- elif platform.system() in ["Linux", "Darwin"] and PathExtended.home().as_posix() not in path.as_posix(): # copied between Unix-like systems with different username
29
+ elif platform.system() in ["Linux", "Darwin"] and Path.home().as_posix() not in path.as_posix(): # copied between Unix-like systems with different username
30
30
  skip_parts = 3 # Both /home/username and /Users/username have 3 parts to skip
31
- path = PathExtended.home().joinpath(*path.parts[skip_parts:])
31
+ path = Path.home().joinpath(*path.parts[skip_parts:])
32
32
  assert path.exists(), f"File not found: {path}"
33
33
  current_os = "Linux" if platform.system() == "Linux" else "macOS"
34
34
  source_os = "Linux" if path.as_posix().startswith("/home") else "macOS"
@@ -36,12 +36,12 @@ def sanitize_path(a_path: str) -> PathExtended:
36
36
  elif path.as_posix().startswith("C:"):
37
37
  if platform.system() in ["Linux", "Darwin"]: # path copied from Windows to Linux/Mac
38
38
  xx = str(a_path).replace("\\\\", "/")
39
- path = PathExtended.home().joinpath(*PathExtended(xx).parts[3:]) # exclude C:\\Users\\username
39
+ path = Path.home().joinpath(*Path(xx).parts[3:]) # exclude C:\\Users\\username
40
40
  assert path.exists(), f"File not found: {path}"
41
41
  target_os = "Linux" if platform.system() == "Linux" else "macOS"
42
42
  console.print(Panel(f"🔗 PATH MAPPING | Windows → {target_os}: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
43
- elif platform.system() == "Windows" and PathExtended.home().as_posix() not in path.as_posix(): # copied from Windows to Windows with different username
44
- path = PathExtended.home().joinpath(*path.parts[2:])
43
+ elif platform.system() == "Windows" and Path.home().as_posix() not in path.as_posix(): # copied from Windows to Windows with different username
44
+ path = Path.home().joinpath(*path.parts[2:])
45
45
  assert path.exists(), f"File not found: {path}"
46
46
  console.print(Panel(f"🔗 PATH MAPPING | Windows → Windows: `{a_path}` ➡️ `{path}`", title="Path Mapping", expand=False))
47
47
  return path.expanduser().absolute()
@@ -66,12 +66,12 @@ def find_scripts(root: Path, name_substring: str, suffixes: set[str]) -> tuple[l
66
66
  return filename_matches, partial_path_matches
67
67
 
68
68
 
69
- def match_file_name(sub_string: str, search_root: PathExtended, suffixes: set[str]) -> PathExtended:
69
+ def match_file_name(sub_string: str, search_root: Path, suffixes: set[str]) -> Path:
70
70
  search_root_obj = search_root.absolute()
71
71
  # assume subscript is filename only, not a sub_path. There is no need to fzf over the paths.
72
72
  filename_matches, partial_path_matches = find_scripts(search_root_obj, sub_string, suffixes)
73
73
  if len(filename_matches) == 1:
74
- return PathExtended(filename_matches[0])
74
+ return Path(filename_matches[0])
75
75
  console.print(Panel(f"Partial filename {search_root_obj} match with case-insensitivity failed. This generated #{len(filename_matches)} results.", title="Search", expand=False))
76
76
  if len(filename_matches) < 20:
77
77
  print("\n".join([a_potential_match.as_posix() for a_potential_match in filename_matches]))
@@ -80,23 +80,23 @@ def match_file_name(sub_string: str, search_root: PathExtended, suffixes: set[st
80
80
  # let's see if avoiding .lower() helps narrowing down to one result
81
81
  reduced_scripts = [a_potential_match for a_potential_match in filename_matches if sub_string in a_potential_match.name]
82
82
  if len(reduced_scripts) == 1:
83
- return PathExtended(reduced_scripts[0])
83
+ return Path(reduced_scripts[0])
84
84
  elif len(reduced_scripts) > 1:
85
85
  from machineconfig.utils.options import choose_from_options
86
86
  choice = choose_from_options(multi=False, msg="Multiple matches found", options=reduced_scripts, fzf=True)
87
- return PathExtended(choice)
87
+ return Path(choice)
88
88
  print(f"Result: This still generated {len(reduced_scripts)} results.")
89
89
  if len(reduced_scripts) < 10:
90
90
  print("\n".join([a_potential_match.as_posix() for a_potential_match in reduced_scripts]))
91
91
 
92
92
  console.print(Panel(f"Partial path match with case-insensitivity failed. This generated #{len(partial_path_matches)} results.", title="Search", expand=False))
93
93
  if len(partial_path_matches) == 1:
94
- return PathExtended(partial_path_matches[0])
94
+ return Path(partial_path_matches[0])
95
95
  elif len(partial_path_matches) > 1:
96
96
  print("Try to narrow down partial_path_matches search by case-sensitivity.")
97
97
  reduced_scripts = [a_potential_match for a_potential_match in partial_path_matches if sub_string in a_potential_match.as_posix()]
98
98
  if len(reduced_scripts) == 1:
99
- return PathExtended(reduced_scripts[0])
99
+ return Path(reduced_scripts[0])
100
100
  print(f"Result: This still generated {len(reduced_scripts)} results.")
101
101
 
102
102
  try:
@@ -131,21 +131,24 @@ def match_file_name(sub_string: str, search_root: PathExtended, suffixes: set[st
131
131
  return search_root_obj.joinpath(res)
132
132
 
133
133
 
134
- def search_for_files_of_interest(path_obj: PathExtended, suffixes: set[str]):
135
- if path_obj.joinpath(".venv").exists():
136
- path_objects = path_obj.search("*", not_in=[".venv"])
137
- files: list[PathExtended] = []
138
- for a_path_obj in path_objects:
139
- files += search_for_files_of_interest(path_obj=a_path_obj, suffixes=suffixes)
140
- return files
134
+ def search_for_files_of_interest(path_obj: Path, suffixes: set[str]) -> list[Path]:
141
135
  if path_obj.is_file():
142
136
  return [path_obj]
143
- files: list[PathExtended] = []
144
- for a_suffix in suffixes:
145
- if a_suffix == ".py":
146
- files += path_obj.search(pattern="*.py", r=True, not_in=["__init__.py"])
147
- else:
148
- files += path_obj.search(pattern=f"*{a_suffix}", r=True)
137
+ files: list[Path] = []
138
+ directories_to_visit: list[Path] = [path_obj]
139
+ while directories_to_visit:
140
+ current_dir = directories_to_visit.pop()
141
+ for entry in current_dir.iterdir():
142
+ if entry.is_dir():
143
+ if entry.name == ".venv":
144
+ continue
145
+ directories_to_visit.append(entry)
146
+ continue
147
+ if entry.suffix not in suffixes:
148
+ continue
149
+ if entry.suffix == ".py" and entry.name == "__init__.py":
150
+ continue
151
+ files.append(entry)
149
152
  return files
150
153
 
151
154
 
@@ -160,15 +163,15 @@ def get_choice_file(path: str, suffixes: Optional[set[str]]):
160
163
  else:
161
164
  suffixes = {".py"}
162
165
  if not path_obj.exists():
163
- print(f"🔍 Searching for file matching `{path}` under `{PathExtended.cwd()}`, but only if suffix matches {suffixes}")
164
- choice_file = match_file_name(sub_string=path, search_root=PathExtended.cwd(), suffixes=suffixes)
166
+ print(f"🔍 Searching for file matching `{path}` under `{Path.cwd()}`, but only if suffix matches {suffixes}")
167
+ choice_file = match_file_name(sub_string=path, search_root=Path.cwd(), suffixes=suffixes)
165
168
  elif path_obj.is_dir():
166
169
  print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
167
170
  files = search_for_files_of_interest(path_obj, suffixes=suffixes)
168
171
  print(f"🔍 Got #{len(files)} results.")
169
172
  from machineconfig.utils.options import choose_from_options
170
173
  choice_file = choose_from_options(multi=False, options=files, fzf=True, msg="Choose one option")
171
- choice_file = PathExtended(choice_file)
174
+ choice_file = Path(choice_file)
172
175
  else:
173
176
  choice_file = path_obj
174
177
  return choice_file
@@ -13,7 +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
+ tabWeight: NotRequired[int]
17
17
 
18
18
 
19
19
  class LayoutConfig(TypedDict):
@@ -8,7 +8,7 @@ from machineconfig.utils.terminal import Response
8
8
  from machineconfig.utils.accessories import pprint, randstr
9
9
  from machineconfig.utils.meta import lambda_to_python_script
10
10
  UV_RUN_CMD = "$HOME/.local/bin/uv run" if platform.system() != "Windows" else """& "$env:USERPROFILE/.local/bin/uv" run"""
11
- MACHINECONFIG_VERSION = "machineconfig>=7.57"
11
+ MACHINECONFIG_VERSION = "machineconfig>=7.64"
12
12
  DEFAULT_PICKLE_SUBDIR = "tmp_results/tmp_scripts/ssh"
13
13
 
14
14
  class SSH:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: machineconfig
3
- Version: 7.57
3
+ Version: 7.64
4
4
  Summary: Dotfiles management package
5
5
  Author-email: Alex Al-Saffar <programmer@usa.com>
6
6
  License: Apache 2.0