utim-cli 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.
- utim_cli/__init__.py +40 -0
- utim_cli/agent.py +359 -0
- utim_cli/auth.py +208 -0
- utim_cli/backup.py +101 -0
- utim_cli/billing.py +40 -0
- utim_cli/blender_agent.py +1018 -0
- utim_cli/bootstrap.py +324 -0
- utim_cli/client_utils.py +135 -0
- utim_cli/config.py +194 -0
- utim_cli/context_pruner.py +504 -0
- utim_cli/doctor.py +118 -0
- utim_cli/knowledge_graph.py +462 -0
- utim_cli/logger.py +121 -0
- utim_cli/mcp_clean_wrapper.py +55 -0
- utim_cli/mcp_client.py +198 -0
- utim_cli/mcp_registry.json +1102 -0
- utim_cli/orchestrator.py +3209 -0
- utim_cli/reflection.py +200 -0
- utim_cli/report.py +100 -0
- utim_cli/scrapy_search.py +229 -0
- utim_cli/share.py +320 -0
- utim_cli/share_tui.py +554 -0
- utim_cli/situational_scoring.py +269 -0
- utim_cli/state.py +15 -0
- utim_cli/tools.py +3381 -0
- utim_cli/utim.py +4051 -0
- utim_cli/vector_memory.py +629 -0
- utim_cli/workspace.py +33 -0
- utim_cli-1.0.0.dist-info/METADATA +134 -0
- utim_cli-1.0.0.dist-info/RECORD +34 -0
- utim_cli-1.0.0.dist-info/WHEEL +5 -0
- utim_cli-1.0.0.dist-info/entry_points.txt +2 -0
- utim_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
- utim_cli-1.0.0.dist-info/top_level.txt +1 -0
utim_cli/backup.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import json
|
|
4
|
+
import sqlite3
|
|
5
|
+
from utim_cli.logger import log_info, log_error, log_warning
|
|
6
|
+
|
|
7
|
+
BACKUP_DIR = ".utim_backup"
|
|
8
|
+
UTIM_DIR = ".utim"
|
|
9
|
+
|
|
10
|
+
def is_json_valid(path: str) -> bool:
|
|
11
|
+
"""Check if JSON file exists and is parseable."""
|
|
12
|
+
if not os.path.exists(path):
|
|
13
|
+
return True
|
|
14
|
+
try:
|
|
15
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
16
|
+
json.load(f)
|
|
17
|
+
return True
|
|
18
|
+
except Exception:
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
def is_sqlite_valid(path: str) -> bool:
|
|
22
|
+
"""Check if SQLite DB is valid via PRAGMA integrity_check."""
|
|
23
|
+
if not os.path.exists(path):
|
|
24
|
+
return True
|
|
25
|
+
try:
|
|
26
|
+
conn = sqlite3.connect(path)
|
|
27
|
+
c = conn.cursor()
|
|
28
|
+
c.execute("PRAGMA integrity_check(1)")
|
|
29
|
+
res = c.fetchone()
|
|
30
|
+
conn.close()
|
|
31
|
+
return res is not None and res[0] == "ok"
|
|
32
|
+
except Exception:
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
def is_file_valid(path: str) -> bool:
|
|
36
|
+
"""Determine if a file is not corrupted based on its extension."""
|
|
37
|
+
if path.endswith(".json"):
|
|
38
|
+
return is_json_valid(path)
|
|
39
|
+
elif path.endswith(".db"):
|
|
40
|
+
return is_sqlite_valid(path)
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
def backup_state():
|
|
44
|
+
"""Backup active configuration and database files to .utim_backup/."""
|
|
45
|
+
try:
|
|
46
|
+
if not os.path.exists(UTIM_DIR):
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
os.makedirs(BACKUP_DIR, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
files_to_backup = [
|
|
52
|
+
"config.json", "mcp.json", "utim_local.db",
|
|
53
|
+
"memory.json", "session_state.json"
|
|
54
|
+
]
|
|
55
|
+
for f in files_to_backup:
|
|
56
|
+
src = os.path.join(UTIM_DIR, f)
|
|
57
|
+
dst = os.path.join(BACKUP_DIR, f)
|
|
58
|
+
if os.path.exists(src):
|
|
59
|
+
# Verify file is not corrupted before backing it up
|
|
60
|
+
if not is_file_valid(src):
|
|
61
|
+
log_warning("backup", f"Skipping backup of corrupted file {f}")
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
shutil.copy2(src, dst)
|
|
65
|
+
|
|
66
|
+
log_info("backup", "State backup completed successfully.")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
log_error("backup", f"State backup failed: {e}", e)
|
|
69
|
+
|
|
70
|
+
def restore_state() -> bool:
|
|
71
|
+
"""Restore configuration and database files from backup if corrupt or missing."""
|
|
72
|
+
try:
|
|
73
|
+
if not os.path.exists(BACKUP_DIR):
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
os.makedirs(UTIM_DIR, exist_ok=True)
|
|
77
|
+
restored = False
|
|
78
|
+
|
|
79
|
+
files_to_restore = [
|
|
80
|
+
"config.json", "mcp.json", "utim_local.db",
|
|
81
|
+
"memory.json", "session_state.json"
|
|
82
|
+
]
|
|
83
|
+
for f in files_to_restore:
|
|
84
|
+
src = os.path.join(BACKUP_DIR, f)
|
|
85
|
+
dst = os.path.join(UTIM_DIR, f)
|
|
86
|
+
|
|
87
|
+
# Check if destination is missing or corrupted
|
|
88
|
+
needs_restore = not os.path.exists(dst) or not is_file_valid(dst)
|
|
89
|
+
|
|
90
|
+
if needs_restore and os.path.exists(src) and is_file_valid(src):
|
|
91
|
+
shutil.copy2(src, dst)
|
|
92
|
+
restored = True
|
|
93
|
+
log_info("backup", f"Restored {f} from backup.")
|
|
94
|
+
|
|
95
|
+
if restored:
|
|
96
|
+
log_info("backup", "State backup restored successfully.")
|
|
97
|
+
return restored
|
|
98
|
+
except Exception as e:
|
|
99
|
+
log_error("backup", f"State restoration failed: {e}", e)
|
|
100
|
+
return False
|
|
101
|
+
|
utim_cli/billing.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
class CreditManager:
|
|
5
|
+
def __init__(self, config_path=None):
|
|
6
|
+
if config_path is None:
|
|
7
|
+
config_path = os.path.join(".utim_tmp", "utim_state.json")
|
|
8
|
+
self.config_path = config_path
|
|
9
|
+
self.costs = {
|
|
10
|
+
"gemini": 1, # 1 credit per 1000 tokens
|
|
11
|
+
"claude": 15, # 15 credits per 1000 tokens (more expensive)
|
|
12
|
+
"codex": 10 # 10 credits per 1000 tokens
|
|
13
|
+
}
|
|
14
|
+
self.state = self._load_state()
|
|
15
|
+
|
|
16
|
+
def _load_state(self):
|
|
17
|
+
if os.path.exists(self.config_path):
|
|
18
|
+
with open(self.config_path, "r") as f:
|
|
19
|
+
return json.load(f)
|
|
20
|
+
return {"credits": 0} # Start with 0 free credits
|
|
21
|
+
|
|
22
|
+
def save_state(self):
|
|
23
|
+
dir_name = os.path.dirname(self.config_path)
|
|
24
|
+
if dir_name:
|
|
25
|
+
os.makedirs(dir_name, exist_ok=True)
|
|
26
|
+
with open(self.config_path, "w") as f:
|
|
27
|
+
json.dump(self.state, f, indent=4)
|
|
28
|
+
|
|
29
|
+
def has_credits(self, agent_name, estimate_tokens=1000):
|
|
30
|
+
cost = (estimate_tokens / 1000) * self.costs.get(agent_name, 1)
|
|
31
|
+
return self.state["credits"] >= cost
|
|
32
|
+
|
|
33
|
+
def deduct_credits(self, agent_name, actual_tokens):
|
|
34
|
+
cost = (actual_tokens / 1000) * self.costs.get(agent_name, 1)
|
|
35
|
+
self.state["credits"] -= cost
|
|
36
|
+
self.save_state()
|
|
37
|
+
return cost
|
|
38
|
+
|
|
39
|
+
def get_balance(self):
|
|
40
|
+
return self.state["credits"]
|