aniate 1.0.0__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.
aniate/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ """
2
+ Aniate - Terminal Intelligence Layer
3
+ """
4
+
5
+ __version__ = "1.0.0"
6
+ __author__ = "Kabir Murjani"
7
+
8
+ from .cli import app
9
+
10
+ __all__ = ["app"]
aniate/auth.py ADDED
@@ -0,0 +1,96 @@
1
+ import os
2
+ import json
3
+ import re
4
+ import getpass
5
+ from rich.console import Console
6
+ from rich.prompt import Prompt
7
+ from supabase import create_client, Client
8
+ from aniate.config import SUPABASE_URL, SUPABASE_KEY, SESSION_FILE, CONFIG_DIR
9
+
10
+ console = Console()
11
+ supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
12
+
13
+ def get_session():
14
+ """Load session from file."""
15
+ if not SESSION_FILE.exists():
16
+ return None
17
+ try:
18
+ with open(SESSION_FILE, 'r') as f:
19
+ return json.load(f)
20
+ except:
21
+ return None
22
+
23
+ def save_session(data):
24
+ """Save session to file."""
25
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
26
+ with open(SESSION_FILE, 'w') as f:
27
+ json.dump(data, f)
28
+
29
+ def signup():
30
+ """Create a new account."""
31
+ session = get_session()
32
+ if session:
33
+ console.print("[yellow]Already logged in. Run: ant logout[/yellow]")
34
+ return
35
+
36
+ console.print("\n[bold white]ANIATE[/bold white] [dim]Create Account[/dim]\n")
37
+ email = Prompt.ask("[white]Email[/white]")
38
+ while True:
39
+ password = getpass.getpass("Password (8+ chars, 1 number): ")
40
+ if len(password) < 8 or not re.search(r"\d", password):
41
+ console.print("[red]Password too weak. Try again.[/red]")
42
+ continue
43
+ break
44
+ with console.status("[dim]Creating account...[/dim]", spinner="dots"):
45
+ try:
46
+ res = supabase.auth.sign_up({"email": email, "password": password})
47
+ if res.user:
48
+ console.print(f"\n[green]Account created.[/green] {email}")
49
+ console.print("[dim]Check your email to verify.\n[/dim]")
50
+ else:
51
+ console.print("[yellow]Account created. Check email for verification.[/yellow]")
52
+ except Exception as e:
53
+ console.print(f"[red]Failed:[/red] {str(e)}")
54
+
55
+ def login():
56
+ """Login via Email/Password."""
57
+ session = get_session()
58
+ if session:
59
+ console.print(f"[dim]Already authenticated as[/dim] {session['email']}")
60
+ return
61
+
62
+ console.print("\n[bold white]ANIATE[/bold white] [dim]Login[/dim]\n")
63
+ email = Prompt.ask("[white]Email[/white]")
64
+ password = getpass.getpass("Password: ")
65
+
66
+ with console.status("[dim]Authenticating...[/dim]", spinner="dots"):
67
+ try:
68
+ res = supabase.auth.sign_in_with_password({"email": email, "password": password})
69
+ save_session({
70
+ "access_token": res.session.access_token,
71
+ "refresh_token": res.session.refresh_token,
72
+ "user_id": res.user.id,
73
+ "email": res.user.email
74
+ })
75
+ console.print(f"\n[green]Authenticated.[/green] {res.user.email}\n")
76
+ except Exception as e:
77
+ console.print(f"[red]Authentication failed.[/red] Check credentials.")
78
+
79
+ def logout():
80
+ """Logout and clear session."""
81
+ if not SESSION_FILE.exists():
82
+ console.print("[dim]No active session.[/dim]")
83
+ return
84
+
85
+ os.remove(SESSION_FILE)
86
+ console.print("[dim]Session cleared.[/dim]")
87
+
88
+ def whoami():
89
+ """Show current logged in user."""
90
+ session = get_session()
91
+ if not session:
92
+ console.print("[dim]No active session. Run: ant login[/dim]")
93
+ return
94
+
95
+ console.print(f"\n[white]{session['email']}[/white]")
96
+ console.print(f"[dim]{session['user_id']}[/dim]\n")
aniate/brew.py ADDED
@@ -0,0 +1,73 @@
1
+ """
2
+ Brew System - Create and manage custom commands
3
+ """
4
+ from rich.console import Console
5
+ from pathlib import Path
6
+
7
+ console = Console()
8
+
9
+ def brew_cmd(name: str):
10
+ """Brew a custom command template."""
11
+ console.print(f"\n[bold cyan]🍺 Brewing Command: {name}[/bold cyan]\n")
12
+
13
+ commands_dir = Path(__file__).parent / "commands"
14
+ cmd_file = commands_dir / f"{name}.py"
15
+
16
+ if cmd_file.exists():
17
+ console.print(f"[yellow]Command '{name}' already exists.[/yellow]")
18
+ return
19
+
20
+ # Template for custom commands
21
+ template = f'''"""
22
+ {name.title()} Command - Custom brewed command
23
+ """
24
+ import requests
25
+ from rich.console import Console
26
+ import sys
27
+ import os
28
+ # Package imports
29
+
30
+ from aniate.config import SERVER_URL
31
+
32
+ console = Console()
33
+
34
+ def {name}_execute(arg: str):
35
+ """Execute the {name} command."""
36
+ console.print(f"[cyan]{name.title()} command executing:[/cyan] {{arg}}")
37
+
38
+ # TODO: Implement your command logic here
39
+ # You can use the LLM via SERVER_URL
40
+ # You can access other APIs
41
+ # You can process files, data, etc.
42
+
43
+ console.print(f"[green]✓ {name.title()} completed[/green]")
44
+ '''
45
+
46
+ try:
47
+ cmd_file.write_text(template)
48
+ console.print(f"[green]✓ Command '{name}' brewed successfully[/green]")
49
+ console.print(f"[dim]Edit: {cmd_file}[/dim]")
50
+ console.print(f"[dim]Use: ant {name} \"your input\"[/dim]\n")
51
+ except Exception as e:
52
+ console.print(f"[red]Brew failed:[/red] {e}")
53
+
54
+ def delete_cmd(name: str):
55
+ """Delete a custom brewed command."""
56
+ commands_dir = Path(__file__).parent / "commands"
57
+ cmd_file = commands_dir / f"{name}.py"
58
+
59
+ if not cmd_file.exists():
60
+ console.print(f"[yellow]Command '{name}' not found.[/yellow]")
61
+ return
62
+
63
+ # Prevent deleting core commands
64
+ core_commands = ['chat', 'net']
65
+ if name in core_commands:
66
+ console.print(f"[red]Cannot delete core command '{name}'[/red]")
67
+ return
68
+
69
+ try:
70
+ cmd_file.unlink()
71
+ console.print(f"[bold red]✓ Command '{name}' deleted[/bold red]")
72
+ except Exception as e:
73
+ console.print(f"[red]Delete failed:[/red] {e}")
@@ -0,0 +1,12 @@
1
+ """
2
+ Global Brews - Cloud-enabled commands accessible from any terminal
3
+ """
4
+ from .document_storage import save_file, fetch_file, list_files
5
+ from .secrets import add_secret, get_secret, list_secrets, delete_secret
6
+ from .maker import brew_make
7
+
8
+ __all__ = [
9
+ 'save_file', 'fetch_file', 'list_files',
10
+ 'add_secret', 'get_secret', 'list_secrets', 'delete_secret',
11
+ 'brew_make'
12
+ ]
@@ -0,0 +1,6 @@
1
+ """Document Storage - Cloud file management"""
2
+ from .save import save_file
3
+ from .fetch import fetch_file
4
+ from .files import list_files
5
+
6
+ __all__ = ['save_file', 'fetch_file', 'list_files']
@@ -0,0 +1,143 @@
1
+ """
2
+ Fetch Command - Download files from cloud storage
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Optional
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
10
+ from rich.prompt import Prompt
11
+ from rich.table import Table
12
+ from supabase import create_client
13
+
14
+ import sys
15
+ # Package imports
16
+
17
+ from aniate.config import SUPABASE_URL, SUPABASE_KEY
18
+ from aniate.auth import get_session
19
+
20
+ console = Console()
21
+
22
+ def fetch_file(filename: Optional[str] = typer.Argument(None, help="File to download")):
23
+ """Download a file from cloud storage."""
24
+ session = get_session()
25
+ if not session:
26
+ console.print("[red]Login required. Run: ant login[/red]")
27
+ return
28
+
29
+ # Create Supabase client
30
+ supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
31
+ supabase.auth.set_session(session['access_token'], session.get('refresh_token', ''))
32
+
33
+ user_id = session['user_id']
34
+
35
+ # If no filename provided, show interactive list
36
+ if not filename:
37
+ filename = _select_file_interactive(supabase, user_id)
38
+ if not filename:
39
+ return
40
+
41
+ # Get file metadata
42
+ try:
43
+ result = supabase.table("user_files").select("*").eq("user_id", user_id).eq("filename", filename).execute()
44
+
45
+ if not result.data:
46
+ console.print(f"[red]File not found in cloud:[/red] {filename}")
47
+ return
48
+
49
+ file_meta = result.data[0]
50
+ storage_path = file_meta['storage_path']
51
+ file_size = file_meta['file_size']
52
+
53
+ # Always save to Desktop
54
+ desktop = Path.home() / "Desktop"
55
+ dest_path = desktop / filename
56
+
57
+ # Check if file exists locally
58
+ if dest_path.exists():
59
+ overwrite = Prompt.ask(f"[yellow]File exists. Overwrite?[/yellow]", choices=["y", "n"], default="n")
60
+ if overwrite == 'n':
61
+ console.print("[dim]Download cancelled[/dim]")
62
+ return
63
+
64
+ # Download file
65
+ with Progress(
66
+ SpinnerColumn(),
67
+ TextColumn("[cyan]{task.description}"),
68
+ BarColumn(),
69
+ TaskProgressColumn(),
70
+ console=console
71
+ ) as progress:
72
+ task = progress.add_task(f"Downloading {filename}...", total=100)
73
+
74
+ # Download from Supabase Storage
75
+ file_data = supabase.storage.from_('user-files').download(storage_path)
76
+
77
+ progress.update(task, advance=70)
78
+
79
+ # Write to local file
80
+ with open(dest_path, 'wb') as f:
81
+ f.write(file_data)
82
+
83
+ progress.update(task, advance=30)
84
+
85
+ # Success message
86
+ size_str = _format_size(file_size)
87
+ console.print(f"[bold green]✓ Downloaded {filename}[/bold green] [dim]({size_str})[/dim]")
88
+ console.print(f"[dim]Saved to: {dest_path}[/dim]")
89
+
90
+ except Exception as e:
91
+ console.print(f"[red]Download failed:[/red] {e}")
92
+
93
+ def _select_file_interactive(supabase, user_id):
94
+ """Show interactive file selector."""
95
+ try:
96
+ result = supabase.table("user_files").select("*").eq("user_id", user_id).order("uploaded_at", desc=True).execute()
97
+
98
+ if not result.data:
99
+ console.print("[dim]No files in cloud storage[/dim]")
100
+ return None
101
+
102
+ # Show table
103
+ table = Table(title="Cloud Files")
104
+ table.add_column("#", style="cyan")
105
+ table.add_column("Filename", style="white")
106
+ table.add_column("Size", style="dim")
107
+ table.add_column("Date", style="dim")
108
+
109
+ for idx, file in enumerate(result.data, 1):
110
+ size = _format_size(file['file_size'])
111
+ date = file['uploaded_at'][:10] # Just the date
112
+ table.add_row(str(idx), file['filename'], size, date)
113
+
114
+ console.print(table)
115
+
116
+ # Prompt for selection
117
+ choice = Prompt.ask("\n[cyan]Select file number (or 'q' to quit)[/cyan]")
118
+
119
+ if choice.lower() == 'q':
120
+ return None
121
+
122
+ try:
123
+ idx = int(choice) - 1
124
+ if 0 <= idx < len(result.data):
125
+ return result.data[idx]['filename']
126
+ else:
127
+ console.print("[red]Invalid selection[/red]")
128
+ return None
129
+ except ValueError:
130
+ console.print("[red]Invalid input[/red]")
131
+ return None
132
+
133
+ except Exception as e:
134
+ console.print(f"[red]Error listing files:[/red] {e}")
135
+ return None
136
+
137
+ def _format_size(bytes_size):
138
+ """Format bytes to human-readable size."""
139
+ for unit in ['B', 'KB', 'MB', 'GB']:
140
+ if bytes_size < 1024.0:
141
+ return f"{bytes_size:.1f} {unit}"
142
+ bytes_size /= 1024.0
143
+ return f"{bytes_size:.1f} TB"
@@ -0,0 +1,65 @@
1
+ """
2
+ Files Command - List all files in cloud storage
3
+ """
4
+ import os
5
+ from rich.console import Console
6
+ from rich.table import Table
7
+ from supabase import create_client
8
+
9
+ import sys
10
+ # Package imports
11
+
12
+ from aniate.config import SUPABASE_URL, SUPABASE_KEY
13
+ from aniate.auth import get_session
14
+
15
+ console = Console()
16
+
17
+ def list_files():
18
+ """List all files in cloud storage."""
19
+ session = get_session()
20
+ if not session:
21
+ console.print("[red]Login required. Run: ant login[/red]")
22
+ return
23
+
24
+ # Create Supabase client
25
+ supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
26
+ supabase.auth.set_session(session['access_token'], session.get('refresh_token', ''))
27
+
28
+ user_id = session['user_id']
29
+
30
+ try:
31
+ result = supabase.table("user_files").select("*").eq("user_id", user_id).order("uploaded_at", desc=True).execute()
32
+
33
+ if not result.data:
34
+ console.print("[dim]No files in cloud storage[/dim]")
35
+ return
36
+
37
+ # Calculate totals
38
+ total_size = sum(f['file_size'] for f in result.data)
39
+ total_count = len(result.data)
40
+
41
+ # Create table
42
+ table = Table(title=f"Cloud Files ({total_count} files, {_format_size(total_size)} total)")
43
+ table.add_column("Filename", style="cyan")
44
+ table.add_column("Size", style="white")
45
+ table.add_column("Type", style="dim")
46
+ table.add_column("Uploaded", style="dim")
47
+
48
+ for file in result.data:
49
+ size = _format_size(file['file_size'])
50
+ mime = file['mime_type'].split('/')[-1] if file['mime_type'] else 'unknown'
51
+ date = file['uploaded_at'][:10] # Just the date
52
+ table.add_row(file['filename'], size, mime, date)
53
+
54
+ console.print(table)
55
+
56
+ except Exception as e:
57
+ console.print(f"[red]Error listing files:[/red] {e}")
58
+
59
+ def _format_size(bytes_size):
60
+ """Format bytes to human-readable size."""
61
+ for unit in ['B', 'KB', 'MB', 'GB']:
62
+ if bytes_size < 1024.0:
63
+ return f"{bytes_size:.1f} {unit}"
64
+ bytes_size /= 1024.0
65
+ return f"{bytes_size:.1f} TB"
@@ -0,0 +1,112 @@
1
+ """
2
+ Save Command - Upload files to cloud storage
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Optional
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
10
+ from supabase import create_client
11
+ import mimetypes
12
+
13
+ import sys
14
+ # Package imports
15
+
16
+ from aniate.config import SUPABASE_URL, SUPABASE_KEY
17
+ from aniate.auth import get_session
18
+
19
+ console = Console()
20
+
21
+ def save_file(filepath: str = typer.Argument(..., help="File to upload"), cloud_name: Optional[str] = typer.Argument(None, help="Custom name in cloud")):
22
+ """Upload a file to cloud storage."""
23
+ session = get_session()
24
+ if not session:
25
+ console.print("[red]Login required. Run: ant login[/red]")
26
+ return
27
+
28
+ # Resolve file path
29
+ file_path = Path(filepath).expanduser().resolve()
30
+
31
+ if not file_path.exists():
32
+ console.print(f"[red]File not found:[/red] {filepath}")
33
+ return
34
+
35
+ if not file_path.is_file():
36
+ console.print(f"[red]Not a file:[/red] {filepath}")
37
+ return
38
+
39
+ # Get file info
40
+ file_size = file_path.stat().st_size
41
+ filename = cloud_name if cloud_name else file_path.name
42
+ mime_type = mimetypes.guess_type(str(file_path))[0] or 'application/octet-stream'
43
+
44
+ # Size warning for large files
45
+ if file_size > 10 * 1024 * 1024: # 10MB
46
+ size_mb = file_size / (1024 * 1024)
47
+ console.print(f"[yellow]Warning: Large file ({size_mb:.1f} MB)[/yellow]")
48
+ confirm = console.input("Continue? (y/n): ")
49
+ if confirm.lower() != 'y':
50
+ console.print("[dim]Upload cancelled[/dim]")
51
+ return
52
+
53
+ # Create Supabase client
54
+ supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
55
+ supabase.auth.set_session(session['access_token'], session.get('refresh_token', ''))
56
+
57
+ user_id = session['user_id']
58
+ storage_path = f"{user_id}/{filename}"
59
+
60
+ try:
61
+ with Progress(
62
+ SpinnerColumn(),
63
+ TextColumn("[cyan]{task.description}"),
64
+ BarColumn(),
65
+ TaskProgressColumn(),
66
+ console=console
67
+ ) as progress:
68
+ task = progress.add_task(f"Uploading {filename}...", total=100)
69
+
70
+ # Read file
71
+ with open(file_path, 'rb') as f:
72
+ file_data = f.read()
73
+
74
+ progress.update(task, advance=30)
75
+
76
+ # Upload to Supabase Storage
77
+ supabase.storage.from_('user-files').upload(
78
+ storage_path,
79
+ file_data,
80
+ file_options={"content-type": mime_type, "upsert": "true"}
81
+ )
82
+
83
+ progress.update(task, advance=40)
84
+
85
+ # Save metadata to database
86
+ metadata = {
87
+ "user_id": user_id,
88
+ "filename": filename,
89
+ "storage_path": storage_path,
90
+ "file_size": file_size,
91
+ "mime_type": mime_type
92
+ }
93
+
94
+ # Upsert (insert or update)
95
+ supabase.table("user_files").upsert(metadata, on_conflict="user_id,filename").execute()
96
+
97
+ progress.update(task, advance=30)
98
+
99
+ # Success message
100
+ size_str = _format_size(file_size)
101
+ console.print(f"[bold green]✓ Uploaded {filename}[/bold green] [dim]({size_str})[/dim]")
102
+
103
+ except Exception as e:
104
+ console.print(f"[red]Upload failed:[/red] {e}")
105
+
106
+ def _format_size(bytes_size):
107
+ """Format bytes to human-readable size."""
108
+ for unit in ['B', 'KB', 'MB', 'GB']:
109
+ if bytes_size < 1024.0:
110
+ return f"{bytes_size:.1f} {unit}"
111
+ bytes_size /= 1024.0
112
+ return f"{bytes_size:.1f} TB"