sentinel-ai-os 1.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.
- sentinel/__init__.py +0 -0
- sentinel/auth.py +40 -0
- sentinel/cli.py +9 -0
- sentinel/core/__init__.py +0 -0
- sentinel/core/agent.py +298 -0
- sentinel/core/audit.py +48 -0
- sentinel/core/cognitive.py +94 -0
- sentinel/core/config.py +99 -0
- sentinel/core/llm.py +143 -0
- sentinel/core/registry.py +351 -0
- sentinel/core/scheduler.py +61 -0
- sentinel/core/schema.py +11 -0
- sentinel/core/setup.py +101 -0
- sentinel/core/ui.py +112 -0
- sentinel/main.py +110 -0
- sentinel/paths.py +77 -0
- sentinel/tools/__init__.py +0 -0
- sentinel/tools/apps.py +462 -0
- sentinel/tools/audio.py +30 -0
- sentinel/tools/browser.py +66 -0
- sentinel/tools/calendar_ops.py +163 -0
- sentinel/tools/clock.py +25 -0
- sentinel/tools/context.py +40 -0
- sentinel/tools/desktop.py +116 -0
- sentinel/tools/email_ops.py +62 -0
- sentinel/tools/factory.py +125 -0
- sentinel/tools/file_ops.py +81 -0
- sentinel/tools/flights.py +62 -0
- sentinel/tools/gmail_auth.py +47 -0
- sentinel/tools/indexer.py +156 -0
- sentinel/tools/installer.py +69 -0
- sentinel/tools/macros.py +58 -0
- sentinel/tools/memory_ops.py +281 -0
- sentinel/tools/navigation.py +109 -0
- sentinel/tools/notes.py +78 -0
- sentinel/tools/office.py +67 -0
- sentinel/tools/organizer.py +150 -0
- sentinel/tools/smart_index.py +76 -0
- sentinel/tools/sql_index.py +186 -0
- sentinel/tools/system_ops.py +86 -0
- sentinel/tools/vision.py +94 -0
- sentinel/tools/weather_ops.py +59 -0
- sentinel_ai_os-1.0.dist-info/METADATA +282 -0
- sentinel_ai_os-1.0.dist-info/RECORD +48 -0
- sentinel_ai_os-1.0.dist-info/WHEEL +5 -0
- sentinel_ai_os-1.0.dist-info/entry_points.txt +2 -0
- sentinel_ai_os-1.0.dist-info/licenses/LICENSE +21 -0
- sentinel_ai_os-1.0.dist-info/top_level.txt +1 -0
sentinel/core/ui.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# FILE: core/ui.py
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.panel import Panel
|
|
4
|
+
from rich.markdown import Markdown
|
|
5
|
+
from rich import box
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UI:
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def print_banner():
|
|
14
|
+
logo = """
|
|
15
|
+
███████╗███████╗███╗ ██╗████████╗██╗███╗ ██╗███████╗██╗
|
|
16
|
+
██╔════╝██╔════╝████╗ ██║╚══██╔══╝██║████╗ ██║██╔════╝██║
|
|
17
|
+
███████╗█████╗ ██╔██╗ ██║ ██║ ██║██╔██╗ ██║█████╗ ██║
|
|
18
|
+
╚════██║██╔══╝ ██║╚██╗██║ ██║ ██║██║╚██╗██║██╔══╝ ██║
|
|
19
|
+
███████║███████╗██║ ╚████║ ██║ ██║██║ ╚████║███████╗███████╗
|
|
20
|
+
╚══════╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝
|
|
21
|
+
"""
|
|
22
|
+
UI.console.print(f"[bold cyan]{logo}[/bold cyan]")
|
|
23
|
+
UI.console.print("[dim]OPERATING SYSTEM v1.0[/dim]\n")
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def print_system(msg):
|
|
27
|
+
UI.console.print(f"[dim]System: {msg}[/dim]")
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def print_success(msg):
|
|
31
|
+
UI.console.print(f"[bold green]✔ {msg}[/bold green]")
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def print_warning(msg):
|
|
35
|
+
UI.console.print(f"[bold yellow]⚠ {msg}[/bold yellow]")
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def print_error(msg):
|
|
39
|
+
UI.console.print(Panel(
|
|
40
|
+
f"[bold white]{msg}[/bold white]",
|
|
41
|
+
title="System Alert",
|
|
42
|
+
border_style="red",
|
|
43
|
+
box=box.ROUNDED,
|
|
44
|
+
expand=False
|
|
45
|
+
))
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def print_agent(text, model=None):
|
|
49
|
+
"""The SENTINEL Response Panel."""
|
|
50
|
+
if not isinstance(text, Markdown):
|
|
51
|
+
text = Markdown(str(text))
|
|
52
|
+
|
|
53
|
+
# Add Model Info to Title
|
|
54
|
+
title = "[bold cyan]SENTINEL[/bold cyan]"
|
|
55
|
+
if model:
|
|
56
|
+
title += f" [dim]({model})[/dim]"
|
|
57
|
+
|
|
58
|
+
UI.console.print(Panel(
|
|
59
|
+
text,
|
|
60
|
+
title=title,
|
|
61
|
+
title_align="left",
|
|
62
|
+
border_style="cyan",
|
|
63
|
+
box=box.ROUNDED,
|
|
64
|
+
padding=(1, 2),
|
|
65
|
+
expand=True
|
|
66
|
+
))
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def print_tool(tool_name):
|
|
70
|
+
UI.console.print(f"[dim] ⚙ Executing: {tool_name}...[/dim]")
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def print_result(result):
|
|
74
|
+
text = str(result)
|
|
75
|
+
if len(text) > 400:
|
|
76
|
+
text = text[:400] + "... (truncated)"
|
|
77
|
+
UI.console.print(Panel(
|
|
78
|
+
text,
|
|
79
|
+
title="Action Result",
|
|
80
|
+
border_style="dim white",
|
|
81
|
+
box=box.ROUNDED,
|
|
82
|
+
expand=False
|
|
83
|
+
))
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def print_help():
|
|
87
|
+
table = Table(title="Sentinel Control Panel", border_style="cyan", box=box.ROUNDED)
|
|
88
|
+
table.add_column("Command", style="bold cyan")
|
|
89
|
+
table.add_column("Description", style="white")
|
|
90
|
+
|
|
91
|
+
table.add_row("/help", "Show this menu")
|
|
92
|
+
table.add_row("/exit", "Shutdown system")
|
|
93
|
+
table.add_row("/status", "View current model, provider, and memory stats")
|
|
94
|
+
table.add_row("/memory [n]", "Set Context Window size (e.g., /memory 5)")
|
|
95
|
+
table.add_row("/log [on/off]", "Toggle audit logging (Default: OFF)")
|
|
96
|
+
|
|
97
|
+
table.add_row("/clear", "Clear active chat memory (RAM only)")
|
|
98
|
+
table.add_row("/wipe", "Wipe long-term memory (Vector DB + brain.db)")
|
|
99
|
+
table.add_row("/factory_reset", "[bold red]FULL FACTORY RESET[/bold red] (Deletes EVERYTHING)")
|
|
100
|
+
|
|
101
|
+
table.add_row("/switch [p] [m]", "Switch Brain (e.g., /switch groq llama3)")
|
|
102
|
+
table.add_row("/setkey [p] [k]", "Update API Key")
|
|
103
|
+
|
|
104
|
+
UI.console.print(table)
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def get_input():
|
|
108
|
+
try:
|
|
109
|
+
UI.console.print()
|
|
110
|
+
return UI.console.input("[bold cyan]>>> [/bold cyan]")
|
|
111
|
+
except KeyboardInterrupt:
|
|
112
|
+
return None
|
sentinel/main.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# FILE: main.py
|
|
2
|
+
import typer
|
|
3
|
+
from sentinel.core.config import ConfigManager
|
|
4
|
+
from sentinel.core.agent import SentinelAgent
|
|
5
|
+
from sentinel.core.ui import UI
|
|
6
|
+
from sentinel.core.registry import initialize_tools
|
|
7
|
+
from sentinel.core.setup import setup_wizard
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(
|
|
10
|
+
name="Sentinel",
|
|
11
|
+
help="Autonomous AI Agent for your OS",
|
|
12
|
+
add_completion=False,
|
|
13
|
+
no_args_is_help=False
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def boot_sequence(briefing: bool = False):
|
|
18
|
+
"""
|
|
19
|
+
Shared startup logic for both default run and 'start' command.
|
|
20
|
+
"""
|
|
21
|
+
UI.console.clear()
|
|
22
|
+
|
|
23
|
+
# 1. Check Configuration
|
|
24
|
+
cfg = ConfigManager()
|
|
25
|
+
if not cfg.exists():
|
|
26
|
+
setup_wizard()
|
|
27
|
+
cfg = ConfigManager() # Reload after wizard
|
|
28
|
+
|
|
29
|
+
# 2. Boot Up
|
|
30
|
+
UI.print_banner()
|
|
31
|
+
|
|
32
|
+
# Check for keys
|
|
33
|
+
# We check if ANY key is present or if using Ollama
|
|
34
|
+
has_key = (
|
|
35
|
+
cfg.get_key("openai") or
|
|
36
|
+
cfg.get_key("anthropic") or
|
|
37
|
+
cfg.get_key("groq") or
|
|
38
|
+
cfg.get("llm.provider") == "ollama"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if not has_key:
|
|
42
|
+
UI.print_warning("No LLM API Key found. System running in Limited Mode.")
|
|
43
|
+
else:
|
|
44
|
+
provider = cfg.get("llm.provider", "unknown")
|
|
45
|
+
UI.print_system(f"Brain Active: [green]{provider.upper()}[/green]")
|
|
46
|
+
|
|
47
|
+
# 3. Initialize Background Services
|
|
48
|
+
initialize_tools()
|
|
49
|
+
|
|
50
|
+
# 4. Run Briefing (If requested)
|
|
51
|
+
if briefing:
|
|
52
|
+
if has_key:
|
|
53
|
+
UI.print_system("Generating Daily Briefing...")
|
|
54
|
+
try:
|
|
55
|
+
from sentinel.core.cognitive import get_daily_briefing
|
|
56
|
+
report = get_daily_briefing(cfg)
|
|
57
|
+
UI.print_agent(report)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
UI.print_error(f"Briefing failed: {e}")
|
|
60
|
+
else:
|
|
61
|
+
UI.print_error("Skipping Daily Briefing (Offline Mode).")
|
|
62
|
+
|
|
63
|
+
# 5. Start Agent Loop
|
|
64
|
+
try:
|
|
65
|
+
agent = SentinelAgent(cfg)
|
|
66
|
+
agent.run_loop()
|
|
67
|
+
except KeyboardInterrupt:
|
|
68
|
+
UI.print_system("Shutting down...")
|
|
69
|
+
except Exception as e:
|
|
70
|
+
UI.print_error(f"Critical System Failure: {e}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@app.callback(invoke_without_command=True)
|
|
74
|
+
def main(
|
|
75
|
+
ctx: typer.Context,
|
|
76
|
+
briefing: bool = typer.Option(False, "--briefing", "-b", help="Run Daily Briefing on startup")
|
|
77
|
+
):
|
|
78
|
+
"""
|
|
79
|
+
Main Entry Point. Checks state and routes to Setup or Runtime.
|
|
80
|
+
"""
|
|
81
|
+
# If a subcommand (like 'config' or 'auth') is called, skip this default boot
|
|
82
|
+
if ctx.invoked_subcommand is not None:
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
boot_sequence(briefing=briefing)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@app.command()
|
|
89
|
+
def start(
|
|
90
|
+
briefing: bool = typer.Option(False, "--briefing", "-b", help="Run Daily Briefing on startup")
|
|
91
|
+
):
|
|
92
|
+
"""Explicit start command."""
|
|
93
|
+
boot_sequence(briefing=briefing)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@app.command()
|
|
97
|
+
def config():
|
|
98
|
+
"""Re-run the configuration wizard."""
|
|
99
|
+
setup_wizard()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@app.command()
|
|
103
|
+
def auth():
|
|
104
|
+
"""Run the Google Authentication fix tool."""
|
|
105
|
+
from sentinel.auth import fix_authentication
|
|
106
|
+
fix_authentication()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
app()
|
sentinel/paths.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# FILE: sentinel/core/paths.py
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
# ==========================================
|
|
7
|
+
# 1. USER DATA STORAGE (Mutable)
|
|
8
|
+
# Stores DBs, Configs, Logs, and Auth Tokens
|
|
9
|
+
# Location: ~/.sentinel (Cross-platform)
|
|
10
|
+
# ==========================================
|
|
11
|
+
|
|
12
|
+
USER_DATA_DIR = Path.home() / ".sentinel"
|
|
13
|
+
USER_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
14
|
+
|
|
15
|
+
# Subdirectories
|
|
16
|
+
LOGS_DIR = USER_DATA_DIR / "logs"
|
|
17
|
+
LOGS_DIR.mkdir(exist_ok=True)
|
|
18
|
+
|
|
19
|
+
DRAFTS_DIR = USER_DATA_DIR / "drafts"
|
|
20
|
+
DRAFTS_DIR.mkdir(exist_ok=True)
|
|
21
|
+
|
|
22
|
+
# Critical System Files
|
|
23
|
+
CONFIG_PATH = USER_DATA_DIR / "config.json"
|
|
24
|
+
DB_PATH = USER_DATA_DIR / "brain.db"
|
|
25
|
+
VECTOR_PATH = USER_DATA_DIR / "brain_vectors" # ChromaDB folder
|
|
26
|
+
AUDIT_LOG_PATH = USER_DATA_DIR / "audit_log.jsonl"
|
|
27
|
+
MEMORY_FILE = USER_DATA_DIR / "memory.json"
|
|
28
|
+
|
|
29
|
+
# Search Indexes
|
|
30
|
+
FILE_INDEX_DB = USER_DATA_DIR / "file_index.db"
|
|
31
|
+
SMART_INDEX_DB = USER_DATA_DIR / "smart_files.db"
|
|
32
|
+
|
|
33
|
+
# Authentication
|
|
34
|
+
CREDENTIALS_PATH = USER_DATA_DIR / "credentials.json"
|
|
35
|
+
TOKEN_PATH = USER_DATA_DIR / "token.json"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ==========================================
|
|
39
|
+
# 2. PACKAGE ASSETS (Immutable)
|
|
40
|
+
# Locates scripts/tools bundled inside the pip package
|
|
41
|
+
# Location: .../site-packages/sentinel/scripts/
|
|
42
|
+
# ==========================================
|
|
43
|
+
|
|
44
|
+
def get_script_path(filename: str) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Returns the absolute path to a script bundled inside the pip package.
|
|
47
|
+
Expects scripts to be in: src/sentinel/scripts/
|
|
48
|
+
"""
|
|
49
|
+
# This file is in: .../sentinel/core/paths.py
|
|
50
|
+
# We want: .../sentinel/scripts/filename
|
|
51
|
+
|
|
52
|
+
# Get the directory of THIS file (core/)
|
|
53
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
54
|
+
|
|
55
|
+
# Go up one level to the package root (sentinel/)
|
|
56
|
+
pkg_root = os.path.dirname(current_dir)
|
|
57
|
+
|
|
58
|
+
# Target path
|
|
59
|
+
script_path = os.path.join(pkg_root, "scripts", filename)
|
|
60
|
+
|
|
61
|
+
if not os.path.exists(script_path):
|
|
62
|
+
# Fallback for Development Mode (running from source without pip install)
|
|
63
|
+
# In dev repo: sentinel/core/paths.py -> ../../../scripts/
|
|
64
|
+
repo_root = os.path.dirname(os.path.dirname(pkg_root))
|
|
65
|
+
dev_path = os.path.join(repo_root, "scripts", filename)
|
|
66
|
+
|
|
67
|
+
if os.path.exists(dev_path):
|
|
68
|
+
return dev_path
|
|
69
|
+
|
|
70
|
+
# If we still can't find it, that's a build error
|
|
71
|
+
raise FileNotFoundError(
|
|
72
|
+
f"Could not find bundled script '{filename}'.\n"
|
|
73
|
+
f"Checked: {script_path}\n"
|
|
74
|
+
f"Checked: {dev_path}"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return script_path
|
|
File without changes
|