4u-cli 1.0.0__tar.gz

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.
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: 4u-cli
3
+ Version: 1.0.0
4
+ Summary: 4u - A modern CLI package manager for Python tools
5
+ Author: 4u Team
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Environment :: Console
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: click>=8.0
14
+ Requires-Dist: rich>=13.0
15
+ Requires-Dist: requests>=2.28
16
+ Requires-Dist: supabase>=2.0
17
+ Dynamic: author
18
+ Dynamic: classifier
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: license
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # 4u CLI
27
+
28
+ > A modern CLI package manager for Python tools ⚡
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install -e .
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ```bash
39
+ # Show help and banner
40
+ 4u
41
+
42
+ # Search for tools
43
+ 4u search <query>
44
+
45
+ # Install a tool
46
+ 4u install <tool_name>
47
+
48
+ # List installed tools
49
+ 4u list
50
+
51
+ # Update tools
52
+ 4u update # Update all
53
+ 4u update <tool> # Update specific tool
54
+
55
+ # Uninstall a tool
56
+ 4u uninstall <tool_name>
57
+
58
+ # Run an installed tool directly
59
+ 4u <tool_name> [args...]
60
+ ```
61
+
62
+ ## Configuration
63
+
64
+ Set environment variables for Supabase connection:
65
+
66
+ ```bash
67
+ export FOURU_SUPABASE_URL="https://your-project.supabase.co"
68
+ export FOURU_SUPABASE_KEY="your-anon-key"
69
+ ```
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,23 @@
1
+ README.md
2
+ setup.py
3
+ 4u_cli.egg-info/PKG-INFO
4
+ 4u_cli.egg-info/SOURCES.txt
5
+ 4u_cli.egg-info/dependency_links.txt
6
+ 4u_cli.egg-info/entry_points.txt
7
+ 4u_cli.egg-info/requires.txt
8
+ 4u_cli.egg-info/top_level.txt
9
+ fourutil/__init__.py
10
+ fourutil/config.py
11
+ fourutil/main.py
12
+ fourutil/supabase_client.py
13
+ fourutil/commands/__init__.py
14
+ fourutil/commands/install.py
15
+ fourutil/commands/list_tools.py
16
+ fourutil/commands/run.py
17
+ fourutil/commands/search.py
18
+ fourutil/commands/uninstall.py
19
+ fourutil/commands/update.py
20
+ fourutil/utils/__init__.py
21
+ fourutil/utils/downloader.py
22
+ fourutil/utils/manifest.py
23
+ fourutil/utils/printer.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ 4u = fourutil.main:cli
@@ -0,0 +1,4 @@
1
+ click>=8.0
2
+ rich>=13.0
3
+ requests>=2.28
4
+ supabase>=2.0
@@ -0,0 +1 @@
1
+ fourutil
4u_cli-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: 4u-cli
3
+ Version: 1.0.0
4
+ Summary: 4u - A modern CLI package manager for Python tools
5
+ Author: 4u Team
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Environment :: Console
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: click>=8.0
14
+ Requires-Dist: rich>=13.0
15
+ Requires-Dist: requests>=2.28
16
+ Requires-Dist: supabase>=2.0
17
+ Dynamic: author
18
+ Dynamic: classifier
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: license
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # 4u CLI
27
+
28
+ > A modern CLI package manager for Python tools ⚡
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install -e .
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ```bash
39
+ # Show help and banner
40
+ 4u
41
+
42
+ # Search for tools
43
+ 4u search <query>
44
+
45
+ # Install a tool
46
+ 4u install <tool_name>
47
+
48
+ # List installed tools
49
+ 4u list
50
+
51
+ # Update tools
52
+ 4u update # Update all
53
+ 4u update <tool> # Update specific tool
54
+
55
+ # Uninstall a tool
56
+ 4u uninstall <tool_name>
57
+
58
+ # Run an installed tool directly
59
+ 4u <tool_name> [args...]
60
+ ```
61
+
62
+ ## Configuration
63
+
64
+ Set environment variables for Supabase connection:
65
+
66
+ ```bash
67
+ export FOURU_SUPABASE_URL="https://your-project.supabase.co"
68
+ export FOURU_SUPABASE_KEY="your-anon-key"
69
+ ```
70
+
71
+ ## License
72
+
73
+ MIT
4u_cli-1.0.0/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # 4u CLI
2
+
3
+ > A modern CLI package manager for Python tools ⚡
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install -e .
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ # Show help and banner
15
+ 4u
16
+
17
+ # Search for tools
18
+ 4u search <query>
19
+
20
+ # Install a tool
21
+ 4u install <tool_name>
22
+
23
+ # List installed tools
24
+ 4u list
25
+
26
+ # Update tools
27
+ 4u update # Update all
28
+ 4u update <tool> # Update specific tool
29
+
30
+ # Uninstall a tool
31
+ 4u uninstall <tool_name>
32
+
33
+ # Run an installed tool directly
34
+ 4u <tool_name> [args...]
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ Set environment variables for Supabase connection:
40
+
41
+ ```bash
42
+ export FOURU_SUPABASE_URL="https://your-project.supabase.co"
43
+ export FOURU_SUPABASE_KEY="your-anon-key"
44
+ ```
45
+
46
+ ## License
47
+
48
+ MIT
@@ -0,0 +1,3 @@
1
+ """4u CLI - A modern package manager for Python tools."""
2
+
3
+ __version__ = "1.0.0"
File without changes
@@ -0,0 +1,80 @@
1
+ """
2
+ أمر التثبيت: 4u install <slug>
3
+ Install command – downloads a tool from Supabase and registers it locally.
4
+ """
5
+
6
+ import click
7
+ from rich.panel import Panel
8
+
9
+ from fourutil.supabase_client import get_client
10
+ from fourutil.config import TOOLS_DIR, TOOLS_TABLE
11
+ from fourutil.utils.printer import console, print_success, print_error, print_info, print_warning
12
+ from fourutil.utils.manifest import add_tool, get_tool
13
+ from fourutil.utils.downloader import download_file
14
+
15
+
16
+ @click.command("install")
17
+ @click.argument("slug")
18
+ def install(slug: str) -> None:
19
+ """Install a tool by its slug name. ⬇️"""
20
+ console.print()
21
+ console.print(f" 🔍 Looking up [bold cyan]{slug}[/bold cyan] …")
22
+
23
+ # --- استعلام Supabase ---
24
+ try:
25
+ client = get_client()
26
+ result = (
27
+ client.table(TOOLS_TABLE)
28
+ .select("*")
29
+ .eq("slug", slug)
30
+ .execute()
31
+ )
32
+ except Exception as exc:
33
+ print_error(f"Failed to query Supabase: {exc}")
34
+ return
35
+
36
+ if not result.data:
37
+ print_error(f"Tool [bold]{slug}[/bold] not found in the registry.")
38
+ console.print(" 💡 Try [bold]4u search <query>[/bold] to find tools.", style="dim")
39
+ return
40
+
41
+ tool = result.data[0]
42
+ remote_version = tool.get("version", "0.0.0")
43
+ file_url = tool.get("file_url", "")
44
+ tool_name = tool.get("name", slug)
45
+ description = tool.get("description", "")
46
+
47
+ # --- هل الأداة مثبتة بالفعل؟ ---
48
+ local = get_tool(slug)
49
+ if local and local.get("version") == remote_version:
50
+ print_info(f"[bold]{tool_name}[/bold] v{remote_version} is already installed and up to date.")
51
+ return
52
+
53
+ if not file_url:
54
+ print_error("No download URL found for this tool.")
55
+ return
56
+
57
+ # --- تحميل الملف ---
58
+ filename = f"{slug}.py"
59
+ dest = TOOLS_DIR / filename
60
+
61
+ console.print(f" ⬇️ Downloading [bold]{tool_name}[/bold] v{remote_version} …")
62
+ ok = download_file(file_url, dest)
63
+ if not ok:
64
+ return
65
+
66
+ # --- تسجيل في manifest ---
67
+ add_tool(slug, remote_version, filename)
68
+
69
+ # --- رسالة نجاح ---
70
+ console.print()
71
+ console.print(
72
+ Panel(
73
+ f"[bold green]{tool_name}[/bold green] v{remote_version} installed successfully!\n"
74
+ f"[dim]{description}[/dim]\n\n"
75
+ f"Run it with: [bold cyan]4u {slug}[/bold cyan]",
76
+ title="✅ Installed",
77
+ border_style="green",
78
+ padding=(1, 2),
79
+ )
80
+ )
@@ -0,0 +1,54 @@
1
+ """
2
+ أمر العرض: 4u list
3
+ List command – shows all locally installed tools in a rich table.
4
+ """
5
+
6
+ import click
7
+ from rich.table import Table
8
+ from rich.panel import Panel
9
+
10
+ from fourutil.utils.printer import console, print_info
11
+ from fourutil.utils.manifest import get_all_tools
12
+
13
+
14
+ @click.command("list")
15
+ def list_tools() -> None:
16
+ """List all installed tools. 📦"""
17
+ console.print()
18
+
19
+ tools = get_all_tools()
20
+
21
+ if not tools:
22
+ print_info("No tools installed yet.")
23
+ console.print(" 💡 Use [bold cyan]4u search <query>[/bold cyan] to find tools.", style="dim")
24
+ console.print(" 💡 Use [bold cyan]4u install <name>[/bold cyan] to install one.", style="dim")
25
+ return
26
+
27
+ table = Table(
28
+ title="📦 Installed Tools",
29
+ title_style="bold cyan",
30
+ border_style="bright_blue",
31
+ show_lines=True,
32
+ padding=(0, 1),
33
+ )
34
+ table.add_column("#", style="dim", justify="right", width=4)
35
+ table.add_column("Name", style="bold white", min_width=15)
36
+ table.add_column("Version", style="green", justify="center", min_width=10)
37
+ table.add_column("Installed At", style="yellow", min_width=20)
38
+ table.add_column("File", style="dim", min_width=15)
39
+
40
+ for idx, (slug, info) in enumerate(tools.items(), start=1):
41
+ installed_at = info.get("installed_at", "—")
42
+ # تنسيق التاريخ لعرض أجمل
43
+ if "T" in installed_at:
44
+ installed_at = installed_at.replace("T", " ").split(".")[0]
45
+ table.add_row(
46
+ str(idx),
47
+ slug,
48
+ info.get("version", "?"),
49
+ installed_at,
50
+ info.get("file", "—"),
51
+ )
52
+
53
+ console.print(table)
54
+ console.print(f"\n Total: [bold]{len(tools)}[/bold] tool(s) installed.\n", style="dim")
@@ -0,0 +1,37 @@
1
+ """
2
+ المشغّل الديناميكي للأدوات المثبتة
3
+ Dynamic tool runner – executes an installed tool as a subprocess.
4
+ """
5
+
6
+ import sys
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+ from fourutil.config import TOOLS_DIR
11
+ from fourutil.utils.printer import print_error, console
12
+
13
+
14
+ def run_tool(name: str, args: tuple) -> None:
15
+ """
16
+ تشغيل أداة مثبتة باسمها.
17
+ Runs ~/.4u/commands/<name>.py with the remaining CLI arguments.
18
+ """
19
+ tool_path = TOOLS_DIR / f"{name}.py"
20
+
21
+ if not tool_path.exists():
22
+ print_error(f"Tool [bold]{name}[/bold] is not installed.")
23
+ console.print(
24
+ f" 💡 Use [bold cyan]4u install {name}[/bold cyan] to install it.",
25
+ style="dim",
26
+ )
27
+ sys.exit(1)
28
+
29
+ cmd = [sys.executable, str(tool_path)] + list(args)
30
+ try:
31
+ result = subprocess.run(cmd)
32
+ sys.exit(result.returncode)
33
+ except KeyboardInterrupt:
34
+ sys.exit(130)
35
+ except Exception as exc:
36
+ print_error(f"Failed to run [bold]{name}[/bold]: {exc}")
37
+ sys.exit(1)
@@ -0,0 +1,66 @@
1
+ """
2
+ أمر البحث: 4u search <query>
3
+ Search command – queries Supabase for tools matching a keyword.
4
+ """
5
+
6
+ import click
7
+ from rich.table import Table
8
+ from rich.panel import Panel
9
+
10
+ from fourutil.supabase_client import get_client
11
+ from fourutil.config import TOOLS_TABLE
12
+ from fourutil.utils.printer import console, print_error, print_info
13
+
14
+
15
+ @click.command("search")
16
+ @click.argument("query")
17
+ def search(query: str) -> None:
18
+ """Search for tools in the registry. 🔎"""
19
+ console.print()
20
+ console.print(f" 🔎 Searching for [bold cyan]{query}[/bold cyan] …\n")
21
+
22
+ try:
23
+ client = get_client()
24
+ # بحث في الاسم أو الوصف باستخدام ilike
25
+ result = (
26
+ client.table(TOOLS_TABLE)
27
+ .select("*")
28
+ .or_(f"name.ilike.%{query}%,description.ilike.%{query}%,slug.ilike.%{query}%")
29
+ .execute()
30
+ )
31
+ except Exception as exc:
32
+ print_error(f"Search failed: {exc}")
33
+ return
34
+
35
+ if not result.data:
36
+ print_info(f"No tools found matching [bold]{query}[/bold].")
37
+ return
38
+
39
+ table = Table(
40
+ title=f"🔎 Search Results for \"{query}\"",
41
+ title_style="bold cyan",
42
+ border_style="bright_blue",
43
+ show_lines=True,
44
+ padding=(0, 1),
45
+ )
46
+ table.add_column("#", style="dim", justify="right", width=4)
47
+ table.add_column("Slug", style="bold white", min_width=12)
48
+ table.add_column("Name", style="cyan", min_width=15)
49
+ table.add_column("Version", style="green", justify="center", min_width=8)
50
+ table.add_column("Description", style="white", min_width=30)
51
+
52
+ for idx, tool in enumerate(result.data, start=1):
53
+ table.add_row(
54
+ str(idx),
55
+ tool.get("slug", "—"),
56
+ tool.get("name", "—"),
57
+ tool.get("version", "—"),
58
+ tool.get("description", "—"),
59
+ )
60
+
61
+ console.print(table)
62
+ console.print(
63
+ f"\n Found [bold]{len(result.data)}[/bold] tool(s). "
64
+ f"Install with [bold cyan]4u install <slug>[/bold cyan]\n",
65
+ style="dim",
66
+ )
@@ -0,0 +1,42 @@
1
+ """
2
+ أمر الحذف: 4u uninstall <slug>
3
+ Uninstall command – removes a tool's file and manifest entry.
4
+ """
5
+
6
+ import click
7
+ from rich.panel import Panel
8
+
9
+ from fourutil.config import TOOLS_DIR
10
+ from fourutil.utils.printer import console, print_success, print_error
11
+ from fourutil.utils.manifest import remove_tool, get_tool
12
+
13
+
14
+ @click.command("uninstall")
15
+ @click.argument("slug")
16
+ def uninstall(slug: str) -> None:
17
+ """Uninstall a tool by its slug name. 🗑️"""
18
+ console.print()
19
+
20
+ local = get_tool(slug)
21
+ if not local:
22
+ print_error(f"[bold]{slug}[/bold] is not installed.")
23
+ return
24
+
25
+ # --- حذف الملف ---
26
+ filename = local.get("file", f"{slug}.py")
27
+ file_path = TOOLS_DIR / filename
28
+ if file_path.exists():
29
+ file_path.unlink()
30
+
31
+ # --- حذف من manifest ---
32
+ remove_tool(slug)
33
+
34
+ # --- رسالة نجاح ---
35
+ console.print(
36
+ Panel(
37
+ f"[bold]{slug}[/bold] has been uninstalled. 👋",
38
+ title="🗑️ Removed",
39
+ border_style="red",
40
+ padding=(0, 2),
41
+ )
42
+ )
@@ -0,0 +1,110 @@
1
+ """
2
+ أمر التحديث: 4u update [slug]
3
+ Update command – checks for newer versions and updates tools.
4
+ """
5
+
6
+ import click
7
+ from rich.panel import Panel
8
+
9
+ from fourutil.supabase_client import get_client
10
+ from fourutil.config import TOOLS_DIR, TOOLS_TABLE
11
+ from fourutil.utils.printer import console, print_success, print_error, print_info, print_warning
12
+ from fourutil.utils.manifest import add_tool, get_tool, get_all_tools
13
+ from fourutil.utils.downloader import download_file
14
+
15
+
16
+ def _update_single(slug: str) -> bool:
17
+ """
18
+ تحديث أداة واحدة.
19
+ Returns True if an update was applied, False otherwise.
20
+ """
21
+ local = get_tool(slug)
22
+ if not local:
23
+ print_error(f"[bold]{slug}[/bold] is not installed.")
24
+ return False
25
+
26
+ local_version = local.get("version", "0.0.0")
27
+
28
+ # --- جلب الإصدار البعيد ---
29
+ try:
30
+ client = get_client()
31
+ result = (
32
+ client.table(TOOLS_TABLE)
33
+ .select("*")
34
+ .eq("slug", slug)
35
+ .execute()
36
+ )
37
+ except Exception as exc:
38
+ print_error(f"Failed to query Supabase for {slug}: {exc}")
39
+ return False
40
+
41
+ if not result.data:
42
+ print_warning(f"[bold]{slug}[/bold] no longer exists in the registry.")
43
+ return False
44
+
45
+ tool = result.data[0]
46
+ remote_version = tool.get("version", "0.0.0")
47
+ file_url = tool.get("file_url", "")
48
+ tool_name = tool.get("name", slug)
49
+
50
+ if remote_version == local_version:
51
+ print_info(f"[bold]{tool_name}[/bold] v{local_version} is already up to date.")
52
+ return False
53
+
54
+ if not file_url:
55
+ print_error(f"No download URL found for [bold]{slug}[/bold].")
56
+ return False
57
+
58
+ console.print(f" ⬆️ Updating [bold]{tool_name}[/bold] {local_version} → {remote_version} …")
59
+ filename = f"{slug}.py"
60
+ dest = TOOLS_DIR / filename
61
+
62
+ ok = download_file(file_url, dest)
63
+ if not ok:
64
+ return False
65
+
66
+ add_tool(slug, remote_version, filename)
67
+ print_success(f"[bold]{tool_name}[/bold] updated to v{remote_version}")
68
+ return True
69
+
70
+
71
+ @click.command("update")
72
+ @click.argument("slug", required=False, default=None)
73
+ def update(slug: str | None) -> None:
74
+ """Update one or all installed tools. 🔄"""
75
+ console.print()
76
+
77
+ if slug:
78
+ # --- تحديث أداة واحدة ---
79
+ _update_single(slug)
80
+ else:
81
+ # --- تحديث جميع الأدوات ---
82
+ tools = get_all_tools()
83
+ if not tools:
84
+ print_info("No tools installed yet. Use [bold]4u install <name>[/bold].")
85
+ return
86
+
87
+ console.print(" 🔄 Checking updates for all installed tools …\n")
88
+ updated_count = 0
89
+ for tool_slug in tools:
90
+ if _update_single(tool_slug):
91
+ updated_count += 1
92
+ console.print()
93
+
94
+ if updated_count == 0:
95
+ console.print(
96
+ Panel(
97
+ "All tools are already up to date! 🎉",
98
+ border_style="green",
99
+ padding=(0, 2),
100
+ )
101
+ )
102
+ else:
103
+ console.print(
104
+ Panel(
105
+ f"Updated [bold]{updated_count}[/bold] tool(s) successfully!",
106
+ title="✅ Update Complete",
107
+ border_style="green",
108
+ padding=(0, 2),
109
+ )
110
+ )
@@ -0,0 +1,30 @@
1
+ """
2
+ تكوين الإعدادات الأساسية لأداة 4u
3
+ Configuration constants for the 4u CLI tool.
4
+ """
5
+
6
+ import os
7
+ from pathlib import Path
8
+
9
+ # --- المسارات المحلية (Local Paths) ---
10
+ HOME_DIR = Path.home()
11
+ FOURU_DIR = HOME_DIR / ".4u"
12
+ TOOLS_DIR = FOURU_DIR / "commands"
13
+ MANIFEST_FILE = FOURU_DIR / "manifest.json"
14
+
15
+ # --- إعدادات Supabase ---
16
+ SUPABASE_URL = os.environ.get(
17
+ "FOURU_SUPABASE_URL",
18
+ "https://nvdmkhehhelulfasmpob.supabase.co",
19
+ )
20
+ SUPABASE_KEY = os.environ.get(
21
+ "FOURU_SUPABASE_KEY",
22
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im52ZG1raGVoaGVsdWxmYXNtcG9iIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc5MzYzNTM2NiwiZXhwIjoyMTA5MjExMzY2fQ.ZNq5iLFMGMFdBgKmF_jujnEu1LRY-CIRf5548og44uM",
23
+ )
24
+
25
+ # --- أسماء الجداول والتخزين (Table & Bucket Names) ---
26
+ TOOLS_TABLE = "tools"
27
+ TOOLS_BUCKET = "tools-bucket"
28
+
29
+ # --- إصدار التطبيق ---
30
+ APP_VERSION = "1.0.0"
@@ -0,0 +1,80 @@
1
+ """
2
+ نقطة الدخول الرئيسية لـ 4u CLI
3
+ Main entry point – CLI router with dynamic tool dispatch.
4
+ """
5
+
6
+ import sys
7
+ import os
8
+
9
+ # ضبط دعم UTF-8 في ويندوز لتفادي UnicodeEncodeError مع الرموز التعبيرية
10
+ if sys.platform == "win32":
11
+ try:
12
+ if sys.stdout.encoding.lower() not in ("utf-8", "utf8"):
13
+ sys.stdout.reconfigure(encoding="utf-8")
14
+ if sys.stderr.encoding.lower() not in ("utf-8", "utf8"):
15
+ sys.stderr.reconfigure(encoding="utf-8")
16
+ except Exception:
17
+ pass
18
+
19
+ import click
20
+
21
+ from fourutil import __version__
22
+ from fourutil.utils.printer import print_banner, console
23
+ from fourutil.commands.install import install
24
+ from fourutil.commands.update import update
25
+ from fourutil.commands.uninstall import uninstall
26
+ from fourutil.commands.list_tools import list_tools
27
+ from fourutil.commands.search import search
28
+ from fourutil.commands.run import run_tool
29
+
30
+
31
+ # --- الأوامر المدمجة ---
32
+ BUILTIN_COMMANDS = {"install", "update", "uninstall", "list", "search", "--help", "--version", "-h"}
33
+
34
+
35
+ class FourUCLI(click.Group):
36
+ """
37
+ مجموعة أوامر مخصصة تدعم التوجيه الديناميكي.
38
+ Custom click Group that falls back to running installed tools
39
+ when the sub-command is not a built-in.
40
+ """
41
+
42
+ def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
43
+ """Override to intercept unknown commands before click raises an error."""
44
+ # إذا لم تكن هناك وسائط، اعرض الشعار
45
+ if not args:
46
+ print_banner()
47
+ ctx.exit(0)
48
+ return [] # لن يصل إلى هنا لكن مطلوب للتوقيع
49
+
50
+ # تحقق مما إذا كان الأمر الأول مدمجاً
51
+ first = args[0]
52
+ if first not in BUILTIN_COMMANDS and not first.startswith("-"):
53
+ # أمر غير مدمج → حاول تشغيله كأداة مثبتة
54
+ run_tool(first, tuple(args[1:]))
55
+ ctx.exit(0)
56
+ return []
57
+
58
+ return super().parse_args(ctx, args)
59
+
60
+
61
+ @click.group(cls=FourUCLI, invoke_without_command=True)
62
+ @click.version_option(version=__version__, prog_name="4u", message="%(prog)s v%(version)s")
63
+ @click.pass_context
64
+ def cli(ctx: click.Context) -> None:
65
+ """4u — A modern package manager for Python tools."""
66
+ # إذا لم يتم استدعاء أمر فرعي، اعرض الشعار
67
+ if ctx.invoked_subcommand is None:
68
+ print_banner()
69
+
70
+
71
+ # --- تسجيل الأوامر المدمجة ---
72
+ cli.add_command(install)
73
+ cli.add_command(update)
74
+ cli.add_command(uninstall)
75
+ cli.add_command(list_tools, name="list")
76
+ cli.add_command(search)
77
+
78
+
79
+ if __name__ == "__main__":
80
+ cli()
@@ -0,0 +1,17 @@
1
+ """
2
+ مساعد الاتصال بـ Supabase
3
+ Supabase connection helper – creates a singleton client.
4
+ """
5
+
6
+ from supabase import create_client, Client
7
+ from fourutil.config import SUPABASE_URL, SUPABASE_KEY
8
+
9
+ _client: Client | None = None
10
+
11
+
12
+ def get_client() -> Client:
13
+ """إرجاع عميل Supabase (Return a Supabase client instance)."""
14
+ global _client
15
+ if _client is None:
16
+ _client = create_client(SUPABASE_URL, SUPABASE_KEY)
17
+ return _client
File without changes
@@ -0,0 +1,64 @@
1
+ """
2
+ مساعد تحميل الملفات
3
+ File download helper – downloads tool scripts from remote URLs.
4
+ """
5
+
6
+ import requests
7
+ from pathlib import Path
8
+
9
+ from rich.progress import (
10
+ Progress,
11
+ SpinnerColumn,
12
+ TextColumn,
13
+ BarColumn,
14
+ DownloadColumn,
15
+ TransferSpeedColumn,
16
+ )
17
+
18
+ from fourutil.utils.printer import console, print_error
19
+
20
+
21
+ def download_file(url: str, dest: Path) -> bool:
22
+ """
23
+ تحميل ملف من URL وحفظه في المسار المحدد.
24
+ Download a file from *url* and save it to *dest*.
25
+ Returns True on success, False on failure.
26
+ """
27
+ try:
28
+ response = requests.get(url, stream=True, timeout=30)
29
+ response.raise_for_status()
30
+
31
+ total = int(response.headers.get("content-length", 0))
32
+
33
+ # تأكد من وجود المجلد الأب
34
+ dest.parent.mkdir(parents=True, exist_ok=True)
35
+
36
+ with Progress(
37
+ SpinnerColumn(),
38
+ TextColumn("[bold blue]Downloading…"),
39
+ BarColumn(bar_width=30),
40
+ DownloadColumn(),
41
+ TransferSpeedColumn(),
42
+ console=console,
43
+ transient=True,
44
+ ) as progress:
45
+ task = progress.add_task("download", total=total or None)
46
+ with open(dest, "wb") as f:
47
+ for chunk in response.iter_content(chunk_size=8192):
48
+ f.write(chunk)
49
+ progress.update(task, advance=len(chunk))
50
+
51
+ return True
52
+
53
+ except requests.ConnectionError:
54
+ print_error("Connection error – check your internet connection.")
55
+ return False
56
+ except requests.Timeout:
57
+ print_error("Download timed out. Please try again.")
58
+ return False
59
+ except requests.HTTPError as exc:
60
+ print_error(f"HTTP error: {exc.response.status_code}")
61
+ return False
62
+ except Exception as exc:
63
+ print_error(f"Download failed: {exc}")
64
+ return False
@@ -0,0 +1,65 @@
1
+ """
2
+ مدير ملف manifest.json
3
+ Handles reading / writing the local manifest that tracks installed tools.
4
+ """
5
+
6
+ import json
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
10
+ from fourutil.config import MANIFEST_FILE, FOURU_DIR, TOOLS_DIR
11
+
12
+
13
+ def _ensure_dirs() -> None:
14
+ """تأكد من وجود المجلدات الضرورية"""
15
+ FOURU_DIR.mkdir(parents=True, exist_ok=True)
16
+ TOOLS_DIR.mkdir(parents=True, exist_ok=True)
17
+
18
+
19
+ def load_manifest() -> dict:
20
+ """تحميل ملف manifest.json أو إرجاع قالب فارغ"""
21
+ _ensure_dirs()
22
+ if MANIFEST_FILE.exists():
23
+ with open(MANIFEST_FILE, "r", encoding="utf-8") as f:
24
+ return json.load(f)
25
+ return {"installed": {}}
26
+
27
+
28
+ def save_manifest(data: dict) -> None:
29
+ """حفظ البيانات في manifest.json"""
30
+ _ensure_dirs()
31
+ with open(MANIFEST_FILE, "w", encoding="utf-8") as f:
32
+ json.dump(data, f, indent=2, ensure_ascii=False)
33
+
34
+
35
+ def add_tool(slug: str, version: str, filename: str) -> None:
36
+ """إضافة أداة إلى manifest"""
37
+ manifest = load_manifest()
38
+ manifest["installed"][slug] = {
39
+ "version": version,
40
+ "installed_at": datetime.now().isoformat(),
41
+ "file": filename,
42
+ }
43
+ save_manifest(manifest)
44
+
45
+
46
+ def remove_tool(slug: str) -> bool:
47
+ """حذف أداة من manifest — يُرجع True إذا كانت موجودة"""
48
+ manifest = load_manifest()
49
+ if slug in manifest["installed"]:
50
+ del manifest["installed"][slug]
51
+ save_manifest(manifest)
52
+ return True
53
+ return False
54
+
55
+
56
+ def get_tool(slug: str) -> dict | None:
57
+ """إرجاع بيانات أداة معينة أو None"""
58
+ manifest = load_manifest()
59
+ return manifest["installed"].get(slug)
60
+
61
+
62
+ def get_all_tools() -> dict:
63
+ """إرجاع جميع الأدوات المثبتة"""
64
+ manifest = load_manifest()
65
+ return manifest["installed"]
@@ -0,0 +1,65 @@
1
+ """
2
+ مساعد الطباعة الملونة في الطرفية
3
+ Colored terminal output helper using rich.
4
+ """
5
+
6
+ from rich.console import Console
7
+ from rich.panel import Panel
8
+ from rich.text import Text
9
+
10
+ console = Console()
11
+
12
+ # --- ثوابت الألوان ---
13
+ SUCCESS = "bold green"
14
+ ERROR = "bold red"
15
+ WARNING = "bold yellow"
16
+ INFO = "bold cyan"
17
+ MUTED = "dim"
18
+
19
+
20
+ def print_success(message: str) -> None:
21
+ """طباعة رسالة نجاح"""
22
+ console.print(f" ✅ {message}", style=SUCCESS)
23
+
24
+
25
+ def print_error(message: str) -> None:
26
+ """طباعة رسالة خطأ"""
27
+ console.print(f" ❌ {message}", style=ERROR)
28
+
29
+
30
+ def print_warning(message: str) -> None:
31
+ """طباعة رسالة تحذير"""
32
+ console.print(f" ⚠️ {message}", style=WARNING)
33
+
34
+
35
+ def print_info(message: str) -> None:
36
+ """طباعة رسالة معلومات"""
37
+ console.print(f" ℹ️ {message}", style=INFO)
38
+
39
+
40
+ def print_banner() -> None:
41
+ """عرض شعار 4u عند تشغيله بدون أوامر"""
42
+ logo = Text()
43
+ logo.append(" ██╗ ██╗", style="bold cyan")
44
+ logo.append("██╗ ██╗\n", style="bold magenta")
45
+ logo.append(" ██║ ██║", style="bold cyan")
46
+ logo.append("██║ ██║\n", style="bold magenta")
47
+ logo.append(" ███████║", style="bold cyan")
48
+ logo.append("██║ ██║\n", style="bold magenta")
49
+ logo.append(" ██║", style="bold cyan")
50
+ logo.append("██║ ██║\n", style="bold magenta")
51
+ logo.append(" ██║", style="bold cyan")
52
+ logo.append("╚██████╔╝\n", style="bold magenta")
53
+ logo.append(" ╚═╝", style="bold cyan")
54
+ logo.append(" ╚═════╝ ", style="bold magenta")
55
+
56
+ panel = Panel(
57
+ logo,
58
+ title="[bold white]⚡ 4u Package Manager ⚡[/bold white]",
59
+ subtitle="[dim]v1.0.0 • Type [bold]4u --help[/bold] for commands[/dim]",
60
+ border_style="bright_blue",
61
+ padding=(1, 4),
62
+ )
63
+ console.print()
64
+ console.print(panel)
65
+ console.print()
4u_cli-1.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
4u_cli-1.0.0/setup.py ADDED
@@ -0,0 +1,31 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="4u-cli",
5
+ version="1.0.0",
6
+ description="4u - A modern CLI package manager for Python tools",
7
+ long_description=open("README.md", encoding="utf-8").read(),
8
+ long_description_content_type="text/markdown",
9
+ author="4u Team",
10
+ license="MIT",
11
+ packages=find_packages(),
12
+ include_package_data=True,
13
+ python_requires=">=3.8",
14
+ install_requires=[
15
+ "click>=8.0",
16
+ "rich>=13.0",
17
+ "requests>=2.28",
18
+ "supabase>=2.0",
19
+ ],
20
+ entry_points={
21
+ "console_scripts": [
22
+ "4u=fourutil.main:cli",
23
+ ],
24
+ },
25
+ classifiers=[
26
+ "Programming Language :: Python :: 3",
27
+ "License :: OSI Approved :: MIT License",
28
+ "Operating System :: OS Independent",
29
+ "Environment :: Console",
30
+ ],
31
+ )