sentinel-ai-os 1.0.1__py3-none-any.whl → 1.0.2__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/core/agent.py +2 -13
- sentinel/core/audit.py +2 -4
- sentinel/core/config.py +2 -7
- sentinel/core/llm.py +0 -8
- sentinel/core/registry.py +0 -17
- sentinel/core/scheduler.py +0 -4
- sentinel/core/schema.py +0 -2
- sentinel/core/setup.py +0 -3
- sentinel/core/ui.py +0 -2
- sentinel/paths.py +2 -31
- sentinel/scripts/factory_reset.bat +17 -0
- sentinel/scripts/wipe_vector.bat +7 -0
- sentinel/tools/apps.py +17 -34
- sentinel/tools/audio.py +1 -1
- sentinel/tools/browser.py +0 -2
- sentinel/tools/context.py +0 -1
- sentinel/tools/factory.py +0 -6
- sentinel/tools/file_ops.py +0 -2
- sentinel/tools/flights.py +1 -1
- sentinel/tools/gmail_auth.py +1 -9
- sentinel/tools/indexer.py +1 -9
- sentinel/tools/notes.py +0 -1
- sentinel/tools/smart_index.py +1 -1
- sentinel/tools/sql_index.py +4 -18
- sentinel/tools/system_ops.py +0 -1
- sentinel/tools/weather_ops.py +0 -1
- {sentinel_ai_os-1.0.1.dist-info → sentinel_ai_os-1.0.2.dist-info}/METADATA +4 -3
- sentinel_ai_os-1.0.2.dist-info/RECORD +50 -0
- sentinel_ai_os-1.0.1.dist-info/RECORD +0 -48
- {sentinel_ai_os-1.0.1.dist-info → sentinel_ai_os-1.0.2.dist-info}/WHEEL +0 -0
- {sentinel_ai_os-1.0.1.dist-info → sentinel_ai_os-1.0.2.dist-info}/entry_points.txt +0 -0
- {sentinel_ai_os-1.0.1.dist-info → sentinel_ai_os-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {sentinel_ai_os-1.0.1.dist-info → sentinel_ai_os-1.0.2.dist-info}/top_level.txt +0 -0
sentinel/core/agent.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# FILE: core/agent.py
|
|
2
|
-
|
|
3
1
|
import sys
|
|
4
2
|
import json
|
|
5
3
|
import os
|
|
@@ -13,7 +11,6 @@ from sentinel.core.schema import AgentAction
|
|
|
13
11
|
from sentinel.tools import memory_ops
|
|
14
12
|
from sentinel.paths import USER_DATA_DIR, DB_PATH, VECTOR_PATH, AUDIT_LOG_PATH as AUDIT_LOG
|
|
15
13
|
|
|
16
|
-
# Absolute path to scripts folder
|
|
17
14
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
18
15
|
SCRIPTS_DIR = os.path.join(BASE_DIR, "scripts")
|
|
19
16
|
|
|
@@ -33,7 +30,6 @@ class SentinelAgent:
|
|
|
33
30
|
try:
|
|
34
31
|
json_data = json.loads(text)
|
|
35
32
|
except:
|
|
36
|
-
# Extract first valid JSON object using a stack
|
|
37
33
|
start = text.find("{")
|
|
38
34
|
if start == -1:
|
|
39
35
|
return None
|
|
@@ -93,7 +89,7 @@ class SentinelAgent:
|
|
|
93
89
|
if input("⚠️ ARE YOU SURE? This will delete ALL long-term memories. (y/n): ").lower() == 'y':
|
|
94
90
|
UI.print_system("Initiating Wipe...")
|
|
95
91
|
|
|
96
|
-
|
|
92
|
+
|
|
97
93
|
try:
|
|
98
94
|
from sentinel.core import scheduler
|
|
99
95
|
scheduler.stop_all_jobs()
|
|
@@ -106,7 +102,7 @@ class SentinelAgent:
|
|
|
106
102
|
except:
|
|
107
103
|
pass
|
|
108
104
|
|
|
109
|
-
# 3. Delete the specific files
|
|
105
|
+
# 3. Delete the specific files
|
|
110
106
|
UI.print_system(f"Wiping data from {USER_DATA_DIR}...")
|
|
111
107
|
|
|
112
108
|
# Delete Vector DB Folder
|
|
@@ -148,7 +144,6 @@ class SentinelAgent:
|
|
|
148
144
|
sys.exit(0)
|
|
149
145
|
except Exception as e:
|
|
150
146
|
print(f"Reset failed: {e}")
|
|
151
|
-
# Fallback: If locked, tell user to delete manually
|
|
152
147
|
print(f"Please manually delete this folder: {USER_DATA_DIR}")
|
|
153
148
|
return True
|
|
154
149
|
|
|
@@ -169,7 +164,6 @@ class SentinelAgent:
|
|
|
169
164
|
return True
|
|
170
165
|
|
|
171
166
|
if cmd in ["switch", "model"]:
|
|
172
|
-
# (Keeping it brief for copy-paste)
|
|
173
167
|
provider = args[0] if args else "openai"
|
|
174
168
|
model = args[1] if len(args) > 1 else None
|
|
175
169
|
from sentinel.tools import system_ops
|
|
@@ -228,7 +222,6 @@ class SentinelAgent:
|
|
|
228
222
|
if self.process_slash_command(user_input):
|
|
229
223
|
continue
|
|
230
224
|
|
|
231
|
-
# ---- Recall Memory ----
|
|
232
225
|
relevant_context = memory_ops.retrieve_relevant_context(user_input)
|
|
233
226
|
current_sys = SYSTEM_PROMPT
|
|
234
227
|
if relevant_context:
|
|
@@ -242,7 +235,6 @@ class SentinelAgent:
|
|
|
242
235
|
full_resp = self.brain.query(current_sys, messages)
|
|
243
236
|
action = self._parse_action(full_resp)
|
|
244
237
|
|
|
245
|
-
# ---- Normal LLM response ----
|
|
246
238
|
if not action:
|
|
247
239
|
clean = full_resp.replace("```json", "").replace("```", "").strip()
|
|
248
240
|
|
|
@@ -257,7 +249,6 @@ class SentinelAgent:
|
|
|
257
249
|
|
|
258
250
|
tool, args = action.tool, action.args
|
|
259
251
|
|
|
260
|
-
# ---- Explicit response tool ----
|
|
261
252
|
if tool == "response":
|
|
262
253
|
text = args.get("text", "").strip()
|
|
263
254
|
if not text:
|
|
@@ -267,13 +258,11 @@ class SentinelAgent:
|
|
|
267
258
|
self.history.append({"role": "assistant", "content": action.model_dump_json()})
|
|
268
259
|
break
|
|
269
260
|
|
|
270
|
-
# ---- Tool execution ----
|
|
271
261
|
if tool in TOOLS:
|
|
272
262
|
UI.print_tool(tool)
|
|
273
263
|
try:
|
|
274
264
|
res = TOOLS[tool](**args)
|
|
275
265
|
|
|
276
|
-
# UX fallback
|
|
277
266
|
if not res or not str(res).strip():
|
|
278
267
|
res = "No long-term memories stored about you yet."
|
|
279
268
|
|
sentinel/core/audit.py
CHANGED
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
BASE_DIR = Path.home() / ".sentinel-1"
|
|
9
9
|
BASE_DIR.mkdir(exist_ok=True)
|
|
10
|
-
LOG_FILE = BASE_DIR / "audit_log.jsonl"
|
|
10
|
+
LOG_FILE = BASE_DIR / "audit_log.jsonl"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class AuditLogger:
|
|
@@ -15,7 +15,6 @@ class AuditLogger:
|
|
|
15
15
|
self.cfg = ConfigManager()
|
|
16
16
|
|
|
17
17
|
def is_enabled(self):
|
|
18
|
-
# Check config (default to False if not set)
|
|
19
18
|
return self.cfg.get("system.audit_logging", False)
|
|
20
19
|
|
|
21
20
|
def toggle(self, state: bool):
|
|
@@ -33,7 +32,7 @@ class AuditLogger:
|
|
|
33
32
|
"provider": provider,
|
|
34
33
|
"model": model,
|
|
35
34
|
"duration_ms": round(duration_ms, 2),
|
|
36
|
-
"input": str(input_data)[:2000],
|
|
35
|
+
"input": str(input_data)[:2000],
|
|
37
36
|
"output": str(output_data)
|
|
38
37
|
}
|
|
39
38
|
|
|
@@ -43,6 +42,5 @@ class AuditLogger:
|
|
|
43
42
|
except Exception as e:
|
|
44
43
|
print(f"Logger Error: {e}")
|
|
45
44
|
|
|
46
|
-
|
|
47
45
|
# Global Instance
|
|
48
46
|
audit = AuditLogger()
|
sentinel/core/config.py
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
# FILE: sentinel/core/config.py
|
|
2
1
|
import json
|
|
3
2
|
import os
|
|
4
3
|
import keyring
|
|
5
4
|
from typing import Any, Optional
|
|
6
|
-
from sentinel.paths import CONFIG_PATH
|
|
7
|
-
|
|
8
|
-
APP_NAME = "sentinel-1"
|
|
5
|
+
from sentinel.paths import CONFIG_PATH
|
|
9
6
|
|
|
7
|
+
APP_NAME = "sentinel-ai"
|
|
10
8
|
|
|
11
9
|
class ConfigManager:
|
|
12
10
|
def __init__(self):
|
|
13
11
|
self._ensure_config_exists()
|
|
14
12
|
|
|
15
13
|
def _ensure_config_exists(self):
|
|
16
|
-
# Check if the file exists at ~/.sentinel/config.json
|
|
17
14
|
if not CONFIG_PATH.exists():
|
|
18
15
|
default_config = {
|
|
19
16
|
"user": {"name": "User", "location": "New York"},
|
|
@@ -28,14 +25,12 @@ class ConfigManager:
|
|
|
28
25
|
|
|
29
26
|
def load(self) -> dict:
|
|
30
27
|
try:
|
|
31
|
-
# CONFIG_PATH is a Path object, open() handles it natively
|
|
32
28
|
with open(CONFIG_PATH, "r") as f:
|
|
33
29
|
return json.load(f)
|
|
34
30
|
except (FileNotFoundError, json.JSONDecodeError):
|
|
35
31
|
return {}
|
|
36
32
|
|
|
37
33
|
def save(self, data: dict):
|
|
38
|
-
# Ensure the directory exists (just in case the folder was deleted manually)
|
|
39
34
|
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
40
35
|
|
|
41
36
|
with open(CONFIG_PATH, "w") as f:
|
sentinel/core/llm.py
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
# FILE: core/llm.py
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
1
|
from openai import OpenAI
|
|
5
2
|
import anthropic
|
|
6
3
|
from sentinel.core.ui import UI
|
|
@@ -38,12 +35,9 @@ class LLMEngine:
|
|
|
38
35
|
if verbose:
|
|
39
36
|
is_ready = self.api_key is not None or self.provider == "ollama"
|
|
40
37
|
if is_ready:
|
|
41
|
-
# We can comment this out if we want it strictly in the UI panel now
|
|
42
|
-
# UI.print_system(f"Brain Loaded: [bold cyan]{self.provider.upper()}[/bold cyan] | Model: [bold white]{self.model}[/bold white]")
|
|
43
38
|
pass
|
|
44
39
|
|
|
45
40
|
def stream_query(self, system_prompt, history):
|
|
46
|
-
# Hot-reload config (Silent)
|
|
47
41
|
self.reload_config(verbose=False)
|
|
48
42
|
|
|
49
43
|
if not self.api_key and self.provider != "ollama":
|
|
@@ -51,8 +45,6 @@ class LLMEngine:
|
|
|
51
45
|
yield f"Please run: [cyan]/setkey {self.provider} YOUR_KEY_HERE[/cyan]"
|
|
52
46
|
return
|
|
53
47
|
|
|
54
|
-
# --- PREPARE MESSAGES ---
|
|
55
|
-
# 1. System Prompt is separate
|
|
56
48
|
messages = []
|
|
57
49
|
for msg in history:
|
|
58
50
|
# Anthropic hates "system" role in messages list
|
sentinel/core/registry.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# FILE: core/registry.py
|
|
2
|
-
|
|
3
1
|
import platform
|
|
4
2
|
import datetime
|
|
5
3
|
import threading
|
|
@@ -8,7 +6,6 @@ from sentinel.core.config import ConfigManager
|
|
|
8
6
|
import schedule
|
|
9
7
|
from sentinel.tools.smart_index import smart_find
|
|
10
8
|
|
|
11
|
-
# Import all tools
|
|
12
9
|
from sentinel.tools import (
|
|
13
10
|
apps, browser, clock, email_ops, file_ops, indexer, notes, office,
|
|
14
11
|
system_ops, navigation, flights, sql_index, desktop, vision, audio,
|
|
@@ -17,12 +14,10 @@ from sentinel.tools import (
|
|
|
17
14
|
)
|
|
18
15
|
from sentinel.core import scheduler, cognitive
|
|
19
16
|
|
|
20
|
-
# --- DETECT OS ---
|
|
21
17
|
CURRENT_OS = platform.system()
|
|
22
18
|
OS_VERSION = platform.release()
|
|
23
19
|
NOW = datetime.datetime.now()
|
|
24
20
|
|
|
25
|
-
# --- LOAD CONFIG ---
|
|
26
21
|
cfg = ConfigManager()
|
|
27
22
|
settings = cfg.load() if cfg.exists() else {}
|
|
28
23
|
|
|
@@ -30,21 +25,13 @@ settings = cfg.load() if cfg.exists() else {}
|
|
|
30
25
|
def initialize_tools():
|
|
31
26
|
print("\n[System] 🔄 Initializing File Systems...")
|
|
32
27
|
|
|
33
|
-
# 1. Fast Filename Indexer (Metadata Only)
|
|
34
28
|
t1 = threading.Thread(target=sql_index.build_index, args=(True,))
|
|
35
29
|
t1.daemon = True
|
|
36
30
|
t1.start()
|
|
37
31
|
|
|
38
|
-
# REMOVE: The content indexer thread (t2)
|
|
39
|
-
|
|
40
|
-
# 3. Schedule recurring updates (Metadata only)
|
|
41
32
|
schedule.every(60).minutes.do(sql_index.build_index, silent=True)
|
|
42
|
-
|
|
43
33
|
scheduler.start_scheduler_service()
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
# --- SAFETY WRAPPERS ---
|
|
47
|
-
|
|
48
35
|
def ask_permission(tool_name, func, **kwargs):
|
|
49
36
|
"""
|
|
50
37
|
Intervention Layer: Pauses execution to ask the user for confirmation.
|
|
@@ -58,7 +45,6 @@ def ask_permission(tool_name, func, **kwargs):
|
|
|
58
45
|
|
|
59
46
|
if choice == 'y':
|
|
60
47
|
try:
|
|
61
|
-
# Log successful dangerous actions to memory
|
|
62
48
|
log_args = {k: v for k, v in kwargs.items() if k != 'agent_config'}
|
|
63
49
|
memory_ops.log_activity(tool_name, str(log_args))
|
|
64
50
|
except:
|
|
@@ -90,8 +76,6 @@ def draft_code(filename, content):
|
|
|
90
76
|
except Exception as e:
|
|
91
77
|
return f"Error drafting code: {e}"
|
|
92
78
|
|
|
93
|
-
|
|
94
|
-
# --- TOOL MAPPING ---
|
|
95
79
|
TOOLS = {
|
|
96
80
|
# System & Apps
|
|
97
81
|
"open_app": apps.open_app,
|
|
@@ -193,7 +177,6 @@ TOOLS = {
|
|
|
193
177
|
}
|
|
194
178
|
|
|
195
179
|
# --- PROMPT ---
|
|
196
|
-
# Note: JSON examples use DOUBLE BRACES {{ }} to escape f-string formatting
|
|
197
180
|
SYSTEM_PROMPT = f"""
|
|
198
181
|
You are **Sentinel**, an autonomous AI Operating System layer.
|
|
199
182
|
Your role is to translate user intent into safe, deterministic system actions.
|
sentinel/core/scheduler.py
CHANGED
|
@@ -3,7 +3,6 @@ import time
|
|
|
3
3
|
import threading
|
|
4
4
|
from sentinel.core.llm import LLMEngine
|
|
5
5
|
|
|
6
|
-
# Global registry
|
|
7
6
|
ACTIVE_JOBS = {}
|
|
8
7
|
|
|
9
8
|
|
|
@@ -11,14 +10,11 @@ def _job_runner(task_description, agent_config):
|
|
|
11
10
|
"""Runs the background task."""
|
|
12
11
|
print(f"\n[Scheduler] ⏰ Executing background task: {task_description}")
|
|
13
12
|
|
|
14
|
-
# Create a transient brain for this task
|
|
15
13
|
brain = LLMEngine(agent_config)
|
|
16
14
|
|
|
17
|
-
# Security: Background agents get a strictly limited prompt
|
|
18
15
|
sys_prompt = f"You are a background monitoring agent. Current Task: {task_description}. Output JSON only."
|
|
19
16
|
|
|
20
17
|
try:
|
|
21
|
-
# We pass an empty history because background tasks should be stateless
|
|
22
18
|
response = brain.query(sys_prompt, [])
|
|
23
19
|
print(f"[Scheduler Result]: {response}")
|
|
24
20
|
except Exception as e:
|
sentinel/core/schema.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# FILE: core/schema.py
|
|
2
1
|
from pydantic import BaseModel, Field, ValidationError
|
|
3
2
|
from typing import Dict, Any, Optional
|
|
4
3
|
|
|
@@ -7,5 +6,4 @@ class AgentAction(BaseModel):
|
|
|
7
6
|
args: Dict[str, Any] = Field(default_factory=dict)
|
|
8
7
|
|
|
9
8
|
class Config:
|
|
10
|
-
# Allows the model to ignore extra fields if the LLM hallucinates them
|
|
11
9
|
extra = "ignore"
|
sentinel/core/setup.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
# sentinel/core/setup.py
|
|
2
|
-
|
|
3
1
|
import time
|
|
4
2
|
from rich.console import Console
|
|
5
3
|
from rich.panel import Panel
|
|
@@ -84,7 +82,6 @@ def setup_wizard():
|
|
|
84
82
|
console.print(f"[bold cyan]{CREDS_FILE}[/bold cyan]")
|
|
85
83
|
console.print("\nThen run: sentinel auth")
|
|
86
84
|
|
|
87
|
-
# 5. Finalize
|
|
88
85
|
with Progress(
|
|
89
86
|
SpinnerColumn(),
|
|
90
87
|
TextColumn("[progress.description]{task.description}"),
|
sentinel/core/ui.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# FILE: core/ui.py
|
|
2
1
|
from rich.console import Console
|
|
3
2
|
from rich.panel import Panel
|
|
4
3
|
from rich.markdown import Markdown
|
|
@@ -50,7 +49,6 @@ class UI:
|
|
|
50
49
|
if not isinstance(text, Markdown):
|
|
51
50
|
text = Markdown(str(text))
|
|
52
51
|
|
|
53
|
-
# Add Model Info to Title
|
|
54
52
|
title = "[bold cyan]SENTINEL[/bold cyan]"
|
|
55
53
|
if model:
|
|
56
54
|
title += f" [dim]({model})[/dim]"
|
sentinel/paths.py
CHANGED
|
@@ -1,73 +1,44 @@
|
|
|
1
|
-
# FILE: sentinel/core/paths.py
|
|
2
1
|
import os
|
|
3
2
|
import sys
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
|
|
6
|
-
# ==========================================
|
|
7
|
-
# 1. USER DATA STORAGE (Mutable)
|
|
8
|
-
# Stores DBs, Configs, Logs, and Auth Tokens
|
|
9
|
-
# Location: ~/.sentinel (Cross-platform)
|
|
10
|
-
# ==========================================
|
|
11
|
-
|
|
12
5
|
USER_DATA_DIR = Path.home() / ".sentinel"
|
|
13
6
|
USER_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
14
|
-
|
|
15
|
-
# Subdirectories
|
|
16
7
|
LOGS_DIR = USER_DATA_DIR / "logs"
|
|
17
8
|
LOGS_DIR.mkdir(exist_ok=True)
|
|
18
9
|
|
|
19
10
|
DRAFTS_DIR = USER_DATA_DIR / "drafts"
|
|
20
11
|
DRAFTS_DIR.mkdir(exist_ok=True)
|
|
21
|
-
|
|
22
|
-
# Critical System Files
|
|
23
12
|
CONFIG_PATH = USER_DATA_DIR / "config.json"
|
|
24
13
|
DB_PATH = USER_DATA_DIR / "brain.db"
|
|
25
14
|
VECTOR_PATH = USER_DATA_DIR / "brain_vectors" # ChromaDB folder
|
|
26
15
|
AUDIT_LOG_PATH = USER_DATA_DIR / "audit_log.jsonl"
|
|
27
16
|
MEMORY_FILE = USER_DATA_DIR / "memory.json"
|
|
28
|
-
|
|
29
|
-
# Search Indexes
|
|
30
17
|
FILE_INDEX_DB = USER_DATA_DIR / "file_index.db"
|
|
31
18
|
SMART_INDEX_DB = USER_DATA_DIR / "smart_files.db"
|
|
32
19
|
|
|
33
|
-
|
|
20
|
+
|
|
34
21
|
CREDENTIALS_PATH = USER_DATA_DIR / "credentials.json"
|
|
35
22
|
TOKEN_PATH = USER_DATA_DIR / "token.json"
|
|
36
23
|
|
|
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
24
|
def get_script_path(filename: str) -> str:
|
|
45
25
|
"""
|
|
46
26
|
Returns the absolute path to a script bundled inside the pip package.
|
|
47
27
|
Expects scripts to be in: src/sentinel/scripts/
|
|
48
28
|
"""
|
|
49
|
-
# This file is in: .../sentinel/core/paths.py
|
|
50
|
-
# We want: .../sentinel/scripts/filename
|
|
51
29
|
|
|
52
|
-
# Get the directory of THIS file (core/)
|
|
53
30
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
54
|
-
|
|
55
|
-
# Go up one level to the package root (sentinel/)
|
|
56
31
|
pkg_root = os.path.dirname(current_dir)
|
|
57
|
-
|
|
58
|
-
# Target path
|
|
59
32
|
script_path = os.path.join(pkg_root, "scripts", filename)
|
|
60
33
|
|
|
61
34
|
if not os.path.exists(script_path):
|
|
62
|
-
|
|
63
|
-
# In dev repo: sentinel/core/paths.py -> ../../../scripts/
|
|
35
|
+
|
|
64
36
|
repo_root = os.path.dirname(os.path.dirname(pkg_root))
|
|
65
37
|
dev_path = os.path.join(repo_root, "scripts", filename)
|
|
66
38
|
|
|
67
39
|
if os.path.exists(dev_path):
|
|
68
40
|
return dev_path
|
|
69
41
|
|
|
70
|
-
# If we still can't find it, that's a build error
|
|
71
42
|
raise FileNotFoundError(
|
|
72
43
|
f"Could not find bundled script '{filename}'.\n"
|
|
73
44
|
f"Checked: {script_path}\n"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
title Sentinel Factory Reset
|
|
3
|
+
|
|
4
|
+
echo.
|
|
5
|
+
echo !!! THIS WILL DELETE ALL SENTINEL DATA !!!
|
|
6
|
+
echo Location:
|
|
7
|
+
echo %USERPROFILE%\.sentinel
|
|
8
|
+
echo.
|
|
9
|
+
pause
|
|
10
|
+
|
|
11
|
+
rmdir /s /q "%USERPROFILE%\.sentinel"
|
|
12
|
+
mkdir "%USERPROFILE%\.sentinel"
|
|
13
|
+
|
|
14
|
+
echo.
|
|
15
|
+
echo Sentinel has been FACTORY RESET.
|
|
16
|
+
echo All memory, vectors, tokens, logs deleted.
|
|
17
|
+
exit
|
sentinel/tools/apps.py
CHANGED
|
@@ -7,7 +7,6 @@ import webbrowser
|
|
|
7
7
|
import difflib
|
|
8
8
|
import logging
|
|
9
9
|
|
|
10
|
-
# Ensure APP_CACHE exists (from your global scope)
|
|
11
10
|
APP_CACHE = globals().get("APP_CACHE", {})
|
|
12
11
|
|
|
13
12
|
def _native_open(target):
|
|
@@ -33,8 +32,6 @@ def _run_command(cmd_str):
|
|
|
33
32
|
Safely runs a command string cross-platform.
|
|
34
33
|
"""
|
|
35
34
|
try:
|
|
36
|
-
# On Windows, shell=True is often needed for system aliases (like 'dir' or 'start')
|
|
37
|
-
# On Unix, we prefer passing a list, but for complex aliases we use shell=True safely.
|
|
38
35
|
subprocess.Popen(cmd_str, shell=True)
|
|
39
36
|
return True
|
|
40
37
|
except Exception as e:
|
|
@@ -78,7 +75,7 @@ def _get_os_aliases():
|
|
|
78
75
|
"ppt": "powerpnt",
|
|
79
76
|
"outlook": "outlook",
|
|
80
77
|
"onenote": "onenote",
|
|
81
|
-
"teams": "ms-teams:",
|
|
78
|
+
"teams": "ms-teams:",
|
|
82
79
|
"access": "msaccess",
|
|
83
80
|
|
|
84
81
|
# --- SYSTEM TOOLS ---
|
|
@@ -87,7 +84,7 @@ def _get_os_aliases():
|
|
|
87
84
|
"settings": "start ms-settings:",
|
|
88
85
|
"control panel": "control",
|
|
89
86
|
"task manager": "taskmgr",
|
|
90
|
-
"cmd": "start cmd", #
|
|
87
|
+
"cmd": "start cmd", #
|
|
91
88
|
"command prompt": "start cmd",
|
|
92
89
|
"terminal": "wt", # Windows Terminal
|
|
93
90
|
"powershell": "start powershell",
|
|
@@ -109,7 +106,6 @@ def _get_os_aliases():
|
|
|
109
106
|
"pycharm": "pycharm64",
|
|
110
107
|
"intellij": "idea64",
|
|
111
108
|
"docker": "start \"Docker Desktop\" \"C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe\"",
|
|
112
|
-
# Hard path often needed
|
|
113
109
|
"wsl": "wsl",
|
|
114
110
|
"git": "git-bash",
|
|
115
111
|
|
|
@@ -121,7 +117,7 @@ def _get_os_aliases():
|
|
|
121
117
|
"calc": "calc",
|
|
122
118
|
"calculator": "calc",
|
|
123
119
|
"snipping tool": "snippingtool",
|
|
124
|
-
"screenshot": "snippingtool",
|
|
120
|
+
"screenshot": "snippingtool",
|
|
125
121
|
"photos": "ms-photos:",
|
|
126
122
|
"camera": "microsoft.windows.camera:",
|
|
127
123
|
"clock": "ms-clock:",
|
|
@@ -144,7 +140,6 @@ def _get_os_aliases():
|
|
|
144
140
|
}}
|
|
145
141
|
|
|
146
142
|
# 3. macOS Specifics (Darwin)
|
|
147
|
-
# 3. macOS Specifics (Darwin)
|
|
148
143
|
elif system == "Darwin":
|
|
149
144
|
return {**aliases, **{
|
|
150
145
|
# --- BROWSERS ---
|
|
@@ -167,12 +162,12 @@ def _get_os_aliases():
|
|
|
167
162
|
|
|
168
163
|
# --- DEV TOOLS ---
|
|
169
164
|
"vscode": "open -a 'Visual Studio Code'",
|
|
170
|
-
"code": "open -a 'Visual Studio Code'",
|
|
165
|
+
"code": "open -a 'Visual Studio Code'",
|
|
171
166
|
"sublime": "open -a 'Sublime Text'",
|
|
172
167
|
"iterm": "open -a iTerm",
|
|
173
168
|
"terminal": "open -a Terminal",
|
|
174
169
|
"docker": "open -a Docker",
|
|
175
|
-
"pycharm": "open -a 'PyCharm CE'",
|
|
170
|
+
"pycharm": "open -a 'PyCharm CE'",
|
|
176
171
|
"intellij": "open -a 'IntelliJ IDEA CE'",
|
|
177
172
|
"xcode": "open -a Xcode",
|
|
178
173
|
|
|
@@ -182,7 +177,7 @@ def _get_os_aliases():
|
|
|
182
177
|
"whatsapp": "open -a WhatsApp",
|
|
183
178
|
"telegram": "open -a Telegram",
|
|
184
179
|
"signal": "open -a Signal",
|
|
185
|
-
"zoom": "open -a zoom.us",
|
|
180
|
+
"zoom": "open -a zoom.us",
|
|
186
181
|
"messages": "open -a Messages",
|
|
187
182
|
"imessage": "open -a Messages",
|
|
188
183
|
"mail": "open -a Mail",
|
|
@@ -208,12 +203,12 @@ def _get_os_aliases():
|
|
|
208
203
|
|
|
209
204
|
# --- SYSTEM UTILITIES ---
|
|
210
205
|
"finder": "open .",
|
|
211
|
-
"explorer": "open .",
|
|
212
|
-
"settings": "open -b com.apple.systempreferences",
|
|
206
|
+
"explorer": "open .",
|
|
207
|
+
"settings": "open -b com.apple.systempreferences",
|
|
213
208
|
"preferences": "open -b com.apple.systempreferences",
|
|
214
209
|
"app store": "open -a 'App Store'",
|
|
215
210
|
"activity monitor": "open -a 'Activity Monitor'",
|
|
216
|
-
"task manager": "open -a 'Activity Monitor'",
|
|
211
|
+
"task manager": "open -a 'Activity Monitor'",
|
|
217
212
|
"disk utility": "open -a 'Disk Utility'",
|
|
218
213
|
"calculator": "open -a Calculator",
|
|
219
214
|
"calc": "open -a Calculator",
|
|
@@ -230,7 +225,7 @@ def _get_os_aliases():
|
|
|
230
225
|
"chromium": "chromium-browser",
|
|
231
226
|
"firefox": "firefox",
|
|
232
227
|
"brave": "brave-browser",
|
|
233
|
-
"edge": "microsoft-edge",
|
|
228
|
+
"edge": "microsoft-edge",
|
|
234
229
|
"opera": "opera",
|
|
235
230
|
|
|
236
231
|
# --- OFFICE (LibreOffice Suite) ---
|
|
@@ -240,7 +235,7 @@ def _get_os_aliases():
|
|
|
240
235
|
"calc": "libreoffice --calc",
|
|
241
236
|
"powerpoint": "libreoffice --impress",
|
|
242
237
|
"impress": "libreoffice --impress",
|
|
243
|
-
"teams": "teams-for-linux",
|
|
238
|
+
"teams": "teams-for-linux",
|
|
244
239
|
|
|
245
240
|
# --- DEV TOOLS ---
|
|
246
241
|
"vscode": "code",
|
|
@@ -249,8 +244,6 @@ def _get_os_aliases():
|
|
|
249
244
|
"vim": "vim",
|
|
250
245
|
"nano": "nano",
|
|
251
246
|
"docker": "docker",
|
|
252
|
-
# "terminal" usually defaults to x-terminal-emulator,
|
|
253
|
-
# but we explicitly try common ones if that fails:
|
|
254
247
|
"terminal": "gnome-terminal",
|
|
255
248
|
"konsole": "konsole",
|
|
256
249
|
|
|
@@ -267,25 +260,25 @@ def _get_os_aliases():
|
|
|
267
260
|
"spotify": "spotify",
|
|
268
261
|
"rhythmbox": "rhythmbox",
|
|
269
262
|
"mpv": "mpv",
|
|
270
|
-
"photos": "eog",
|
|
263
|
+
"photos": "eog",
|
|
271
264
|
"gimp": "gimp",
|
|
272
265
|
"obs": "obs",
|
|
273
266
|
|
|
274
267
|
# --- SYSTEM UTILITIES ---
|
|
275
268
|
"explorer": "xdg-open .",
|
|
276
269
|
"finder": "xdg-open .",
|
|
277
|
-
"nautilus": "nautilus",
|
|
278
|
-
"dolphin": "dolphin",
|
|
279
|
-
"thunar": "thunar",
|
|
270
|
+
"nautilus": "nautilus",
|
|
271
|
+
"dolphin": "dolphin",
|
|
272
|
+
"thunar": "thunar",
|
|
273
|
+
|
|
280
274
|
|
|
281
|
-
# "Settings" varies wildly, but these covers 80% of users:
|
|
282
275
|
"settings": "gnome-control-center",
|
|
283
276
|
"control panel": "gnome-control-center",
|
|
284
277
|
|
|
285
278
|
# Task Manager equivalents
|
|
286
279
|
"task manager": "gnome-system-monitor",
|
|
287
280
|
"system monitor": "gnome-system-monitor",
|
|
288
|
-
"htop": "x-terminal-emulator -e htop",
|
|
281
|
+
"htop": "x-terminal-emulator -e htop",
|
|
289
282
|
|
|
290
283
|
"calculator": "gnome-calculator",
|
|
291
284
|
"screenshot": "gnome-screenshot",
|
|
@@ -323,7 +316,6 @@ def refresh_app_cache():
|
|
|
323
316
|
for app_dir in app_dirs:
|
|
324
317
|
if not os.path.exists(app_dir): continue
|
|
325
318
|
try:
|
|
326
|
-
# macOS apps are folders ending in .app
|
|
327
319
|
for item in os.listdir(app_dir):
|
|
328
320
|
if item.endswith(".app"):
|
|
329
321
|
name = item.replace(".app", "").lower()
|
|
@@ -334,7 +326,6 @@ def refresh_app_cache():
|
|
|
334
326
|
|
|
335
327
|
# --- LINUX INDEXING ---
|
|
336
328
|
elif system == "Linux":
|
|
337
|
-
# Scan for .desktop files
|
|
338
329
|
desktop_dirs = ["/usr/share/applications", os.path.expanduser("~/.local/share/applications")]
|
|
339
330
|
for d_dir in desktop_dirs:
|
|
340
331
|
if not os.path.exists(d_dir): continue
|
|
@@ -343,14 +334,11 @@ def refresh_app_cache():
|
|
|
343
334
|
if item.endswith(".desktop"):
|
|
344
335
|
name = item.replace(".desktop", "").lower()
|
|
345
336
|
full_path = os.path.join(d_dir, item)
|
|
346
|
-
# We map the simple name to the .desktop file
|
|
347
|
-
# xdg-open handles .desktop files automatically
|
|
348
337
|
APP_CACHE[name] = full_path
|
|
349
338
|
except PermissionError:
|
|
350
339
|
continue
|
|
351
340
|
|
|
352
341
|
|
|
353
|
-
# Run indexing on import
|
|
354
342
|
refresh_app_cache()
|
|
355
343
|
|
|
356
344
|
|
|
@@ -377,9 +365,7 @@ def open_app(name):
|
|
|
377
365
|
lower_name = name.lower()
|
|
378
366
|
|
|
379
367
|
# --- 1. DIRECT PATH ---
|
|
380
|
-
# If the user provides a full path that exists
|
|
381
368
|
if os.path.exists(name):
|
|
382
|
-
# Optional: index_file(name) if available in your scope
|
|
383
369
|
if 'index_file' in globals(): globals()['index_file'](name)
|
|
384
370
|
|
|
385
371
|
if _native_open(name):
|
|
@@ -395,7 +381,6 @@ def open_app(name):
|
|
|
395
381
|
return f"Failed to launch alias: {lower_name}"
|
|
396
382
|
|
|
397
383
|
# --- 3. DYNAMIC SEARCH (Fuzzy Match) ---
|
|
398
|
-
# Works if APP_CACHE is populated (Windows mostly, unless you add Linux/Mac indexers)
|
|
399
384
|
if APP_CACHE:
|
|
400
385
|
# Exact Match
|
|
401
386
|
if lower_name in APP_CACHE:
|
|
@@ -412,13 +397,11 @@ def open_app(name):
|
|
|
412
397
|
return f"Launched {best_match} (matched to '{name}')"
|
|
413
398
|
|
|
414
399
|
# --- 4. EXECUTABLE IN PATH ---
|
|
415
|
-
# Check if 'name' is a command available in the system PATH (e.g., 'npm', 'docker')
|
|
416
400
|
if shutil.which(lower_name):
|
|
417
401
|
_run_command(lower_name)
|
|
418
402
|
return f"Executed command: {lower_name}"
|
|
419
403
|
|
|
420
404
|
# --- 5. WEB URL FALLBACK ---
|
|
421
|
-
# If it looks like a URL or domain
|
|
422
405
|
if "." in name and " " not in name:
|
|
423
406
|
url = name if name.startswith(("http://", "https://")) else f"https://{name}"
|
|
424
407
|
webbrowser.open(url)
|
sentinel/tools/audio.py
CHANGED
sentinel/tools/browser.py
CHANGED
|
@@ -17,7 +17,6 @@ def search_web(query):
|
|
|
17
17
|
try:
|
|
18
18
|
from tavily import TavilyClient
|
|
19
19
|
client = TavilyClient(api_key=tavily_key)
|
|
20
|
-
# 'search_context' returns optimized text for AI
|
|
21
20
|
response = client.get_search_context(query=query, search_depth="basic", max_tokens=1500)
|
|
22
21
|
return f"[Source: Tavily]\n{response}"
|
|
23
22
|
except ImportError:
|
|
@@ -25,7 +24,6 @@ def search_web(query):
|
|
|
25
24
|
except Exception:
|
|
26
25
|
pass
|
|
27
26
|
|
|
28
|
-
# 2. Fallback to DuckDuckGo
|
|
29
27
|
try:
|
|
30
28
|
results = DDGS().text(query, max_results=4)
|
|
31
29
|
summary = []
|
sentinel/tools/context.py
CHANGED
sentinel/tools/factory.py
CHANGED
|
@@ -13,14 +13,10 @@ def _convert_to_pdf(docx_path):
|
|
|
13
13
|
For this MVP, we will try a pure Python fallback if system tools aren't found.
|
|
14
14
|
"""
|
|
15
15
|
try:
|
|
16
|
-
# 1. Try LibreOffice (Best Quality, Cross Platform)
|
|
17
|
-
# This requires LibreOffice to be in your PATH (soffice/libreoffice)
|
|
18
16
|
cmd = f"soffice --headless --convert-to pdf \"{docx_path}\" --outdir \"{os.path.dirname(docx_path)}\""
|
|
19
17
|
if os.system(cmd) == 0:
|
|
20
18
|
return docx_path.replace(".docx", ".pdf")
|
|
21
19
|
|
|
22
|
-
# 2. Try Microsoft Word (Windows Only, requires 'docx2pdf')
|
|
23
|
-
# pip install docx2pdf
|
|
24
20
|
try:
|
|
25
21
|
from docx2pdf import convert
|
|
26
22
|
convert(docx_path)
|
|
@@ -103,7 +99,6 @@ def create_document(filename, blocks):
|
|
|
103
99
|
if i < len(row_cells):
|
|
104
100
|
row_cells[i].text = str(cell_text)
|
|
105
101
|
|
|
106
|
-
# --- BREAKS ---
|
|
107
102
|
elif b_type == "page_break":
|
|
108
103
|
doc.add_page_break()
|
|
109
104
|
|
|
@@ -116,7 +111,6 @@ def create_document(filename, blocks):
|
|
|
116
111
|
|
|
117
112
|
doc.save(docx_path)
|
|
118
113
|
|
|
119
|
-
# Try converting to PDF automatically
|
|
120
114
|
pdf_result = _convert_to_pdf(docx_path)
|
|
121
115
|
|
|
122
116
|
return f"Document created: {docx_path} (PDF Status: {pdf_result})"
|
sentinel/tools/file_ops.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# FILE: tools/file_ops.py
|
|
2
1
|
import os
|
|
3
2
|
import fitz # PyMuPDF
|
|
4
3
|
import pandas as pd
|
|
@@ -19,7 +18,6 @@ def read_file(path):
|
|
|
19
18
|
ext = os.path.splitext(path)[1].lower()
|
|
20
19
|
|
|
21
20
|
try:
|
|
22
|
-
# --- PDF (Smart Read using PyMuPDF) ---
|
|
23
21
|
if ext == '.pdf':
|
|
24
22
|
text = ""
|
|
25
23
|
try:
|
sentinel/tools/flights.py
CHANGED
|
@@ -43,7 +43,7 @@ def search_flights(departure_id, arrival_id, date, travel_type=2):
|
|
|
43
43
|
|
|
44
44
|
for i, f in enumerate(flights[:5], 1):
|
|
45
45
|
price = f.get("price", "N/A")
|
|
46
|
-
duration = f.get("total_duration", "N/A")
|
|
46
|
+
duration = f.get("total_duration", "N/A")
|
|
47
47
|
|
|
48
48
|
leg = f["flights"][0]
|
|
49
49
|
airline = leg.get("airline", "Unknown")
|
sentinel/tools/gmail_auth.py
CHANGED
|
@@ -3,10 +3,8 @@ from google.auth.transport.requests import Request
|
|
|
3
3
|
from google.oauth2.credentials import Credentials
|
|
4
4
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
5
5
|
|
|
6
|
-
# --- FIX: IMPORT PATHS FROM CENTRAL MODULE ---
|
|
7
6
|
from sentinel.paths import CREDENTIALS_PATH, TOKEN_PATH
|
|
8
7
|
|
|
9
|
-
# The permissions the AI needs
|
|
10
8
|
SCOPES = [
|
|
11
9
|
'https://www.googleapis.com/auth/gmail.readonly',
|
|
12
10
|
'https://www.googleapis.com/auth/gmail.send'
|
|
@@ -16,32 +14,26 @@ SCOPES = [
|
|
|
16
14
|
def get_gmail_service():
|
|
17
15
|
creds = None
|
|
18
16
|
|
|
19
|
-
# 1. Check if we already logged in (using the centralized token path)
|
|
20
17
|
if os.path.exists(TOKEN_PATH):
|
|
21
18
|
creds = Credentials.from_authorized_user_file(TOKEN_PATH, SCOPES)
|
|
22
19
|
|
|
23
|
-
# 2. If not logged in or token expired, log in
|
|
24
20
|
if not creds or not creds.valid:
|
|
25
21
|
if creds and creds.expired and creds.refresh_token:
|
|
26
22
|
creds.refresh(Request())
|
|
27
23
|
else:
|
|
28
|
-
|
|
24
|
+
|
|
29
25
|
if not os.path.exists(CREDENTIALS_PATH):
|
|
30
26
|
print(f"Error: credentials.json not found at {CREDENTIALS_PATH}")
|
|
31
27
|
return None
|
|
32
28
|
|
|
33
|
-
# This opens your browser to login
|
|
34
29
|
flow = InstalledAppFlow.from_client_secrets_file(
|
|
35
30
|
CREDENTIALS_PATH, SCOPES)
|
|
36
31
|
creds = flow.run_local_server(port=0)
|
|
37
32
|
|
|
38
|
-
# Save the token for next time
|
|
39
|
-
# Ensure the directory exists
|
|
40
33
|
TOKEN_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
41
34
|
|
|
42
35
|
with open(TOKEN_PATH, 'w') as token:
|
|
43
36
|
token.write(creds.to_json())
|
|
44
37
|
|
|
45
|
-
# 3. Return the API Service
|
|
46
38
|
from googleapiclient.discovery import build
|
|
47
39
|
return build('gmail', 'v1', credentials=creds)
|
sentinel/tools/indexer.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# FILE: tools/indexer.py
|
|
2
1
|
import os
|
|
3
2
|
import sqlite3
|
|
4
3
|
import time
|
|
@@ -82,7 +81,6 @@ def build_index(root=None, verbose=False):
|
|
|
82
81
|
count = 0
|
|
83
82
|
updated = 0
|
|
84
83
|
|
|
85
|
-
# We scan these specific user folders to stay efficient
|
|
86
84
|
target_dirs = [
|
|
87
85
|
os.path.join(root, "Documents"),
|
|
88
86
|
os.path.join(root, "Desktop"),
|
|
@@ -93,11 +91,9 @@ def build_index(root=None, verbose=False):
|
|
|
93
91
|
if not os.path.exists(target): continue
|
|
94
92
|
|
|
95
93
|
for r, dirs, files in os.walk(target):
|
|
96
|
-
# Clean skip dirs in-place
|
|
97
94
|
dirs[:] = [d for d in dirs if d not in SKIP_DIRS and not d.startswith(".")]
|
|
98
95
|
|
|
99
96
|
for file in files:
|
|
100
|
-
# CPU BREATHING ROOM
|
|
101
97
|
time.sleep(THROTTLE_SEC)
|
|
102
98
|
|
|
103
99
|
ext = os.path.splitext(file)[1].lower()
|
|
@@ -111,23 +107,20 @@ def build_index(root=None, verbose=False):
|
|
|
111
107
|
stats = os.stat(path)
|
|
112
108
|
current_mtime = stats.st_mtime
|
|
113
109
|
|
|
114
|
-
# Skip unchanged
|
|
115
110
|
if path in existing_meta and existing_meta[path] == current_mtime:
|
|
116
111
|
continue
|
|
117
112
|
|
|
118
113
|
content = _get_text_from_file(path, ext)
|
|
119
114
|
if not content or len(content) < 5: continue
|
|
120
115
|
|
|
121
|
-
# Update Database
|
|
122
116
|
c.execute("DELETE FROM files WHERE path = ?", (path,))
|
|
123
117
|
c.execute("INSERT INTO files (path, name, content) VALUES (?,?,?)", (path, file, content))
|
|
124
118
|
c.execute("INSERT OR REPLACE INTO file_meta (path, mtime) VALUES (?,?)", (path, current_mtime))
|
|
125
|
-
conn.commit()
|
|
119
|
+
conn.commit()
|
|
126
120
|
|
|
127
121
|
updated += 1
|
|
128
122
|
count += 1
|
|
129
123
|
|
|
130
|
-
# LOGGING: Only print every 10th file to reduce spam
|
|
131
124
|
if verbose and updated % 10 == 0:
|
|
132
125
|
print(f" [Indexer] Indexed: {file} ({updated} total)")
|
|
133
126
|
|
|
@@ -144,7 +137,6 @@ def search_index(query):
|
|
|
144
137
|
if not os.path.exists(DB_FILE): return "Index missing."
|
|
145
138
|
conn = sqlite3.connect(DB_FILE)
|
|
146
139
|
try:
|
|
147
|
-
# Search content and return snippets
|
|
148
140
|
res = conn.execute(
|
|
149
141
|
"SELECT path, snippet(files, 2, '[', ']', '...', 10) FROM files WHERE files MATCH ? LIMIT 5",
|
|
150
142
|
(query,)
|
sentinel/tools/notes.py
CHANGED
sentinel/tools/smart_index.py
CHANGED
sentinel/tools/sql_index.py
CHANGED
|
@@ -10,8 +10,7 @@ BASE_DIR.mkdir(exist_ok=True)
|
|
|
10
10
|
|
|
11
11
|
DB_FILE = BASE_DIR / "sentinel-1.db"
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
# These are the "System Ones" we skip. Everything else is fair game.
|
|
13
|
+
|
|
15
14
|
SKIP_DIRS = {
|
|
16
15
|
# System Folders
|
|
17
16
|
"Windows", "Program Files", "Program Files (x86)",
|
|
@@ -19,7 +18,7 @@ SKIP_DIRS = {
|
|
|
19
18
|
"$RECYCLE.BIN", "System Volume Information", "Recovery",
|
|
20
19
|
"boot", "efi",
|
|
21
20
|
|
|
22
|
-
# Dev Junk
|
|
21
|
+
# Dev Junk
|
|
23
22
|
"node_modules", "venv", ".venv", ".git", "__pycache__", ".idea", ".vscode"
|
|
24
23
|
}
|
|
25
24
|
|
|
@@ -68,10 +67,8 @@ def build_index(silent=True):
|
|
|
68
67
|
cursor.execute("CREATE INDEX IF NOT EXISTS idx_name ON files(name)")
|
|
69
68
|
|
|
70
69
|
# 2. Identify Scan Targets
|
|
71
|
-
# Strategy: Scan the User Profile on C:, plus the ROOT of every other drive.
|
|
72
70
|
targets = []
|
|
73
71
|
|
|
74
|
-
# A. Always get standard user folders (Safest way to scan C:)
|
|
75
72
|
home = os.path.expanduser("~")
|
|
76
73
|
targets.extend([
|
|
77
74
|
os.path.join(home, "Desktop"),
|
|
@@ -80,23 +77,17 @@ def build_index(silent=True):
|
|
|
80
77
|
os.path.join(home, "Pictures"),
|
|
81
78
|
os.path.join(home, "Music"),
|
|
82
79
|
os.path.join(home, "Videos"),
|
|
83
|
-
# Add your code folder explicitly if it's not in the above
|
|
84
80
|
r"D:\Python Projects"
|
|
85
81
|
])
|
|
86
82
|
|
|
87
|
-
# B. Add ALL other drives (D:, E:, etc)
|
|
88
83
|
all_drives = get_all_drives()
|
|
89
84
|
for drive in all_drives:
|
|
90
|
-
# We generally avoid scanning C:\ root because it's 90% OS junk.
|
|
91
|
-
# But D:\, E:\, etc. are usually pure data, so we scan their ROOT.
|
|
92
85
|
if "C:" in drive.upper():
|
|
93
86
|
continue
|
|
94
87
|
targets.append(drive)
|
|
95
88
|
|
|
96
|
-
# Remove duplicates
|
|
97
89
|
targets = list(set(targets))
|
|
98
90
|
|
|
99
|
-
# 3. Load Cache (for speed)
|
|
100
91
|
if not silent: print("[System] Loading existing file index...")
|
|
101
92
|
cursor.execute("SELECT path, mtime_raw FROM files")
|
|
102
93
|
existing_files = {row['path']: row['mtime_raw'] for row in cursor.fetchall()}
|
|
@@ -107,17 +98,15 @@ def build_index(silent=True):
|
|
|
107
98
|
if not silent:
|
|
108
99
|
print(f"[System] Scanning {len(targets)} locations:\n" + "\n".join([f" - {t}" for t in targets]))
|
|
109
100
|
|
|
110
|
-
# 4. The Scan Loop
|
|
111
101
|
for folder in targets:
|
|
112
102
|
if not os.path.exists(folder): continue
|
|
113
103
|
|
|
114
104
|
for root, dirs, filenames in os.walk(folder):
|
|
115
|
-
|
|
116
|
-
# We modify 'dirs' in-place so os.walk doesn't even enter them.
|
|
105
|
+
|
|
117
106
|
dirs[:] = [d for d in dirs if d not in SKIP_DIRS and not d.startswith(".")]
|
|
118
107
|
|
|
119
108
|
for f in filenames:
|
|
120
|
-
|
|
109
|
+
|
|
121
110
|
if f.startswith("~$") or f.startswith("."): continue
|
|
122
111
|
|
|
123
112
|
fullpath = os.path.join(root, f)
|
|
@@ -128,7 +117,6 @@ def build_index(silent=True):
|
|
|
128
117
|
stats = os.stat(fullpath)
|
|
129
118
|
current_mtime = stats.st_mtime
|
|
130
119
|
|
|
131
|
-
# Optimization: Skip if file hasn't changed since last scan
|
|
132
120
|
if fullpath in existing_files and abs(existing_files[fullpath] - current_mtime) < 1.0:
|
|
133
121
|
continue
|
|
134
122
|
|
|
@@ -149,7 +137,6 @@ def build_index(silent=True):
|
|
|
149
137
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
150
138
|
''', updates)
|
|
151
139
|
|
|
152
|
-
# 6. Cleanup (Remove files that were deleted from disk)
|
|
153
140
|
deleted_paths = set(existing_files.keys()) - seen_paths
|
|
154
141
|
if deleted_paths:
|
|
155
142
|
deleted_list = list(deleted_paths)
|
|
@@ -169,7 +156,6 @@ def search_db(query):
|
|
|
169
156
|
|
|
170
157
|
sql_query = f"%{query}%"
|
|
171
158
|
|
|
172
|
-
# Prioritize exact matches in name, then partial matches
|
|
173
159
|
cursor.execute('''
|
|
174
160
|
SELECT name, path, modified_date
|
|
175
161
|
FROM files
|
sentinel/tools/system_ops.py
CHANGED
|
@@ -52,7 +52,6 @@ def switch_model(provider, model=None):
|
|
|
52
52
|
return f"⚡ Brain updated to [bold green]{provider.upper()}[/bold green] | Model: {model}"
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
# ... (Keep get_clipboard, get_system_stats, run_cmd, kill_process as they were) ...
|
|
56
55
|
def get_clipboard():
|
|
57
56
|
try:
|
|
58
57
|
return pyperclip.paste()
|
sentinel/tools/weather_ops.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sentinel-ai-os
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Autonomous AI Operating System layer
|
|
5
5
|
Author-email: Sam Selvaraj <samselvaraj1801@gmail.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Keywords: ai,agent,automation,llm
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python:
|
|
10
|
+
Requires-Python: <3.14,>=3.9
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Requires-Dist: rich
|
|
@@ -49,6 +49,7 @@ Requires-Dist: comtypes
|
|
|
49
49
|
Requires-Dist: pyperclip
|
|
50
50
|
Requires-Dist: plyer
|
|
51
51
|
Requires-Dist: pyttsx3
|
|
52
|
+
Requires-Dist: sentence-transformers
|
|
52
53
|
Requires-Dist: pillow
|
|
53
54
|
Requires-Dist: opencv-python
|
|
54
55
|
Requires-Dist: SpeechRecognition
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
sentinel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
sentinel/auth.py,sha256=T_cP-0NEtnrWU93vlkKYaqUDRVKCO1PD5T616v_YbyM,1071
|
|
3
|
+
sentinel/cli.py,sha256=nMGUOc5BrBNj9wnzpv8GNt8-c1a19V6RBvtdmD8KFIM,226
|
|
4
|
+
sentinel/main.py,sha256=42ijKl0119pFpXXZipT4BtOI174_KwwkC8XSLwSHt_M,3092
|
|
5
|
+
sentinel/paths.py,sha256=pGFRPkSJyTYWtuDWdN2WAQP0pSS3laUcQZ4pbWWbJps,1549
|
|
6
|
+
sentinel/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
sentinel/core/agent.py,sha256=5kMUcUxscf-_x3iQnEocHLMwus91Je9IFrb2F_ONd20,10882
|
|
8
|
+
sentinel/core/audit.py,sha256=jxRMuATsI8aruPNiKLRbFDCJ9jt5gR8gMjV66YTNJaE,1339
|
|
9
|
+
sentinel/core/cognitive.py,sha256=2GSOeDaVgpVbIIhriThhZkbcs2NvZuu95OHYx9BgOFg,2749
|
|
10
|
+
sentinel/core/config.py,sha256=VclsNdQY8SEWPnGACBUB1rY0bR4CFndLQjrTVgTiPjU,3006
|
|
11
|
+
sentinel/core/llm.py,sha256=xPX16CqRSaEwoyaTnibIAyRVO_XDGzk3lClZP20EYTc,5536
|
|
12
|
+
sentinel/core/registry.py,sha256=mzQ1fKkDptTEF7DQtmcdF_qUi5qM8y354bWLEofqawU,11061
|
|
13
|
+
sentinel/core/scheduler.py,sha256=eHn9tCJ9k2bB1EPhXikUSK0ueJdOfZerIij4M_hcSLc,1596
|
|
14
|
+
sentinel/core/schema.py,sha256=T73XBZoJ59Q0DvHQZjnZ8NYGVPGGYFlTiY_7lhid6rg,245
|
|
15
|
+
sentinel/core/setup.py,sha256=XF4LSJTRCXZ6wgPfJ85ppTJjPrND3obGHDODdiiAef8,3526
|
|
16
|
+
sentinel/core/ui.py,sha256=xqpqAdCTDns8khvmrfV_YzNeNIraNn1jOr5vxa7ri3o,4326
|
|
17
|
+
sentinel/scripts/factory_reset.bat,sha256=acO2CcZSWz_wmTIN0k_xEHxLHIU_lyJlkdI5q4S0TuU,334
|
|
18
|
+
sentinel/scripts/wipe_vector.bat,sha256=0i4AXuvUZbS6epUtY3rFqGkPtSbUTj19YNXZ6BIyKAo,214
|
|
19
|
+
sentinel/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
sentinel/tools/apps.py,sha256=WfEGE-nX0IbN1tual6ioDxjxCtVfpXGTQD4LGtfMYVc,15088
|
|
21
|
+
sentinel/tools/audio.py,sha256=XYbWUGc1IDSivYZTQo5X9NI_KKeKdiHWdy3UOqgpDQ0,891
|
|
22
|
+
sentinel/tools/browser.py,sha256=FLi44NpZzaDQ3khkbjb1XCvHqtcrY_7csNaekjjlC24,2065
|
|
23
|
+
sentinel/tools/calendar_ops.py,sha256=ru57V_DE8nH__gAPwGMuN2FsdDr73uLtTSr5gq6qRg8,4848
|
|
24
|
+
sentinel/tools/clock.py,sha256=T-v3Flh6oQk-lK8gsG3HlPuu07stK-s94naJvBxRn7Y,984
|
|
25
|
+
sentinel/tools/context.py,sha256=PsecDYDoDQJtWXldkJ8ne8cwOz6xRdPtL3imx4Ms9tY,1099
|
|
26
|
+
sentinel/tools/desktop.py,sha256=0ApWHU-Qxkmf_e0nRq2m3c40MWluCb4VmLS4du_-4fs,2907
|
|
27
|
+
sentinel/tools/email_ops.py,sha256=mEg6xL-wDRk6-Eid44hXDGtiSIITT06IKmQrCExDk7o,2251
|
|
28
|
+
sentinel/tools/factory.py,sha256=_XwOzqsd4AhmZyRuPfT9p-26kza2oyQiAH7GjPXg0vM,4203
|
|
29
|
+
sentinel/tools/file_ops.py,sha256=7nFexEgS9BUTkGJVHzdyRFzwBvNih0DQrqURpgNboWg,2491
|
|
30
|
+
sentinel/tools/flights.py,sha256=7I7mbqxeV8ALzq7j5-ume71ila7_2auuyFaqAaHTb00,1813
|
|
31
|
+
sentinel/tools/gmail_auth.py,sha256=ePLsSGdQAjThANKkh_Qg06y4gYp-Z-K2K5Ox2v8tf0k,1253
|
|
32
|
+
sentinel/tools/indexer.py,sha256=8hnxLx0NPx0MLIAtutYST3CRS88YOXHPBgYoxBKU8Do,5250
|
|
33
|
+
sentinel/tools/installer.py,sha256=ItCKH2SlW019_D4K-3hbhuKAEmStFnUg7cF2WxeLIVY,1723
|
|
34
|
+
sentinel/tools/macros.py,sha256=iLhn781z4XObfhd0bJvaScRkL9fMIRk8_MiFrea3sA0,1578
|
|
35
|
+
sentinel/tools/memory_ops.py,sha256=ZNbtfuX6kfu0eMHsS_w5W50cUpJXGJ-gK_mwx1r6K5M,8123
|
|
36
|
+
sentinel/tools/navigation.py,sha256=8tB37xUIcO-57JlHa5bzuShffyhYoGNoJkAXOQQ5Cos,3282
|
|
37
|
+
sentinel/tools/notes.py,sha256=5dIGU9UfIriNG0KQmusgODneZNRGhMxr0lumoQJyUtA,1979
|
|
38
|
+
sentinel/tools/office.py,sha256=qoICUPdmhDjnBbgV0FJHQrihTXD7PyRSLgC-bt27Fdk,2177
|
|
39
|
+
sentinel/tools/organizer.py,sha256=zUabr6CRXOV5LAGYqFlB021AvSzh1K5dojLlxTZ_sUA,5191
|
|
40
|
+
sentinel/tools/smart_index.py,sha256=-hdeoUvLQiF07l1UF1cMxJp5czrXHxaVkYtGy8bkiyE,1799
|
|
41
|
+
sentinel/tools/sql_index.py,sha256=6jumv6Fg7KUQL0shWlY87u4o3hKiEWGMZB3N4VEngkk,5309
|
|
42
|
+
sentinel/tools/system_ops.py,sha256=1KK3oVM2tp5-ehL46nS1kXBtjr8ZH9gy7XuvOZAlJ80,2723
|
|
43
|
+
sentinel/tools/vision.py,sha256=VfCSiZ-RJvqxQWm1jl6xfVdJv-OB6MHvt8OmEujn4CY,2582
|
|
44
|
+
sentinel/tools/weather_ops.py,sha256=7YD5Ohf7qQSiC8Kl6zDn4UmJZ-8XytyuHpKfNBeLfsc,1698
|
|
45
|
+
sentinel_ai_os-1.0.2.dist-info/licenses/LICENSE,sha256=40oa9ZxNrrfZI6ezONZ1Mhm8uIaXviB4sC6GOFPTDPk,1088
|
|
46
|
+
sentinel_ai_os-1.0.2.dist-info/METADATA,sha256=RwBqH7hnC8MvqyNac1qxlaYwXBOWLqYidtzuq8pZQrk,11570
|
|
47
|
+
sentinel_ai_os-1.0.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
48
|
+
sentinel_ai_os-1.0.2.dist-info/entry_points.txt,sha256=lM13uXDC1VH0W4OITOhMkcxEJVyF_Xlw3rJEsgDs0nM,47
|
|
49
|
+
sentinel_ai_os-1.0.2.dist-info/top_level.txt,sha256=tffn0oUCkyTZs6yuGcwFCZJxRUrJi_wFmpKwWBYlzVE,9
|
|
50
|
+
sentinel_ai_os-1.0.2.dist-info/RECORD,,
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
sentinel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
sentinel/auth.py,sha256=T_cP-0NEtnrWU93vlkKYaqUDRVKCO1PD5T616v_YbyM,1071
|
|
3
|
-
sentinel/cli.py,sha256=nMGUOc5BrBNj9wnzpv8GNt8-c1a19V6RBvtdmD8KFIM,226
|
|
4
|
-
sentinel/main.py,sha256=42ijKl0119pFpXXZipT4BtOI174_KwwkC8XSLwSHt_M,3092
|
|
5
|
-
sentinel/paths.py,sha256=IeG4p4nUrJDrS7wpp7c2lM2hk3HHzBITVxqzUpf8K3k,2551
|
|
6
|
-
sentinel/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
sentinel/core/agent.py,sha256=Z4qK1r3-xGz8PZHJj7q49Fb-h0TrInrDcYHiBYQVVj0,11431
|
|
8
|
-
sentinel/core/audit.py,sha256=EgtNeDY76ZKFufMTuH3yqnUBXyI6uEo_-s6P5PBqdq4,1453
|
|
9
|
-
sentinel/core/cognitive.py,sha256=2GSOeDaVgpVbIIhriThhZkbcs2NvZuu95OHYx9BgOFg,2749
|
|
10
|
-
sentinel/core/config.py,sha256=LaeSoDIc0YpRZEAgwf3d0Pi1wcnep2yPtiqH9U_KiVY,3296
|
|
11
|
-
sentinel/core/llm.py,sha256=qzxtZyRsx8ac_gcy9BcZncD41ZP617OIbF9Iirw0z60,5917
|
|
12
|
-
sentinel/core/registry.py,sha256=Holt9TLwK7Wks_uhMk6VkyNsnOIVKqynKV57nagtPHU,11497
|
|
13
|
-
sentinel/core/scheduler.py,sha256=s_WynyY1P2pxUq-v72bfDg8285talEZU0NBt1nPyF4Y,1807
|
|
14
|
-
sentinel/core/schema.py,sha256=x3oabPJIP3o2fH8cQo6BMCyoFJ65RvqqY5fHFGrzRuM,349
|
|
15
|
-
sentinel/core/setup.py,sha256=P0Lh-PMXa0ULWSjVQ84iCG4PIB0Push1gdWZe5WKpRY,3573
|
|
16
|
-
sentinel/core/ui.py,sha256=ZjiUWUgGtTmuoVwS2fa_LFA6TsCQ9eFFoAWX-9w0XSQ,4381
|
|
17
|
-
sentinel/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
sentinel/tools/apps.py,sha256=YN5kIPNV1M4UYH5FbQGCwWNyVMlLAz9_B9ibKfJm2lE,16667
|
|
19
|
-
sentinel/tools/audio.py,sha256=UFEZvxtaLT243-bwvrBPouDqaUX_I9w-yf6MJiGJpfc,934
|
|
20
|
-
sentinel/tools/browser.py,sha256=qi9tWNtssUgEbodpaG_18c4CoibddW9GwJYlS8pSUmQ,2168
|
|
21
|
-
sentinel/tools/calendar_ops.py,sha256=ru57V_DE8nH__gAPwGMuN2FsdDr73uLtTSr5gq6qRg8,4848
|
|
22
|
-
sentinel/tools/clock.py,sha256=T-v3Flh6oQk-lK8gsG3HlPuu07stK-s94naJvBxRn7Y,984
|
|
23
|
-
sentinel/tools/context.py,sha256=9cmy-rs_BdbtYMlrTYW3xu2nIpH7EEKbKRZdzRmU2T4,1194
|
|
24
|
-
sentinel/tools/desktop.py,sha256=0ApWHU-Qxkmf_e0nRq2m3c40MWluCb4VmLS4du_-4fs,2907
|
|
25
|
-
sentinel/tools/email_ops.py,sha256=mEg6xL-wDRk6-Eid44hXDGtiSIITT06IKmQrCExDk7o,2251
|
|
26
|
-
sentinel/tools/factory.py,sha256=FsgVJrqd0WkNsS7yojPu_4uQ_fLKAmMiTMtqcbahCFA,4520
|
|
27
|
-
sentinel/tools/file_ops.py,sha256=aXUmwp-UIRNBPvQDXvXOKeNGDLJ_trgbvyzvPu2Qq_I,2568
|
|
28
|
-
sentinel/tools/flights.py,sha256=Hq7uLo_6RI7ymOITD0TZKypUdr0uYnJtGXBliScLL08,1822
|
|
29
|
-
sentinel/tools/gmail_auth.py,sha256=d9VFjApohlTQsvtCatoxQEtHR0aWRsWO0CQn10dGHvs,1691
|
|
30
|
-
sentinel/tools/indexer.py,sha256=G4I1t8h7FgYKVMP1IIM8EMYhNtQTtOsJvsUiCypwJG8,5645
|
|
31
|
-
sentinel/tools/installer.py,sha256=ItCKH2SlW019_D4K-3hbhuKAEmStFnUg7cF2WxeLIVY,1723
|
|
32
|
-
sentinel/tools/macros.py,sha256=iLhn781z4XObfhd0bJvaScRkL9fMIRk8_MiFrea3sA0,1578
|
|
33
|
-
sentinel/tools/memory_ops.py,sha256=ZNbtfuX6kfu0eMHsS_w5W50cUpJXGJ-gK_mwx1r6K5M,8123
|
|
34
|
-
sentinel/tools/navigation.py,sha256=8tB37xUIcO-57JlHa5bzuShffyhYoGNoJkAXOQQ5Cos,3282
|
|
35
|
-
sentinel/tools/notes.py,sha256=67eD9HQZfbGsYmFm2hQ6OUtS0zQsw_k6NSX7S5n__xM,2019
|
|
36
|
-
sentinel/tools/office.py,sha256=qoICUPdmhDjnBbgV0FJHQrihTXD7PyRSLgC-bt27Fdk,2177
|
|
37
|
-
sentinel/tools/organizer.py,sha256=zUabr6CRXOV5LAGYqFlB021AvSzh1K5dojLlxTZ_sUA,5191
|
|
38
|
-
sentinel/tools/smart_index.py,sha256=pgemHu4JlVdQbUFS7z85ilsH4ZPi0jGfmXqhYvvNcIA,1840
|
|
39
|
-
sentinel/tools/sql_index.py,sha256=xm9ey2Rjy8EnHYRU5mDOwp5s6MspF_s_BGxp84I0SKw,6367
|
|
40
|
-
sentinel/tools/system_ops.py,sha256=2yhDQe6pAb626jbf3FMXcP4fj33c7IIBVaNBPelb81E,2809
|
|
41
|
-
sentinel/tools/vision.py,sha256=VfCSiZ-RJvqxQWm1jl6xfVdJv-OB6MHvt8OmEujn4CY,2582
|
|
42
|
-
sentinel/tools/weather_ops.py,sha256=e32FCR9HtE-DqlVJIb6QgPqoNoUd7esE0Fut5p1DygA,1774
|
|
43
|
-
sentinel_ai_os-1.0.1.dist-info/licenses/LICENSE,sha256=40oa9ZxNrrfZI6ezONZ1Mhm8uIaXviB4sC6GOFPTDPk,1088
|
|
44
|
-
sentinel_ai_os-1.0.1.dist-info/METADATA,sha256=GXjFtljcGPluQXZVkWsY1rQuOAwi-MZqYLAwx4slpO4,11515
|
|
45
|
-
sentinel_ai_os-1.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
46
|
-
sentinel_ai_os-1.0.1.dist-info/entry_points.txt,sha256=lM13uXDC1VH0W4OITOhMkcxEJVyF_Xlw3rJEsgDs0nM,47
|
|
47
|
-
sentinel_ai_os-1.0.1.dist-info/top_level.txt,sha256=tffn0oUCkyTZs6yuGcwFCZJxRUrJi_wFmpKwWBYlzVE,9
|
|
48
|
-
sentinel_ai_os-1.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|