machineconfig 3.83__py3-none-any.whl → 3.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 (35) hide show
  1. machineconfig/cluster/sessions_managers/zellij_local.py +2 -2
  2. machineconfig/cluster/sessions_managers/zellij_remote.py +2 -2
  3. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +2 -2
  4. machineconfig/jobs/python/vscode/api.py +8 -7
  5. machineconfig/scripts/python/ai/initai.py +1 -11
  6. machineconfig/scripts/python/cloud_copy.py +33 -27
  7. machineconfig/scripts/python/cloud_manager.py +0 -2
  8. machineconfig/scripts/python/cloud_mount.py +13 -10
  9. machineconfig/scripts/python/cloud_sync.py +30 -28
  10. machineconfig/scripts/python/croshell.py +46 -53
  11. machineconfig/scripts/python/dotfile.py +16 -16
  12. machineconfig/scripts/python/fire_agents.py +4 -5
  13. machineconfig/scripts/python/fire_jobs.py +30 -14
  14. machineconfig/scripts/python/fire_jobs_args_helper.py +43 -15
  15. machineconfig/scripts/python/fire_jobs_layout_helper.py +24 -16
  16. machineconfig/scripts/python/helpers/helpers4.py +0 -2
  17. machineconfig/scripts/python/repos.py +10 -88
  18. machineconfig/scripts/python/repos_helper_action.py +335 -0
  19. machineconfig/scripts/python/scheduler.py +0 -1
  20. machineconfig/scripts/python/share_terminal.py +41 -0
  21. machineconfig/scripts/python/start_slidev.py +15 -13
  22. machineconfig/scripts/python/start_terminals.py +19 -19
  23. machineconfig/scripts/python/t4.py +17 -0
  24. machineconfig/scripts/python/wifi_conn.py +16 -14
  25. machineconfig/scripts/python/wsl_windows_transfer.py +23 -29
  26. machineconfig/utils/accessories.py +45 -7
  27. machineconfig/utils/ai/generate_file_checklist.py +17 -17
  28. {machineconfig-3.83.dist-info → machineconfig-3.86.dist-info}/METADATA +1 -1
  29. {machineconfig-3.83.dist-info → machineconfig-3.86.dist-info}/RECORD +32 -32
  30. {machineconfig-3.83.dist-info → machineconfig-3.86.dist-info}/entry_points.txt +5 -5
  31. machineconfig/cluster/templates/cli_gooey.py +0 -115
  32. machineconfig/jobs/python/vscode/link_ve.py +0 -63
  33. machineconfig/jobs/python/vscode/select_interpreter.py +0 -87
  34. {machineconfig-3.83.dist-info → machineconfig-3.86.dist-info}/WHEEL +0 -0
  35. {machineconfig-3.83.dist-info → machineconfig-3.86.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,335 @@
1
+ from machineconfig.utils.path_extended import PathExtended as PathExtended
2
+ from machineconfig.utils.accessories import randstr
3
+ from machineconfig.scripts.python.repos_helper_update import update_repository
4
+
5
+ from typing import Optional
6
+ from dataclasses import dataclass
7
+ from enum import Enum
8
+
9
+ from rich import print as pprint
10
+
11
+
12
+ class GitAction(Enum):
13
+ commit = "commit"
14
+ push = "push"
15
+ pull = "pull"
16
+
17
+
18
+ @dataclass
19
+ class GitOperationResult:
20
+ """Result of a git operation on a single repository."""
21
+ repo_path: PathExtended
22
+ action: str
23
+ success: bool
24
+ message: str
25
+ is_git_repo: bool = True
26
+ had_changes: bool = False
27
+ remote_count: int = 0
28
+
29
+
30
+ @dataclass
31
+ class GitOperationSummary:
32
+ """Summary of all git operations performed."""
33
+ # Basic statistics
34
+ total_paths_processed: int = 0
35
+ git_repos_found: int = 0
36
+ non_git_paths: int = 0
37
+
38
+ # Per-operation statistics
39
+ commits_attempted: int = 0
40
+ commits_successful: int = 0
41
+ commits_no_changes: int = 0
42
+ commits_failed: int = 0
43
+
44
+ pulls_attempted: int = 0
45
+ pulls_successful: int = 0
46
+ pulls_failed: int = 0
47
+
48
+ pushes_attempted: int = 0
49
+ pushes_successful: int = 0
50
+ pushes_failed: int = 0
51
+
52
+ def __post_init__(self):
53
+ self.failed_operations: list[GitOperationResult] = []
54
+ self.repos_without_remotes: list[PathExtended] = []
55
+
56
+
57
+ def git_action(path: PathExtended, action: GitAction, mess: Optional[str] = None, r: bool = False, auto_sync: bool = True) -> GitOperationResult:
58
+ """Perform git actions using Python instead of shell scripts. Returns detailed operation result."""
59
+ from git.exc import InvalidGitRepositoryError
60
+ from git.repo import Repo
61
+
62
+ try:
63
+ repo = Repo(str(path), search_parent_directories=False)
64
+ except InvalidGitRepositoryError:
65
+ pprint(f"⚠️ Skipping {path} because it is not a git repository.")
66
+ if r:
67
+ results = [git_action(path=sub_path, action=action, mess=mess, r=r, auto_sync=auto_sync) for sub_path in path.search()]
68
+ # For recursive calls, we need to aggregate results somehow
69
+ # For now, return success if all recursive operations succeeded
70
+ all_successful = all(result.success for result in results)
71
+ return GitOperationResult(
72
+ repo_path=path,
73
+ action=action.value,
74
+ success=all_successful,
75
+ message=f"Recursive operation: {len([r for r in results if r.success])}/{len(results)} succeeded",
76
+ is_git_repo=False
77
+ )
78
+ else:
79
+ return GitOperationResult(
80
+ repo_path=path,
81
+ action=action.value,
82
+ success=False,
83
+ message="Not a git repository",
84
+ is_git_repo=False
85
+ )
86
+
87
+ print(f">>>>>>>>> 🔧{action} - {path}")
88
+ remote_count = len(repo.remotes)
89
+
90
+ try:
91
+ if action == GitAction.commit:
92
+ if mess is None:
93
+ mess = "auto_commit_" + randstr()
94
+
95
+ # Check if there are changes to commit
96
+ if repo.is_dirty() or repo.untracked_files:
97
+ repo.git.add(A=True) # Stage all changes
98
+ repo.index.commit(mess)
99
+ print(f"✅ Committed changes with message: {mess}")
100
+ return GitOperationResult(
101
+ repo_path=path,
102
+ action=action.value,
103
+ success=True,
104
+ message=f"Committed changes with message: {mess}",
105
+ had_changes=True,
106
+ remote_count=remote_count
107
+ )
108
+ else:
109
+ print("ℹ️ No changes to commit")
110
+ return GitOperationResult(
111
+ repo_path=path,
112
+ action=action.value,
113
+ success=True,
114
+ message="No changes to commit",
115
+ had_changes=False,
116
+ remote_count=remote_count
117
+ )
118
+
119
+ elif action == GitAction.push:
120
+ if not repo.remotes:
121
+ print("⚠️ No remotes configured for push")
122
+ return GitOperationResult(
123
+ repo_path=path,
124
+ action=action.value,
125
+ success=False,
126
+ message="No remotes configured",
127
+ remote_count=0
128
+ )
129
+
130
+ success = True
131
+ failed_remotes = []
132
+ for remote in repo.remotes:
133
+ try:
134
+ print(f"🚀 Pushing to {remote.url}")
135
+ remote.push(repo.active_branch.name)
136
+ print(f"✅ Pushed to {remote.name}")
137
+ except Exception as e:
138
+ print(f"❌ Failed to push to {remote.name}: {e}")
139
+ failed_remotes.append(f"{remote.name}: {str(e)}")
140
+ success = False
141
+
142
+ message = "Push successful" if success else f"Push failed for: {', '.join(failed_remotes)}"
143
+ return GitOperationResult(
144
+ repo_path=path,
145
+ action=action.value,
146
+ success=success,
147
+ message=message,
148
+ remote_count=remote_count
149
+ )
150
+
151
+ elif action == GitAction.pull:
152
+ # Use the enhanced update function with uv sync support
153
+ try:
154
+ update_repository(repo, auto_sync=auto_sync, allow_password_prompt=False)
155
+ print("✅ Pull completed")
156
+ return GitOperationResult(
157
+ repo_path=path,
158
+ action=action.value,
159
+ success=True,
160
+ message="Pull completed successfully",
161
+ remote_count=remote_count
162
+ )
163
+ except Exception as e:
164
+ print(f"❌ Pull failed: {e}")
165
+ return GitOperationResult(
166
+ repo_path=path,
167
+ action=action.value,
168
+ success=False,
169
+ message=f"Pull failed: {str(e)}",
170
+ remote_count=remote_count
171
+ )
172
+
173
+ except Exception as e:
174
+ print(f"❌ Error performing {action} on {path}: {e}")
175
+ return GitOperationResult(
176
+ repo_path=path,
177
+ action=action.value,
178
+ success=False,
179
+ message=f"Error: {str(e)}",
180
+ remote_count=remote_count
181
+ )
182
+
183
+ # This should never be reached, but just in case
184
+ return GitOperationResult(
185
+ repo_path=path,
186
+ action=action.value,
187
+ success=False,
188
+ message="Unknown error",
189
+ remote_count=remote_count
190
+ )
191
+
192
+
193
+ def print_git_operations_summary(summary: GitOperationSummary, operations_performed: list[str]) -> None:
194
+ """Print a detailed summary of git operations similar to repos_helper_record.py."""
195
+ print("\n📊 Git Operations Summary:")
196
+ print(f" Total paths processed: {summary.total_paths_processed}")
197
+ print(f" Git repositories found: {summary.git_repos_found}")
198
+ print(f" Non-git paths skipped: {summary.non_git_paths}")
199
+
200
+ # Show per-operation statistics
201
+ if "commit" in operations_performed:
202
+ print("\n💾 Commit Operations:")
203
+ print(f" Attempted: {summary.commits_attempted}")
204
+ print(f" Successful: {summary.commits_successful}")
205
+ print(f" No changes: {summary.commits_no_changes}")
206
+ print(f" Failed: {summary.commits_failed}")
207
+
208
+ if "pull" in operations_performed:
209
+ print("\n⬇️ Pull Operations:")
210
+ print(f" Attempted: {summary.pulls_attempted}")
211
+ print(f" Successful: {summary.pulls_successful}")
212
+ print(f" Failed: {summary.pulls_failed}")
213
+
214
+ if "push" in operations_performed:
215
+ print("\n🚀 Push Operations:")
216
+ print(f" Attempted: {summary.pushes_attempted}")
217
+ print(f" Successful: {summary.pushes_successful}")
218
+ print(f" Failed: {summary.pushes_failed}")
219
+
220
+ # Show repositories without remotes (important for push operations)
221
+ if summary.repos_without_remotes:
222
+ print(f"\n⚠️ WARNING: {len(summary.repos_without_remotes)} repositories have no remote configurations:")
223
+ for repo_path in summary.repos_without_remotes:
224
+ print(f" • {repo_path.name} ({repo_path})")
225
+ print(" These repositories cannot be pushed to remote servers.")
226
+ else:
227
+ if "push" in operations_performed:
228
+ print("\n✅ All repositories have remote configurations.")
229
+
230
+ # Show failed operations
231
+ if summary.failed_operations:
232
+ print(f"\n❌ FAILED OPERATIONS ({len(summary.failed_operations)} total):")
233
+
234
+ # Group failed operations by type
235
+ failed_by_action = {}
236
+ for failed_op in summary.failed_operations:
237
+ if failed_op.action not in failed_by_action:
238
+ failed_by_action[failed_op.action] = []
239
+ failed_by_action[failed_op.action].append(failed_op)
240
+
241
+ for action, failures in failed_by_action.items():
242
+ print(f"\n {action.upper()} failures ({len(failures)}):")
243
+ for failure in failures:
244
+ if not failure.is_git_repo:
245
+ print(f" • {failure.repo_path.name} ({failure.repo_path}) - Not a git repository")
246
+ else:
247
+ print(f" • {failure.repo_path.name} ({failure.repo_path}) - {failure.message}")
248
+ else:
249
+ print("\n✅ All git operations completed successfully!")
250
+
251
+ # Overall success assessment
252
+ total_failed = len(summary.failed_operations)
253
+ total_operations = (summary.commits_attempted + summary.pulls_attempted +
254
+ summary.pushes_attempted)
255
+
256
+ if total_failed == 0 and total_operations > 0:
257
+ print(f"\n🎉 SUCCESS: All {total_operations} operations completed successfully!")
258
+ elif total_operations == 0:
259
+ print("\n📝 No git operations were performed.")
260
+ else:
261
+ success_rate = ((total_operations - total_failed) / total_operations * 100) if total_operations > 0 else 0
262
+ print(f"\n⚖️ SUMMARY: {total_operations - total_failed}/{total_operations} operations succeeded ({success_rate:.1f}% success rate)")
263
+ if total_failed > 0:
264
+ print(" Review the failed operations above for details on what needs attention.")
265
+
266
+
267
+ def perform_git_operations(repos_root: PathExtended, pull: bool, commit: bool, push: bool, recursive: bool, auto_sync: bool) -> None:
268
+ """Perform git operations on all repositories and provide detailed summary."""
269
+ print(f"\n🔄 Performing Git actions on repositories @ `{repos_root}`...")
270
+
271
+ # Initialize summary tracking
272
+ summary = GitOperationSummary()
273
+ operations_performed = []
274
+
275
+ # Determine which operations to perform
276
+ if pull:
277
+ operations_performed.append("pull")
278
+ if commit:
279
+ operations_performed.append("commit")
280
+ if push:
281
+ operations_performed.append("push")
282
+
283
+ for a_path in repos_root.search("*"):
284
+ print(f"{('Handling ' + str(a_path)).center(80, '-')}")
285
+ summary.total_paths_processed += 1
286
+
287
+ # Check if this is a git repository first
288
+ from git.exc import InvalidGitRepositoryError
289
+ from git.repo import Repo
290
+
291
+ try:
292
+ repo = Repo(str(a_path), search_parent_directories=False)
293
+ summary.git_repos_found += 1
294
+
295
+ # Track repos without remotes
296
+ if len(repo.remotes) == 0:
297
+ summary.repos_without_remotes.append(a_path)
298
+
299
+ # Now perform the actual operations
300
+ if pull:
301
+ result = git_action(path=a_path, action=GitAction.pull, r=recursive, auto_sync=auto_sync)
302
+ summary.pulls_attempted += 1
303
+ if result.success:
304
+ summary.pulls_successful += 1
305
+ else:
306
+ summary.pulls_failed += 1
307
+ summary.failed_operations.append(result)
308
+
309
+ if commit:
310
+ result = git_action(a_path, action=GitAction.commit, r=recursive, auto_sync=auto_sync)
311
+ summary.commits_attempted += 1
312
+ if result.success:
313
+ if result.had_changes:
314
+ summary.commits_successful += 1
315
+ else:
316
+ summary.commits_no_changes += 1
317
+ else:
318
+ summary.commits_failed += 1
319
+ summary.failed_operations.append(result)
320
+
321
+ if push:
322
+ result = git_action(a_path, action=GitAction.push, r=recursive, auto_sync=auto_sync)
323
+ summary.pushes_attempted += 1
324
+ if result.success:
325
+ summary.pushes_successful += 1
326
+ else:
327
+ summary.pushes_failed += 1
328
+ summary.failed_operations.append(result)
329
+
330
+ except InvalidGitRepositoryError:
331
+ summary.non_git_paths += 1
332
+ pprint(f"⚠️ Skipping {a_path} because it is not a git repository.")
333
+
334
+ # Print the detailed summary
335
+ print_git_operations_summary(summary, operations_performed)
@@ -4,7 +4,6 @@
4
4
  # from machineconfig.utils.scheduling import PathExtended, Report, DEFAULT_CONFIG, read_task_from_dir, main
5
5
 
6
6
  # def main_parse():
7
- # import argparse
8
7
  # print("\n" + "=" * 50)
9
8
  # print("📅 Welcome to the Scheduler CLI")
10
9
  # print("=" * 50 + "\n")
@@ -0,0 +1,41 @@
1
+
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+ # import typer
6
+
7
+
8
+ """
9
+ reference:
10
+ # https://github.com/tsl0922/ttyd/wiki/Serving-web-fonts
11
+ # -t "fontFamily=CaskaydiaCove" bash
12
+ # --terminal-type xterm-kitty
13
+
14
+ """
15
+
16
+
17
+ def share_terminal(port: int, password: Optional[str]) -> None:
18
+ if password is None:
19
+ pwd_path = Path.home().joinpath("dotfiles/creds/passwords/quick_password")
20
+ if pwd_path.exists():
21
+ password = pwd_path.read_text(encoding="utf-8").strip()
22
+ else:
23
+ raise ValueError("Password not provided and default password file does not exist.")
24
+
25
+ import socket
26
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
27
+ s.connect(('8.8.8.8',80))
28
+ local_ip_v4 = s.getsockname()[0]
29
+ s.close()
30
+
31
+ print(f"\n🌐 Access your terminal at: http://{local_ip_v4}:{port}\n")
32
+
33
+ code = f"""
34
+ #!/bin/bash
35
+ uv run --python 3.13 --with machineconfig install -ttyd
36
+
37
+ ttyd --writable -t enableSixel=true --port {port} --credential "$USER:{password}" -t 'theme={"background": "black"}' bash
38
+
39
+ """
40
+ import subprocess
41
+ subprocess.run(code, shell=True, check=True)
@@ -6,6 +6,8 @@ from machineconfig.utils.source_of_truth import CONFIG_PATH
6
6
  from machineconfig.utils.code import print_code
7
7
  from machineconfig.utils.path_extended import PathExtended as PathExtended
8
8
  from machineconfig.utils.terminal import Terminal
9
+ from typing import Annotated, Optional
10
+ import typer
9
11
  import subprocess
10
12
  import platform
11
13
 
@@ -47,28 +49,24 @@ def jupyter_to_markdown(file: PathExtended):
47
49
  return op_dir
48
50
 
49
51
 
50
- def main() -> None:
51
- import argparse
52
-
52
+ def main(
53
+ directory: Annotated[Optional[str], typer.Option("-d", "--directory", help="📁 Directory of the report.")] = None,
54
+ jupyter_file: Annotated[Optional[str], typer.Option("-j", "--jupyter-file", help="📓 Jupyter notebook file to convert to slides. If not provided, slides.md is used.")] = None,
55
+ ) -> None:
53
56
  print("\n" + "=" * 50)
54
57
  print("🎥 Welcome to the Slidev Presentation Tool")
55
58
  print("=" * 50 + "\n")
56
59
 
57
- parser = argparse.ArgumentParser()
58
- parser.add_argument("-d", "--directory", default=None, help="📁 Directory of the report.")
59
- parser.add_argument("-j", "--jupyter-file", default=None, help="📓 Jupyter notebook file to convert to slides. If not provided, slides.md is used.")
60
- args = parser.parse_args()
61
-
62
60
  port = PORT_DEFAULT
63
61
 
64
- if args.jupyter_file is not None:
62
+ if jupyter_file is not None:
65
63
  print("📓 Jupyter file provided. Converting to markdown...")
66
- report_dir = jupyter_to_markdown(PathExtended(args.jupyter_file))
64
+ report_dir = jupyter_to_markdown(PathExtended(jupyter_file))
67
65
  else:
68
- if args.directory is None:
66
+ if directory is None:
69
67
  report_dir = PathExtended.cwd()
70
68
  else:
71
- report_dir = PathExtended(args.directory)
69
+ report_dir = PathExtended(directory)
72
70
 
73
71
  assert report_dir.exists(), f"❌ Directory {report_dir} does not exist."
74
72
  assert report_dir.is_dir(), f"❌ {report_dir} is not a directory."
@@ -108,5 +106,9 @@ def main() -> None:
108
106
  print_code(code=program, lexer="bash", desc="Run the following command to start the presentation")
109
107
 
110
108
 
109
+ def arg_parser() -> None:
110
+ typer.run(main)
111
+
112
+
111
113
  if __name__ == "__main__":
112
- main()
114
+ arg_parser()
@@ -3,7 +3,8 @@
3
3
  from machineconfig.utils.options import choose_from_options, get_ssh_hosts
4
4
  import platform
5
5
  from itertools import cycle
6
- from typing import Literal
6
+ from typing import Literal, Optional, Annotated
7
+ import typer
7
8
 
8
9
 
9
10
  COLOR_SCHEMES = ["Campbell", "Campbell Powershell", "Solarized Dark", "Ubuntu-ColorScheme", "Retro"]
@@ -70,40 +71,35 @@ wt --window {window} --title {hosts[0]} powershell -Command "ssh {host_linux} {s
70
71
  return cmd
71
72
 
72
73
 
73
- def main():
74
- import argparse
75
-
74
+ def main(
75
+ panes: Annotated[Optional[int], typer.Option("--panes", "-p", help="🔲 The number of panes to open.")] = 4,
76
+ vertical: Annotated[bool, typer.Option("--vertical", "-V", help="↕️ Switch orientation to vertical from default horizontal.")] = False,
77
+ window: Annotated[int, typer.Option("--window", "-w", help="🪟 The window ID to use.")] = 0,
78
+ hosts: Annotated[Optional[list[str]], typer.Option("--hosts", "-H", help="🌐 The hosts to connect to.")] = None,
79
+ ) -> None:
76
80
  print("\n" + "=" * 50)
77
81
  print("🖥️ Welcome to the Terminal Starter Tool")
78
82
  print("=" * 50 + "\n")
79
83
 
80
- parser = argparse.ArgumentParser()
81
- parser.add_argument("--panes", "-p", type=int, help="🔲 The number of panes to open.", default=4)
82
- parser.add_argument("--vertical", "-V", action="store_true", help="↕️ Switch orientation to vertical from default horizontal.")
83
- parser.add_argument("--window", "-w", type=int, help="🪟 The window ID to use.", default=0) # 0 refers to this window.
84
- parser.add_argument("--hosts", "-H", type=str, nargs="*", help="🌐 The hosts to connect to.", default=None)
85
- args = parser.parse_args()
86
-
87
- if args.panes:
84
+ if panes:
88
85
  print("🔲 Configuring panes...")
89
- cmd = f"wt --window {args.window} --colorScheme '{next(THEMES_ITER)}' pwsh -NoExit -Command '{next(INIT_COMMANDS_ITER)}' "
86
+ cmd = f"wt --window {window} --colorScheme '{next(THEMES_ITER)}' pwsh -NoExit -Command '{next(INIT_COMMANDS_ITER)}' "
90
87
  cmd += f" `; new-tab --colorScheme '{next(THEMES_ITER)}' --profile pwsh --title 't2' --tabColor '#f59218' "
91
88
  cmd += f" `; new-tab --colorScheme '{next(THEMES_ITER)}' --profile pwsh --title 't3' --tabColor '#009999' "
92
- for idx in range(args.panes):
89
+ for idx in range(panes):
93
90
  if idx % 2 == 0:
94
91
  cmd += f" `; move-focus down split-pane --horizontal --size {next(SIZE_ITER)} --colorScheme '{next(THEMES_ITER)}' pwsh -NoExit -Command '{next(INIT_COMMANDS_ITER)}' "
95
92
  else:
96
93
  cmd += f" `; move-focus up split-pane --vertical --size {next(SIZE_ITER)} --colorScheme '{next(THEMES_ITER)}' pwsh -NoExit -Command '{next(INIT_COMMANDS_ITER)}' "
97
94
 
98
95
  else:
99
- if args.hosts is None:
96
+ if hosts is None:
100
97
  print("🌐 No hosts provided. Displaying options...")
101
98
  hosts = choose_from_options(msg="Select hosts:", options=get_ssh_hosts() + [THIS_MACHINE], multi=True, fzf=True)
102
99
  else:
103
- print("🌐 Using provided hosts:", args.hosts)
104
- hosts = args.hosts
100
+ print("🌐 Using provided hosts:", hosts)
105
101
  assert isinstance(hosts, list)
106
- cmd = main_windows_and_wsl(window=args.window, hosts=hosts, orientation="vertical" if args.vertical else "horizontal")
102
+ cmd = main_windows_and_wsl(window=window, hosts=hosts, orientation="vertical" if vertical else "horizontal")
107
103
 
108
104
  print("\n📋 Generated Command:")
109
105
  print("-" * 50)
@@ -117,5 +113,9 @@ def main():
117
113
  print("✅ Command saved successfully!\n")
118
114
 
119
115
 
116
+ def arg_parser() -> None:
117
+ typer.run(main)
118
+
119
+
120
120
  if __name__ == "__main__":
121
- main()
121
+ arg_parser()
@@ -0,0 +1,17 @@
1
+
2
+ import typer
3
+ from typing import Annotated
4
+
5
+ def func(name: str):
6
+ print(f"Hello, {name}! from func")
7
+
8
+ def hello(*, name: Annotated[str, typer.Option(..., help="Name to greet")]):
9
+ print(f"Hello, {name}!")
10
+
11
+ def main():
12
+ typer.run(hello)
13
+
14
+ if __name__ == "__main__":
15
+ # typer.run(hello)
16
+ main()
17
+ pass
@@ -28,7 +28,8 @@ Usage examples:
28
28
 
29
29
  """
30
30
 
31
- import argparse
31
+ from typing import Annotated
32
+ import typer
32
33
  import configparser
33
34
  from pathlib import Path
34
35
  import os
@@ -262,24 +263,21 @@ def manual_network_selection() -> bool:
262
263
  return False
263
264
 
264
265
 
265
- def main():
266
+ def main(
267
+ ssid: Annotated[str, typer.Option("-n", "--ssid", help="🔗 SSID of WiFi (from config)")] = "MyPhoneHotSpot",
268
+ manual: Annotated[bool, typer.Option("-m", "--manual", help="🔍 Manual network selection mode")] = False,
269
+ list_: Annotated[bool, typer.Option("-l", "--list", help="📡 List available networks only")] = False,
270
+ ) -> None:
266
271
  """Main function with fallback network selection"""
267
272
  console.print(Panel("📶 Welcome to the WiFi Connector Tool", title="[bold blue]WiFi Connection[/bold blue]", border_style="blue"))
268
273
 
269
- parser = argparse.ArgumentParser(description="WiFi Connector")
270
- parser.add_argument("-n", "--ssid", help="🔗 SSID of WiFi (from config)", default="MyPhoneHotSpot")
271
- parser.add_argument("-m", "--manual", action="store_true", help="🔍 Manual network selection mode")
272
- parser.add_argument("-l", "--list", action="store_true", help="📡 List available networks only")
273
-
274
- args = parser.parse_args()
275
-
276
274
  # If user just wants to list networks
277
- if args.list:
275
+ if list_:
278
276
  display_available_networks()
279
277
  return
280
278
 
281
279
  # If user wants manual mode, skip config and go straight to selection
282
- if args.manual:
280
+ if manual:
283
281
  console.print("[blue]🔍 Manual network selection mode[/blue]")
284
282
  if manual_network_selection():
285
283
  console.print("[green]🎉 Successfully connected![/green]")
@@ -288,9 +286,9 @@ def main():
288
286
  return
289
287
 
290
288
  # Try to connect using configuration first
291
- console.print(f"[blue]🔍 Attempting to connect to configured network: {args.ssid}[/blue]")
289
+ console.print(f"[blue]🔍 Attempting to connect to configured network: {ssid}[/blue]")
292
290
 
293
- if try_config_connection(args.ssid):
291
+ if try_config_connection(ssid):
294
292
  console.print("[green]🎉 Successfully connected using configuration![/green]")
295
293
  return
296
294
 
@@ -306,6 +304,10 @@ def main():
306
304
  console.print("[blue]👋 Goodbye![/blue]")
307
305
 
308
306
 
307
+ def arg_parser() -> None:
308
+ typer.run(main)
309
+
310
+
309
311
  def get_current_wifi_name() -> str:
310
312
  """Get the name of the currently connected WiFi network"""
311
313
  console.print("\n[blue]🔍 Checking current WiFi connection...[/blue]")
@@ -413,4 +415,4 @@ def create_new_connection(name: str, ssid: str, password: str):
413
415
 
414
416
 
415
417
  if __name__ == "__main__":
416
- main()
418
+ arg_parser()