UNPKG

build-in-public-bot

Version:

AI-powered CLI bot for automating build-in-public tweets with code screenshots

257 lines 10.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigService = void 0; const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); const os_1 = __importDefault(require("os")); const yaml_1 = __importDefault(require("yaml")); const errors_1 = require("../utils/errors"); const logger_1 = require("../utils/logger"); class ConfigService { static instance; configDir; configPath; config = null; constructor(configDir) { if (configDir) { this.configDir = configDir; } else if (process.env.NODE_ENV === 'test' && process.env.TEST_HOME) { this.configDir = path_1.default.join(process.env.TEST_HOME, '.bip'); } else { this.configDir = path_1.default.join(os_1.default.homedir(), '.bip'); } this.configPath = path_1.default.join(this.configDir, 'config.yml'); } static getInstance() { if (!ConfigService.instance) { ConfigService.instance = new ConfigService(); } return ConfigService.instance; } async init() { try { await promises_1.default.mkdir(this.configDir, { recursive: true }); try { await promises_1.default.access(this.configPath); throw new errors_1.ConfigError('Configuration already exists. Use "bip style" to modify settings.'); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } const defaultConfig = { version: '1.0.0', twitter: { username: '', sessionData: null }, ai: { provider: 'openai', model: 'gpt-4-turbo-preview', apiKey: process.env.OPENROUTER_API_KEY || '' }, style: { tone: 'casual-technical', emojis: { frequency: 'moderate', preferred: ['🚀', '💡', '🔧', '✨', '🎯', '💻', '🛠️', '⚡'] }, hashtags: { always: ['#buildinpublic'], contextual: ['#webdev', '#typescript', '#nodejs', '#opensource'] }, examples: [ 'Just shipped a new feature that makes X 10x faster 🚀 Used Y technique to optimize Z. The difference is wild! #buildinpublic', 'Debugging session turned into a refactoring marathon 🔧 Sometimes the best features come from fixing bugs. Added proper error handling and the UX is so much smoother now ✨', 'TIL: You can use X to solve Y problem. Been struggling with this for hours and the solution was so simple 💡 Love when things just click! #buildinpublic #webdev' ] }, screenshots: { theme: 'dracula', backgroundColor: '#282a36', windowTheme: 'mac', padding: 32, language: 'auto' } }; await this.save(defaultConfig); logger_1.logger.success('Configuration initialized successfully!'); logger_1.logger.info(`Config file created at: ${this.configPath}`); } catch (error) { if (error instanceof errors_1.ConfigError) { throw error; } throw new errors_1.ConfigError('Failed to initialize configuration', error); } } async load() { if (this.config) { return this.config; } try { const configContent = await promises_1.default.readFile(this.configPath, 'utf-8'); this.config = yaml_1.default.parse(configContent); return this.config; } catch (error) { if (error.code === 'ENOENT') { throw new errors_1.ConfigError('Configuration not found. Run "bip init" first.'); } throw new errors_1.ConfigError('Failed to load configuration', error); } } async save(config) { try { const yamlContent = yaml_1.default.stringify(config, { indent: 2 }); await promises_1.default.writeFile(this.configPath, yamlContent, 'utf-8'); this.config = config; } catch (error) { throw new errors_1.ConfigError('Failed to save configuration', error); } } async update(updates) { const current = await this.load(); const updated = this.deepMerge(current, updates); await this.save(updated); } async updateStyle(styleUpdates) { const current = await this.load(); current.style = this.deepMerge(current.style, styleUpdates); await this.save(current); } deepMerge(target, source) { if (Array.isArray(source)) { return source; } if (!source || typeof source !== 'object') { return source; } const output = { ...target }; for (const key in source) { if (source[key] instanceof Object && !Array.isArray(source[key]) && key in target && target[key] instanceof Object && !Array.isArray(target[key])) { output[key] = this.deepMerge(target[key], source[key]); } else { output[key] = source[key]; } } return output; } getConfigPath() { return this.configPath; } getConfigDir() { return this.configDir; } async validate() { const errors = []; try { const config = await this.load(); if (!config.version) { errors.push('Missing required field: version'); } if (!config.twitter) { errors.push('Missing required field: twitter'); } else { if (!config.twitter.username) { errors.push('Twitter username is required'); } } if (!config.ai) { errors.push('Missing required field: ai'); } else { if (!config.ai.provider) { errors.push('AI provider is required'); } if (!config.ai.model) { errors.push('AI model is required'); } if (!config.ai.apiKey) { errors.push('AI API key is required'); } } if (!config.style) { errors.push('Missing required field: style'); } else { const validTones = ['casual', 'professional', 'technical', 'humorous', 'casual-technical', 'enthusiastic', 'minimalist']; if (!validTones.includes(config.style.tone)) { errors.push(`Invalid tone: ${config.style.tone}. Must be one of: ${validTones.join(', ')}`); } const validFrequencies = ['none', 'low', 'moderate', 'high']; if (!validFrequencies.includes(config.style.emojis.frequency)) { errors.push(`Invalid emoji frequency: ${config.style.emojis.frequency}. Must be one of: ${validFrequencies.join(', ')}`); } } if (!config.screenshots) { errors.push('Missing required field: screenshots'); } return { valid: errors.length === 0, errors }; } catch (error) { return { valid: false, errors: ['Failed to load configuration for validation'] }; } } async reset() { try { const defaultConfig = { version: '1.0.0', twitter: { username: '', sessionData: null }, ai: { provider: 'openai', model: 'gpt-4-turbo-preview', apiKey: process.env.OPENROUTER_API_KEY || '' }, style: { tone: 'casual-technical', emojis: { frequency: 'moderate', preferred: ['🚀', '💡', '🔧', '✨', '🎯', '💻', '🛠️', '⚡'] }, hashtags: { always: ['#buildinpublic'], contextual: ['#webdev', '#typescript', '#nodejs', '#opensource'] }, examples: [ 'Just shipped a new feature that makes X 10x faster 🚀 Used Y technique to optimize Z. The difference is wild! #buildinpublic', 'Debugging session turned into a refactoring marathon 🔧 Sometimes the best features come from fixing bugs. Added proper error handling and the UX is so much smoother now ✨', 'TIL: You can use X to solve Y problem. Been struggling with this for hours and the solution was so simple 💡 Love when things just click! #buildinpublic #webdev' ] }, screenshots: { theme: 'dracula', backgroundColor: '#282a36', windowTheme: 'mac', padding: 32, language: 'auto' } }; await this.save(defaultConfig); this.config = null; } catch (error) { throw new errors_1.ConfigError('Failed to reset configuration', error); } } } exports.ConfigService = ConfigService; //# sourceMappingURL=config.js.map