machineconfig 5.37__py3-none-any.whl → 5.39__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of machineconfig might be problematic. Click here for more details.
- machineconfig/cluster/remote/script_execution.py +0 -1
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +2 -4
- machineconfig/jobs/installer/check_installations.py +2 -2
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +12 -12
- machineconfig/jobs/installer/installer_data.json +53 -2
- machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -3
- machineconfig/profile/create_helper.py +26 -0
- machineconfig/profile/{create.py → create_links.py} +6 -6
- machineconfig/profile/{create_frontend.py → create_links_export.py} +6 -10
- machineconfig/profile/{shell.py → create_shell_profile.py} +12 -37
- machineconfig/scripts/linux/{share_cloud.sh → other/share_cloud.sh} +3 -0
- machineconfig/scripts/linux/z_ls +2 -2
- machineconfig/scripts/python/agents.py +0 -1
- machineconfig/scripts/python/ai/initai.py +3 -4
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +0 -1
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +0 -1
- machineconfig/scripts/python/ai/solutions/generic.py +1 -0
- machineconfig/scripts/python/croshell.py +12 -2
- machineconfig/scripts/python/croshell_helpers/start_slidev.py +2 -2
- machineconfig/scripts/python/devops.py +4 -21
- machineconfig/scripts/python/devops_helpers/cli_config.py +8 -8
- machineconfig/scripts/python/devops_helpers/cli_config_dotfile.py +3 -3
- machineconfig/scripts/python/devops_helpers/cli_nw.py +2 -1
- machineconfig/scripts/python/devops_helpers/cli_repos.py +1 -3
- machineconfig/scripts/python/devops_helpers/cli_self.py +22 -9
- machineconfig/scripts/python/devops_helpers/cli_share_server.py +109 -0
- machineconfig/scripts/python/devops_helpers/cli_terminal.py +35 -23
- machineconfig/scripts/python/devops_helpers/devops_status.py +7 -80
- machineconfig/scripts/python/devops_helpers/devops_update_repos.py +64 -45
- machineconfig/scripts/python/devops_helpers/themes/choose_pwsh_theme.ps1 +16 -15
- machineconfig/scripts/python/devops_navigator.py +183 -80
- machineconfig/scripts/python/fire_jobs.py +4 -1
- machineconfig/scripts/python/ftpx.py +0 -1
- machineconfig/scripts/python/helpers_fire/{fire_gemini.py → agentic_frameworks/fire_gemini.py} +12 -9
- machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_qwen.py +43 -0
- machineconfig/scripts/python/helpers_fire/fire_agents_help_launch.py +4 -4
- machineconfig/scripts/python/helpers_fire/template.ps1 +29 -0
- machineconfig/scripts/python/helpers_fire/template.sh +1 -1
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +3 -3
- machineconfig/scripts/python/interactive.py +5 -4
- machineconfig/scripts/python/nw/mount_nfs +1 -1
- machineconfig/scripts/python/nw/mount_nw_drive +1 -2
- machineconfig/scripts/python/repos_helpers/count_lines_frontend.py +1 -1
- machineconfig/scripts/python/repos_helpers/entrypoint.py +2 -2
- machineconfig/scripts/python/repos_helpers/record.py +2 -2
- machineconfig/scripts/python/sessions_helpers/sessions_multiprocess.py +3 -1
- machineconfig/scripts/windows/{mount_nfs.ps1 → mounts/mount_nfs.ps1} +1 -3
- machineconfig/scripts/windows/{mount_ssh.ps1 → mounts/mount_ssh.ps1} +1 -1
- machineconfig/settings/lf/linux/lfrc +4 -7
- machineconfig/settings/lf/windows/lfrc +4 -22
- machineconfig/settings/lvim/windows/archive/config_additional.lua +0 -6
- machineconfig/settings/pistol/pistol.conf +1 -1
- machineconfig/settings/shells/bash/init.sh +9 -8
- machineconfig/settings/shells/pwsh/init.ps1 +10 -4
- machineconfig/settings/svim/linux/init.toml +0 -4
- machineconfig/settings/svim/windows/init.toml +0 -3
- machineconfig/setup_linux/web_shortcuts/interactive.sh +22 -0
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +34 -1
- machineconfig/utils/files/dbms.py +4 -1
- machineconfig/utils/installer_utils/installer.py +12 -0
- machineconfig/utils/installer_utils/installer_abc.py +26 -9
- machineconfig/utils/installer_utils/installer_class.py +1 -1
- machineconfig/utils/io.py +0 -18
- machineconfig/utils/scheduler.py +3 -4
- machineconfig/utils/source_of_truth.py +2 -4
- machineconfig/utils/ssh.py +1 -1
- {machineconfig-5.37.dist-info → machineconfig-5.39.dist-info}/METADATA +8 -4
- {machineconfig-5.37.dist-info → machineconfig-5.39.dist-info}/RECORD +84 -114
- machineconfig/jobs/windows/start_terminal.ps1 +0 -6
- machineconfig/jobs/windows/startup_file.cmd +0 -2
- machineconfig/scripts/cloud/init.sh +0 -105
- machineconfig/scripts/linux/agents +0 -2
- machineconfig/scripts/linux/cloud +0 -2
- machineconfig/scripts/linux/croshell +0 -3
- machineconfig/scripts/linux/devops +0 -2
- machineconfig/scripts/linux/fire +0 -2
- machineconfig/scripts/linux/ftpx +0 -2
- machineconfig/scripts/linux/kill_process +0 -2
- machineconfig/scripts/linux/sessions +0 -2
- machineconfig/scripts/linux/start_terminals +0 -3
- machineconfig/scripts/windows/agents.ps1 +0 -1
- machineconfig/scripts/windows/cloud.ps1 +0 -1
- machineconfig/scripts/windows/croshell.ps1 +0 -1
- machineconfig/scripts/windows/devops.ps1 +0 -1
- machineconfig/scripts/windows/fire.ps1 +0 -1
- machineconfig/scripts/windows/ftpx.ps1 +0 -1
- machineconfig/scripts/windows/gpt.ps1 +0 -1
- machineconfig/scripts/windows/grep.ps1 +0 -2
- machineconfig/scripts/windows/kill_process.ps1 +0 -1
- machineconfig/scripts/windows/nano.ps1 +0 -3
- machineconfig/scripts/windows/pomodoro.ps1 +0 -1
- machineconfig/scripts/windows/reload_path.ps1 +0 -3
- machineconfig/scripts/windows/scheduler.ps1 +0 -1
- machineconfig/scripts/windows/sessions.ps1 +0 -1
- machineconfig/scripts/windows/snapshot.ps1 +0 -1
- machineconfig/scripts/windows/start_slidev.ps1 +0 -1
- machineconfig/scripts/windows/start_terminals.ps1 +0 -1
- machineconfig/scripts/windows/wsl_rdp_windows_port_forwarding.ps1 +0 -46
- machineconfig/scripts/windows/wsl_ssh_windows_port_forwarding.ps1 +0 -76
- machineconfig/setup_linux/machineconfig.sh +0 -20
- machineconfig/setup_windows/machineconfig.ps1 +0 -27
- /machineconfig/scripts/linux/{share_nfs → other/share_nfs} +0 -0
- /machineconfig/scripts/linux/{share_smb → other/share_smb} +0 -0
- /machineconfig/scripts/linux/{start_docker → other/start_docker} +0 -0
- /machineconfig/scripts/linux/{switch_ip → other/switch_ip} +0 -0
- /machineconfig/scripts/{windows/share_nfs.ps1 → python/helpers_fire/agentic_frameworks/__init__.py} +0 -0
- /machineconfig/scripts/python/helpers_fire/{fire_crush.json → agentic_frameworks/fire_crush.json} +0 -0
- /machineconfig/scripts/python/helpers_fire/{fire_crush.py → agentic_frameworks/fire_crush.py} +0 -0
- /machineconfig/scripts/python/helpers_fire/{fire_cursor_agents.py → agentic_frameworks/fire_cursor_agents.py} +0 -0
- /machineconfig/scripts/windows/{mount_nw.ps1 → mounts/mount_nw.ps1} +0 -0
- /machineconfig/scripts/windows/{mount_smb.ps1 → mounts/mount_smb.ps1} +0 -0
- /machineconfig/scripts/windows/{share_cloud.cmd → mounts/share_cloud.cmd} +0 -0
- /machineconfig/scripts/windows/{share_smb.ps1 → mounts/share_smb.ps1} +0 -0
- /machineconfig/scripts/windows/{unlock_bitlocker.ps1 → mounts/unlock_bitlocker.ps1} +0 -0
- {machineconfig-5.37.dist-info → machineconfig-5.39.dist-info}/WHEEL +0 -0
- {machineconfig-5.37.dist-info → machineconfig-5.39.dist-info}/entry_points.txt +0 -0
- {machineconfig-5.37.dist-info → machineconfig-5.39.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import machineconfig.scripts.python.devops_helpers.cli_terminal as cli_terminal
|
|
3
|
+
import machineconfig.scripts.python.devops_helpers.cli_share_server as cli_share_server
|
|
3
4
|
import typer
|
|
4
5
|
from typing import Optional
|
|
5
6
|
|
|
@@ -7,7 +8,7 @@ nw_apps = typer.Typer(help="🔐 Network subcommands", no_args_is_help=True)
|
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
nw_apps.command(name="share-terminal", help="📡 Share terminal via web browser")(cli_terminal.main)
|
|
10
|
-
|
|
11
|
+
nw_apps.command(name="share-server", help="🌐 Start local/global server to share files/folders via web browser", no_args_is_help=True)(cli_share_server.main)
|
|
11
12
|
|
|
12
13
|
@nw_apps.command()
|
|
13
14
|
def install_ssh_server():
|
|
@@ -8,7 +8,6 @@ in the event that username@github.com is not mentioned in the remote url.
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Annotated, Optional
|
|
10
10
|
import typer
|
|
11
|
-
from git import Repo, InvalidGitRepositoryError
|
|
12
11
|
from machineconfig.scripts.python.helpers_repos.secure_repo import main as secure_repo_main
|
|
13
12
|
|
|
14
13
|
|
|
@@ -26,7 +25,6 @@ CloudOption = Annotated[Optional[str], typer.Option("--cloud", "-c", help="☁
|
|
|
26
25
|
def push(directory: DirectoryArgument = None, recursive: RecursiveOption = False, no_sync: NoSyncOption = False) -> None:
|
|
27
26
|
"""🚀 Push changes across repositories."""
|
|
28
27
|
from machineconfig.scripts.python.repos_helpers.entrypoint import git_operations
|
|
29
|
-
|
|
30
28
|
git_operations(directory, pull=False, commit=False, push=True, recursive=recursive, no_sync=no_sync)
|
|
31
29
|
|
|
32
30
|
|
|
@@ -143,7 +141,7 @@ def cleanup(repo: DirectoryArgument = None, recursive: RecursiveOption = False)
|
|
|
143
141
|
repo = Path.cwd().as_posix()
|
|
144
142
|
|
|
145
143
|
arg_path = Path(repo).expanduser().absolute()
|
|
146
|
-
|
|
144
|
+
from git import Repo, InvalidGitRepositoryError
|
|
147
145
|
if not recursive:
|
|
148
146
|
# Check if the directory is a git repo
|
|
149
147
|
try:
|
|
@@ -1,41 +1,54 @@
|
|
|
1
1
|
|
|
2
|
+
from git import Optional
|
|
2
3
|
import typer
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
cli_app = typer.Typer(help="🔄 SELF operations subcommands", no_args_is_help=True)
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
@
|
|
8
|
+
@cli_app.command()
|
|
8
9
|
def update():
|
|
9
10
|
"""🔄 UPDATE essential repos"""
|
|
10
11
|
import machineconfig.scripts.python.devops_helpers.devops_update_repos as helper
|
|
11
12
|
helper.main()
|
|
12
|
-
@
|
|
13
|
+
@cli_app.command()
|
|
13
14
|
def interactive():
|
|
14
15
|
"""🤖 INTERACTIVE configuration of machine."""
|
|
15
16
|
from machineconfig.scripts.python.interactive import main
|
|
16
17
|
main()
|
|
17
|
-
@
|
|
18
|
+
@cli_app.command()
|
|
18
19
|
def status():
|
|
19
20
|
"""📊 STATUS of machine, shell profile, apps, symlinks, dotfiles, etc."""
|
|
20
21
|
import machineconfig.scripts.python.devops_helpers.devops_status as helper
|
|
21
22
|
helper.main()
|
|
22
|
-
@
|
|
23
|
+
@cli_app.command()
|
|
23
24
|
def clone():
|
|
24
25
|
"""📋 CLONE machienconfig locally and incorporate to shell profile for faster execution and nightly updates."""
|
|
25
26
|
import platform
|
|
26
27
|
from machineconfig.utils.code import run_shell_script
|
|
27
|
-
from machineconfig.profile.
|
|
28
|
+
from machineconfig.profile.create_shell_profile import create_default_shell_profile
|
|
28
29
|
if platform.system() == "Windows":
|
|
29
30
|
from machineconfig.setup_windows import MACHINECONFIG
|
|
30
|
-
create_default_shell_profile(
|
|
31
|
+
create_default_shell_profile()
|
|
31
32
|
else:
|
|
32
33
|
from machineconfig.setup_linux import MACHINECONFIG
|
|
33
|
-
create_default_shell_profile(
|
|
34
|
+
create_default_shell_profile()
|
|
34
35
|
run_shell_script(MACHINECONFIG.read_text(encoding="utf-8"))
|
|
35
36
|
|
|
36
|
-
@
|
|
37
|
+
@cli_app.command(no_args_is_help=False)
|
|
37
38
|
def navigate():
|
|
38
39
|
"""📚 NAVIGATE command structure with TUI"""
|
|
39
40
|
from machineconfig.scripts.python.devops_navigator import main
|
|
40
41
|
main()
|
|
41
42
|
|
|
43
|
+
|
|
44
|
+
@cli_app.command(no_args_is_help=True)
|
|
45
|
+
def run_python(ip: str = typer.Argument(..., help="Python command to run in the machineconfig environment"),
|
|
46
|
+
command: Optional[bool] = typer.Option(False, "--command", "-c", help="Run as command")):
|
|
47
|
+
"""🐍 RUN python command/file in the machineconfig environment"""
|
|
48
|
+
if command:
|
|
49
|
+
exec(ip)
|
|
50
|
+
return
|
|
51
|
+
import machineconfig
|
|
52
|
+
import subprocess
|
|
53
|
+
import sys
|
|
54
|
+
subprocess.run([sys.executable, ip], cwd=machineconfig.__path__[0])
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional, Annotated
|
|
3
|
+
import typer
|
|
4
|
+
# import typer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def display_share_url(local_ip_v4: str, port: int, protocol: str = "http") -> None:
|
|
9
|
+
"""Display a flashy, unmissable share URL announcement."""
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
from rich.align import Align
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
# Create the main message with styling
|
|
18
|
+
url_text = Text(f"{protocol}://{local_ip_v4}:{port}", style="bold bright_cyan underline")
|
|
19
|
+
message = Text.assemble(
|
|
20
|
+
("🚀 ", "bright_red"),
|
|
21
|
+
("Share server is now accessible at: ", "bright_white bold"),
|
|
22
|
+
url_text,
|
|
23
|
+
(" 🚀", "bright_red")
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Create a fancy panel with borders and styling
|
|
27
|
+
panel = Panel(
|
|
28
|
+
Align.center(message),
|
|
29
|
+
title="[bold bright_green]🌐 SHARE SERVER READY 🌐[/bold bright_green]",
|
|
30
|
+
subtitle="[italic bright_yellow]⚡ Click the link above to access your shared files! ⚡[/italic bright_yellow]",
|
|
31
|
+
border_style="bright_magenta",
|
|
32
|
+
padding=(1, 2),
|
|
33
|
+
expand=False
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Print with extra spacing and attention-grabbing elements
|
|
37
|
+
console.print(panel)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main(
|
|
41
|
+
path: Annotated[str, typer.Argument(help="Path to the file or directory to share")],
|
|
42
|
+
port: Annotated[Optional[int], typer.Option("--port", "-p", help="Port to run the share server on (default: 8080)")] = None,
|
|
43
|
+
username: Annotated[Optional[str], typer.Option("--username", "-u", help="Username for share access (default: current user)")] = None,
|
|
44
|
+
password: Annotated[Optional[str], typer.Option("--password", "-w", help="Password for share access (default: from ~/dotfiles/creds/passwords/quick_password)")] = None,
|
|
45
|
+
over_internet: Annotated[bool, typer.Option("--over-internet", "-i", help="Expose the share server over the internet using ngrok")] = False
|
|
46
|
+
) -> None:
|
|
47
|
+
from machineconfig.utils.installer_utils.installer import install_if_missing
|
|
48
|
+
install_if_missing("ezshare")
|
|
49
|
+
if over_internet: install_if_missing("ngrok")
|
|
50
|
+
|
|
51
|
+
if username is None:
|
|
52
|
+
import getpass
|
|
53
|
+
username = getpass.getuser()
|
|
54
|
+
if password is None:
|
|
55
|
+
pwd_path = Path.home().joinpath("dotfiles/creds/passwords/quick_password")
|
|
56
|
+
if pwd_path.exists():
|
|
57
|
+
password = pwd_path.read_text(encoding="utf-8").strip()
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError("Password not provided and default password file does not exist.")
|
|
60
|
+
|
|
61
|
+
if port is None:
|
|
62
|
+
port = 8080 # Default port for ezshare
|
|
63
|
+
|
|
64
|
+
import socket
|
|
65
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
66
|
+
s.connect(('8.8.8.8',80))
|
|
67
|
+
local_ip_v4 = s.getsockname()[0]
|
|
68
|
+
s.close()
|
|
69
|
+
|
|
70
|
+
# Display the flashy share announcement
|
|
71
|
+
protocol = "http"
|
|
72
|
+
display_share_url(local_ip_v4, port, protocol)
|
|
73
|
+
import subprocess
|
|
74
|
+
import time
|
|
75
|
+
# Build ezshare command
|
|
76
|
+
ezshare_cmd = f"ezshare --port {port} --username {username} --password {password} {path}"
|
|
77
|
+
ezshare_process = subprocess.Popen(ezshare_cmd, shell=True)
|
|
78
|
+
processes = [ezshare_process]
|
|
79
|
+
|
|
80
|
+
if over_internet:
|
|
81
|
+
ngrok_process = subprocess.Popen(f"ngrok http {port}", shell=True)
|
|
82
|
+
processes.append(ngrok_process)
|
|
83
|
+
time.sleep(3)
|
|
84
|
+
try:
|
|
85
|
+
import requests
|
|
86
|
+
response = requests.get("http://localhost:4040/api/tunnels")
|
|
87
|
+
data = response.json()
|
|
88
|
+
public_url = data['tunnels'][0]['public_url']
|
|
89
|
+
print(f"🌐 Ngrok tunnel ready: {public_url}")
|
|
90
|
+
except Exception as e:
|
|
91
|
+
print(f"Could not retrieve ngrok URL: {e}")
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
while True:
|
|
95
|
+
print("Share server is running. Press Ctrl+C to stop.")
|
|
96
|
+
time.sleep(2)
|
|
97
|
+
except KeyboardInterrupt:
|
|
98
|
+
print("\nTerminating processes...")
|
|
99
|
+
for p in processes:
|
|
100
|
+
p.terminate()
|
|
101
|
+
p.wait()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def main_with_parser():
|
|
105
|
+
typer.run(main)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
pass
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
from pathlib import Path
|
|
4
2
|
from typing import Optional, Annotated
|
|
5
3
|
import typer
|
|
4
|
+
import subprocess
|
|
5
|
+
import time
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
@@ -49,19 +49,6 @@ def display_terminal_url(local_ip_v4: str, port: int, protocol: str = "http") ->
|
|
|
49
49
|
# console.print("🔥" * 60 + "\n", style="bright_red bold")
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def install_ttyd():
|
|
53
|
-
# uv run --python 3.13 --with machineconfig devops install ttyd
|
|
54
|
-
from machineconfig.utils.installer_utils.installer_abc import check_tool_exists
|
|
55
|
-
exists = check_tool_exists("ttyd")
|
|
56
|
-
if exists:
|
|
57
|
-
print("✅ ttyd is already installed.")
|
|
58
|
-
return
|
|
59
|
-
print("⏳ ttyd not found. Installing...")
|
|
60
|
-
from machineconfig.utils.installer_utils.installer import main
|
|
61
|
-
main(which="ttyd")
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
52
|
def main(
|
|
66
53
|
port: Annotated[Optional[int], typer.Option("--port", "-p", help="Port to run the terminal server on (default: 7681)")] = None,
|
|
67
54
|
username: Annotated[Optional[str], typer.Option("--username", "-u", help="Username for terminal access (default: current user)")] = None,
|
|
@@ -70,9 +57,13 @@ def main(
|
|
|
70
57
|
ssl: Annotated[bool, typer.Option("--ssl", "-S", help="Enable SSL")] = False,
|
|
71
58
|
ssl_cert: Annotated[Optional[str], typer.Option("--ssl-cert", "-C", help="SSL certificate file path")] = None,
|
|
72
59
|
ssl_key: Annotated[Optional[str], typer.Option("--ssl-key", "-K", help="SSL key file path")] = None,
|
|
73
|
-
ssl_ca: Annotated[Optional[str], typer.Option("--ssl-ca", "-A", help="SSL CA file path for client certificate verification")] = None
|
|
60
|
+
ssl_ca: Annotated[Optional[str], typer.Option("--ssl-ca", "-A", help="SSL CA file path for client certificate verification")] = None,
|
|
61
|
+
over_internet: Annotated[bool, typer.Option("--over-internet", "-i", help="Expose the terminal over the internet using ngrok")] = False
|
|
74
62
|
) -> None:
|
|
75
|
-
|
|
63
|
+
from machineconfig.utils.installer_utils.installer import install_if_missing
|
|
64
|
+
install_if_missing("ttyd")
|
|
65
|
+
if over_internet: install_if_missing("ngrok")
|
|
66
|
+
|
|
76
67
|
if username is None:
|
|
77
68
|
import getpass
|
|
78
69
|
username = getpass.getuser()
|
|
@@ -128,12 +119,33 @@ def main(
|
|
|
128
119
|
start_command = "powershell"
|
|
129
120
|
else:
|
|
130
121
|
start_command = "bash"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
|
|
123
|
+
ttyd_cmd = f"ttyd --writable -t enableSixel=true {ssl_args} --port {port} --credential \"{username}:{password}\" -t 'theme={{\"background\": \"black\"}}' {start_command}"
|
|
124
|
+
ttyd_process = subprocess.Popen(ttyd_cmd, shell=True)
|
|
125
|
+
processes = [ttyd_process]
|
|
126
|
+
|
|
127
|
+
if over_internet:
|
|
128
|
+
ngrok_process = subprocess.Popen(f"ngrok http {port}", shell=True)
|
|
129
|
+
processes.append(ngrok_process)
|
|
130
|
+
time.sleep(3)
|
|
131
|
+
try:
|
|
132
|
+
import requests
|
|
133
|
+
response = requests.get("http://localhost:4040/api/tunnels")
|
|
134
|
+
data = response.json()
|
|
135
|
+
public_url = data['tunnels'][0]['public_url']
|
|
136
|
+
print(f"🌐 Ngrok tunnel ready: {public_url}")
|
|
137
|
+
except Exception as e:
|
|
138
|
+
print(f"Could not retrieve ngrok URL: {e}")
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
while True:
|
|
142
|
+
print("Terminal server is running. Press Ctrl+C to stop.")
|
|
143
|
+
time.sleep(2)
|
|
144
|
+
except KeyboardInterrupt:
|
|
145
|
+
print("\nTerminating processes...")
|
|
146
|
+
for p in processes:
|
|
147
|
+
p.terminate()
|
|
148
|
+
p.wait()
|
|
137
149
|
|
|
138
150
|
|
|
139
151
|
def main_with_parser():
|
|
@@ -11,7 +11,7 @@ from rich.table import Table
|
|
|
11
11
|
from rich.text import Text
|
|
12
12
|
|
|
13
13
|
from machineconfig.utils.path_extended import PathExtended
|
|
14
|
-
from machineconfig.utils.source_of_truth import
|
|
14
|
+
from machineconfig.utils.source_of_truth import CONFIG_ROOT, DEFAULTS_PATH, LIBRARY_ROOT
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
console = Console()
|
|
@@ -36,7 +36,7 @@ def _check_system_info() -> dict[str, str]:
|
|
|
36
36
|
|
|
37
37
|
def _check_shell_profile_status() -> dict[str, Any]:
|
|
38
38
|
"""Check shell profile configuration status."""
|
|
39
|
-
from machineconfig.profile.
|
|
39
|
+
from machineconfig.profile.create_shell_profile import get_shell_profile_path
|
|
40
40
|
|
|
41
41
|
try:
|
|
42
42
|
profile_path = get_shell_profile_path()
|
|
@@ -46,13 +46,13 @@ def _check_shell_profile_status() -> dict[str, Any]:
|
|
|
46
46
|
profile_content = profile_path.read_text(encoding="utf-8")
|
|
47
47
|
system_name = platform.system()
|
|
48
48
|
if system_name == "Windows":
|
|
49
|
-
init_script = PathExtended(
|
|
50
|
-
init_script_copy = PathExtended(
|
|
49
|
+
init_script = PathExtended(CONFIG_ROOT).joinpath("settings/shells/pwsh/init.ps1")
|
|
50
|
+
init_script_copy = PathExtended(CONFIG_ROOT).joinpath("profile/init.ps1").collapseuser()
|
|
51
51
|
source_reference = f". {str(init_script.collapseuser()).replace('~', '$HOME')}"
|
|
52
52
|
source_copy = f". {str(init_script_copy).replace('~', '$HOME')}"
|
|
53
53
|
else:
|
|
54
|
-
init_script = PathExtended(
|
|
55
|
-
init_script_copy = PathExtended(
|
|
54
|
+
init_script = PathExtended(CONFIG_ROOT).joinpath("settings/shells/bash/init.sh")
|
|
55
|
+
init_script_copy = PathExtended(CONFIG_ROOT).joinpath("profile/init.sh").collapseuser()
|
|
56
56
|
source_reference = f"source {str(init_script.collapseuser()).replace('~', '$HOME')}"
|
|
57
57
|
source_copy = f"source {str(init_script_copy).replace('~', '$HOME')}"
|
|
58
58
|
|
|
@@ -79,34 +79,6 @@ def _check_shell_profile_status() -> dict[str, Any]:
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def _check_machineconfig_repo() -> dict[str, Any]:
|
|
83
|
-
"""Check machineconfig repository status."""
|
|
84
|
-
repo_path = Path.home().joinpath("code/machineconfig")
|
|
85
|
-
if not repo_path.exists():
|
|
86
|
-
return {"exists": False, "is_repo": False, "clean": False, "branch": "N/A", "commit": "N/A", "remotes": []}
|
|
87
|
-
|
|
88
|
-
try:
|
|
89
|
-
import git
|
|
90
|
-
|
|
91
|
-
repo = git.Repo(str(repo_path))
|
|
92
|
-
is_dirty = repo.is_dirty(untracked_files=True)
|
|
93
|
-
current_branch = repo.active_branch.name if not repo.head.is_detached else "DETACHED"
|
|
94
|
-
current_commit = repo.head.commit.hexsha[:8]
|
|
95
|
-
remotes = [remote.name for remote in repo.remotes]
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
"exists": True,
|
|
99
|
-
"is_repo": True,
|
|
100
|
-
"clean": not is_dirty,
|
|
101
|
-
"branch": current_branch,
|
|
102
|
-
"commit": current_commit,
|
|
103
|
-
"remotes": remotes,
|
|
104
|
-
"path": str(repo_path),
|
|
105
|
-
}
|
|
106
|
-
except Exception as ex:
|
|
107
|
-
return {"exists": True, "is_repo": False, "clean": False, "branch": "Error", "commit": "N/A", "remotes": [], "error": str(ex)}
|
|
108
|
-
|
|
109
|
-
|
|
110
82
|
def _check_repos_status() -> dict[str, Any]:
|
|
111
83
|
"""Check configured repositories status."""
|
|
112
84
|
from machineconfig.utils.io import read_ini
|
|
@@ -178,7 +150,7 @@ def _check_ssh_status() -> dict[str, Any]:
|
|
|
178
150
|
|
|
179
151
|
def _check_config_files_status() -> dict[str, Any]:
|
|
180
152
|
"""Check public and private configuration files status."""
|
|
181
|
-
from machineconfig.profile.
|
|
153
|
+
from machineconfig.profile.create_links import read_mapper
|
|
182
154
|
|
|
183
155
|
try:
|
|
184
156
|
mapper = read_mapper()
|
|
@@ -320,48 +292,6 @@ def _display_shell_status(status: dict[str, Any]) -> None:
|
|
|
320
292
|
)
|
|
321
293
|
|
|
322
294
|
|
|
323
|
-
def _display_machineconfig_repo(info: dict[str, Any]) -> None:
|
|
324
|
-
"""Display machineconfig repository status."""
|
|
325
|
-
console.rule("[bold magenta]📦 Machineconfig Repository[/bold magenta]")
|
|
326
|
-
|
|
327
|
-
if not info["exists"]:
|
|
328
|
-
console.print(
|
|
329
|
-
Panel(
|
|
330
|
-
"❌ Machineconfig repository not found at ~/code/machineconfig",
|
|
331
|
-
title="Repository Status",
|
|
332
|
-
border_style="red",
|
|
333
|
-
padding=(1, 2),
|
|
334
|
-
expand=False,
|
|
335
|
-
)
|
|
336
|
-
)
|
|
337
|
-
return
|
|
338
|
-
|
|
339
|
-
if not info["is_repo"]:
|
|
340
|
-
console.print(
|
|
341
|
-
Panel(
|
|
342
|
-
f"❌ Directory exists but is not a git repository\n{info.get('error', 'Unknown error')}",
|
|
343
|
-
title="Repository Status",
|
|
344
|
-
border_style="red",
|
|
345
|
-
padding=(1, 2),
|
|
346
|
-
expand=False,
|
|
347
|
-
)
|
|
348
|
-
)
|
|
349
|
-
return
|
|
350
|
-
|
|
351
|
-
table = Table(show_header=False, box=None, padding=(0, 1), expand=False)
|
|
352
|
-
table.add_column("Property", style="cyan", no_wrap=True)
|
|
353
|
-
table.add_column("Value", style="white")
|
|
354
|
-
|
|
355
|
-
table.add_row("📁 Path", info["path"])
|
|
356
|
-
table.add_row("🌿 Branch", info["branch"])
|
|
357
|
-
table.add_row("🔖 Commit", info["commit"])
|
|
358
|
-
table.add_row(f"{'✅' if info['clean'] else '⚠️'} Status", "Clean" if info["clean"] else "Uncommitted changes")
|
|
359
|
-
table.add_row("📡 Remotes", ", ".join(info["remotes"]) if info["remotes"] else "None")
|
|
360
|
-
|
|
361
|
-
border_style = "green" if info["clean"] else "yellow"
|
|
362
|
-
console.print(Panel(table, title="Machineconfig Repository", border_style=border_style, padding=(1, 2), expand=False))
|
|
363
|
-
|
|
364
|
-
|
|
365
295
|
def _display_repos_status(status: dict[str, Any]) -> None:
|
|
366
296
|
"""Display configured repositories status."""
|
|
367
297
|
console.rule("[bold cyan]📚 Configured Repositories[/bold cyan]")
|
|
@@ -557,9 +487,6 @@ def main() -> None:
|
|
|
557
487
|
shell_status = _check_shell_profile_status()
|
|
558
488
|
_display_shell_status(shell_status)
|
|
559
489
|
|
|
560
|
-
machineconfig_repo = _check_machineconfig_repo()
|
|
561
|
-
_display_machineconfig_repo(machineconfig_repo)
|
|
562
|
-
|
|
563
490
|
repos_status = _check_repos_status()
|
|
564
491
|
_display_repos_status(repos_status)
|
|
565
492
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Update repositories with fancy output"""
|
|
2
2
|
|
|
3
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import git
|
|
@@ -16,6 +17,55 @@ from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
|
16
17
|
console = Console()
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
def _process_single_repo(expanded_path: Path, allow_password_prompt: bool) -> tuple[RepositoryUpdateResult, Path | None]:
|
|
21
|
+
"""Process a single repository and return the result."""
|
|
22
|
+
try:
|
|
23
|
+
repo = git.Repo(str(expanded_path), search_parent_directories=True)
|
|
24
|
+
# Update repository and get detailed results
|
|
25
|
+
result = update_repository(repo, allow_password_prompt=allow_password_prompt, auto_sync=True)
|
|
26
|
+
|
|
27
|
+
# Keep track of repos with dependency changes for additional uv sync
|
|
28
|
+
repo_path = None
|
|
29
|
+
if result["dependencies_changed"] and not result["uv_sync_ran"]:
|
|
30
|
+
repo_path = Path(repo.working_dir)
|
|
31
|
+
|
|
32
|
+
return result, repo_path
|
|
33
|
+
|
|
34
|
+
except Exception as ex:
|
|
35
|
+
# Create a result for failed repos
|
|
36
|
+
error_result: RepositoryUpdateResult = {
|
|
37
|
+
"repo_path": str(expanded_path),
|
|
38
|
+
"status": "error",
|
|
39
|
+
"had_uncommitted_changes": False,
|
|
40
|
+
"uncommitted_files": [],
|
|
41
|
+
"commit_before": "",
|
|
42
|
+
"commit_after": "",
|
|
43
|
+
"commits_changed": False,
|
|
44
|
+
"pyproject_changed": False,
|
|
45
|
+
"dependencies_changed": False,
|
|
46
|
+
"uv_sync_ran": False,
|
|
47
|
+
"uv_sync_success": False,
|
|
48
|
+
"remotes_processed": [],
|
|
49
|
+
"remotes_skipped": [],
|
|
50
|
+
"error_message": str(ex),
|
|
51
|
+
"is_machineconfig_repo": False,
|
|
52
|
+
"permissions_updated": False,
|
|
53
|
+
}
|
|
54
|
+
console.print(
|
|
55
|
+
Panel(
|
|
56
|
+
"\n".join(
|
|
57
|
+
[
|
|
58
|
+
f"❌ Repository error: {expanded_path}",
|
|
59
|
+
f"Exception: {ex}",
|
|
60
|
+
]
|
|
61
|
+
),
|
|
62
|
+
border_style="red",
|
|
63
|
+
padding=(1, 2),
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
return error_result, None
|
|
67
|
+
|
|
68
|
+
|
|
19
69
|
def _display_summary(results: list[RepositoryUpdateResult]) -> None:
|
|
20
70
|
"""Display a comprehensive summary of all repository update operations."""
|
|
21
71
|
|
|
@@ -148,7 +198,7 @@ def _display_summary(results: list[RepositoryUpdateResult]) -> None:
|
|
|
148
198
|
def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
|
|
149
199
|
"""Main function to update all configured repositories."""
|
|
150
200
|
_ = verbose
|
|
151
|
-
repos: list[Path] = [
|
|
201
|
+
repos: list[Path] = []
|
|
152
202
|
try:
|
|
153
203
|
tmp = read_ini(DEFAULTS_PATH)["general"]["repos"].split(",")
|
|
154
204
|
if tmp[-1] == "":
|
|
@@ -189,54 +239,23 @@ def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
|
|
|
189
239
|
)
|
|
190
240
|
)
|
|
191
241
|
|
|
192
|
-
# Process repositories
|
|
242
|
+
# Process repositories in parallel
|
|
193
243
|
results: list[RepositoryUpdateResult] = []
|
|
194
244
|
repos_with_changes = []
|
|
195
245
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
246
|
+
with ThreadPoolExecutor(max_workers=min(len(repos), 8)) as executor:
|
|
247
|
+
# Submit all tasks
|
|
248
|
+
future_to_repo = {
|
|
249
|
+
executor.submit(_process_single_repo, expanded_path, allow_password_prompt): expanded_path
|
|
250
|
+
for expanded_path in repos
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# Collect results as they complete
|
|
254
|
+
for future in as_completed(future_to_repo):
|
|
255
|
+
result, repo_path = future.result()
|
|
201
256
|
results.append(result)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if result["dependencies_changed"] and not result["uv_sync_ran"]:
|
|
205
|
-
repos_with_changes.append(Path(repo.working_dir))
|
|
206
|
-
|
|
207
|
-
except Exception as ex:
|
|
208
|
-
# Create a result for failed repos
|
|
209
|
-
error_result: RepositoryUpdateResult = {
|
|
210
|
-
"repo_path": str(expanded_path),
|
|
211
|
-
"status": "error",
|
|
212
|
-
"had_uncommitted_changes": False,
|
|
213
|
-
"uncommitted_files": [],
|
|
214
|
-
"commit_before": "",
|
|
215
|
-
"commit_after": "",
|
|
216
|
-
"commits_changed": False,
|
|
217
|
-
"pyproject_changed": False,
|
|
218
|
-
"dependencies_changed": False,
|
|
219
|
-
"uv_sync_ran": False,
|
|
220
|
-
"uv_sync_success": False,
|
|
221
|
-
"remotes_processed": [],
|
|
222
|
-
"remotes_skipped": [],
|
|
223
|
-
"error_message": str(ex),
|
|
224
|
-
"is_machineconfig_repo": False,
|
|
225
|
-
"permissions_updated": False,
|
|
226
|
-
}
|
|
227
|
-
results.append(error_result)
|
|
228
|
-
console.print(
|
|
229
|
-
Panel(
|
|
230
|
-
"\n".join(
|
|
231
|
-
[
|
|
232
|
-
f"❌ Repository error: {expanded_path}",
|
|
233
|
-
f"Exception: {ex}",
|
|
234
|
-
]
|
|
235
|
-
),
|
|
236
|
-
border_style="red",
|
|
237
|
-
padding=(1, 2),
|
|
238
|
-
)
|
|
239
|
-
)
|
|
257
|
+
if repo_path is not None:
|
|
258
|
+
repos_with_changes.append(repo_path)
|
|
240
259
|
# Run uv sync for repositories where pyproject.toml changed but sync wasn't run yet
|
|
241
260
|
for repo_path in repos_with_changes:
|
|
242
261
|
run_uv_sync(repo_path)
|
|
@@ -49,29 +49,30 @@ if ($selectedThemeName) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
# Read existing profile content or create empty array
|
|
52
|
-
$profileContent = @()
|
|
53
52
|
if (Test-Path $profilePath) {
|
|
54
|
-
$profileContent = Get-Content $profilePath
|
|
53
|
+
$profileContent = Get-Content $profilePath -Raw
|
|
54
|
+
} else {
|
|
55
|
+
$profileContent = ""
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
# Check if oh-my-posh line already exists and replace it
|
|
58
|
-
$
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
# Check if oh-my-posh line already exists and replace it
|
|
59
|
+
if ($profileContent -match "oh-my-posh init pwsh[^\r\n]*") {
|
|
60
|
+
# Replace existing oh-my-posh line
|
|
61
|
+
$profileContent = $profileContent -replace "oh-my-posh init pwsh[^\r\n]*", $ompLine
|
|
62
|
+
} else {
|
|
63
|
+
# Add the oh-my-posh line with proper newlines
|
|
64
|
+
if ($profileContent.Length -gt 0 -and -not $profileContent.EndsWith("`n")) {
|
|
65
|
+
$profileContent += "`n"
|
|
66
|
+
}
|
|
67
|
+
if ($profileContent.Length -gt 0) {
|
|
68
|
+
$profileContent += "`n"
|
|
64
69
|
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (-not $found) {
|
|
68
|
-
# Add the line at the end with a blank line before it
|
|
69
|
-
$profileContent += ""
|
|
70
70
|
$profileContent += $ompLine
|
|
71
|
+
$profileContent += "`n"
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
# Write back to profile
|
|
74
|
-
$profileContent | Set-Content $profilePath -Encoding UTF8
|
|
75
|
+
$profileContent | Set-Content $profilePath -Encoding UTF8 -NoNewline
|
|
75
76
|
|
|
76
77
|
Write-Host "Profile updated successfully!" -ForegroundColor Green
|
|
77
78
|
Write-Host "The theme will be applied automatically in future PowerShell sessions." -ForegroundColor Cyan
|