UNPKG

@meldscience/meld

Version:

pipeable one-shot prompt scripting toolkit

172 lines 6.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RC_FILE = void 0; exports.loadConfig = loadConfig; exports.setupConfig = setupConfig; exports.checkAndPromptConfig = checkAndPromptConfig; const fs_1 = require("fs"); const os_1 = require("os"); const path_1 = require("path"); const errors_1 = require("./errors"); const child_process_1 = require("child_process"); exports.RC_FILE = '.meldrc'; function resolveSecretReference(value) { if (!value.startsWith('op://')) { return value; } try { return (0, child_process_1.execSync)(`op read "${value}"`, { encoding: 'utf-8' }).trim(); } catch (error) { throw new errors_1.ToolError(`Failed to resolve 1Password secret reference: ${error.message}`, 'SECRET_REFERENCE_ERROR'); } } function loadConfig(options) { // Start with empty config const config = {}; // Try to load .meldrc from home directory (lowest priority) try { const rcPath = (0, path_1.join)((0, os_1.homedir)(), exports.RC_FILE); if ((0, fs_1.existsSync)(rcPath)) { const rcContent = (0, fs_1.readFileSync)(rcPath, 'utf-8'); const rcConfig = JSON.parse(rcContent); // Resolve any secret references in the config if (rcConfig.anthropicApiKey) { rcConfig.anthropicApiKey = resolveSecretReference(rcConfig.anthropicApiKey); } if (rcConfig.openaiApiKey) { rcConfig.openaiApiKey = resolveSecretReference(rcConfig.openaiApiKey); } Object.assign(config, rcConfig); } } catch (error) { if (error instanceof errors_1.ToolError) { throw error; // Re-throw our own errors } // Ignore other errors reading config file - it's optional } // Load from environment variables (middle priority) if (process.env.DEFAULT_MODEL) { config.defaultModel = process.env.DEFAULT_MODEL; } if (process.env.ANTHROPIC_API_KEY) { config.anthropicApiKey = process.env.ANTHROPIC_API_KEY; } if (process.env.OPENAI_API_KEY) { config.openaiApiKey = process.env.OPENAI_API_KEY; } // Override with any passed options (highest priority) if (options) { // Only override values that are explicitly set in options Object.keys(options).forEach(key => { const value = options[key]; if (value !== undefined) { config[key] = value; } }); } return config; } function setupConfig(options) { // Don't create config if no options provided if (Object.keys(options).length === 0) { return; } const rcPath = (0, path_1.join)((0, os_1.homedir)(), exports.RC_FILE); let config = {}; // Read existing config if it exists if ((0, fs_1.existsSync)(rcPath)) { try { const rcContent = (0, fs_1.readFileSync)(rcPath, 'utf-8'); config = JSON.parse(rcContent); } catch (error) { // If file exists but is invalid, we'll overwrite it console.warn('⚠️ Existing config file is invalid, creating new one'); } } // Set default model aliases if none exist if (!config.modelAliases) { config.modelAliases = { 'claude': 'claude-3-5-sonnet-latest', 'sonnet': 'claude-3-5-sonnet-latest', 'opus': 'claude-3-opus-latest', 'haiku': 'claude-3-5-haiku-latest', '4o': 'chatgpt-4o-latest', 'o1': 'o1', 'o1-mini': 'o1-mini' }; } // Set default models if not set if (!config.defaultOneshotModel) { config.defaultOneshotModel = 'claude-3-5-sonnet-latest'; } if (!config.defaultOneshotcatModel) { config.defaultOneshotcatModel = 'claude-3-opus-latest'; } // Update config with new values if (options.anthropicApiKey) config.anthropicApiKey = options.anthropicApiKey; if (options.openaiApiKey) config.openaiApiKey = options.openaiApiKey; if (options.defaultModel) config.defaultModel = options.defaultModel; if (options.defaultOneshotModel) config.defaultOneshotModel = options.defaultOneshotModel; if (options.defaultOneshotcatModel) config.defaultOneshotcatModel = options.defaultOneshotcatModel; if (options.modelAliases) { config.modelAliases = { ...config.modelAliases, ...options.modelAliases }; } // Write config file (0, fs_1.writeFileSync)(rcPath, JSON.stringify(config, null, 2)); } function checkAndPromptConfig(models) { const config = loadConfig(); const rcPath = (0, path_1.join)((0, os_1.homedir)(), exports.RC_FILE); const needsAnthropicKey = models.some(m => m.startsWith('claude')) && !config.anthropicApiKey; const needsOpenAIKey = models.some(m => m.startsWith('gpt')) && !config.openaiApiKey; if (!needsAnthropicKey && !needsOpenAIKey) { return; // All required keys are present } console.log('\n⚠️ Missing API key configuration'); console.log('--------------------------------'); if (needsAnthropicKey) { console.log('• Anthropic API key required for Claude models'); } if (needsOpenAIKey) { console.log('• OpenAI API key required for GPT models'); } console.log('\nTo configure your API keys, use:'); if (needsAnthropicKey) { console.log(' meld-config --anthropic-key <your-key>'); } if (needsOpenAIKey) { console.log(' meld-config --openai-key <your-key>'); } console.log('\nOr use 1Password CLI secret references in .meldrc:'); console.log(' {'); if (needsAnthropicKey) { console.log(' "anthropicApiKey": "op://vault-name/anthropic/api-key"'); } if (needsOpenAIKey) { console.log(' "openaiApiKey": "op://vault-name/openai/api-key"'); } console.log(' }'); console.log('\nOr set environment variables:'); if (needsAnthropicKey) { console.log(' export ANTHROPIC_API_KEY=<your-key>'); } if (needsOpenAIKey) { console.log(' export OPENAI_API_KEY=<your-key>'); } console.log(`\nConfiguration will be saved to: ${rcPath}`); console.log('\nFor more options, run: meld-config --help'); // Exit with error throw new errors_1.ToolError('API keys required', 'MISSING_API_KEYS'); } //# sourceMappingURL=config.js.map