fix(ncp): find Ollama on PATH, not only the bundled copy

'ncp doctor' reported a missing Ollama binary on Windows, where winget installs
it to %LOCALAPPDATA%\Programs and on PATH rather than into the repo; the same
assumption made 'ncp models install' refuse to run there. Adds ollama_bin()
mirroring _ollama_bin() in ollama_manager.py.

Also renames wait_for_port's early message to READY - 'already running' for a
service the same command just started reads like a stale process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jon
2026-07-22 15:49:18 -05:00
co-authored by Claude Opus 4.8
parent 6755a2477d
commit 97d7c3cb14
+25 -6
View File
@@ -64,6 +64,19 @@ def http_ok(url: str, timeout: float = 1.0) -> bool:
return False
def ollama_bin() -> str | None:
"""The bundled binary if we have one, else whatever is on PATH.
Only Linux ships a copy in the repo (bin/fetch-ollama.sh); on Windows the
installer gets Ollama from winget, which puts it in %LOCALAPPDATA%\\Programs
and on PATH. Checking only the bundled path made `ncp doctor` report a red
"Ollama binary missing" on every Windows box, and made `ncp models install`
refuse to run there at all. Mirrors _ollama_bin() in ollama_manager.py."""
if OLLAMA_BIN.exists():
return str(OLLAMA_BIN)
return shutil.which("ollama")
def npm() -> str | None:
"""npm, resolving through nvm. The bash version sourced ~/.nvm/nvm.sh; a
non-login shell has neither, so fall back to globbing the nvm install."""
@@ -191,7 +204,11 @@ def check(svc: Service) -> bool:
def wait_for_port(svc: Service, timeout: int = 30) -> bool:
if http_ok(svc.url):
print(f" {svc.label} already running (:{svc.port})")
# "READY", not "already running": `ncp start` launches memory and backend
# together and only then waits on each, so by the time the backend's turn
# comes it is normally up - and reporting "already running" for a service
# this same command started two seconds ago reads like a stale process.
print(f" {svc.label} READY (:{svc.port})")
return True
for _ in range(timeout):
time.sleep(1)
@@ -477,8 +494,9 @@ def cmd_doctor() -> None:
"Memory database directory writable", "Memory database directory not writable")
print("\nChecking Ollama...")
mark(OLLAMA_BIN.exists(), f"Ollama binary found ({OLLAMA_BIN})",
f"Ollama binary missing at {OLLAMA_BIN}")
_obin = ollama_bin()
mark(bool(_obin), f"Ollama binary found ({_obin})",
f"Ollama binary missing (not at {OLLAMA_BIN}, not on PATH)")
mark(OLLAMA_MODELS_DIR.is_dir(), f"Ollama models directory found ({OLLAMA_MODELS_DIR})",
f"Ollama models directory missing at {OLLAMA_MODELS_DIR}")
print()
@@ -575,11 +593,12 @@ def cmd_models(action, name) -> None:
print("Usage: ncp models install <model>")
print("Run 'ncp models available' to see options.")
return
if not OLLAMA_BIN.exists():
print(f"Ollama binary not found at {OLLAMA_BIN}")
obin = ollama_bin()
if not obin:
print(f"Ollama binary not found at {OLLAMA_BIN}, and no 'ollama' on PATH")
return
print(f"Pulling '{name}' into {OLLAMA_MODELS_DIR} ...\n")
subprocess.run([str(OLLAMA_BIN), "pull", name],
subprocess.run([obin, "pull", name],
env={**os.environ, "OLLAMA_MODELS": str(OLLAMA_MODELS_DIR)})
print("\nDone. Run 'ncp models list' to verify.")
else: