fix(packaging): remove the memory-service integration this CLI reintroduced
synapse/memory/service.py was deleted on 2026-08-25 when memory curation moved in-process (curator.py) - there is no longer a second FastAPI app to run on :8001. This CLI was evidently built against a pre-curator baseline: `nexus serve` spawned `synapse.memory.service:app` (fails with ModuleNotFoundError, logged only to memory.log where nobody would see it), `nexus start memory`/`stop memory` had no handler at all (silently fell through to show_help()), and doctor/status/monitor all carried a "memory service" row that could never be anything but down. Removed rather than repaired, since there's nothing to repair: the service, its SERVICES entry, --memory-port/--no-memory, the -m/--memory target everywhere it was offered (start/stop/logs/LEGACY_TARGETS), and the memory_port/memory_url settings this PR had added. The `nexus memory list|add|rm` data commands (nexus_api.py, hitting the backend's own /memory REST endpoint) are untouched - unrelated, and still work.
This commit is contained in:
+21
-55
@@ -24,10 +24,8 @@ from . import ncp as services
|
||||
CONFIG_SCHEMA = {
|
||||
"home": "path",
|
||||
"api_url": "url",
|
||||
"memory_url": "url",
|
||||
"bind_host": "text",
|
||||
"backend_port": "port",
|
||||
"memory_port": "port",
|
||||
"provider": "provider",
|
||||
"provider_url": "url",
|
||||
"provider_timeout": "positive_int",
|
||||
@@ -39,8 +37,6 @@ CONFIG_SCHEMA = {
|
||||
}
|
||||
|
||||
LEGACY_TARGETS = {
|
||||
"-m": "memory",
|
||||
"--memory": "memory",
|
||||
"-b": "backend",
|
||||
"--backend": "backend",
|
||||
"-f": "frontend",
|
||||
@@ -303,14 +299,13 @@ def diagnostics() -> dict:
|
||||
)
|
||||
add(
|
||||
"service ports",
|
||||
all(1 <= port <= 65535 for port in (settings.backend_port, settings.memory_port)),
|
||||
f"backend={settings.backend_port}, memory={settings.memory_port}",
|
||||
1 <= settings.backend_port <= 65535,
|
||||
f"backend={settings.backend_port}",
|
||||
)
|
||||
for module in ("fastapi", "uvicorn", "httpx", "pydantic", "yaml"):
|
||||
add(f"import:{module}", _check_import(module), module)
|
||||
|
||||
add("backend", _http_ok(settings.api_url + "/status"), settings.api_url, required=False)
|
||||
add("memory service", _http_ok(settings.memory_url + "/"), settings.memory_url, required=False)
|
||||
provider = _provider_payload()
|
||||
add("provider", provider["reachable"], provider["url"], required=False)
|
||||
if settings.manage_ollama:
|
||||
@@ -362,7 +357,7 @@ def cmd_doctor(args) -> int:
|
||||
|
||||
def service_status() -> dict:
|
||||
payload = {}
|
||||
for key in ("backend", "memory", "frontend"):
|
||||
for key in ("backend", "frontend"):
|
||||
svc = services.SERVICES[key]
|
||||
pid = services.read_pid(svc)
|
||||
payload[key] = {
|
||||
@@ -380,7 +375,7 @@ def cmd_status(args) -> int:
|
||||
_emit(payload, True)
|
||||
return 0
|
||||
print("Nexus Service Status:\n")
|
||||
for key in ("backend", "memory", "frontend"):
|
||||
for key in ("backend", "frontend"):
|
||||
info = payload[key]
|
||||
suffix = f" (PID {info['pid']})" if info["pid"] else ""
|
||||
print(f" {key:<10} {'RUNNING' if info['running'] else 'STOPPED'}{suffix} {info['url']}")
|
||||
@@ -400,7 +395,6 @@ def cmd_monitor(args) -> int:
|
||||
|
||||
def _target_flag(target: str | None):
|
||||
return {
|
||||
"memory": "--memory",
|
||||
"backend": "--backend",
|
||||
"frontend": "--frontend",
|
||||
"ai": "--ai",
|
||||
@@ -485,18 +479,14 @@ def cmd_serve(args) -> int:
|
||||
return 2
|
||||
|
||||
settings.backend_port = args.port
|
||||
settings.memory_port = args.memory_port
|
||||
settings.bind_host = host
|
||||
settings.api_url = f"http://127.0.0.1:{args.port}"
|
||||
settings.memory_url = f"http://127.0.0.1:{args.memory_port}"
|
||||
os.environ["NEXUS_BACKEND_PORT"] = str(args.port)
|
||||
os.environ["NEXUS_MEMORY_PORT"] = str(args.memory_port)
|
||||
os.environ["NEXUS_BIND_HOST"] = host
|
||||
for origin_host in ("localhost", "127.0.0.1"):
|
||||
for port in (args.port, args.memory_port):
|
||||
origin = f"http://{origin_host}:{port}"
|
||||
if origin not in config.ALLOWED_ORIGINS:
|
||||
config.ALLOWED_ORIGINS.append(origin)
|
||||
origin = f"http://{origin_host}:{args.port}"
|
||||
if origin not in config.ALLOWED_ORIGINS:
|
||||
config.ALLOWED_ORIGINS.append(origin)
|
||||
if args.allow_lan:
|
||||
# Widen to the addresses this bind actually answers on - NOT "*".
|
||||
# ALLOWED_HOSTS drives TrustedHostMiddleware, which is the DNS-rebinding
|
||||
@@ -508,10 +498,9 @@ def cmd_serve(args) -> int:
|
||||
for name in names:
|
||||
if name not in config.ALLOWED_HOSTS:
|
||||
config.ALLOWED_HOSTS.append(name)
|
||||
for port in (args.port, args.memory_port):
|
||||
origin = f"http://{_origin_host(name)}:{port}"
|
||||
if origin not in config.ALLOWED_ORIGINS:
|
||||
config.ALLOWED_ORIGINS.append(origin)
|
||||
origin = f"http://{_origin_host(name)}:{args.port}"
|
||||
if origin not in config.ALLOWED_ORIGINS:
|
||||
config.ALLOWED_ORIGINS.append(origin)
|
||||
os.environ.setdefault("NEXUS_ALLOWED_HOSTS", ",".join(config.ALLOWED_HOSTS))
|
||||
os.environ.setdefault("NEXUS_ALLOWED_ORIGINS", ",".join(config.ALLOWED_ORIGINS))
|
||||
print(
|
||||
@@ -520,35 +509,13 @@ def cmd_serve(args) -> int:
|
||||
" has full admin and data access."
|
||||
)
|
||||
|
||||
memory_proc = None
|
||||
memory_log = None
|
||||
try:
|
||||
if not args.no_memory and not _http_ok(settings.memory_url + "/"):
|
||||
log_path = settings.runtime_dir / "memory.log"
|
||||
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
memory_log = open(log_path, "ab")
|
||||
memory_proc = subprocess.Popen(
|
||||
[sys.executable, "-m", "uvicorn", "synapse.memory.service:app",
|
||||
"--host", host, "--port", str(args.memory_port)],
|
||||
stdout=memory_log, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL,
|
||||
)
|
||||
print(f"Memory service starting on {host}:{args.memory_port} (log: {log_path})")
|
||||
print(f"NexusOS serving on http://{host}:{args.port}")
|
||||
import uvicorn
|
||||
uvicorn.run(
|
||||
"synapse.main:sio_app", host=host, port=args.port,
|
||||
reload=bool(args.reload and settings.source_checkout),
|
||||
log_level=args.log_level,
|
||||
)
|
||||
finally:
|
||||
if memory_proc is not None and memory_proc.poll() is None:
|
||||
memory_proc.terminate()
|
||||
try:
|
||||
memory_proc.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
memory_proc.kill()
|
||||
if memory_log is not None:
|
||||
memory_log.close()
|
||||
print(f"NexusOS serving on http://{host}:{args.port}")
|
||||
import uvicorn
|
||||
uvicorn.run(
|
||||
"synapse.main:sio_app", host=host, port=args.port,
|
||||
reload=bool(args.reload and settings.source_checkout),
|
||||
log_level=args.log_level,
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -608,7 +575,7 @@ def cmd_nvidia_reqs(_args) -> int:
|
||||
|
||||
|
||||
def cmd_logs(args) -> int:
|
||||
keys = ("backend", "memory", "frontend") if args.target == "all" else (args.target,)
|
||||
keys = ("backend", "frontend") if args.target == "all" else (args.target,)
|
||||
paths = [services.SERVICES[key].log_file for key in keys]
|
||||
for path in paths:
|
||||
print(f"=== {path.name} ===")
|
||||
@@ -751,8 +718,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
p = sub.add_parser("serve", help="run NexusOS in the foreground")
|
||||
p.add_argument("--host"); p.add_argument("--port", type=_port, default=settings.backend_port)
|
||||
p.add_argument("--memory-port", type=_port, default=settings.memory_port)
|
||||
p.add_argument("--no-memory", action="store_true"); p.add_argument("--allow-lan", action="store_true")
|
||||
p.add_argument("--allow-lan", action="store_true")
|
||||
p.add_argument("--reload", action="store_true"); p.add_argument("--log-level", default="info")
|
||||
p.set_defaults(fn=cmd_serve)
|
||||
|
||||
@@ -761,7 +727,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
("stop", cmd_stop, "stop background services"),
|
||||
):
|
||||
p = sub.add_parser(name, help=help_text)
|
||||
p.add_argument("target", nargs="?", choices=["all", "backend", "memory", "frontend", "ai"], default="all")
|
||||
p.add_argument("target", nargs="?", choices=["all", "backend", "frontend", "ai"], default="all")
|
||||
p.set_defaults(fn=fn)
|
||||
sub.add_parser("restart", aliases=["refresh"], help="restart all services").set_defaults(fn=cmd_refresh)
|
||||
sub.add_parser("kill", help="force-stop NexusOS-owned processes").set_defaults(fn=lambda _a: services.cmd_kill() or 0)
|
||||
@@ -771,7 +737,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
sub.add_parser("web", help="legacy desktop alias for open").set_defaults(fn=cmd_web)
|
||||
sub.add_parser("panel", help="launch the legacy desktop control panel").set_defaults(fn=cmd_panel)
|
||||
p = sub.add_parser("logs", help="read or follow service logs")
|
||||
p.add_argument("target", nargs="?", choices=["all", "backend", "memory", "frontend"], default="all")
|
||||
p.add_argument("target", nargs="?", choices=["all", "backend", "frontend"], default="all")
|
||||
p.add_argument("--lines", type=int, choices=range(1, 10001), default=50, metavar="1..10000")
|
||||
p.add_argument("--follow", "-f", action="store_true"); p.set_defaults(fn=cmd_logs)
|
||||
sub.add_parser("clean", help="remove runtime logs and stale PID files").set_defaults(fn=cmd_clean)
|
||||
|
||||
@@ -62,7 +62,7 @@ def _provider_payload() -> dict:
|
||||
|
||||
def _service_status() -> dict:
|
||||
payload = {}
|
||||
for key in ("backend", "memory", "frontend"):
|
||||
for key in ("backend", "frontend"):
|
||||
svc = services.SERVICES[key]
|
||||
pid = services.read_pid(svc)
|
||||
payload[key] = {
|
||||
@@ -253,7 +253,6 @@ def collect_snapshot() -> dict:
|
||||
services_payload = _service_status()
|
||||
pids = [
|
||||
services_payload.get("backend", {}).get("pid"),
|
||||
services_payload.get("memory", {}).get("pid"),
|
||||
services_payload.get("frontend", {}).get("pid"),
|
||||
]
|
||||
api = _api_counts(settings.api_url)
|
||||
@@ -268,7 +267,6 @@ def collect_snapshot() -> dict:
|
||||
"recent_tools": _recent_tools(settings.logs_dir / "chat.log"),
|
||||
"paths": {
|
||||
"api_url": settings.api_url,
|
||||
"memory_url": settings.memory_url,
|
||||
"runtime_dir": str(settings.runtime_dir),
|
||||
},
|
||||
}
|
||||
@@ -328,7 +326,7 @@ def render_frame(snapshot: dict, *, width: int | None = None, unicode: bool | No
|
||||
|
||||
lines.append(_row(box, "SERVICES", width))
|
||||
svcs = snapshot.get("services") or {}
|
||||
for key, label in (("backend", "backend"), ("memory", "memory"), ("frontend", "frontend")):
|
||||
for key, label in (("backend", "backend"), ("frontend", "frontend")):
|
||||
info = svcs.get(key) or {}
|
||||
running = bool(info.get("running"))
|
||||
pid = info.get("pid")
|
||||
|
||||
@@ -146,9 +146,6 @@ def _uvicorn(app: str, port: int):
|
||||
|
||||
|
||||
SERVICES = {
|
||||
"memory": Service("memory", "NEXUS MEMORY SERVICE", settings.memory_port, settings.state_dir,
|
||||
["uvicorn synapse.memory"],
|
||||
lambda: _uvicorn("synapse.memory.service:app", settings.memory_port)),
|
||||
"backend": Service("backend", "NEXUS BACKEND SERVICE", settings.backend_port, settings.state_dir,
|
||||
["uvicorn synapse.main"],
|
||||
lambda: _uvicorn("synapse.main:sio_app", settings.backend_port)),
|
||||
@@ -468,7 +465,6 @@ def cmd_kill() -> None:
|
||||
print("Force-killing all Nexus processes...")
|
||||
targets = [
|
||||
(settings.backend_port, "SYNAPSE"),
|
||||
(settings.memory_port, "MEMORY"),
|
||||
(5173, "INTERFACE"),
|
||||
]
|
||||
patterns = ["uvicorn synapse", "npm run dev", "vite --host"]
|
||||
|
||||
@@ -184,8 +184,6 @@ from .memory.store import store, MemoryItem
|
||||
from .playbooks.store import playbook_store, PlaybookItem
|
||||
from .search import needs_web_search, web_search
|
||||
|
||||
MEMORY_SERVICE = settings.memory_url
|
||||
|
||||
app = FastAPI(title="Synapse Backend", version=VERSION)
|
||||
|
||||
# Alias for startup scripts
|
||||
|
||||
@@ -317,13 +317,9 @@ class Settings:
|
||||
"bind_host", "NEXUS_BIND_HOST", "127.0.0.1"
|
||||
))
|
||||
self.backend_port: int = _int_value("backend_port", "NEXUS_BACKEND_PORT", 8000)
|
||||
self.memory_port: int = _int_value("memory_port", "NEXUS_MEMORY_PORT", 8001)
|
||||
self.api_url: str = str(_value(
|
||||
"api_url", "NEXUS_API", f"http://127.0.0.1:{self.backend_port}"
|
||||
)).rstrip("/")
|
||||
self.memory_url: str = str(_value(
|
||||
"memory_url", "NEXUS_MEMORY_URL", f"http://127.0.0.1:{self.memory_port}"
|
||||
)).rstrip("/")
|
||||
|
||||
def as_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
@@ -345,8 +341,6 @@ class Settings:
|
||||
"api_url": self.api_url,
|
||||
"bind_host": self.bind_host,
|
||||
"backend_port": self.backend_port,
|
||||
"memory_port": self.memory_port,
|
||||
"memory_url": self.memory_url,
|
||||
}
|
||||
|
||||
# --- local-access allowlists (shared by the backend + memory FastAPI apps) ---
|
||||
@@ -369,7 +363,6 @@ _LOCAL_ORIGINS = [
|
||||
for h in ("localhost", "127.0.0.1")
|
||||
for p in (
|
||||
_int_value("backend_port", "NEXUS_BACKEND_PORT", 8000),
|
||||
_int_value("memory_port", "NEXUS_MEMORY_PORT", 8001),
|
||||
5173,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -40,12 +40,10 @@ def test_legacy_cli_spellings_remain_compatible():
|
||||
|
||||
assert _normalize_legacy_argv(["start", "-b"]) == ["start", "backend"]
|
||||
assert _normalize_legacy_argv(["stop", "--ai"]) == ["stop", "ai"]
|
||||
assert _normalize_legacy_argv(["logs", "-m", "--follow"]) == ["logs", "memory", "--follow"]
|
||||
assert _normalize_legacy_argv(["backup", "full"]) == ["backup", "--full"]
|
||||
# -f is --follow for `logs`, but --frontend for start/stop. Translating it
|
||||
# for logs turned `logs -f` into a one-shot tail of the frontend log.
|
||||
assert _normalize_legacy_argv(["logs", "-f"]) == ["logs", "-f"]
|
||||
assert _normalize_legacy_argv(["logs", "-m", "-f"]) == ["logs", "memory", "-f"]
|
||||
assert _normalize_legacy_argv(["start", "-f"]) == ["start", "frontend"]
|
||||
assert _normalize_legacy_argv(["restore", "-f"]) == ["restore"]
|
||||
assert _normalize_legacy_argv(["help"]) == ["--help"]
|
||||
|
||||
@@ -22,7 +22,6 @@ def test_render_frame_contains_sections():
|
||||
"version": "0.0.0",
|
||||
"services": {
|
||||
"backend": {"running": True, "pid": 11, "url": "http://127.0.0.1:8000"},
|
||||
"memory": {"running": False, "pid": None, "url": "http://127.0.0.1:8001"},
|
||||
"frontend": {"running": False, "pid": None, "url": "http://127.0.0.1:5173"},
|
||||
"provider": {
|
||||
"provider": "ollama",
|
||||
@@ -55,7 +54,7 @@ def test_render_frame_contains_sections():
|
||||
assert "DATA / TOOLS" in frame
|
||||
assert "RUN TOOLCHAINS" in frame
|
||||
assert "backend" in frame and "UP" in frame
|
||||
assert "memory" in frame and "DOWN" in frame
|
||||
assert "frontend" in frame and "DOWN" in frame
|
||||
assert "run_snippet" in frame
|
||||
assert "ready python" in frame
|
||||
assert "missing rust" in frame
|
||||
|
||||
Reference in New Issue
Block a user