#!/usr/bin/env node const { spawn, execSync } = require("child_process") const fs = require("fs") const path = require("path") const RESET = "\x1b[0m" const BOLD = "\x1b[1m" const GREEN = "\x1b[32m" const YELLOW = "\x1b[33m" const RED = "\x1b[31m" const CYAN = "\x1b[36m" const DIM = "\x1b[90m" function log(msg) { console.log(msg) } function logSuccess(msg) { console.log(`${GREEN}✓${RESET} ${msg}`) } function logError(msg) { console.error(`${RED}✗${RESET} ${msg}`) } function logInfo(msg) { console.log(`${CYAN}ℹ${RESET} ${msg}`) } function logWarning(msg) { console.log(`${YELLOW}!${RESET} ${msg}`) } function checkPython() { const commands = ["python3", "python"] for (const cmd of commands) { try { const result = execSync(`${cmd} --version`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }) const match = result.match(/Python (\d+)\.(\d+)/) if (match) { const major = parseInt(match[1]) const minor = parseInt(match[2]) if (major >= 3 && minor >= 10) { return { cmd, version: result.trim() } } } } catch {} } return null } function installPythonDeps(pythonCmd) { return new Promise((resolve, reject) => { const packages = ["toothfairyai", "pydantic", "httpx", "rich"] log(`${DIM}Installing Python packages: ${packages.join(", ")}...${RESET}`) // Try with --user first, then --break-system-packages if needed const args = ["-m", "pip", "install", "--user", ...packages] const proc = spawn(pythonCmd, args, { stdio: "inherit", shell: process.platform === "win32", }) proc.on("close", (code) => { if (code === 0) { resolve() } else { // Try with --break-system-packages flag log(`${DIM}Retrying with --break-system-packages...${RESET}`) const retryArgs = ["-m", "pip", "install", "--user", "--break-system-packages", ...packages] const retry = spawn(pythonCmd, retryArgs, { stdio: "inherit", shell: process.platform === "win32", }) retry.on("close", (retryCode) => { if (retryCode === 0) { resolve() } else { reject(new Error(`pip install exited with code ${retryCode}`)) } }) retry.on("error", (err) => { reject(err) }) } }) proc.on("error", (err) => { reject(err) }) }) } function copyAppDir() { let platform switch (process.platform) { case "darwin": platform = "darwin" break case "linux": platform = "linux" break case "win32": platform = "windows" break default: platform = process.platform } let arch switch (process.arch) { case "x64": arch = "x64" break case "arm64": arch = "arm64" break default: arch = process.arch } const pkgName = `@toothfairyai/tfcode-${platform}-${arch}` const appDir = path.join(__dirname, "app") if (fs.existsSync(path.join(appDir, "dist", "index.html"))) { logSuccess("Web app already present") return } try { const pkgDir = path.dirname(require.resolve(`${pkgName}/package.json`)) const srcAppDir = path.join(pkgDir, "bin", "app") if (!fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) { logWarning("Web app dist not found in platform package") return } fs.cpSync(srcAppDir, appDir, { recursive: true }) logSuccess("Web app copied from platform package") } catch (e) { logWarning(`Could not copy web app: ${e.message}`) } } async function main() { log("") log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`) log(`${BOLD} tfcode - ToothFairyAI's official coding agent${RESET}`) log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`) log("") copyAppDir() // Check for Python logInfo("Checking Python installation...") const python = checkPython() if (!python) { log("") logError("Python 3.10+ is required but not found.") log("") log(`${BOLD}Please install Python 3.10 or later:${RESET}`) log("") log(` ${CYAN}macOS:${RESET} brew install python@3.12`) log(` ${CYAN}Ubuntu:${RESET} sudo apt-get install python3.12`) log(` ${CYAN}Windows:${RESET} Download from https://python.org/downloads`) log("") log(`${DIM}After installing Python, run: npm rebuild tfcode${RESET}`) log("") process.exit(1) } logSuccess(`Found ${python.version} (${python.cmd})`) log("") // Install Python dependencies logInfo("Installing ToothFairyAI Python SDK...") try { await installPythonDeps(python.cmd) logSuccess("Python dependencies installed") } catch (err) { logWarning(`Failed to install Python dependencies: ${err.message}`) log("") log(`${DIM}You can install them manually with:${RESET}`) log(` ${CYAN}${python.cmd} -m pip install toothfairyai pydantic httpx rich${RESET}`) log("") // Don't exit - user might install manually later } log("") log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`) log(`${GREEN}✓ tfcode installed successfully!${RESET}`) log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`) log("") log(`${BOLD}Quick Start:${RESET}`) log("") log(` ${CYAN}1.${RESET} Set your ToothFairyAI credentials:`) log(` ${DIM}export TF_WORKSPACE_ID="your-workspace-id"${RESET}`) log(` ${DIM}export TF_API_KEY="your-api-key"${RESET}`) log("") log(` ${CYAN}2.${RESET} Validate your credentials:`) log(` ${DIM}tfcode validate${RESET}`) log("") log(` ${CYAN}3.${RESET} Sync tools from your workspace:`) log(` ${DIM}tfcode sync${RESET}`) log("") log(` ${CYAN}4.${RESET} Start coding:`) log(` ${DIM}tfcode${RESET}`) log("") log(`${DIM}Documentation: https://toothfairyai.com/developers/tfcode${RESET}`) log("") } main().catch((err) => { logError(`Installation failed: ${err.message}`) process.exit(1) })