mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-16 21:54:56 +00:00
The web command crashed because postinstall scripts never copied the app/dist directory from the platform package. Added copyAppDir() to both postinstall scripts, multi-path resolution in findAppDir() and server static serving, and updated branding to use local favicon. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
226 lines
6.5 KiB
JavaScript
226 lines
6.5 KiB
JavaScript
#!/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)
|
||
})
|