import * as p from "@clack/prompts"; import pc from "picocolors"; import { detectHardware, type HardwareTier } from "./hardware.js"; import { printBuildthisBanner } from "./banner.js"; /** * Probe whether a Nexus instance is running on the given port. * Returns true if the health endpoint responds with HTTP 200 within 2 seconds. */ export async function probeRunningInstance(port: number): Promise { try { const res = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(2000), }); return res.ok; } catch { return false; } } /** * Build the provider selection options based on hardware tier. * Excludes local AI for cpu_only machines (too slow for practical use). */ export function getProviderOptions(tier: HardwareTier): Array<{ value: string; label: string; hint: string }> { const options: Array<{ value: string; label: string; hint: string }> = [ { value: "puter", label: "Puter — free, zero-config", hint: "No API key needed", }, { value: "google", label: "Google — Gemini free tier", hint: "Sign in with Google", }, { value: "apikey", label: "API key — subscription provider", hint: "OpenAI, Anthropic, Groq", }, ]; if (tier !== "cpu_only") { options.push({ value: "local", label: "Local AI (Ollama)", hint: "Private, offline", }); } options.push({ value: "skip", label: "Skip for now", hint: "Set up a provider later", }); return options; } /** * Main bootstrap function. * * Two paths: * 1. Nexus is already running on port 3100 — open the browser * 2. Fresh install — detect hardware, show provider options, print next steps */ export async function bootstrap(): Promise { printBuildthisBanner(); const port = 3100; // Non-TTY guard: prevent hanging in CI/piped environments if (!process.stdin.isTTY || !process.stdout.isTTY) { console.log(pc.bold("Run this command in an interactive terminal.")); console.log(""); console.log(" To open Nexus in your browser:"); console.log(pc.cyan(` http://127.0.0.1:${port}`)); console.log(""); console.log(" To install Nexus on this machine:"); console.log(pc.cyan(" npx paperclipai@latest onboard")); process.exit(0); return; } // Path 1: Running instance detected const isRunning = await probeRunningInstance(port); if (isRunning) { const s = p.spinner(); s.start("Opening Nexus..."); const { default: open } = await import("open"); await open(`http://127.0.0.1:${port}`); s.stop(pc.green("Nexus opened in your browser")); return; } // Path 2: Fresh install — detect hardware and guide setup const s = p.spinner(); s.start("Detecting hardware..."); const hw = await detectHardware(); s.stop("Hardware detected"); // Display hardware info if (hw.tier === "apple_silicon") { p.log.info( `${pc.bold("Apple Silicon detected")} — unified memory (${hw.totalGb} GB), runs entirely on your machine` ); } else if (hw.tier === "gpu") { p.log.info( `${pc.bold("GPU detected")}: ${hw.gpuName} (${hw.gpuVramGb} GB VRAM) — ${hw.totalGb} GB RAM` ); } else { p.log.info( `${pc.bold("CPU-only machine")} (${hw.totalGb} GB RAM) — cloud AI recommended for best experience` ); } // Provider selection const provider = await p.select({ message: "Which AI provider would you like to use?", options: getProviderOptions(hw.tier), }); if (p.isCancel(provider)) { p.cancel("Cancelled"); process.exit(0); return; } // Print next-step instructions console.log(""); console.log(pc.bold("To install and start Nexus:")); console.log(pc.cyan(" npx paperclipai@latest onboard")); console.log(""); if (provider === "local") { console.log(pc.dim("Make sure Ollama is installed: https://ollama.com")); console.log(""); } else if (provider === "puter") { console.log(pc.dim("Puter provides free AI — no setup needed")); console.log(""); } p.outro(pc.bold("Happy building!")); }