import { spawn, execSync } from "node:child_process"; import { OLLAMA_URL } from "../config.js"; import { getActiveChatModel, getActiveAutocompleteModel, } from "../runtime-config.js"; import { log, warn, err } from "../log.js"; import { commandExists, runPassthrough } from "../util.js"; async function ollamaHealthy(): Promise { try { const res = await fetch(`${OLLAMA_URL}/api/tags`); return res.ok; } catch { return false; } } async function waitForOllama(timeoutSec: number): Promise { process.stdout.write(" Waiting for Ollama"); for (let i = 0; i < timeoutSec; i++) { if (await ollamaHealthy()) { console.log(" ready!"); return true; } process.stdout.write("."); await new Promise((r) => setTimeout(r, 1000)); } console.log(" timed out."); return false; } export async function ensureOllama(): Promise { if (await ollamaHealthy()) return; if (!commandExists("ollama")) { log("Ollama not found. Installing via Homebrew..."); runPassthrough("brew install ollama"); } log("Starting Ollama..."); const child = spawn("ollama", ["serve"], { stdio: "ignore", detached: true, env: { ...process.env, OLLAMA_HOST: "127.0.0.1" }, }); child.unref(); const ready = await waitForOllama(15); if (!ready) { err("Ollama failed to start. Run: ollama serve"); } } export function isModelPulled(ollamaTag: string): boolean { try { const output = execSync("ollama list", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }); return output.includes(ollamaTag); } catch { return false; } } export async function pullIfNeeded(ollamaTag: string, label: string): Promise { if (isModelPulled(ollamaTag)) { log(`${label} already pulled: ${ollamaTag}`); return; } log(`Pulling ${label}: ${ollamaTag} (this may take a while)...`); runPassthrough(`ollama pull ${ollamaTag}`); } export async function startServers(): Promise { await ensureOllama(); const chatModel = getActiveChatModel(); const autoModel = getActiveAutocompleteModel(); await pullIfNeeded(chatModel.ollamaTag, `chat model (${chatModel.name})`); await pullIfNeeded(autoModel.ollamaTag, `autocomplete model (${autoModel.name})`); log("Ollama is running. Models are pulled and ready."); } export function stopServers(): void { try { execSync("pkill -f ollama", { stdio: "ignore" }); log("Ollama stopped."); } catch { warn("Ollama not running."); } }