UNPKG

@bramato/openrouter-mock-generator

Version:

AI-powered mock data generator using OpenRouter API with JSON mode support

743 lines • 33.8 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); require("dotenv/config"); const fs_1 = require("fs"); const path_1 = require("path"); const promises_1 = __importDefault(require("readline/promises")); const openrouter_agents_1 = require("openrouter-agents"); const ENV_FILE_PATH = (0, path_1.join)(process.cwd(), '.env'); const ENV_BACKUP_PATH = (0, path_1.join)(process.cwd(), '.env.backup'); // Validation patterns (only for format validation, not content) const VALIDATION_PATTERNS = { AWS_REGION: /^[a-z0-9-]+$/, AWS_S3_BUCKET_NAME: /^[a-z0-9.-]{3,63}$/, DO_SPACES_REGION: /^[a-z0-9-]+$/, DO_SPACES_NAME: /^[a-z0-9-]{3,63}$/, }; class InitWizard { constructor() { this.rl = promises_1.default.createInterface({ input: process.stdin, output: process.stdout, }); this.api = new openrouter_agents_1.OpenRouterAPI(); } async run() { try { console.log('šŸ¤– OpenRouter AI Tools Configuration\\n'); console.log('This wizard will help you set up your OpenRouter API connection.\\n'); // Create backup of existing .env file await this.createBackupIfNeeded(); const config = await this.collectConfiguration(); await this.writeConfiguration(config); console.log('\\nāœ… Configuration completed successfully!'); console.log('\\nšŸš€ You can now use the AI mock generator:'); console.log(' ai-generate-mock <input.json> --count 50'); const hasImageConfig = config.HUGGINGFACE_API_KEY || config.STORAGE_PROVIDER; if (hasImageConfig) { console.log(' ai-generate-mock <input.json> --images --count 20 # With AI image processing'); } console.log(''); } catch (error) { console.error('\\nāŒ Configuration failed:', error instanceof Error ? error.message : 'Unknown error'); // Attempt to restore backup if configuration failed await this.restoreBackupIfNeeded(); process.exit(1); } finally { this.rl.close(); } } async collectConfiguration() { try { // Step 1: API Key const apiKey = await this.getApiKey(); // Step 2: Model Selection const models = await this.selectModels(); // Step 3: Image Processing Configuration (optional) const imageConfig = await this.configureImageProcessing(); return { OPENROUTER_API_KEY: apiKey, OPENROUTER_DEFAULT_MODEL: models.default, OPENROUTER_MOCK_GENERATOR_MODEL: models.mockGenerator, ...imageConfig, }; } catch (error) { throw new Error(`Configuration collection failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async getApiKey() { try { console.log('Step 1: OpenRouter API Key\\n'); console.log('šŸ“Œ Get your API key from: \\x1b[36mhttps://openrouter.ai/settings/keys\\x1b[0m'); console.log(' (Ctrl/Cmd + Click to open in browser)\\n'); const existingKey = this.getExistingApiKey(); if (existingKey) { const keep = await this.askQuestion(`Found existing API key (${this.maskApiKey(existingKey)}). Keep it? (y/n) `); if (keep.toLowerCase() === 'y' || keep.toLowerCase() === 'yes') { return existingKey; } } let apiKey = ''; let attempts = 0; const maxAttempts = 3; while (!apiKey && attempts < maxAttempts) { apiKey = await this.askSecureQuestion('Enter your OpenRouter API key: '); if (!apiKey.trim()) { console.log('āŒ API key cannot be empty.'); attempts++; continue; } // API key accepted as-is, no format validation break; } if (!apiKey) { throw new Error('Failed to get valid API key after maximum attempts'); } return apiKey; } catch (error) { throw new Error(`API key collection failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async selectModels() { try { console.log('\\nStep 2: Model Selection'); console.log('Different services can use different models for optimal performance.\\n'); console.log('šŸ”„ Fetching JSON-capable models from OpenRouter...'); let models = []; try { models = await this.api.fetchAvailableModels(true); console.log(`\\nāœ… Found ${models.length} JSON-capable models\\n`); } catch (error) { console.log(`\\nāš ļø Failed to fetch models from OpenRouter: ${error instanceof Error ? error.message : 'Unknown error'}`); console.log('Using fallback models...\\n'); models = this.api.getFallbackModels(true); } if (models.length === 0) { console.log('āš ļø No models available. Using default configuration.'); return { default: 'anthropic/claude-3.5-sonnet', mockGenerator: undefined, }; } const defaultModel = await this.selectModelFromCategories(models, 'default'); const mockModel = await this.selectModelFromCategories(models, 'mock generator'); return { default: defaultModel, mockGenerator: mockModel, }; } catch (error) { throw new Error(`Model selection failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async selectModelFromCategories(models, serviceType) { console.log(`\\nšŸŽÆ Select model for ${serviceType}:`); const categories = this.categorizeModels(models); let currentIndex = 1; categories.forEach(category => { console.log(`\\nšŸ“ ${category.name}`); console.log(` ${category.description}`); category.models.forEach((model, index) => { console.log(` ${currentIndex++}. ${this.api.formatModelForDisplay(model)}`); }); }); console.log(`\\n ${currentIndex++}. Custom (enter your own model ID)`); console.log(` ${currentIndex++}. Skip (use default: ${categories[0]?.models[0]?.name || 'Claude 3.5 Sonnet'})`); const choice = await this.askQuestion(`\\nChoose model (1-${currentIndex - 1}): `); const choiceNum = parseInt(choice); if (isNaN(choiceNum) || choiceNum < 1 || choiceNum > currentIndex - 1) { console.log('Invalid choice, using default.'); return categories[0]?.models[0]?.id; } // Handle custom and skip options if (choiceNum === currentIndex - 2) { // Custom const customModel = await this.askQuestion('Enter model ID: '); return customModel.trim() || undefined; } if (choiceNum === currentIndex - 1) { // Skip return undefined; } // Find the selected model let selectedModel; let currentModelIndex = 1; for (const category of categories) { for (const model of category.models) { if (currentModelIndex === choiceNum) { selectedModel = model; break; } currentModelIndex++; } if (selectedModel) break; } return selectedModel?.id; } async getHuggingFaceApiKey() { try { console.log('\\nšŸ¤– Hugging Face API Configuration'); console.log('Hugging Face provides the AI models for image generation (FLUX.1-dev, Qwen-Image).\\n'); console.log('šŸ“Œ Create a token at: \\x1b[36mhttps://huggingface.co/settings/tokens\\x1b[0m'); console.log(' • Select "Read" access level'); console.log(' • Enable "Make calls to the serverless Inference API"\\n'); const useHF = await this.askQuestion('Do you want to configure Hugging Face API key? (y/n) '); if (useHF.toLowerCase() !== 'y' && useHF.toLowerCase() !== 'yes') { console.log(' Skipping Hugging Face API key. Image generation will use free tier with rate limits.'); return undefined; } const existingKey = this.getExistingHuggingFaceKey(); if (existingKey) { const keep = await this.askQuestion(`Found existing HF token (${this.maskApiKey(existingKey)}). Keep it? (y/n) `); if (keep.toLowerCase() === 'y' || keep.toLowerCase() === 'yes') { return existingKey; } } let apiKey = ''; let attempts = 0; const maxAttempts = 3; while (!apiKey && attempts < maxAttempts) { apiKey = await this.askSecureQuestion('Enter your Hugging Face token (hf_...): '); if (!apiKey.trim()) { console.log('Skipping Hugging Face token.'); return undefined; } // HuggingFace token accepted as-is, no format validation break; } if (!apiKey) { console.log('āš ļø Maximum attempts reached. Skipping HuggingFace configuration.'); return undefined; } console.log('āœ… Hugging Face token configured! You can now generate images with better rate limits.'); return apiKey; } catch (error) { console.error(`āŒ HuggingFace configuration failed: ${error instanceof Error ? error.message : 'Unknown error'}`); return undefined; } } getExistingHuggingFaceKey() { try { if ((0, fs_1.existsSync)(ENV_FILE_PATH)) { const envContent = (0, fs_1.readFileSync)(ENV_FILE_PATH, 'utf8'); const match = envContent.match(/HUGGINGFACE_API_KEY=(.+)/); return match ? match[1].trim() : null; } return null; } catch (error) { console.warn(`āš ļø Could not read existing HuggingFace key: ${error instanceof Error ? error.message : 'Unknown error'}`); return null; } } categorizeModels(models) { const categories = [ { name: 'Recommended for Mock Generation', description: 'Best models for generating realistic mock data', models: [], }, { name: 'Fast & Economical', description: 'Quick responses at lower cost', models: [], }, ]; const recommendedIds = [ 'anthropic/claude-3.5-sonnet', 'openai/gpt-4.1-nano', 'anthropic/claude-3-haiku', ]; const fastIds = ['openai/gpt-4.1-nano', 'anthropic/claude-3-haiku', 'openai/gpt-3.5-turbo']; models.forEach(model => { if (recommendedIds.includes(model.id)) { categories[0].models.push(model); } else if (fastIds.includes(model.id)) { categories[1].models.push(model); } }); return categories.filter(cat => cat.models.length > 0); } async askQuestion(prompt) { try { const answer = await this.rl.question(prompt); return answer.trim(); } catch (error) { throw new Error(`Input error: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async askSecureQuestion(prompt) { try { // For sensitive inputs, we'll use a more secure approach // Note: In a real implementation, you might want to use a library like 'read' for hidden input console.log('šŸ”’ Secure input mode (input will be visible for now - consider using a secure input method)'); const answer = await this.rl.question(prompt); return answer.trim(); } catch (error) { throw new Error(`Secure input error: ${error instanceof Error ? error.message : 'Unknown error'}`); } } validateInput(type, value) { if (!value || !value.trim()) { return false; } const pattern = VALIDATION_PATTERNS[type]; if (!pattern) { // No pattern defined, accept any non-empty value return true; } return pattern.test(value.trim()); } async createBackupIfNeeded() { try { if ((0, fs_1.existsSync)(ENV_FILE_PATH)) { console.log('šŸ“‹ Creating backup of existing .env file...'); (0, fs_1.copyFileSync)(ENV_FILE_PATH, ENV_BACKUP_PATH); console.log(`āœ… Backup created: ${ENV_BACKUP_PATH}\\n`); } } catch (error) { console.warn(`āš ļø Could not create backup: ${error instanceof Error ? error.message : 'Unknown error'}`); const proceed = await this.askQuestion('Continue without backup? (y/n) '); if (proceed.toLowerCase() !== 'y' && proceed.toLowerCase() !== 'yes') { throw new Error('User cancelled due to backup failure'); } } } async restoreBackupIfNeeded() { try { if ((0, fs_1.existsSync)(ENV_BACKUP_PATH)) { console.log('šŸ”„ Restoring backup due to configuration failure...'); (0, fs_1.copyFileSync)(ENV_BACKUP_PATH, ENV_FILE_PATH); console.log('āœ… Backup restored successfully'); } } catch (error) { console.error(`āŒ Could not restore backup: ${error instanceof Error ? error.message : 'Unknown error'}`); } } getExistingApiKey() { try { if ((0, fs_1.existsSync)(ENV_FILE_PATH)) { const envContent = (0, fs_1.readFileSync)(ENV_FILE_PATH, 'utf8'); const match = envContent.match(/OPENROUTER_API_KEY=(.+)/); return match ? match[1].trim() : null; } return null; } catch (error) { console.warn(`āš ļø Could not read existing .env file: ${error instanceof Error ? error.message : 'Unknown error'}`); return null; } } maskApiKey(key) { if (key.length <= 8) return '****'; return key.substring(0, 4) + '...' + key.substring(key.length - 4); } async configureImageProcessing() { console.log('\\nStep 3: AI Image Processing Configuration (Optional)'); console.log('Configure AI image generation to replace placeholder images with real AI-generated images.\\n'); console.log('šŸ“Œ This feature can automatically replace Picsum placeholder images in your mock data'); console.log(' with contextually appropriate AI-generated images using Hugging Face models.\\n'); const enableImages = await this.askQuestion('Do you want to enable AI image processing? (y/n) '); if (enableImages.toLowerCase() !== 'y' && enableImages.toLowerCase() !== 'yes') { console.log('āœ… AI image processing disabled. Mock data will use placeholder images as-is.'); return {}; } console.log('\\nšŸŽØ Configuring AI image processing...'); // Step 3a: Hugging Face API Key const hfApiKey = await this.getHuggingFaceApiKey(); // Step 3b: Storage Configuration const storageConfig = await this.configureStorage(); const config = { HUGGINGFACE_API_KEY: hfApiKey, ...storageConfig, }; if (hfApiKey || Object.keys(storageConfig).length > 0) { console.log('\\nāœ… AI image processing configured successfully!'); console.log(' • Images will be generated using AI models'); if (Object.keys(storageConfig).length > 0) { console.log(' • Generated images will be uploaded to cloud storage'); } else { console.log(' • Generated images will be saved as base64 data URLs'); } } return config; } async configureStorage() { console.log('\\nšŸ“¦ Cloud Storage Configuration (Optional)'); console.log('Configure cloud storage to upload generated images. Without storage,'); console.log('images will be embedded as base64 data URLs (larger file sizes).\\n'); const useStorage = await this.askQuestion('Do you want to configure cloud storage for images? (y/n) '); if (useStorage.toLowerCase() !== 'y' && useStorage.toLowerCase() !== 'yes') { console.log(' Skipping cloud storage. Images will be embedded as base64.'); return {}; } console.log('\\nChoose your storage provider:'); console.log('1. AWS S3'); console.log('2. DigitalOcean Spaces'); const choice = await this.askQuestion('Select provider (1-2): '); if (choice === '1') { return this.configureAWS(); } else if (choice === '2') { return this.configureDigitalOcean(); } else { console.log('Invalid choice. Skipping storage configuration.'); return {}; } } async configureAWS() { try { console.log('\\nšŸ“¦ AWS S3 Configuration'); console.log('šŸ“Œ Get AWS credentials from: \\x1b[36mhttps://console.aws.amazon.com/iam/home#/security_credentials\\x1b[0m'); console.log(' (Create an IAM user with S3 permissions)\\n'); let accessKeyId = ''; let attempts = 0; const maxAttempts = 3; while (!accessKeyId && attempts < maxAttempts) { accessKeyId = await this.askSecureQuestion('AWS Access Key ID: '); if (!accessKeyId.trim()) { console.log('āŒ Access Key ID cannot be empty.'); attempts++; continue; } // AWS Access Key accepted as-is, no format validation break; } if (!accessKeyId) return {}; let secretAccessKey = ''; attempts = 0; while (!secretAccessKey && attempts < maxAttempts) { secretAccessKey = await this.askSecureQuestion('AWS Secret Access Key: '); if (!secretAccessKey.trim()) { console.log('āŒ Secret Access Key cannot be empty.'); attempts++; continue; } // AWS Secret Key accepted as-is, no format validation break; } if (!secretAccessKey) return {}; let region = (await this.askQuestion('AWS Region (default: us-east-1): ')) || 'us-east-1'; if (!this.validateInput('AWS_REGION', region)) { console.log('āš ļø Invalid region format, using default: us-east-1'); region = 'us-east-1'; } let bucketName = ''; attempts = 0; while (!bucketName && attempts < maxAttempts) { bucketName = await this.askQuestion('S3 Bucket Name: '); if (!bucketName.trim()) { console.log('āŒ Bucket name cannot be empty.'); attempts++; continue; } // Bucket name accepted as-is, no format validation break; } if (!bucketName) return {}; const endpoint = await this.askQuestion('Custom S3 Endpoint (optional): '); // Test AWS connection console.log('\\nšŸ”„ Testing AWS S3 connection...'); try { const { AWSS3Storage } = await Promise.resolve().then(() => __importStar(require('../services/aws-s3-storage'))); const s3 = new AWSS3Storage({ accessKeyId, secretAccessKey, region, bucketName, endpoint: endpoint || undefined, }); const isConnected = await s3.testConnection(); if (isConnected) { console.log('āœ… AWS S3 connection successful!'); } else { console.log('āš ļø AWS S3 connection test failed, but configuration will be saved.'); } } catch (error) { console.log(`āš ļø Could not test AWS S3 connection: ${error instanceof Error ? error.message : 'Unknown error'}`); console.log('Configuration will be saved anyway.'); } return { STORAGE_PROVIDER: 'aws', AWS_ACCESS_KEY_ID: accessKeyId, AWS_SECRET_ACCESS_KEY: secretAccessKey, AWS_REGION: region, AWS_S3_BUCKET_NAME: bucketName, AWS_S3_ENDPOINT: endpoint || undefined, }; } catch (error) { console.error(`āŒ AWS configuration failed: ${error instanceof Error ? error.message : 'Unknown error'}`); return {}; } } async configureDigitalOcean() { console.log('\\n🌊 DigitalOcean Spaces Configuration'); console.log('šŸ“Œ Get Spaces credentials from: \\x1b[36mhttps://cloud.digitalocean.com/settings/api/tokens\\x1b[0m'); console.log(' (Go to Spaces Keys section)\\n'); // Show available regions const { DigitalOceanSpaces } = await Promise.resolve().then(() => __importStar(require('../services/digitalocean-spaces'))); const regions = DigitalOceanSpaces.getAvailableRegions(); console.log('Available regions:'); regions.forEach((region, index) => { console.log(` ${index + 1}. ${region.code} - ${region.name} (${region.location})`); }); const regionChoice = await this.askQuestion(`\\nSelect region (1-${regions.length}): `); const regionIndex = parseInt(regionChoice) - 1; if (isNaN(regionIndex) || regionIndex < 0 || regionIndex >= regions.length) { console.log('Invalid region choice. Using default: nyc3'); } const selectedRegion = regions[regionIndex] || regions[0]; const accessKey = await this.askQuestion('Spaces Access Key: '); if (!accessKey.trim()) return {}; const secretKey = await this.askQuestion('Spaces Secret Key: '); if (!secretKey.trim()) return {}; // Ask if user wants to create a new Space or use existing one const createNew = (await this.askQuestion('Create a new Space? (y/n, default: y): ')) || 'y'; let spaceName; let spaceCreated = false; if (createNew.toLowerCase() === 'y' || createNew.toLowerCase() === 'yes') { spaceName = await this.askQuestion('New Space name (letters, numbers, hyphens only): '); if (!spaceName.trim()) { spaceName = `ai-images-${Date.now()}`; console.log(`Using auto-generated name: ${spaceName}`); } // Create the Space console.log(`\\nšŸ”„ Creating Space "${spaceName}" in ${selectedRegion.name}...`); try { const doSpaces = new DigitalOceanSpaces({ accessKeyId: accessKey, secretAccessKey: secretKey, region: selectedRegion.code, spaceName, }); await doSpaces.createSpace(spaceName, selectedRegion.code, true); spaceCreated = true; console.log('āœ… Space created successfully!'); } catch (error) { console.log(`āš ļø Failed to create Space: ${error}`); console.log('You can create it manually or use an existing one.'); } } else { // List existing Spaces console.log('\\nšŸ”„ Fetching existing Spaces...'); try { const doSpaces = new DigitalOceanSpaces({ accessKeyId: accessKey, secretAccessKey: secretKey, region: selectedRegion.code, }); const spaces = await doSpaces.listSpaces(); if (spaces.length === 0) { console.log('No existing Spaces found. Please create one first.'); return {}; } console.log('\\nExisting Spaces:'); spaces.forEach((space, index) => { console.log(` ${index + 1}. ${space.name} (${space.region})`); }); const spaceChoice = await this.askQuestion(`\\nSelect Space (1-${spaces.length}): `); const spaceIndex = parseInt(spaceChoice) - 1; if (isNaN(spaceIndex) || spaceIndex < 0 || spaceIndex >= spaces.length) { console.log('Invalid Space choice.'); return {}; } spaceName = spaces[spaceIndex].name; } catch (error) { console.log(`āš ļø Could not list Spaces: ${error}`); spaceName = await this.askQuestion('Enter Space name manually: '); if (!spaceName.trim()) return {}; } } // Test connection console.log('\\nšŸ”„ Testing DigitalOcean Spaces connection...'); try { const doSpaces = new DigitalOceanSpaces({ accessKeyId: accessKey, secretAccessKey: secretKey, region: selectedRegion.code, spaceName, }); const isConnected = await doSpaces.testConnection(); if (isConnected) { console.log('āœ… DigitalOcean Spaces connection successful!'); console.log(`šŸ“” CDN URL: https://${spaceName}.${selectedRegion.code}.cdn.digitaloceanspaces.com`); } else { console.log('āš ļø Connection test failed, but configuration will be saved.'); } } catch (error) { console.log('āš ļø Could not test connection, but configuration will be saved.'); } const doApiToken = await this.askQuestion('DigitalOcean API Token (optional, for advanced features): '); return { STORAGE_PROVIDER: 'digitalocean', DO_SPACES_ACCESS_KEY: accessKey, DO_SPACES_SECRET_KEY: secretKey, DO_SPACES_REGION: selectedRegion.code, DO_SPACES_NAME: spaceName, DO_API_TOKEN: doApiToken || undefined, }; } async writeConfiguration(config) { try { const lines = []; // Preserve existing non-AI related environment variables if ((0, fs_1.existsSync)(ENV_FILE_PATH)) { try { const existing = (0, fs_1.readFileSync)(ENV_FILE_PATH, 'utf8'); const existingLines = existing.split('\\n'); existingLines.forEach(line => { if (!line.startsWith('OPENROUTER_') && !line.startsWith('AWS_') && !line.startsWith('DO_') && !line.startsWith('HUGGINGFACE_') && !line.startsWith('STORAGE_PROVIDER')) { lines.push(line); } }); } catch (error) { console.warn(`āš ļø Could not read existing .env file: ${error instanceof Error ? error.message : 'Unknown error'}`); console.log('Creating new .env file...'); } } // Validate configuration before writing if (!config.OPENROUTER_API_KEY) { throw new Error('OpenRouter API key is required'); } // OpenRouter configuration lines.push(`OPENROUTER_API_KEY=${config.OPENROUTER_API_KEY.replace(/\n/g, '')}`); if (config.OPENROUTER_DEFAULT_MODEL) { lines.push(`OPENROUTER_DEFAULT_MODEL=${config.OPENROUTER_DEFAULT_MODEL.replace(/\n/g, '')}`); } if (config.OPENROUTER_MOCK_GENERATOR_MODEL) { lines.push(`OPENROUTER_MOCK_GENERATOR_MODEL=${config.OPENROUTER_MOCK_GENERATOR_MODEL.replace(/\n/g, '')}`); } // Hugging Face configuration if (config.HUGGINGFACE_API_KEY) { lines.push(`HUGGINGFACE_API_KEY=${config.HUGGINGFACE_API_KEY.replace(/\n/g, '')}`); } // Storage provider if (config.STORAGE_PROVIDER) { lines.push(`STORAGE_PROVIDER=${config.STORAGE_PROVIDER}`); } // AWS configuration (if provided) if (config.AWS_ACCESS_KEY_ID) { lines.push(`AWS_ACCESS_KEY_ID=${config.AWS_ACCESS_KEY_ID.replace(/\n/g, '')}`); } if (config.AWS_SECRET_ACCESS_KEY) { lines.push(`AWS_SECRET_ACCESS_KEY=${config.AWS_SECRET_ACCESS_KEY.replace(/\n/g, '')}`); } if (config.AWS_REGION) { lines.push(`AWS_REGION=${config.AWS_REGION}`); } if (config.AWS_S3_BUCKET_NAME) { lines.push(`AWS_S3_BUCKET_NAME=${config.AWS_S3_BUCKET_NAME}`); } if (config.AWS_S3_ENDPOINT) { lines.push(`AWS_S3_ENDPOINT=${config.AWS_S3_ENDPOINT}`); } // DigitalOcean configuration (if provided) if (config.DO_SPACES_ACCESS_KEY) { lines.push(`DO_SPACES_ACCESS_KEY=${config.DO_SPACES_ACCESS_KEY.replace(/\n/g, '')}`); } if (config.DO_SPACES_SECRET_KEY) { lines.push(`DO_SPACES_SECRET_KEY=${config.DO_SPACES_SECRET_KEY.replace(/\n/g, '')}`); } if (config.DO_SPACES_REGION) { lines.push(`DO_SPACES_REGION=${config.DO_SPACES_REGION}`); } if (config.DO_SPACES_NAME) { lines.push(`DO_SPACES_NAME=${config.DO_SPACES_NAME}`); } if (config.DO_API_TOKEN) { lines.push(`DO_API_TOKEN=${config.DO_API_TOKEN.replace(/\n/g, '')}`); } // Write configuration with error handling const content = lines.filter(line => line.trim()).join('\n') + '\n'; (0, fs_1.writeFileSync)(ENV_FILE_PATH, content, { mode: 0o600 }); // Secure file permissions console.log(`šŸ“ Configuration saved to: ${ENV_FILE_PATH}`); // Clean up backup file on successful write if ((0, fs_1.existsSync)(ENV_BACKUP_PATH)) { try { require('fs').unlinkSync(ENV_BACKUP_PATH); } catch (error) { console.warn(`āš ļø Could not remove backup file: ${error instanceof Error ? error.message : 'Unknown error'}`); } } } catch (error) { throw new Error(`Failed to write configuration: ${error instanceof Error ? error.message : 'Unknown error'}`); } } } // Run the wizard const wizard = new InitWizard(); wizard.run(); //# sourceMappingURL=init.js.map