UNPKG

organ-ai-zer

Version:

AI-powered file organizer CLI tool

286 lines 13.4 kB
"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 }); exports.ConfigService = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const os = __importStar(require("os")); const crypto = __importStar(require("crypto")); const joi_1 = __importDefault(require("joi")); const config_1 = require("../types/config"); class ConfigService { constructor(customConfigPath) { this.config = null; this.configPath = customConfigPath || this.getDefaultConfigPath(); } static getInstance(customConfigPath) { if (!ConfigService.instance) { ConfigService.instance = new ConfigService(customConfigPath); } return ConfigService.instance; } getDefaultConfigPath() { const configDir = path.join(os.homedir(), '.organ-ai-zer'); return path.join(configDir, 'config.json'); } async loadConfig() { if (this.config) { return this.config; } try { if (await fs.pathExists(this.configPath)) { const configData = await fs.readJson(this.configPath); this.config = this.validateAndMergeConfig(configData); } else { console.log('⚠️ No config file found. Use "organ-ai-zer init" to create one.'); this.config = config_1.DEFAULT_CONFIG; } } catch (error) { console.error(`❌ Error loading config: ${error}`); throw new Error(`Failed to load configuration: ${error}`); } return this.config; } async saveConfig(config) { try { const validatedConfig = this.validateConfig(config); await fs.ensureDir(path.dirname(this.configPath)); await fs.writeJson(this.configPath, validatedConfig, { spaces: 2 }); this.config = validatedConfig; console.log(`✅ Config saved to: ${this.configPath}`); } catch (error) { throw new Error(`Failed to save configuration: ${error}`); } } async initializeConfig(force = false) { const configExists = await fs.pathExists(this.configPath); if (configExists && !force) { console.log(`⚠️ Config file already exists at: ${this.configPath}`); console.log('Use --force to overwrite or specify a different path.'); return; } try { // Use a more lenient validation for initialization const validatedConfig = this.validateInitConfig(config_1.DEFAULT_CONFIG); await fs.ensureDir(path.dirname(this.configPath)); await fs.writeJson(this.configPath, validatedConfig, { spaces: 2 }); this.config = validatedConfig; console.log(`✅ Config saved to: ${this.configPath}`); } catch (error) { throw new Error(`Failed to save configuration: ${error}`); } console.log('🎉 Default configuration created!'); console.log('📝 Edit the config file to customize your preferences:'); console.log(` ${this.configPath}`); console.log(''); console.log('🔑 Don\'t forget to add your AI API key in the config file!'); } validateInitConfig(config) { // More lenient validation for initialization - allows empty API key const schema = joi_1.default.object({ ai: joi_1.default.object({ provider: joi_1.default.string().valid('openai', 'anthropic').required(), apiKey: joi_1.default.string().allow('').required(), model: joi_1.default.string().optional(), maxTokens: joi_1.default.number().min(100).max(4000).optional(), temperature: joi_1.default.number().min(0).max(2).optional(), timeout: joi_1.default.number().min(1000).max(180000).optional() }).required(), organization: joi_1.default.object({ confidenceThreshold: joi_1.default.number().min(0).max(1).required(), createBackups: joi_1.default.boolean().required(), preserveOriginalNames: joi_1.default.boolean().required(), maxDepth: joi_1.default.number().min(1).max(10).required(), excludePatterns: joi_1.default.array().items(joi_1.default.string()).required(), includePatterns: joi_1.default.array().items(joi_1.default.string()).required() }).required(), fileTypes: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ enabled: joi_1.default.boolean().required(), namingPatterns: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ pattern: joi_1.default.string().required(), description: joi_1.default.string().required(), example: joi_1.default.string().required() })).required(), organizationRules: joi_1.default.object({ byDate: joi_1.default.boolean().optional(), byType: joi_1.default.boolean().optional(), byProject: joi_1.default.boolean().optional(), customPath: joi_1.default.string().optional() }).required() })).required(), customCategories: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ extensions: joi_1.default.array().items(joi_1.default.string()).required(), config: joi_1.default.object({ enabled: joi_1.default.boolean().required(), namingPatterns: joi_1.default.object().required(), organizationRules: joi_1.default.object().required() }).required() })).optional() }); const { error, value } = schema.validate(config, { allowUnknown: false }); if (error) { throw new Error(`Invalid configuration: ${error.details[0].message}`); } return value; } validateConfig(config) { const schema = joi_1.default.object({ ai: joi_1.default.object({ provider: joi_1.default.string().valid('openai', 'anthropic').required(), apiKey: joi_1.default.string().required(), model: joi_1.default.string().optional(), maxTokens: joi_1.default.number().min(100).max(4000).optional(), temperature: joi_1.default.number().min(0).max(2).optional(), timeout: joi_1.default.number().min(1000).max(180000).optional() }).required(), organization: joi_1.default.object({ confidenceThreshold: joi_1.default.number().min(0).max(1).required(), createBackups: joi_1.default.boolean().required(), preserveOriginalNames: joi_1.default.boolean().required(), maxDepth: joi_1.default.number().min(1).max(10).required(), excludePatterns: joi_1.default.array().items(joi_1.default.string()).required(), includePatterns: joi_1.default.array().items(joi_1.default.string()).required() }).required(), fileTypes: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ enabled: joi_1.default.boolean().required(), namingPatterns: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ pattern: joi_1.default.string().required(), description: joi_1.default.string().required(), example: joi_1.default.string().required() })).required(), organizationRules: joi_1.default.object({ byDate: joi_1.default.boolean().optional(), byType: joi_1.default.boolean().optional(), byProject: joi_1.default.boolean().optional(), customPath: joi_1.default.string().optional() }).required() })).required(), customCategories: joi_1.default.object().pattern(joi_1.default.string(), joi_1.default.object({ extensions: joi_1.default.array().items(joi_1.default.string()).required(), config: joi_1.default.object({ enabled: joi_1.default.boolean().required(), namingPatterns: joi_1.default.object().required(), organizationRules: joi_1.default.object().required() }).required() })).optional() }); const { error, value } = schema.validate(config, { allowUnknown: false }); if (error) { throw new Error(`Invalid configuration: ${error.details[0].message}`); } return value; } validateAndMergeConfig(userConfig) { // Deep merge user config with default config const mergedConfig = this.deepMerge(config_1.DEFAULT_CONFIG, userConfig); return this.validateConfig(mergedConfig); } deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = this.deepMerge(target[key] || {}, source[key]); } else { result[key] = source[key]; } } return result; } getConfigPath() { return this.configPath; } async configExists() { return await fs.pathExists(this.configPath); } async saveInteractiveConfig(config) { try { const validatedConfig = this.validateInitConfig(config); await fs.ensureDir(path.dirname(this.configPath)); await fs.writeJson(this.configPath, validatedConfig, { spaces: 2 }); this.config = validatedConfig; console.log(`✅ Config saved to: ${this.configPath}`); } catch (error) { throw new Error(`Failed to save interactive configuration: ${error}`); } } isConfigured() { return this.config?.ai?.apiKey !== ''; } async validateApiKey() { const config = await this.loadConfig(); if (!config.ai.apiKey) { console.error('❌ No API key configured. Please add your API key to the config file.'); return false; } // Basic validation - actual API validation would require making a test call if (config.ai.apiKey.length < 10) { console.error('❌ API key appears to be invalid (too short).'); return false; } return true; } getConfigHash() { if (!this.config) { throw new Error('Config not loaded'); } // Create hash of configuration that affects AI suggestions const relevantConfig = { ai: { provider: this.config.ai.provider, model: this.config.ai.model, temperature: this.config.ai.temperature }, organization: { confidenceThreshold: this.config.organization.confidenceThreshold, preserveOriginalNames: this.config.organization.preserveOriginalNames, excludePatterns: this.config.organization.excludePatterns, includePatterns: this.config.organization.includePatterns }, fileTypes: this.config.fileTypes }; return crypto.createHash('md5').update(JSON.stringify(relevantConfig)).digest('hex'); } } exports.ConfigService = ConfigService; //# sourceMappingURL=config-service.js.map