machineconfig 7.57__py3-none-any.whl → 7.79__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/sessions_managers/utils/maker.py +21 -9
- machineconfig/jobs/installer/custom/boxes.py +2 -2
- machineconfig/jobs/installer/custom/hx.py +3 -3
- machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
- machineconfig/jobs/installer/custom_dev/sysabc.py +36 -28
- machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
- machineconfig/jobs/installer/installer_data.json +127 -25
- machineconfig/jobs/installer/package_groups.py +20 -13
- machineconfig/profile/create_links_export.py +2 -2
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/wrap_mcfg +1 -1
- machineconfig/scripts/python/agents.py +22 -17
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +3 -0
- machineconfig/scripts/python/croshell.py +22 -17
- machineconfig/scripts/python/devops.py +3 -4
- machineconfig/scripts/python/devops_navigator.py +0 -4
- machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
- machineconfig/scripts/python/fire_jobs.py +19 -18
- machineconfig/scripts/python/ftpx.py +36 -12
- machineconfig/scripts/python/helpers/ast_search.py +74 -0
- machineconfig/scripts/python/helpers/qr_code.py +166 -0
- machineconfig/scripts/python/helpers/repo_rag.py +325 -0
- machineconfig/scripts/python/helpers/symantic_search.py +25 -0
- machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
- machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
- machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
- machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
- machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
- machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
- machineconfig/scripts/python/helpers_devops/cli_nw.py +88 -7
- machineconfig/scripts/python/helpers_devops/cli_self.py +7 -6
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +9 -9
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +13 -12
- machineconfig/scripts/python/helpers_devops/cli_terminal.py +7 -6
- machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -73
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
- machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
- machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +23 -13
- machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +7 -4
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
- machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
- machineconfig/scripts/python/helpers_repos/record.py +2 -1
- machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
- machineconfig/scripts/python/helpers_utils/download.py +152 -0
- machineconfig/scripts/python/helpers_utils/path.py +81 -31
- machineconfig/scripts/python/interactive.py +2 -2
- machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
- machineconfig/scripts/python/msearch.py +21 -2
- machineconfig/scripts/python/nw/address.py +132 -0
- machineconfig/scripts/python/nw/devops_add_ssh_key.py +8 -5
- machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
- machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
- machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
- machineconfig/scripts/python/sessions.py +35 -20
- machineconfig/scripts/python/terminal.py +2 -2
- machineconfig/scripts/python/utils.py +12 -10
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
- machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
- machineconfig/settings/shells/nushell/config.nu +2 -2
- machineconfig/settings/shells/nushell/env.nu +45 -6
- machineconfig/settings/shells/nushell/init.nu +282 -95
- machineconfig/settings/shells/pwsh/init.ps1 +1 -0
- machineconfig/settings/shells/zsh/init.sh +0 -7
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_windows/uv.ps1 +8 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -2
- machineconfig/utils/accessories.py +7 -4
- machineconfig/utils/code.py +6 -4
- machineconfig/utils/files/headers.py +2 -2
- machineconfig/utils/installer_utils/install_from_url.py +180 -0
- machineconfig/utils/installer_utils/installer_class.py +53 -47
- machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +71 -65
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
- machineconfig/utils/links.py +2 -2
- machineconfig/utils/meta.py +30 -16
- machineconfig/utils/options.py +4 -4
- machineconfig/utils/path_extended.py +3 -3
- machineconfig/utils/path_helper.py +33 -31
- machineconfig/utils/schemas/layouts/layout_types.py +1 -1
- machineconfig/utils/ssh.py +143 -409
- machineconfig/utils/ssh_utils/abc.py +8 -0
- machineconfig/utils/ssh_utils/copy_from_here.py +110 -0
- machineconfig/utils/ssh_utils/copy_to_here.py +302 -0
- machineconfig/utils/ssh_utils/utils.py +141 -0
- machineconfig/utils/ssh_utils/wsl.py +168 -0
- machineconfig/utils/upgrade_packages.py +2 -1
- machineconfig/utils/ve.py +11 -4
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/METADATA +1 -1
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/RECORD +102 -92
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/entry_points.txt +2 -2
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- machineconfig/scripts/python/explore.py +0 -49
- /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
- /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
- /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/WHEEL +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def generate_qrcode_grid(
|
|
7
|
+
strings: list[str],
|
|
8
|
+
output_path: str,
|
|
9
|
+
per_row: int = 3,
|
|
10
|
+
qr_size: int = 200,
|
|
11
|
+
label_height: int = 30,
|
|
12
|
+
padding: int = 20,
|
|
13
|
+
label_max_chars: int = 25,
|
|
14
|
+
format: Literal["svg", "png"] = "svg",
|
|
15
|
+
) -> str:
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
if not strings:
|
|
18
|
+
raise ValueError("strings list cannot be empty")
|
|
19
|
+
|
|
20
|
+
output_path_obj = Path(output_path)
|
|
21
|
+
output_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
|
|
23
|
+
if format == "svg":
|
|
24
|
+
return _generate_svg(strings, output_path, per_row, qr_size, label_height, padding, label_max_chars)
|
|
25
|
+
elif format == "png":
|
|
26
|
+
return _generate_png(strings, output_path, per_row, qr_size, label_height, padding, label_max_chars)
|
|
27
|
+
else:
|
|
28
|
+
raise ValueError(f"Unsupported format: {format}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _generate_svg(
|
|
32
|
+
strings: list[str],
|
|
33
|
+
output_path: str,
|
|
34
|
+
per_row: int,
|
|
35
|
+
qr_size: int,
|
|
36
|
+
label_height: int,
|
|
37
|
+
padding: int,
|
|
38
|
+
label_max_chars: int,
|
|
39
|
+
) -> str:
|
|
40
|
+
num_items = len(strings)
|
|
41
|
+
num_rows = (num_items + per_row - 1) // per_row
|
|
42
|
+
|
|
43
|
+
cell_width = qr_size
|
|
44
|
+
cell_height = qr_size + label_height
|
|
45
|
+
total_width = per_row * cell_width + (per_row + 1) * padding
|
|
46
|
+
total_height = num_rows * cell_height + (num_rows + 1) * padding
|
|
47
|
+
|
|
48
|
+
from xml.etree import ElementTree as ET
|
|
49
|
+
|
|
50
|
+
import qrcode
|
|
51
|
+
svg_root = ET.Element(
|
|
52
|
+
"svg",
|
|
53
|
+
xmlns="http://www.w3.org/2000/svg",
|
|
54
|
+
width=str(total_width),
|
|
55
|
+
height=str(total_height),
|
|
56
|
+
viewBox=f"0 0 {total_width} {total_height}",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
_bg_rect = ET.SubElement(svg_root, "rect", width=str(total_width), height=str(total_height), fill="white")
|
|
60
|
+
|
|
61
|
+
for idx, text in enumerate(strings):
|
|
62
|
+
row = idx // per_row
|
|
63
|
+
col = idx % per_row
|
|
64
|
+
|
|
65
|
+
x_offset = padding + col * (cell_width + padding)
|
|
66
|
+
y_offset = padding + row * (cell_height + padding)
|
|
67
|
+
|
|
68
|
+
qr = qrcode.QRCode(version=1, error_correction=qrcode.ERROR_CORRECT_L, box_size=10, border=2, image_factory=qrcode.image.svg.SvgPathImage) # type: ignore
|
|
69
|
+
qr.add_data(text)
|
|
70
|
+
qr.make(fit=True)
|
|
71
|
+
|
|
72
|
+
qr_img = qr.make_image()
|
|
73
|
+
qr_svg_string = qr_img.to_string(encoding="unicode")
|
|
74
|
+
|
|
75
|
+
qr_tree = ET.fromstring(qr_svg_string)
|
|
76
|
+
|
|
77
|
+
group = ET.SubElement(svg_root, "g", transform=f"translate({x_offset}, {y_offset})")
|
|
78
|
+
|
|
79
|
+
qr_group = ET.SubElement(group, "g")
|
|
80
|
+
for child in qr_tree:
|
|
81
|
+
qr_group.append(child)
|
|
82
|
+
|
|
83
|
+
label_text = text[:label_max_chars] if len(text) > label_max_chars else text
|
|
84
|
+
text_y = qr_size + label_height // 2
|
|
85
|
+
|
|
86
|
+
text_elem = ET.SubElement(
|
|
87
|
+
group,
|
|
88
|
+
"text",
|
|
89
|
+
x=str(qr_size // 2),
|
|
90
|
+
y=str(text_y),
|
|
91
|
+
fill="black",
|
|
92
|
+
attrib={
|
|
93
|
+
"font-family": "monospace",
|
|
94
|
+
"font-size": "12",
|
|
95
|
+
"text-anchor": "middle",
|
|
96
|
+
"dominant-baseline": "middle",
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
text_elem.text = label_text
|
|
100
|
+
|
|
101
|
+
tree = ET.ElementTree(svg_root)
|
|
102
|
+
ET.indent(tree, space=" ")
|
|
103
|
+
tree.write(output_path, encoding="unicode", xml_declaration=True)
|
|
104
|
+
|
|
105
|
+
return output_path
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _generate_png(
|
|
109
|
+
strings: list[str],
|
|
110
|
+
output_path: str,
|
|
111
|
+
per_row: int,
|
|
112
|
+
qr_size: int,
|
|
113
|
+
label_height: int,
|
|
114
|
+
padding: int,
|
|
115
|
+
label_max_chars: int,
|
|
116
|
+
) -> str:
|
|
117
|
+
num_items = len(strings)
|
|
118
|
+
num_rows = (num_items + per_row - 1) // per_row
|
|
119
|
+
|
|
120
|
+
cell_width = qr_size
|
|
121
|
+
cell_height = qr_size + label_height
|
|
122
|
+
total_width = per_row * cell_width + (per_row + 1) * padding
|
|
123
|
+
total_height = num_rows * cell_height + (num_rows + 1) * padding
|
|
124
|
+
|
|
125
|
+
import qrcode
|
|
126
|
+
import qrcode.image.pil
|
|
127
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
128
|
+
img = Image.new("RGB", (total_width, total_height), color="white")
|
|
129
|
+
draw = ImageDraw.Draw(img)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", 12)
|
|
133
|
+
except OSError:
|
|
134
|
+
try:
|
|
135
|
+
font = ImageFont.truetype("arial.ttf", 12)
|
|
136
|
+
except OSError:
|
|
137
|
+
font = ImageFont.load_default()
|
|
138
|
+
|
|
139
|
+
for idx, text in enumerate(strings):
|
|
140
|
+
row = idx // per_row
|
|
141
|
+
col = idx % per_row
|
|
142
|
+
|
|
143
|
+
x_offset = padding + col * (cell_width + padding)
|
|
144
|
+
y_offset = padding + row * (cell_height + padding)
|
|
145
|
+
|
|
146
|
+
qr = qrcode.QRCode(version=1, error_correction=qrcode.ERROR_CORRECT_L, box_size=10, border=2, image_factory=qrcode.image.pil.PilImage)
|
|
147
|
+
qr.add_data(text)
|
|
148
|
+
qr.make(fit=True)
|
|
149
|
+
|
|
150
|
+
qr_img = qr.make_image(fill_color="black", back_color="white")
|
|
151
|
+
qr_img_resized = qr_img.resize((qr_size, qr_size), Image.Resampling.LANCZOS)
|
|
152
|
+
|
|
153
|
+
img.paste(qr_img_resized, (x_offset, y_offset))
|
|
154
|
+
|
|
155
|
+
label_text = text[:label_max_chars] if len(text) > label_max_chars else text
|
|
156
|
+
|
|
157
|
+
bbox = draw.textbbox((0, 0), label_text, font=font)
|
|
158
|
+
text_width = bbox[2] - bbox[0]
|
|
159
|
+
text_x = x_offset + (qr_size - text_width) // 2
|
|
160
|
+
text_y = y_offset + qr_size + label_height // 2 - 6
|
|
161
|
+
|
|
162
|
+
draw.text((text_x, text_y), label_text, fill="black", font=font)
|
|
163
|
+
|
|
164
|
+
img.save(output_path, format="PNG")
|
|
165
|
+
|
|
166
|
+
return output_path
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# #!/usr/bin/env python3
|
|
2
|
+
# from pathlib import Path
|
|
3
|
+
# from typing import Annotated
|
|
4
|
+
# import subprocess
|
|
5
|
+
# import typer
|
|
6
|
+
# from rich.console import Console
|
|
7
|
+
# from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
8
|
+
# from rich.table import Table
|
|
9
|
+
# from rich.syntax import Syntax
|
|
10
|
+
|
|
11
|
+
# import chromadb
|
|
12
|
+
# from chromadb.config import Settings
|
|
13
|
+
# from sentence_transformers import SentenceTransformer
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# app = typer.Typer(help="Semantic search over your repository using local RAG")
|
|
17
|
+
# console = Console()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# DEFAULT_EXTENSIONS = [".py", ".sh", ".ps1", ".md", ".toml", ".yaml", ".yml", ".json"]
|
|
21
|
+
# DEFAULT_MODEL = "all-MiniLM-L6-v2"
|
|
22
|
+
# DEFAULT_DB_PATH = Path.home() / ".cache" / "repo_rag"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# class RepoRAG:
|
|
26
|
+
# def __init__(self, db_path: Path, model_name: str = DEFAULT_MODEL) -> None:
|
|
27
|
+
# self.db_path = db_path
|
|
28
|
+
# self.db_path.mkdir(parents=True, exist_ok=True)
|
|
29
|
+
|
|
30
|
+
# with console.status(f"[bold green]Loading embedding model: {model_name}..."):
|
|
31
|
+
# self.model = SentenceTransformer(model_name)
|
|
32
|
+
|
|
33
|
+
# self.client = chromadb.PersistentClient(
|
|
34
|
+
# path=str(db_path),
|
|
35
|
+
# settings=Settings(anonymized_telemetry=False)
|
|
36
|
+
# )
|
|
37
|
+
|
|
38
|
+
# def _get_or_create_collection(self, repo_path: Path) -> chromadb.Collection:
|
|
39
|
+
# collection_name = f"repo_{repo_path.name}".replace("-", "_").replace(".", "_")
|
|
40
|
+
# return self.client.get_or_create_collection(
|
|
41
|
+
# name=collection_name,
|
|
42
|
+
# metadata={"repo_path": str(repo_path)}
|
|
43
|
+
# )
|
|
44
|
+
|
|
45
|
+
# def _chunk_file_content(self, content: str, chunk_size: int = 500, overlap: int = 50) -> list[str]:
|
|
46
|
+
# lines = content.split("\n")
|
|
47
|
+
# chunks: list[str] = []
|
|
48
|
+
# current_chunk: list[str] = []
|
|
49
|
+
# current_size = 0
|
|
50
|
+
|
|
51
|
+
# for line in lines:
|
|
52
|
+
# line_size = len(line)
|
|
53
|
+
# if current_size + line_size > chunk_size and current_chunk:
|
|
54
|
+
# chunks.append("\n".join(current_chunk))
|
|
55
|
+
# overlap_lines = current_chunk[-overlap:] if len(current_chunk) > overlap else current_chunk
|
|
56
|
+
# current_chunk = overlap_lines
|
|
57
|
+
# current_size = sum(len(line_text) for line_text in current_chunk)
|
|
58
|
+
|
|
59
|
+
# current_chunk.append(line)
|
|
60
|
+
# current_size += line_size
|
|
61
|
+
|
|
62
|
+
# if current_chunk:
|
|
63
|
+
# chunks.append("\n".join(current_chunk))
|
|
64
|
+
|
|
65
|
+
# return chunks if chunks else [content]
|
|
66
|
+
|
|
67
|
+
# def index_repo(self, repo_path: Path, extensions: list[str], max_file_size_kb: int = 500) -> None:
|
|
68
|
+
# collection = self._get_or_create_collection(repo_path)
|
|
69
|
+
|
|
70
|
+
# files_to_index: list[Path] = []
|
|
71
|
+
# for ext in extensions:
|
|
72
|
+
# files_to_index.extend(repo_path.rglob(f"*{ext}"))
|
|
73
|
+
|
|
74
|
+
# files_to_index = [
|
|
75
|
+
# f for f in files_to_index
|
|
76
|
+
# if not any(part.startswith('.') for part in f.relative_to(repo_path).parts[:-1])
|
|
77
|
+
# and f.stat().st_size < max_file_size_kb * 1024
|
|
78
|
+
# ]
|
|
79
|
+
|
|
80
|
+
# console.print(f"[bold cyan]Found {len(files_to_index)} files to index")
|
|
81
|
+
|
|
82
|
+
# with Progress(
|
|
83
|
+
# SpinnerColumn(),
|
|
84
|
+
# TextColumn("[progress.description]{task.description}"),
|
|
85
|
+
# console=console
|
|
86
|
+
# ) as progress:
|
|
87
|
+
# task = progress.add_task("Indexing files...", total=len(files_to_index))
|
|
88
|
+
|
|
89
|
+
# for file_path in files_to_index:
|
|
90
|
+
# try:
|
|
91
|
+
# content = file_path.read_text(encoding="utf-8", errors="ignore")
|
|
92
|
+
# rel_path = str(file_path.relative_to(repo_path))
|
|
93
|
+
|
|
94
|
+
# chunks = self._chunk_file_content(content)
|
|
95
|
+
|
|
96
|
+
# for i, chunk in enumerate(chunks):
|
|
97
|
+
# doc_id = f"{rel_path}::chunk_{i}"
|
|
98
|
+
# embedding = self.model.encode(chunk).tolist()
|
|
99
|
+
|
|
100
|
+
# collection.upsert(
|
|
101
|
+
# ids=[doc_id],
|
|
102
|
+
# embeddings=[embedding],
|
|
103
|
+
# documents=[chunk],
|
|
104
|
+
# metadatas=[{
|
|
105
|
+
# "file_path": rel_path,
|
|
106
|
+
# "chunk_index": i,
|
|
107
|
+
# "total_chunks": len(chunks),
|
|
108
|
+
# "extension": file_path.suffix
|
|
109
|
+
# }]
|
|
110
|
+
# )
|
|
111
|
+
|
|
112
|
+
# except Exception as e:
|
|
113
|
+
# console.print(f"[yellow]Warning: Failed to index {file_path}: {e}")
|
|
114
|
+
|
|
115
|
+
# progress.advance(task)
|
|
116
|
+
|
|
117
|
+
# console.print("[bold green]✓ Indexing complete!")
|
|
118
|
+
|
|
119
|
+
# def search(self, repo_path: Path, query: str, n_results: int = 20) -> list[dict[str, str | dict[str, str | int]]]:
|
|
120
|
+
# collection = self._get_or_create_collection(repo_path)
|
|
121
|
+
|
|
122
|
+
# with console.status("[bold green]Searching..."):
|
|
123
|
+
# query_embedding = self.model.encode(query).tolist()
|
|
124
|
+
# results = collection.query(
|
|
125
|
+
# query_embeddings=[query_embedding],
|
|
126
|
+
# n_results=n_results
|
|
127
|
+
# )
|
|
128
|
+
|
|
129
|
+
# if not results["ids"] or not results["ids"][0]:
|
|
130
|
+
# return []
|
|
131
|
+
|
|
132
|
+
# search_results: list[dict[str, str | dict[str, str | int]]] = []
|
|
133
|
+
# for i, doc_id in enumerate(results["ids"][0]):
|
|
134
|
+
# search_results.append({
|
|
135
|
+
# "id": doc_id,
|
|
136
|
+
# "file_path": results["metadatas"][0][i]["file_path"],
|
|
137
|
+
# "content": results["documents"][0][i],
|
|
138
|
+
# "distance": results["distances"][0][i] if results.get("distances") else 0.0,
|
|
139
|
+
# "metadata": results["metadatas"][0][i]
|
|
140
|
+
# })
|
|
141
|
+
|
|
142
|
+
# return search_results
|
|
143
|
+
|
|
144
|
+
# def delete_index(self, repo_path: Path) -> None:
|
|
145
|
+
# collection_name = f"repo_{repo_path.name}".replace("-", "_").replace(".", "_")
|
|
146
|
+
# try:
|
|
147
|
+
# self.client.delete_collection(name=collection_name)
|
|
148
|
+
# console.print(f"[bold green]✓ Deleted index for {repo_path.name}")
|
|
149
|
+
# except Exception as e:
|
|
150
|
+
# console.print(f"[bold red]Error deleting index: {e}")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# @app.command()
|
|
154
|
+
# def index(
|
|
155
|
+
# repo_path: Annotated[Path, typer.Argument(help="Path to repository to index")] = Path.cwd(),
|
|
156
|
+
# extensions: Annotated[str, typer.Option("--ext", "-e", help="Comma-separated list of extensions")] = ",".join(DEFAULT_EXTENSIONS),
|
|
157
|
+
# model: Annotated[str, typer.Option("--model", "-m", help="Sentence transformer model name")] = DEFAULT_MODEL,
|
|
158
|
+
# db_path: Annotated[Path, typer.Option("--db", help="Database path")] = DEFAULT_DB_PATH,
|
|
159
|
+
# max_size_kb: Annotated[int, typer.Option("--max-size", help="Max file size in KB")] = 500,
|
|
160
|
+
# ) -> None:
|
|
161
|
+
# repo_path = repo_path.resolve()
|
|
162
|
+
|
|
163
|
+
# if not repo_path.exists():
|
|
164
|
+
# console.print(f"[bold red]Error: Repository path does not exist: {repo_path}")
|
|
165
|
+
# raise typer.Exit(1)
|
|
166
|
+
|
|
167
|
+
# ext_list = [ext.strip() if ext.startswith(".") else f".{ext.strip()}" for ext in extensions.split(",")]
|
|
168
|
+
|
|
169
|
+
# console.print(f"[bold cyan]Repository:[/] {repo_path}")
|
|
170
|
+
# console.print(f"[bold cyan]Extensions:[/] {', '.join(ext_list)}")
|
|
171
|
+
# console.print(f"[bold cyan]Model:[/] {model}")
|
|
172
|
+
# console.print(f"[bold cyan]Database:[/] {db_path}")
|
|
173
|
+
# console.print()
|
|
174
|
+
|
|
175
|
+
# rag = RepoRAG(db_path, model)
|
|
176
|
+
# rag.index_repo(repo_path, ext_list, max_size_kb)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# @app.command()
|
|
180
|
+
# def search(
|
|
181
|
+
# query: Annotated[str, typer.Argument(help="Search query")],
|
|
182
|
+
# repo_path: Annotated[Path, typer.Option("--repo", "-r", help="Path to repository")] = Path.cwd(),
|
|
183
|
+
# n_results: Annotated[int, typer.Option("--num", "-n", help="Number of results")] = 20,
|
|
184
|
+
# model: Annotated[str, typer.Option("--model", "-m", help="Sentence transformer model name")] = DEFAULT_MODEL,
|
|
185
|
+
# db_path: Annotated[Path, typer.Option("--db", help="Database path")] = DEFAULT_DB_PATH,
|
|
186
|
+
# use_fzf: Annotated[bool, typer.Option("--fzf", help="Use fzf for interactive selection")] = True,
|
|
187
|
+
# show_content: Annotated[bool, typer.Option("--content", "-c", help="Show content snippets")] = True,
|
|
188
|
+
# ) -> None:
|
|
189
|
+
# repo_path = repo_path.resolve()
|
|
190
|
+
|
|
191
|
+
# if not repo_path.exists():
|
|
192
|
+
# console.print(f"[bold red]Error: Repository path does not exist: {repo_path}")
|
|
193
|
+
# raise typer.Exit(1)
|
|
194
|
+
|
|
195
|
+
# rag = RepoRAG(db_path, model)
|
|
196
|
+
# results = rag.search(repo_path, query, n_results)
|
|
197
|
+
|
|
198
|
+
# if not results:
|
|
199
|
+
# console.print("[yellow]No results found")
|
|
200
|
+
# raise typer.Exit(0)
|
|
201
|
+
|
|
202
|
+
# if use_fzf:
|
|
203
|
+
# _search_with_fzf(results, repo_path, show_content)
|
|
204
|
+
# else:
|
|
205
|
+
# _display_results(results, show_content)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# def _display_results(results: list[dict[str, str | dict[str, str | int]]], show_content: bool) -> None:
|
|
209
|
+
# table = Table(title="Search Results", show_header=True, header_style="bold magenta")
|
|
210
|
+
# table.add_column("#", style="cyan", width=4)
|
|
211
|
+
# table.add_column("File", style="green")
|
|
212
|
+
# table.add_column("Chunk", style="yellow", width=8)
|
|
213
|
+
# table.add_column("Score", style="blue", width=8)
|
|
214
|
+
# if show_content:
|
|
215
|
+
# table.add_column("Content Preview", style="white", width=60)
|
|
216
|
+
|
|
217
|
+
# for i, result in enumerate(results, 1):
|
|
218
|
+
# file_path = str(result["file_path"])
|
|
219
|
+
# metadata = result["metadata"]
|
|
220
|
+
# chunk_info = f"{int(metadata['chunk_index']) + 1}/{int(metadata['total_chunks'])}"
|
|
221
|
+
# score = f"{float(result['distance']):.3f}"
|
|
222
|
+
|
|
223
|
+
# row = [str(i), file_path, chunk_info, score]
|
|
224
|
+
|
|
225
|
+
# if show_content:
|
|
226
|
+
# content = str(result["content"])
|
|
227
|
+
# preview = content[:200].replace("\n", " ") + ("..." if len(content) > 200 else "")
|
|
228
|
+
# row.append(preview)
|
|
229
|
+
|
|
230
|
+
# table.add_row(*row)
|
|
231
|
+
|
|
232
|
+
# console.print(table)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# def _search_with_fzf(results: list[dict[str, str | dict[str, str | int]]], repo_path: Path, show_content: bool) -> None:
|
|
236
|
+
# try:
|
|
237
|
+
# fzf_input_lines: list[str] = []
|
|
238
|
+
# for i, result in enumerate(results, 1):
|
|
239
|
+
# file_path = str(result["file_path"])
|
|
240
|
+
# metadata = result["metadata"]
|
|
241
|
+
# chunk_info = f"{int(metadata['chunk_index']) + 1}/{int(metadata['total_chunks'])}"
|
|
242
|
+
# score = f"{float(result['distance']):.3f}"
|
|
243
|
+
|
|
244
|
+
# if show_content:
|
|
245
|
+
# content = str(result["content"]).replace("\n", " ")[:100]
|
|
246
|
+
# line = f"{i:3d} │ {score:6s} │ {chunk_info:5s} │ {file_path:60s} │ {content}"
|
|
247
|
+
# else:
|
|
248
|
+
# line = f"{i:3d} │ {score:6s} │ {chunk_info:5s} │ {file_path}"
|
|
249
|
+
|
|
250
|
+
# fzf_input_lines.append(line)
|
|
251
|
+
|
|
252
|
+
# fzf_input = "\n".join(fzf_input_lines)
|
|
253
|
+
|
|
254
|
+
# result = subprocess.run(
|
|
255
|
+
# ["fzf", "--ansi", "--multi", "--reverse", "--header=Select files to open (TAB for multi-select)"],
|
|
256
|
+
# input=fzf_input.encode(),
|
|
257
|
+
# capture_output=True
|
|
258
|
+
# )
|
|
259
|
+
|
|
260
|
+
# if result.returncode != 0:
|
|
261
|
+
# console.print("[yellow]Selection cancelled")
|
|
262
|
+
# return
|
|
263
|
+
|
|
264
|
+
# selected_lines = result.stdout.decode().strip().split("\n")
|
|
265
|
+
|
|
266
|
+
# for line in selected_lines:
|
|
267
|
+
# if not line:
|
|
268
|
+
# continue
|
|
269
|
+
|
|
270
|
+
# parts = line.split("│")
|
|
271
|
+
# if len(parts) < 4:
|
|
272
|
+
# continue
|
|
273
|
+
|
|
274
|
+
# file_path_str = parts[3].strip().split()[0]
|
|
275
|
+
|
|
276
|
+
# idx = int(parts[0].strip()) - 1
|
|
277
|
+
# if idx < len(results):
|
|
278
|
+
# content = str(results[idx]["content"])
|
|
279
|
+
|
|
280
|
+
# console.print(f"\n[bold green]File:[/] {file_path_str}")
|
|
281
|
+
# console.print("[bold cyan]Content:[/]")
|
|
282
|
+
|
|
283
|
+
# syntax = Syntax(content, "python", theme="monokai", line_numbers=True)
|
|
284
|
+
# console.print(syntax)
|
|
285
|
+
# console.print("\n" + "─" * 80 + "\n")
|
|
286
|
+
|
|
287
|
+
# except FileNotFoundError:
|
|
288
|
+
# console.print("[bold red]Error: fzf not found. Install fzf or use --no-fzf flag")
|
|
289
|
+
# _display_results(results, show_content)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# @app.command()
|
|
293
|
+
# def delete(
|
|
294
|
+
# repo_path: Annotated[Path, typer.Argument(help="Path to repository")] = Path.cwd(),
|
|
295
|
+
# db_path: Annotated[Path, typer.Option("--db", help="Database path")] = DEFAULT_DB_PATH,
|
|
296
|
+
# model: Annotated[str, typer.Option("--model", "-m", help="Sentence transformer model name")] = DEFAULT_MODEL,
|
|
297
|
+
# ) -> None:
|
|
298
|
+
# repo_path = repo_path.resolve()
|
|
299
|
+
# rag = RepoRAG(db_path, model)
|
|
300
|
+
# rag.delete_index(repo_path)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# @app.command()
|
|
304
|
+
# def info() -> None:
|
|
305
|
+
# console.print("[bold cyan]Repo RAG - Semantic Code Search[/]\n")
|
|
306
|
+
|
|
307
|
+
# info_table = Table(show_header=False, box=None)
|
|
308
|
+
# info_table.add_column("Key", style="cyan")
|
|
309
|
+
# info_table.add_column("Value", style="white")
|
|
310
|
+
|
|
311
|
+
# info_table.add_row("Embedding Model", DEFAULT_MODEL)
|
|
312
|
+
# info_table.add_row("Vector Store", "ChromaDB (embedded)")
|
|
313
|
+
# info_table.add_row("Default Extensions", ", ".join(DEFAULT_EXTENSIONS))
|
|
314
|
+
# info_table.add_row("Database Path", str(DEFAULT_DB_PATH))
|
|
315
|
+
|
|
316
|
+
# console.print(info_table)
|
|
317
|
+
|
|
318
|
+
# console.print("\n[bold green]Quick Start:[/]")
|
|
319
|
+
# console.print("1. Index your repo: [yellow]repo-rag index[/]")
|
|
320
|
+
# console.print("2. Search semantically: [yellow]repo-rag search 'your query'[/]")
|
|
321
|
+
# console.print("3. Delete index: [yellow]repo-rag delete[/]")
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# if __name__ == "__main__":
|
|
325
|
+
# app()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
please fully read docs of https://github.com/meilisearch/meilisearch-python
|
|
3
|
+
|
|
4
|
+
then make a cli using typer in this file
|
|
5
|
+
|
|
6
|
+
I want commands to
|
|
7
|
+
create-index (pass name of index)
|
|
8
|
+
command to populate the index with files in folder
|
|
9
|
+
command is add-to-index
|
|
10
|
+
--directory [str] and --extensions .py,.ps1,.sh
|
|
11
|
+
|
|
12
|
+
command to rebuild index
|
|
13
|
+
|
|
14
|
+
coimmand to search
|
|
15
|
+
command show stats and rop index
|
|
16
|
+
|
|
17
|
+
learn from my style of building apps like this #file:msearch.py
|
|
18
|
+
|
|
19
|
+
and add option when building index to say --symantic (means use ai locall embedding to build the index)
|
|
20
|
+
|
|
21
|
+
in all cases we should be able to pass
|
|
22
|
+
MEILI_URL="http://localhost:7700" (default)
|
|
23
|
+
MEILI_MASTER_KEY="YOUR_MASTER_KEY"
|
|
24
|
+
|
|
25
|
+
"""
|
|
@@ -2,26 +2,24 @@
|
|
|
2
2
|
CC
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
6
|
-
from tenacity import retry, stop_after_attempt, wait_chain, wait_fixed
|
|
7
|
-
import getpass
|
|
8
|
-
import os
|
|
9
5
|
from typing import Optional, Annotated
|
|
10
|
-
|
|
11
6
|
import typer
|
|
12
7
|
|
|
13
|
-
from
|
|
14
|
-
from machineconfig.scripts.python.helpers_cloud.cloud_helpers import ArgsDefaults, Args
|
|
15
|
-
from rich.console import Console
|
|
16
|
-
from rich.panel import Panel
|
|
17
|
-
from rich.progress import Progress
|
|
18
|
-
from machineconfig.utils.accessories import pprint
|
|
8
|
+
from tenacity import retry, stop_after_attempt, wait_chain, wait_fixed
|
|
19
9
|
|
|
20
|
-
console = Console()
|
|
21
10
|
|
|
22
11
|
|
|
23
12
|
@retry(stop=stop_after_attempt(3), wait=wait_chain(wait_fixed(1), wait_fixed(4), wait_fixed(9)))
|
|
24
13
|
def get_securely_shared_file(url: Optional[str] = None, folder: Optional[str] = None) -> None:
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.progress import Progress
|
|
17
|
+
import getpass
|
|
18
|
+
import os
|
|
19
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
20
|
+
|
|
21
|
+
console = Console()
|
|
22
|
+
|
|
25
23
|
console.print(Panel("🚀 Secure File Downloader", title="[bold blue]Downloader[/bold blue]", border_style="blue"))
|
|
26
24
|
|
|
27
25
|
folder_obj = PathExtended.cwd() if folder is None else PathExtended(folder)
|
|
@@ -62,21 +60,30 @@ def get_securely_shared_file(url: Optional[str] = None, folder: Optional[str] =
|
|
|
62
60
|
tmp_folder.delete()
|
|
63
61
|
|
|
64
62
|
|
|
63
|
+
|
|
65
64
|
def main(
|
|
66
65
|
source: Annotated[str, typer.Argument(help="📂 file/folder path to be taken from here.")],
|
|
67
66
|
target: Annotated[str, typer.Argument(help="🎯 file/folder path to be be sent to here.")],
|
|
68
|
-
overwrite: Annotated[bool, typer.Option("--overwrite", "-o", help="✍️ Overwrite existing file.")] =
|
|
69
|
-
share: Annotated[bool, typer.Option("--share", "-s", help="🔗 Share file / directory")] =
|
|
70
|
-
rel2home: Annotated[bool, typer.Option("--relative2home", "-r", help="🏠 Relative to `myhome` folder")] =
|
|
71
|
-
root: Annotated[Optional[str], typer.Option("--root", "-R", help="🌳 Remote root. None is the default, unless rel2home is raied, making the default `myhome`.")] =
|
|
72
|
-
key: Annotated[Optional[str], typer.Option("--key", "-k", help="🔑 Key for encryption")] =
|
|
73
|
-
pwd: Annotated[Optional[str], typer.Option("--password", "-p", help="🔒 Password for encryption")] =
|
|
74
|
-
encrypt: Annotated[bool, typer.Option("--encrypt", "-e", help="🔐 Encrypt before sending.")] =
|
|
75
|
-
zip_: Annotated[bool, typer.Option("--zip", "-z", help="📦 unzip after receiving.")] =
|
|
76
|
-
os_specific: Annotated[bool, typer.Option("--os-specific", "-O", help="💻 choose path specific for this OS.")] =
|
|
67
|
+
overwrite: Annotated[bool, typer.Option("--overwrite", "-o", help="✍️ Overwrite existing file.")] = False,
|
|
68
|
+
share: Annotated[bool, typer.Option("--share", "-s", help="🔗 Share file / directory")] = False,
|
|
69
|
+
rel2home: Annotated[bool, typer.Option("--relative2home", "-r", help="🏠 Relative to `myhome` folder")] = False,
|
|
70
|
+
root: Annotated[Optional[str], typer.Option("--root", "-R", help="🌳 Remote root. None is the default, unless rel2home is raied, making the default `myhome`.")] = None,
|
|
71
|
+
key: Annotated[Optional[str], typer.Option("--key", "-k", help="🔑 Key for encryption")] = None,
|
|
72
|
+
pwd: Annotated[Optional[str], typer.Option("--password", "-p", help="🔒 Password for encryption")] = None,
|
|
73
|
+
encrypt: Annotated[bool, typer.Option("--encrypt", "-e", help="🔐 Encrypt before sending.")] = False,
|
|
74
|
+
zip_: Annotated[bool, typer.Option("--zip", "-z", help="📦 unzip after receiving.")] = False,
|
|
75
|
+
os_specific: Annotated[bool, typer.Option("--os-specific", "-O", help="💻 choose path specific for this OS.")] = False,
|
|
77
76
|
config: Annotated[Optional[str], typer.Option("--config", "-c", help="⚙️ path to cloud.json file.")] = None,
|
|
78
77
|
) -> None:
|
|
79
78
|
"""📤 Upload or 📥 Download files/folders to/from cloud storage services like Google Drive, Dropbox, OneDrive, etc."""
|
|
79
|
+
from rich.console import Console
|
|
80
|
+
from rich.panel import Panel
|
|
81
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
82
|
+
from machineconfig.scripts.python.helpers_cloud.helpers2 import parse_cloud_source_target
|
|
83
|
+
from machineconfig.scripts.python.helpers_cloud.cloud_helpers import Args
|
|
84
|
+
from machineconfig.utils.accessories import pprint
|
|
85
|
+
|
|
86
|
+
console = Console()
|
|
80
87
|
console.print(Panel("☁️ Cloud Copy Utility", title="[bold blue]Cloud Copy[/bold blue]", border_style="blue", width=152))
|
|
81
88
|
args_obj = Args(
|
|
82
89
|
overwrite=overwrite,
|
|
@@ -12,6 +12,7 @@ from dataclasses import dataclass
|
|
|
12
12
|
console = Console()
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
|
|
15
16
|
class ArgsDefaults:
|
|
16
17
|
# source: str=None
|
|
17
18
|
# target: str=None
|
|
@@ -25,7 +26,6 @@ class ArgsDefaults:
|
|
|
25
26
|
key = None
|
|
26
27
|
pwd = None
|
|
27
28
|
|
|
28
|
-
|
|
29
29
|
@dataclass
|
|
30
30
|
class Args:
|
|
31
31
|
cloud: Optional[str] = None
|
|
@@ -1,32 +1,27 @@
|
|
|
1
1
|
"""Cloud mount script"""
|
|
2
2
|
|
|
3
|
-
from machineconfig.utils.options import choose_from_options
|
|
4
|
-
from machineconfig.utils.io import read_ini
|
|
5
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
6
|
-
|
|
7
|
-
import platform
|
|
8
|
-
from typing import Optional, Annotated
|
|
9
3
|
import typer
|
|
10
|
-
from
|
|
11
|
-
from rich.panel import Panel
|
|
12
|
-
|
|
13
|
-
console = Console()
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
DEFAULT_MOUNT = "~/data/rclone"
|
|
4
|
+
from typing import Optional, Annotated
|
|
17
5
|
|
|
18
6
|
|
|
19
7
|
def get_rclone_config():
|
|
8
|
+
from machineconfig.utils.io import read_ini
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import platform
|
|
20
11
|
if platform.system() == "Windows":
|
|
21
|
-
config = read_ini(
|
|
12
|
+
config = read_ini(Path.home().joinpath("AppData/Roaming/rclone/rclone.conf"))
|
|
22
13
|
elif platform.system() in ["Linux", "Darwin"]:
|
|
23
|
-
config = read_ini(
|
|
14
|
+
config = read_ini(Path.home().joinpath(".config/rclone/rclone.conf"))
|
|
24
15
|
else:
|
|
25
16
|
raise ValueError("unsupported platform")
|
|
26
17
|
return config
|
|
27
18
|
|
|
28
19
|
|
|
29
20
|
def get_mprocs_mount_txt(cloud: str, rclone_cmd: str, cloud_brand: str): # cloud_brand = config[cloud]["type"]
|
|
21
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
22
|
+
import platform
|
|
23
|
+
DEFAULT_MOUNT = "~/data/rclone"
|
|
24
|
+
|
|
30
25
|
header = f"{' ' + cloud + ' | ' + cloud_brand + ' '}".center(50, "=")
|
|
31
26
|
if platform.system() == "Windows":
|
|
32
27
|
sub_text_path = PathExtended.tmpfile(suffix=".ps1")
|
|
@@ -57,6 +52,13 @@ def mount(
|
|
|
57
52
|
destination: Annotated[Optional[str], typer.Option(help="destination to mount")] = None,
|
|
58
53
|
network: Annotated[Optional[str], typer.Option(help="mount network drive")] = None,
|
|
59
54
|
) -> None:
|
|
55
|
+
from machineconfig.utils.options import choose_from_options
|
|
56
|
+
from pathlib import Path
|
|
57
|
+
import platform
|
|
58
|
+
from rich.console import Console
|
|
59
|
+
from rich.panel import Panel
|
|
60
|
+
console = Console()
|
|
61
|
+
DEFAULT_MOUNT = "~/data/rclone"
|
|
60
62
|
|
|
61
63
|
# draw header box dynamically
|
|
62
64
|
title = "☁️ Cloud Mount Utility"
|
|
@@ -73,9 +75,9 @@ def mount(
|
|
|
73
75
|
|
|
74
76
|
if network is None:
|
|
75
77
|
if destination is None:
|
|
76
|
-
mount_loc =
|
|
78
|
+
mount_loc = Path(DEFAULT_MOUNT).expanduser().joinpath(cloud)
|
|
77
79
|
else:
|
|
78
|
-
mount_loc =
|
|
80
|
+
mount_loc = Path(destination)
|
|
79
81
|
|
|
80
82
|
mount_info = f"📂 Mount location: {mount_loc}"
|
|
81
83
|
console.print(Panel(mount_info, border_style="blue"))
|