diff --git a/CLAUDE.md b/CLAUDE.md index 65f8619..aa27d74 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,6 +25,17 @@ ncp web # memory :8001 ``` This activates the `Promethean` venv and starts the memory service on port 8001 and the Synapse backend on port 8000 (which also serves the built UI). It additionally starts a **Vite dev server** for frontend hot-reload — a Linux-dev convenience, unlike the single-process Windows/production path where the backend serves `dist/` alone. It does **not** start Ollama. +**macOS (community-supported):** +```bash +./install-macos.sh # one-time: Homebrew packages + venv + web build, via bin/sync.py +./launch_nexus.sh # same script as Linux - it's plain bash, no Linux-only calls +``` +No bundled Ollama binary (Linux x86-64 only) and no XFCE desktop branding — both +already no-op on macOS via `bin/sync.py`'s `linux_stage()`. Ollama is instead the +Homebrew-installed native binary, picked up automatically because +`OllamaManager` falls back to `ollama` on PATH when the bundled binary is +absent; that gets full Metal GPU acceleration with no extra config. + **Individual services via CLI:** ```bash # From nexus-core/ with Promethean venv active: @@ -83,7 +94,7 @@ cd interface/web && npm run build ## Architecture ### Python venv -All Python code runs inside `Promethean/` (a local venv). Always activate it before running backend commands: `source Promethean/bin/activate`. Dependencies are layered: `requirements-base.txt` holds the GPU-agnostic core, and a thin overlay pins the right PyTorch build for the target — `requirements-amd.txt` (ROCm), `requirements-nvidia.txt` (CUDA, generated by `bin/gen-nvidia-reqs.py`), or `requirements-windows.txt` (CPU-only). `bin/sync.py` (`requirements()`) selects NVIDIA, AMD, or CPU/Windows requirements from the host. +All Python code runs inside `Promethean/` (a local venv). Always activate it before running backend commands: `source Promethean/bin/activate`. Dependencies are layered: `requirements-base.txt` holds the GPU-agnostic core, and a thin overlay pins the right PyTorch build for the target — `requirements-amd.txt` (ROCm), `requirements-nvidia.txt` (CUDA, generated by `bin/gen-nvidia-reqs.py`), or `requirements-windows.txt` (CPU-only). `bin/sync.py` (`requirements()`) selects NVIDIA, AMD, CPU/Windows, or — via an explicit `sys.platform == "darwin"` check, since `os.name` alone can't tell macOS apart from Linux — `requirements-base.txt` with no overlay at all for macOS, from the host. ### Synapse Backend (`synapse/`) FastAPI app at `synapse/main.py`. Key responsibilities: diff --git a/README.md b/README.md index 9153726..20189a9 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,8 @@ ncp web Python deps are layered: `requirements-base.txt` (GPU-agnostic core) plus one GPU overlay — `requirements-amd.txt` (ROCm) or `requirements-nvidia.txt` (CUDA). `requirements-windows.txt` is the standalone CPU-only runtime (no base overlay). -`bin/sync.py` picks the right one for the host. +macOS uses `requirements-base.txt` directly, no overlay — see the macOS section +below. `bin/sync.py` picks the right one for the host. `./install.sh` is also the update path — re-run it any time to pull the latest and rebuild. `--check` dry-runs it; `--no-desktop` skips the XFCE panel/theme @@ -172,10 +173,38 @@ The app opens at `:8000`; click **Start AI** to launch Ollama. The installer uses `requirements-windows.txt` (CPU-only, pure-Python — no ML stack, since Ollama does all inference over HTTP). +### macOS + +Community-supported — no bundled Ollama binary or XFCE desktop branding (that +stage is Linux/XFCE-only and already skips itself here), but the +backend/frontend/Ollama stack itself runs natively, no VM or container needed. + +```bash +# 1. Install Homebrew first if you don't have it: https://brew.sh + +# 2. Build everything: Homebrew packages (Python, Node, git, Ollama), venv, +# web UI, memory DB. +./install-macos.sh + +# 3. Launch (memory :8001, backend :8000 — backend also serves the built UI) +./launch_nexus.sh +``` + +Ollama here is the Homebrew-installed native binary, not the Linux-only bundled +one — `OllamaManager` already falls back to `ollama` on PATH when +`ollama/bin/ollama` doesn't exist, so **Start AI** in the sidebar (or `ollama +serve` in a terminal) uses it with full Metal GPU acceleration automatically, +no configuration needed. + +`./install-macos.sh` is also the update path, same idea as Linux — re-run it +any time to pull the latest and rebuild; `--check` dry-runs it. It's a thin +wrapper over `bin/sync.py restore`, the same code Linux and `ncp restore` +(any platform) run. + ### Individual services ```bash -# Linux +# Linux / macOS source Promethean/bin/activate uvicorn synapse.main:sio_app --host 0.0.0.0 --port 8000 --reload # backend (serves the UI too) @@ -201,7 +230,7 @@ Nexus's dependencies out of the system Python. To add a package, activate it and `pip install` as usual: ```bash -# Linux +# Linux / macOS source Promethean/bin/activate pip install ``` @@ -276,7 +305,9 @@ are the exception (YAML files in `data/playbooks/`). All paths are defined in - **Filesystem paths** — `synapse/nexus_config.py` - **Frontend API base URL** — `interface/web/src/config.js` - **Python deps** — `requirements-base.txt` + amd/nvidia GPU overlay; - `requirements-windows.txt` = standalone CPU runtime + `requirements-windows.txt` = standalone CPU runtime; macOS uses + `requirements-base.txt` with no overlay (Ollama, not this venv, does + inference — natively, with Metal) ## Issues and feature requests diff --git a/bin/check.sh b/bin/check.sh index 8a64390..48be6a2 100644 --- a/bin/check.sh +++ b/bin/check.sh @@ -50,7 +50,7 @@ else fi echo "== shell parse ==" -for f in scripts/install-termux.sh launch_nexus.sh management/nexus-cli.sh; do +for f in scripts/install-termux.sh install-macos.sh launch_nexus.sh management/nexus-cli.sh; do [ -f "$f" ] && { bash -n "$f" || fail=1; } done diff --git a/bin/sync.py b/bin/sync.py index ab56a4d..9857939 100644 --- a/bin/sync.py +++ b/bin/sync.py @@ -66,9 +66,11 @@ def ensure_exec_bits() -> None: def linux_stage(script: str, *args) -> None: - """Run one of the Linux-only bash stages. A no-op on Windows, where apt, - xfconf, plank and the rest have nothing to act on.""" - if os.name == "nt": + """Run one of the Linux-only bash stages. A no-op on Windows and macOS, + where apt, xfconf, plank and the rest have nothing to act on. os.name is + 'posix' on both Linux and macOS, so the Windows-only os.name check alone + doesn't exclude macOS - needs the explicit darwin check too.""" + if os.name == "nt" or sys.platform == "darwin": return path = ROOT / "bin" / script bash = shutil.which("bash") @@ -109,6 +111,13 @@ def requirements() -> str: """Pick the PyTorch overlay for this host.""" if os.name == "nt": return "requirements-windows.txt" # CPU / pure-Python, right for native Windows + if sys.platform == "darwin": + # No ROCm/CUDA overlay applies here, and none is needed: Ollama does + # all inference over HTTP (see requirements-ml.txt), and on macOS + # that's a natively-installed, Metal-accelerated Ollama binary + # (OllamaManager falls back to it on PATH - see synapse/ollama_manager.py), + # entirely outside this venv. + return "requirements-base.txt" if shutil.which("nvidia-smi"): return "requirements-nvidia.txt" lspci = shutil.which("lspci") diff --git a/install-macos.sh b/install-macos.sh new file mode 100755 index 0000000..45acc1e --- /dev/null +++ b/install-macos.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# NexusOS installer, macOS. One painless command: +# +# git clone nexus-core && cd nexus-core && ./install-macos.sh +# +# Mirrors install.sh's philosophy: every portable step - git pull, venv, pip +# with the right overlay, npm build - lives in bin/sync.py, shared with Linux +# and Windows. This script only does what sync.py can't do for itself on a +# bare Mac: install the Homebrew packages needed before Python even exists to +# run sync.py with. Re-run any time to update; --check dry-runs it. +# +# Ollama itself is *not* fetched here - bin/fetch-ollama.sh only ships a Linux +# x86-64 binary, and linux_stage() in bin/sync.py already no-ops on macOS, so +# that stage is skipped entirely. The Homebrew `ollama` installed below is +# picked up automatically instead: synapse/ollama_manager.py prefers the +# bundled Linux binary and falls back to whatever `ollama` it finds on PATH, +# which on macOS is this one - with full Metal GPU acceleration, no flags +# needed. The XFCE desktop branding (theme/panel/splash) is Linux-only and +# already gated off macOS the same way; nothing to install for it here. +set -euo pipefail +cd "$(dirname "$0")" + +if ! command -v brew >/dev/null; then + echo "Homebrew is required (it installs Python/Node/git/Ollama)." >&2 + echo "Install it, then re-run this script: https://brew.sh" >&2 + exit 1 +fi + +echo "Installing/checking system packages (python@3.12, node, git, ollama)..." +brew install python@3.12 node git ollama + +py="$(brew --prefix python@3.12)/bin/python3.12" +if [ ! -x "$py" ]; then + echo "python3.12 not found at $py after brew install - check 'brew doctor'." >&2 + exit 1 +fi + +# Prefer the venv interpreter once it exists, same as install.sh; the brewed +# interpreter above is only the bootstrap case on a fresh clone. sync.py is +# stdlib-only either way. +[ -x "Promethean/bin/python" ] && py="Promethean/bin/python" + +exec "$py" bin/sync.py restore "$@"