UNPKG

llmsvg

Version:

Convert images to SVG using Gemini → GPT-5 pipeline. Batch process images with AI: Gemini analyzes images, GPT-5 generates SVG code.

235 lines (198 loc) • 6.49 kB
#!/usr/bin/env node /** * llmsvg - Convert images to SVG using Gemini → GPT-5 pipeline * * A two-stage batch processing tool: * 1. Gemini generates detailed image descriptions * 2. GPT-5 creates SVG code from descriptions */ import dotenv from "dotenv"; import path from "path"; import fs from "fs/promises"; import { fileURLToPath } from "url"; import { execSync } from "child_process"; // Load environment variables dotenv.config(); // Get current directory (ESM equivalent of __dirname) const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Parse command line args const args = process.argv.slice(2); const command = args[0]; /** * Show help information */ function showHelp() { console.log(` llmsvg - Convert images to SVG using AI USAGE: llmsvg <command> [options] COMMANDS: init Set up folder structure <batch-name> Process a batch of images <batch-name> -r Resume an interrupted job help Show this help version, -v, --version Show version WORKFLOW: Stage 1: Gemini 2.5 Pro analyzes images and creates descriptions Stage 2: GPT-5 reads descriptions and generates SVG code QUICK START: 1. Initialize: llmsvg init 2. Configure: cp .env.example .env (add your API keys) 3. Add images: mkdir input/my-batch && cp *.png input/my-batch/ 4. Run: llmsvg my-batch EXAMPLES: llmsvg init # Set up folders llmsvg product-images # Process batch llmsvg product-images -r # Resume interrupted batch FOLDER STRUCTURE: input/<batch-name>/ Your images jobs/<batch-name>/ Job tracking files output/<batch-name>/ Generated results REQUIREMENTS: - Node.js 18+ - Gemini API key - OpenAI API key MORE INFO: https://www.npmjs.com/package/llmsvg `); } /** * Initialize folder structure */ async function initFolders() { console.log("Initializing llmsvg...\n"); const folders = ["input", "jobs", "output"]; for (const folder of folders) { const folderPath = path.join(process.cwd(), folder); try { await fs.mkdir(folderPath, { recursive: true }); console.log(`Created: ${folder}/`); } catch (error) { if (error.code === "EEXIST") { console.log(`Exists: ${folder}/`); } else { console.error(`Failed to create ${folder}/:`, error.message); } } } // Create .env.example if it doesn't exist const envExamplePath = path.join(process.cwd(), ".env.example"); const envExampleContent = `# Gemini API Key (for image descriptions) # Get your key at: https://makersuite.google.com/app/apikey GEMINI_API_KEY=your_gemini_api_key_here # OpenAI API Key (for GPT-5 SVG generation) # Get your key at: https://platform.openai.com/api-keys OPENAI_API_KEY=your_openai_api_key_here # Optional: Custom description prompt for Gemini # DESCRIPTION_PROMPT=Your custom prompt... # Optional: Custom SVG generation prompt for GPT-5 # SVG_PROMPT=Your custom prompt... # Optional: Status check interval in milliseconds (default: 300000 = 5 minutes) # CHECK_INTERVAL_MS=300000 `; try { await fs.writeFile(envExamplePath, envExampleContent); console.log("\nCreated: .env.example"); } catch (error) { console.log("\nExists: .env.example"); } // Create .env if it doesn't exist const envPath = path.join(process.cwd(), ".env"); try { await fs.access(envPath); console.log("Exists: .env"); } catch { // Create .env from .env.example try { await fs.writeFile(envPath, envExampleContent); console.log("Created: .env"); console.log("\nNext steps:"); console.log(" 1. Edit .env and add your API keys"); console.log(" 2. Create a batch folder and add images:"); console.log(" mkdir input/my-batch"); console.log(" 3. Run: llmsvg my-batch"); } catch (error) { console.log("\nWARNING: Could not create .env file!"); console.log("Manually copy: cp .env.example .env"); } } console.log("\nInitialization complete.\n"); } /** * Check if required folders exist */ async function checkFoldersExist() { const requiredFolders = ["input", "jobs", "output"]; const missingFolders = []; for (const folder of requiredFolders) { try { await fs.access(path.join(process.cwd(), folder)); } catch { missingFolders.push(folder); } } return missingFolders; } /** * Main CLI logic */ async function main() { // Handle version command if (command === "--version" || command === "-v" || command === "version") { // Read version from package.json const packageJsonPath = path.join(__dirname, "package.json"); const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8")); console.log(`llmsvg v${packageJson.version}`); process.exit(0); } // Handle help command if ( !command || command === "help" || command === "--help" || command === "-h" ) { showHelp(); process.exit(0); } // Handle init command if (command === "init") { await initFolders(); process.exit(0); } // Check if folders exist before running pipeline const missingFolders = await checkFoldersExist(); if (missingFolders.length > 0) { console.error( "ERROR: Required folders not found:", missingFolders.join(", ") ); console.log("\nRun initialization first: llmsvg init\n"); process.exit(1); } // Check if .env exists try { await fs.access(path.join(process.cwd(), ".env")); } catch { console.error("ERROR: .env file not found!"); console.log("\nCreate .env file with your API keys:"); console.log(" 1. cp .env.example .env"); console.log(" 2. Edit .env and add your Gemini and OpenAI API keys\n"); process.exit(1); } // Run the auto pipeline with all args const autoScript = path.join(__dirname, "auto.js"); try { execSync(`node "${autoScript}" ${args.join(" ")}`, { stdio: "inherit", cwd: process.cwd(), }); } catch (error) { process.exit(error.status || 1); } } main().catch((error) => { console.error("\nšŸ’„ Fatal error:", error.message); process.exit(1); });