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
JavaScript
#!/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);
});