#!/usr/bin/env bun /* eslint-disable no-console */ import { parseArgs } from 'util'; import { join } from 'path'; import { ConfigManager } from './config-manager'; import { appConfigSchema } from './schemas'; import { validateConfig, formatValidationResult, checkDeprecations, checkRequiredEnvVars, validateCompleteness } from './utils/validation'; import { redactSecrets } from './utils/secrets'; import type { Environment } from './types'; interface CliOptions { config?: string; env?: string; validate?: boolean; show?: boolean; check?: boolean; json?: boolean; help?: boolean; } const DEPRECATIONS = { 'service.legacyMode': 'Use service.mode instead', 'database.redis': 'Use database.dragonfly instead', }; const REQUIRED_PATHS = [ 'service.name', 'service.port', 'database.postgres.host', 'database.postgres.database', ]; const REQUIRED_ENV_VARS = [ 'NODE_ENV', ]; const SECRET_PATHS = [ 'database.postgres.password', 'database.mongodb.uri', 'providers.quoteMedia.apiKey', 'providers.interactiveBrokers.clientId', ]; function printUsage() { console.log(` Stock Bot Configuration CLI Usage: bun run config-cli [options] Options: --config Path to config directory (default: ./config) --env Environment to use (development, test, production) --validate Validate configuration against schema --show Show current configuration (secrets redacted) --check Run all configuration checks --json Output in JSON format --help Show this help message Examples: # Validate configuration bun run config-cli --validate # Show configuration for production bun run config-cli --env production --show # Run all checks bun run config-cli --check # Output configuration as JSON bun run config-cli --show --json `); } async function main() { const { values } = parseArgs({ args: process.argv.slice(2), options: { config: { type: 'string' }, env: { type: 'string' }, validate: { type: 'boolean' }, show: { type: 'boolean' }, check: { type: 'boolean' }, json: { type: 'boolean' }, help: { type: 'boolean' }, }, }) as { values: CliOptions }; if (values.help) { printUsage(); process.exit(0); } const configPath = values.config || join(process.cwd(), 'config'); const environment = values.env as Environment; try { const manager = new ConfigManager({ configPath, environment, }); const config = await manager.initialize(appConfigSchema); if (values.validate) { const result = validateConfig(config, appConfigSchema); if (values.json) { console.log(JSON.stringify(result, null, 2)); } else { console.log(formatValidationResult(result)); } process.exit(result.valid ? 0 : 1); } if (values.show) { const redacted = redactSecrets(config, SECRET_PATHS); if (values.json) { console.log(JSON.stringify(redacted, null, 2)); } else { console.log('Current Configuration:'); console.log(JSON.stringify(redacted, null, 2)); } } if (values.check) { console.log('Running configuration checks...\n'); // Schema validation console.log('1. Schema Validation:'); const schemaResult = validateConfig(config, appConfigSchema); console.log(formatValidationResult(schemaResult)); console.log(); // Environment variables console.log('2. Required Environment Variables:'); const envResult = checkRequiredEnvVars(REQUIRED_ENV_VARS); console.log(formatValidationResult(envResult)); console.log(); // Required paths console.log('3. Required Configuration Paths:'); const pathResult = validateCompleteness(config, REQUIRED_PATHS); console.log(formatValidationResult(pathResult)); console.log(); // Deprecations console.log('4. Deprecation Warnings:'); const warnings = checkDeprecations(config, DEPRECATIONS); if (warnings && warnings.length > 0) { for (const warning of warnings) { console.log(` ⚠️ ${warning.path}: ${warning.message}`); } } else { console.log(' ✅ No deprecated options found'); } console.log(); // Overall result const allValid = schemaResult.valid && envResult.valid && pathResult.valid; if (allValid) { console.log('✅ All configuration checks passed!'); process.exit(0); } else { console.log('❌ Some configuration checks failed'); process.exit(1); } } if (!values.validate && !values.show && !values.check) { console.log('No action specified. Use --help for usage information.'); process.exit(1); } } catch (error) { if (values.json) { console.error(JSON.stringify({ error: String(error) })); } else { console.error('Error:', error); } process.exit(1); } } // Run CLI if (import.meta.main) { main(); }