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:
Jon Wingender
2026-08-26 13:40:50 -05:00
parent 5f67d19e80
commit a0f033142f
7 changed files with 24 additions and 76 deletions
+21 -55
View File
@@ -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)
+2 -4
View File
@@ -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")
-4
View File
@@ -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"]
-2
View File
@@ -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
-7
View File
@@ -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,
)
]
-2
View File
@@ -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"]
+1 -2
View File
@@ -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