cli-memory-os 0.1.0__tar.gz → 0.1.3__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.
- {cli_memory_os-0.1.0/cli_memory_os.egg-info → cli_memory_os-0.1.3}/PKG-INFO +1 -1
- cli_memory_os-0.1.3/cli/commands/ask.py +67 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/benchmark.py +1 -2
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/doctor.py +13 -2
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/import_cmd.py +0 -1
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/init.py +83 -86
- cli_memory_os-0.1.3/cli/commands/restart.py +23 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/status.py +1 -0
- cli_memory_os-0.1.3/cli/commands/sync.py +70 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/workspace.py +0 -2
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/main.py +17 -29
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/parser.py +6 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3/cli_memory_os.egg-info}/PKG-INFO +1 -1
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/SOURCES.txt +6 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/embedder.py +0 -1
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/llm.py +0 -1
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/vector_store.py +0 -49
- cli_memory_os-0.1.3/infrastructure/compose.py +303 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/docker.py +24 -5
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/health.py +53 -14
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/observability.py +0 -1
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/pyproject.toml +1 -1
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/db.py +0 -38
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/tech_detector.py +1 -39
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_cli_integration.py +31 -2
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_commands_phase2.py +24 -20
- cli_memory_os-0.1.3/tests/test_compose_manager.py +155 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_first_time_user_experience.py +2 -1
- cli_memory_os-0.1.3/tests/test_infrastructure_stabilization.py +138 -0
- cli_memory_os-0.1.3/tests/test_sync_ask_cli.py +111 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_v1_features.py +3 -5
- cli_memory_os-0.1.0/infrastructure/compose.py +0 -136
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/LICENSE +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/MANIFEST.in +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/README.md +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/__init__.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/__init__.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/config_cmd.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/export.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/logs.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/monitor.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/plugins.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/start.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/stop.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/version.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/dependency_links.txt +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/entry_points.txt +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/requires.txt +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/top_level.txt +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/__init__.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/base.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/github.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/gmail.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/notion.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/registry.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/__init__.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/chunker.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/context_builder.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/__init__.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/config.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/workspace.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/models/__init__.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/models/memory.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/setup.cfg +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/__init__.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/graph.py +0 -0
- {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/schema.sql +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command: memory-os ask
|
|
3
|
+
|
|
4
|
+
Queries the RAG pipeline with a question.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import sqlite3
|
|
9
|
+
from core.llm import run_hybrid_rag
|
|
10
|
+
from storage.db import init_db
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_and_print_ask(query: str):
|
|
14
|
+
"""Run RAG query pipeline and print the structured result blocks."""
|
|
15
|
+
rag_res = run_hybrid_rag(query)
|
|
16
|
+
|
|
17
|
+
print("========================================")
|
|
18
|
+
print("ANSWER")
|
|
19
|
+
print("========================================")
|
|
20
|
+
print(rag_res["answer"])
|
|
21
|
+
print("\n========================================")
|
|
22
|
+
print("SOURCES")
|
|
23
|
+
print("========================================")
|
|
24
|
+
if rag_res.get("sources"):
|
|
25
|
+
for s in rag_res["sources"]:
|
|
26
|
+
print(f"- {s}")
|
|
27
|
+
else:
|
|
28
|
+
print("None")
|
|
29
|
+
print("\n========================================")
|
|
30
|
+
print("REPOSITORIES USED")
|
|
31
|
+
print("========================================")
|
|
32
|
+
if rag_res.get("repositories"):
|
|
33
|
+
for r in rag_res["repositories"]:
|
|
34
|
+
print(f"- {r}")
|
|
35
|
+
else:
|
|
36
|
+
print("None")
|
|
37
|
+
print("\n========================================")
|
|
38
|
+
confidence = rag_res.get("confidence", 0.0)
|
|
39
|
+
print(f"Confidence: {confidence:.2f}")
|
|
40
|
+
print("========================================")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def execute(args):
|
|
44
|
+
"""Run the ask command parser handler."""
|
|
45
|
+
# Ensure database is initialized
|
|
46
|
+
try:
|
|
47
|
+
init_db()
|
|
48
|
+
except sqlite3.OperationalError as e:
|
|
49
|
+
print("----------------------------------")
|
|
50
|
+
print("Workspace has not been initialized.")
|
|
51
|
+
print("")
|
|
52
|
+
print("Run:")
|
|
53
|
+
print("memory-os init")
|
|
54
|
+
print("----------------------------------")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
if not args.question:
|
|
58
|
+
print("Usage: memory-os ask <question>")
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
|
|
61
|
+
query = " ".join(args.question)
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
run_and_print_ask(query)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(f"❌ Error executing query: {e}")
|
|
67
|
+
sys.exit(1)
|
|
@@ -6,8 +6,7 @@ graph lookups, LLM invocation, and RAG pipelines.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import time
|
|
9
|
-
import
|
|
10
|
-
from infrastructure.health import check_neo4j, check_qdrant, check_groq_api, check_memory_usage
|
|
9
|
+
from infrastructure.health import check_qdrant, check_groq_api, check_memory_usage
|
|
11
10
|
from core.embedder import Embedder
|
|
12
11
|
|
|
13
12
|
|
|
@@ -53,8 +53,19 @@ def execute(args):
|
|
|
53
53
|
"Active Plugins": "No plugins are authenticated. Run 'memory-os init' to set up connector logins.",
|
|
54
54
|
}
|
|
55
55
|
for name, detail in failed:
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if name == "Neo4j" and "Authentication failed" in detail:
|
|
57
|
+
print(" * Neo4j: Neo4j is running but authentication failed.")
|
|
58
|
+
print(" Recommendations:")
|
|
59
|
+
print(" memory-os config set neo4j.user <username>")
|
|
60
|
+
print(" memory-os config set neo4j.password <password>")
|
|
61
|
+
print(" or")
|
|
62
|
+
print(" memory-os init")
|
|
63
|
+
elif name == "Neo4j" and "Unreachable" in detail:
|
|
64
|
+
rec = recommendations.get(name, "Check log file logs/memory_os.log for errors.")
|
|
65
|
+
print(f" * {name}: {rec} (Detail: {detail})")
|
|
66
|
+
else:
|
|
67
|
+
rec = recommendations.get(name, "Check log file logs/memory_os.log for errors.")
|
|
68
|
+
print(f" * {name}: {rec} (Detail: {detail})")
|
|
58
69
|
print("─────────────────────────────")
|
|
59
70
|
else:
|
|
60
71
|
print("\n All checks passed.")
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Command: memory-os init
|
|
3
3
|
|
|
4
|
-
Performs first-time setup and onboarding for Memory-OS.
|
|
5
|
-
Provisions the workspace directory structure, creates config.toml,
|
|
6
|
-
spins up Neo4j and Qdrant via docker compose, initializes SQLite,
|
|
7
|
-
pre-warms the embedding model, and launches Composio toolkit OAuth flows.
|
|
4
|
+
Performs first-time setup and onboarding for Memory-OS using ComposeManager.
|
|
8
5
|
"""
|
|
9
6
|
|
|
10
7
|
import sys
|
|
11
8
|
import uuid
|
|
12
|
-
import time
|
|
13
9
|
import os
|
|
14
|
-
from pathlib import Path
|
|
15
10
|
from infrastructure.workspace import ensure_workspace, get_db_path
|
|
16
11
|
from infrastructure.config import generate_default_config, save_config, load_config
|
|
17
|
-
from infrastructure.
|
|
18
|
-
from infrastructure.
|
|
19
|
-
from infrastructure.health import run_all_checks
|
|
12
|
+
from infrastructure.compose import ComposeManager, wait_for_services
|
|
13
|
+
from infrastructure.health import run_all_checks, check_docker, check_neo4j, check_qdrant
|
|
20
14
|
from storage.db import init_db
|
|
21
15
|
from core.embedder import Embedder
|
|
22
16
|
|
|
23
17
|
|
|
18
|
+
|
|
24
19
|
def get_input(prompt: str, secret: bool = False, default: str = "") -> str:
|
|
25
20
|
"""Prompt the user for input with an optional default value."""
|
|
26
21
|
import getpass
|
|
@@ -38,14 +33,13 @@ def get_input(prompt: str, secret: bool = False, default: str = "") -> str:
|
|
|
38
33
|
|
|
39
34
|
|
|
40
35
|
def execute(args):
|
|
41
|
-
"""Run the init command."""
|
|
36
|
+
"""Run the init command wizard following the exact 11 steps."""
|
|
42
37
|
print("==================================================")
|
|
43
38
|
print("🧠 MEMORY-OS INITIALIZATION WIZARD")
|
|
44
39
|
print("==================================================")
|
|
45
40
|
|
|
46
|
-
# 1
|
|
47
|
-
print("\n[1/
|
|
48
|
-
|
|
41
|
+
# ── Step 1: Check Python ──────────────────────────────────
|
|
42
|
+
print("\n[Step 1/11] Checking Python version...")
|
|
49
43
|
python_ok = sys.version_info >= (3, 12)
|
|
50
44
|
py_ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
51
45
|
if not python_ok:
|
|
@@ -53,52 +47,73 @@ def execute(args):
|
|
|
53
47
|
sys.exit(1)
|
|
54
48
|
print(f" ✓ Python version: {py_ver}")
|
|
55
49
|
|
|
56
|
-
|
|
50
|
+
# ── Step 2: Check Docker daemon ──────────────────────────
|
|
51
|
+
print("\n[Step 2/11] Checking Docker daemon...")
|
|
52
|
+
docker_ok, docker_detail = check_docker()
|
|
57
53
|
if not docker_ok:
|
|
58
|
-
print("----------------------------------")
|
|
59
|
-
print("Docker is not installed or not in PATH.")
|
|
60
|
-
print("")
|
|
61
|
-
print("Please install Docker Desktop and rerun:")
|
|
62
|
-
print("memory-os init")
|
|
63
|
-
print("----------------------------------")
|
|
64
|
-
sys.exit(1)
|
|
65
|
-
print(f" ✓ Docker: {docker_ver}")
|
|
66
|
-
|
|
67
|
-
compose_ok, compose_ver = check_docker_compose_installed()
|
|
68
|
-
if not compose_ok:
|
|
69
|
-
print("----------------------------------")
|
|
70
|
-
print("Docker Compose is not installed.")
|
|
71
|
-
print("")
|
|
72
|
-
print("Please install Docker Compose and rerun:")
|
|
73
|
-
print("memory-os init")
|
|
74
|
-
print("----------------------------------")
|
|
75
|
-
sys.exit(1)
|
|
76
|
-
print(f" ✓ Docker Compose: {compose_ver}")
|
|
77
|
-
|
|
78
|
-
if not check_docker_running():
|
|
79
54
|
print("----------------------------------")
|
|
80
55
|
print("Docker Desktop is not running.")
|
|
81
56
|
print("")
|
|
57
|
+
print("stderr:")
|
|
58
|
+
print(docker_detail)
|
|
59
|
+
print("")
|
|
82
60
|
print("Start Docker Desktop and rerun:")
|
|
83
61
|
print("memory-os init")
|
|
84
62
|
print("----------------------------------")
|
|
85
63
|
sys.exit(1)
|
|
86
64
|
print(" ✓ Docker Daemon: Running")
|
|
87
65
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
print("\n[2/7] Creating workspace directories...")
|
|
66
|
+
# ── Step 3: Create workspace ─────────────────────────────
|
|
67
|
+
print("\n[Step 3/11] Creating workspace directories...")
|
|
91
68
|
ensure_workspace("default")
|
|
92
69
|
print(" ✓ Workspace path initialized at ~/.memory-os")
|
|
93
70
|
|
|
94
|
-
#
|
|
95
|
-
print("\n[
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
71
|
+
# ── Step 4: Generate docker-compose.yml ──────────────────
|
|
72
|
+
print("\n[Step 4/11] Generating docker-compose.yml...")
|
|
73
|
+
from infrastructure.config import get_config
|
|
74
|
+
existing_config = get_config()
|
|
75
|
+
existing_password = existing_config.get("neo4j", {}).get("password") or "memory_neo"
|
|
76
|
+
|
|
77
|
+
manager = ComposeManager()
|
|
78
|
+
manager.generate_compose(profile="default", password=existing_password)
|
|
79
|
+
print(" ✓ docker-compose.yml generated at ~/.memory-os/docker-compose.yml")
|
|
80
|
+
|
|
81
|
+
# ── Step 5: Launch Neo4j + Qdrant ─────────────────────────
|
|
82
|
+
print("\n[Step 5/11] Launching local services (Neo4j, Qdrant)...")
|
|
83
|
+
neo4j_ok, _ = check_neo4j()
|
|
84
|
+
qdrant_ok, _ = check_qdrant()
|
|
85
|
+
if neo4j_ok and qdrant_ok:
|
|
86
|
+
print(" ✓ Neo4j already running")
|
|
87
|
+
print(" ✓ Qdrant already running")
|
|
88
|
+
else:
|
|
89
|
+
if not manager.up(profile="default"):
|
|
90
|
+
print("❌ Failed to start docker compose services.")
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
print(" ✓ Containers launched.")
|
|
93
|
+
|
|
94
|
+
# ── Step 6: Wait until healthy ────────────────────────────
|
|
95
|
+
print("\n[Step 6/11] Waiting for services to become healthy...")
|
|
96
|
+
if neo4j_ok and qdrant_ok:
|
|
97
|
+
print(" ✓ Services are healthy.")
|
|
98
|
+
else:
|
|
99
|
+
if not wait_for_services(timeout=60):
|
|
100
|
+
print("❌ Services failed to start or respond to health checks in time.")
|
|
101
|
+
sys.exit(1)
|
|
102
|
+
print(" ✓ Services are healthy.")
|
|
103
|
+
|
|
104
|
+
# ── Step 7: Initialize SQLite ─────────────────────────────
|
|
105
|
+
print("\n[Step 7/11] Initializing SQLite database...")
|
|
106
|
+
os.environ["MEMORY_OS_DB_PATH"] = str(get_db_path("default"))
|
|
107
|
+
init_db()
|
|
108
|
+
print(" ✓ SQLite database schema initialized.")
|
|
109
|
+
|
|
110
|
+
# ── Step 8: Configure API Keys ───────────────────────────
|
|
111
|
+
print("\n[Step 8/11] Configuring API keys & settings...")
|
|
112
|
+
neo4j_password = get_input("Set Neo4j database password", secret=True, default=existing_password)
|
|
113
|
+
groq_api_key = get_input("Enter Groq API Key", secret=True, default=existing_config.get("groq", {}).get("api_key", ""))
|
|
114
|
+
composio_api_key = get_input("Enter Composio API Key", secret=True, default=existing_config.get("composio", {}).get("api_key", ""))
|
|
99
115
|
|
|
100
|
-
|
|
101
|
-
composio_user_id = str(uuid.uuid4())
|
|
116
|
+
composio_user_id = existing_config.get("composio", {}).get("user_id") or str(uuid.uuid4())
|
|
102
117
|
|
|
103
118
|
answers = {
|
|
104
119
|
"neo4j_password": neo4j_password,
|
|
@@ -109,49 +124,16 @@ def execute(args):
|
|
|
109
124
|
|
|
110
125
|
config_dict = generate_default_config(answers)
|
|
111
126
|
save_config(config_dict)
|
|
112
|
-
|
|
113
|
-
# Force reload config in current process memory
|
|
114
127
|
load_config()
|
|
115
128
|
|
|
116
|
-
|
|
117
|
-
|
|
129
|
+
if neo4j_password != existing_password:
|
|
130
|
+
manager.generate_compose(profile="default", password=neo4j_password)
|
|
131
|
+
print(" ✓ docker-compose.yml updated with new password.")
|
|
118
132
|
|
|
119
133
|
print(" ✓ Configuration saved to ~/.memory-os/config.toml")
|
|
120
134
|
|
|
121
|
-
#
|
|
122
|
-
print("\n[
|
|
123
|
-
if not compose_up():
|
|
124
|
-
print("❌ Failed to start docker compose services.")
|
|
125
|
-
sys.exit(1)
|
|
126
|
-
print(" ✓ Containers launched. Waiting for services to become healthy...")
|
|
127
|
-
|
|
128
|
-
if not wait_for_services(timeout=60):
|
|
129
|
-
print("❌ Services failed to start or respond to health checks in time.")
|
|
130
|
-
sys.exit(1)
|
|
131
|
-
print(" ✓ Services are healthy.")
|
|
132
|
-
|
|
133
|
-
# 5. Schema & Database Initialization
|
|
134
|
-
print("\n[5/7] Initializing SQLite database...")
|
|
135
|
-
init_db()
|
|
136
|
-
print(" ✓ SQLite database schema initialized.")
|
|
137
|
-
|
|
138
|
-
# 6. Optional Embedding Model Warming
|
|
139
|
-
print("\n[6/7] Preparing embedding model...")
|
|
140
|
-
warm_model = get_input("Download and cache embedding model (all-MiniLM-L6-v2) now? (Y/n)", default="y").lower()
|
|
141
|
-
if warm_model == "y":
|
|
142
|
-
print(" Downloading model (this may take a minute)...")
|
|
143
|
-
try:
|
|
144
|
-
embedder = Embedder()
|
|
145
|
-
_ = embedder.model
|
|
146
|
-
print(" ✓ Embedding model cached successfully.")
|
|
147
|
-
except Exception as e:
|
|
148
|
-
print(f" ⚠️ Warning: Failed to cache model during setup: {e}")
|
|
149
|
-
print(" Model will be downloaded automatically on the first query.")
|
|
150
|
-
else:
|
|
151
|
-
print(" Skipped model download.")
|
|
152
|
-
|
|
153
|
-
# 7. Optional Composio Integration OAuth
|
|
154
|
-
print("\n[7/7] Configuring integration connectors...")
|
|
135
|
+
# ── Step 9: Authenticate Composio ────────────────────────
|
|
136
|
+
print("\n[Step 9/11] Configuring integration connectors...")
|
|
155
137
|
setup_connectors = get_input("Configure connectors (GitHub, Gmail, Notion) now? (Y/n)", default="y").lower()
|
|
156
138
|
if setup_connectors == "y" and composio_api_key:
|
|
157
139
|
try:
|
|
@@ -174,7 +156,6 @@ def execute(args):
|
|
|
174
156
|
print(f" 👉 Please open this URL in your browser to complete authentication:\n {req.redirect_url}")
|
|
175
157
|
print(" Waiting up to 30 seconds for completion...")
|
|
176
158
|
try:
|
|
177
|
-
# Wait for active connection
|
|
178
159
|
req.wait_for_connection(timeout=30000)
|
|
179
160
|
print(f" ✓ {name} connected successfully!")
|
|
180
161
|
except Exception:
|
|
@@ -186,8 +167,24 @@ def execute(args):
|
|
|
186
167
|
else:
|
|
187
168
|
print(" Skipped connectors configuration.")
|
|
188
169
|
|
|
189
|
-
#
|
|
190
|
-
print("\n
|
|
170
|
+
# ── Step 10: Warm embedding model ─────────────────────────
|
|
171
|
+
print("\n[Step 10/11] Preparing embedding model...")
|
|
172
|
+
warm_model = get_input("Download and cache embedding model (all-MiniLM-L6-v2) now? (Y/n)", default="y").lower()
|
|
173
|
+
if warm_model == "y":
|
|
174
|
+
print(" Downloading model (this may take a minute)...")
|
|
175
|
+
try:
|
|
176
|
+
embedder = Embedder()
|
|
177
|
+
_ = embedder.model
|
|
178
|
+
print(" ✓ Embedding model cached successfully.")
|
|
179
|
+
except Exception as e:
|
|
180
|
+
print(f" ⚠️ Warning: Failed to cache model during setup: {e}")
|
|
181
|
+
print(" Model will be downloaded automatically on the first query.")
|
|
182
|
+
else:
|
|
183
|
+
print(" Skipped model download.")
|
|
184
|
+
|
|
185
|
+
# ── Step 11: Done ─────────────────────────────────────────
|
|
186
|
+
print("\n[Step 11/11] Running initial health checks...")
|
|
187
|
+
print("==================================================")
|
|
191
188
|
print("🏥 RUNNING INITIAL HEALTH CHECKS")
|
|
192
189
|
print("==================================================")
|
|
193
190
|
checks = run_all_checks()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command: memory-os restart
|
|
3
|
+
|
|
4
|
+
Restarts the Docker services (Neo4j and Qdrant) via ComposeManager.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from infrastructure.compose import ComposeManager, wait_for_services
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def execute(args):
|
|
12
|
+
"""Run the restart command."""
|
|
13
|
+
print("Restarting Memory-OS local services (Neo4j, Qdrant)...")
|
|
14
|
+
if not ComposeManager().restart():
|
|
15
|
+
print("❌ Failed to restart docker compose services.")
|
|
16
|
+
sys.exit(1)
|
|
17
|
+
|
|
18
|
+
print("Waiting for services to become healthy...")
|
|
19
|
+
if not wait_for_services(timeout=60):
|
|
20
|
+
print("❌ Services failed to start or respond to health checks in time.")
|
|
21
|
+
sys.exit(1)
|
|
22
|
+
|
|
23
|
+
print("✓ Services restarted successfully and are healthy.")
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command: memory-os sync
|
|
3
|
+
|
|
4
|
+
Synchronizes data from connected sources (GitHub, Gmail, Notion),
|
|
5
|
+
chunks documents, generates embeddings, and syncs databases.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
import logging
|
|
11
|
+
import sqlite3
|
|
12
|
+
from storage.db import init_db, clear_all
|
|
13
|
+
from core.vector_store import run_reindexing, close_qdrant_client
|
|
14
|
+
from storage.graph import GraphStore
|
|
15
|
+
from connectors.github import sync_github
|
|
16
|
+
from connectors.gmail import sync_gmail
|
|
17
|
+
from connectors.notion import sync_notion
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def execute(args):
|
|
21
|
+
"""Run the synchronization pipeline."""
|
|
22
|
+
# Ensure database is initialized
|
|
23
|
+
try:
|
|
24
|
+
init_db()
|
|
25
|
+
except sqlite3.OperationalError as e:
|
|
26
|
+
print("----------------------------------")
|
|
27
|
+
print("Workspace has not been initialized.")
|
|
28
|
+
print("")
|
|
29
|
+
print("Run:")
|
|
30
|
+
print("memory-os init")
|
|
31
|
+
print("----------------------------------")
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
start_time = time.perf_counter()
|
|
35
|
+
|
|
36
|
+
rebuild = getattr(args, "rebuild", False)
|
|
37
|
+
source = getattr(args, "source", None)
|
|
38
|
+
|
|
39
|
+
if rebuild:
|
|
40
|
+
print("Performing full rebuild reset...")
|
|
41
|
+
clear_all()
|
|
42
|
+
close_qdrant_client()
|
|
43
|
+
run_reindexing()
|
|
44
|
+
GraphStore().clear_graph()
|
|
45
|
+
|
|
46
|
+
# Sync based on source selection
|
|
47
|
+
if source == "github":
|
|
48
|
+
print("Syncing GitHub source...")
|
|
49
|
+
sync_github()
|
|
50
|
+
elif source == "gmail":
|
|
51
|
+
print("Syncing Gmail source...")
|
|
52
|
+
sync_gmail()
|
|
53
|
+
elif source == "notion":
|
|
54
|
+
print("Syncing Notion source...")
|
|
55
|
+
sync_notion()
|
|
56
|
+
else:
|
|
57
|
+
print("Syncing all sources (GitHub, Gmail, Notion)...")
|
|
58
|
+
sync_github()
|
|
59
|
+
sync_gmail()
|
|
60
|
+
sync_notion()
|
|
61
|
+
|
|
62
|
+
print("Running document chunking and vector reindexing...")
|
|
63
|
+
run_reindexing()
|
|
64
|
+
|
|
65
|
+
print("Extracting and syncing knowledge graph relationships...")
|
|
66
|
+
GraphStore().extract_and_sync_graph()
|
|
67
|
+
|
|
68
|
+
duration = time.perf_counter() - start_time
|
|
69
|
+
print(f"\nSync complete. Total Duration: {duration:.2f}s")
|
|
70
|
+
logging.getLogger("main").info(f"Sync completed in {duration:.2f}s")
|
|
@@ -115,7 +115,6 @@ def run_interactive():
|
|
|
115
115
|
close_qdrant_client,
|
|
116
116
|
)
|
|
117
117
|
from core.embedder import Embedder
|
|
118
|
-
from core.llm import run_hybrid_rag
|
|
119
118
|
from storage.graph import GraphStore
|
|
120
119
|
from connectors.github import sync_github
|
|
121
120
|
from connectors.gmail import sync_gmail
|
|
@@ -283,30 +282,11 @@ def run_interactive():
|
|
|
283
282
|
if not arg:
|
|
284
283
|
print("Usage: ask <question>")
|
|
285
284
|
else:
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
print("\n========================================")
|
|
292
|
-
print("SOURCES")
|
|
293
|
-
print("========================================")
|
|
294
|
-
if rag_res["sources"]:
|
|
295
|
-
for s in rag_res["sources"]:
|
|
296
|
-
print(f"- {s}")
|
|
297
|
-
else:
|
|
298
|
-
print("None")
|
|
299
|
-
print("\n========================================")
|
|
300
|
-
print("REPOSITORIES USED")
|
|
301
|
-
print("========================================")
|
|
302
|
-
if rag_res["repositories"]:
|
|
303
|
-
for r in rag_res["repositories"]:
|
|
304
|
-
print(f"- {r}")
|
|
305
|
-
else:
|
|
306
|
-
print("None")
|
|
307
|
-
print("\n========================================")
|
|
308
|
-
print(f"Confidence: {rag_res['confidence']:.2f}")
|
|
309
|
-
print("========================================")
|
|
285
|
+
from cli.commands.ask import run_and_print_ask
|
|
286
|
+
try:
|
|
287
|
+
run_and_print_ask(arg)
|
|
288
|
+
except Exception as e:
|
|
289
|
+
print(f"❌ Error executing query: {e}")
|
|
310
290
|
|
|
311
291
|
elif cmd == "repo-info":
|
|
312
292
|
if not arg:
|
|
@@ -414,6 +394,9 @@ def route_command(args):
|
|
|
414
394
|
elif command == "stop":
|
|
415
395
|
from cli.commands.stop import execute
|
|
416
396
|
execute(args)
|
|
397
|
+
elif command == "restart":
|
|
398
|
+
from cli.commands.restart import execute
|
|
399
|
+
execute(args)
|
|
417
400
|
elif command == "plugins":
|
|
418
401
|
from cli.commands.plugins import execute
|
|
419
402
|
execute(args)
|
|
@@ -438,11 +421,16 @@ def route_command(args):
|
|
|
438
421
|
elif command == "logs":
|
|
439
422
|
from cli.commands.logs import execute
|
|
440
423
|
execute(args)
|
|
424
|
+
elif command == "sync":
|
|
425
|
+
from cli.commands.sync import execute
|
|
426
|
+
execute(args)
|
|
427
|
+
elif command == "ask":
|
|
428
|
+
from cli.commands.ask import execute
|
|
429
|
+
execute(args)
|
|
441
430
|
else:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
print("Run 'memory-os' (no arguments) for the interactive REPL.")
|
|
431
|
+
from cli.parser import build_parser
|
|
432
|
+
build_parser().print_help()
|
|
433
|
+
sys.exit(1)
|
|
446
434
|
|
|
447
435
|
|
|
448
436
|
|
|
@@ -118,6 +118,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
118
118
|
help="Stop Docker services (preserves data)",
|
|
119
119
|
)
|
|
120
120
|
|
|
121
|
+
# ── restart ────────────────────────────────────────────────
|
|
122
|
+
subparsers.add_parser(
|
|
123
|
+
"restart",
|
|
124
|
+
help="Restart Docker services (Neo4j, Qdrant)",
|
|
125
|
+
)
|
|
126
|
+
|
|
121
127
|
# ── version ───────────────────────────────────────────────
|
|
122
128
|
subparsers.add_parser(
|
|
123
129
|
"version",
|
|
@@ -6,6 +6,7 @@ cli/__init__.py
|
|
|
6
6
|
cli/main.py
|
|
7
7
|
cli/parser.py
|
|
8
8
|
cli/commands/__init__.py
|
|
9
|
+
cli/commands/ask.py
|
|
9
10
|
cli/commands/benchmark.py
|
|
10
11
|
cli/commands/config_cmd.py
|
|
11
12
|
cli/commands/doctor.py
|
|
@@ -15,9 +16,11 @@ cli/commands/init.py
|
|
|
15
16
|
cli/commands/logs.py
|
|
16
17
|
cli/commands/monitor.py
|
|
17
18
|
cli/commands/plugins.py
|
|
19
|
+
cli/commands/restart.py
|
|
18
20
|
cli/commands/start.py
|
|
19
21
|
cli/commands/status.py
|
|
20
22
|
cli/commands/stop.py
|
|
23
|
+
cli/commands/sync.py
|
|
21
24
|
cli/commands/version.py
|
|
22
25
|
cli/commands/workspace.py
|
|
23
26
|
cli_memory_os.egg-info/PKG-INFO
|
|
@@ -54,5 +57,8 @@ storage/schema.sql
|
|
|
54
57
|
storage/tech_detector.py
|
|
55
58
|
tests/test_cli_integration.py
|
|
56
59
|
tests/test_commands_phase2.py
|
|
60
|
+
tests/test_compose_manager.py
|
|
57
61
|
tests/test_first_time_user_experience.py
|
|
62
|
+
tests/test_infrastructure_stabilization.py
|
|
63
|
+
tests/test_sync_ask_cli.py
|
|
58
64
|
tests/test_v1_features.py
|