/** * Brain Cloud Catalog Integration for CLI * * Fetches and displays augmentation catalog * Falls back to local cache if API is unavailable */ import chalk from 'chalk'; import { readFileSync, writeFileSync, existsSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; const CATALOG_API = process.env.BRAIN_CLOUD_CATALOG_URL || 'https://catalog.brain-cloud.soulcraft.com'; const CACHE_PATH = join(homedir(), '.brainy', 'catalog-cache.json'); const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours /** * Fetch catalog from API with caching */ export async function fetchCatalog() { try { // Check cache first const cached = loadCache(); if (cached) return cached; // Fetch from API const response = await fetch(`${CATALOG_API}/api/catalog/cli`); if (!response.ok) throw new Error('API unavailable'); const catalog = await response.json(); // Save to cache saveCache(catalog); return catalog; } catch (error) { // Try loading from cache even if expired const cached = loadCache(true); if (cached) { console.log(chalk.yellow('📡 Using cached catalog (API unavailable)')); return cached; } // Fall back to hardcoded catalog return getDefaultCatalog(); } } /** * Display catalog in CLI */ export async function showCatalog(options) { const catalog = await fetchCatalog(); if (!catalog) { console.log(chalk.red('❌ Could not load augmentation catalog')); return; } console.log(chalk.cyan.bold('🧠 Brain Cloud Augmentation Catalog')); console.log(chalk.gray(`Version ${catalog.version}`)); console.log(''); // Filter augmentations let augmentations = catalog.augmentations; if (options.category) { augmentations = augmentations.filter(a => a.category === options.category); } if (options.search) { const query = options.search.toLowerCase(); augmentations = augmentations.filter(a => a.name.toLowerCase().includes(query) || a.description.toLowerCase().includes(query)); } // Group by category const grouped = groupByCategory(augmentations, catalog.categories); // Display for (const [category, augs] of Object.entries(grouped)) { if (augs.length === 0) continue; const cat = catalog.categories.find(c => c.id === category); console.log(chalk.bold(`${cat?.icon || '📦'} ${cat?.name || category}`)); for (const aug of augs) { const status = getStatusIcon(aug.status); const popular = aug.popular ? chalk.yellow(' ⭐') : ''; const eta = aug.eta ? chalk.gray(` (${aug.eta})`) : ''; console.log(` ${status} ${aug.name}${popular}${eta}`); if (options.detailed) { console.log(chalk.gray(` ${aug.description}`)); } } console.log(''); } // Show summary const available = augmentations.filter(a => a.status === 'available').length; const coming = augmentations.filter(a => a.status === 'coming_soon').length; console.log(chalk.gray('─'.repeat(50))); console.log(chalk.green(`✅ ${available} available`) + chalk.gray(` • `) + chalk.yellow(`🔜 ${coming} coming soon`)); console.log(''); console.log(chalk.dim('Sign up at app.soulcraft.com to activate')); console.log(chalk.dim('Run "brainy augment info " for details')); } /** * Show detailed info about an augmentation */ export async function showAugmentationInfo(id) { const catalog = await fetchCatalog(); if (!catalog) { console.log(chalk.red('❌ Could not load augmentation catalog')); return; } const aug = catalog.augmentations.find(a => a.id === id); if (!aug) { console.log(chalk.red(`❌ Augmentation not found: ${id}`)); console.log(''); console.log('Available augmentations:'); catalog.augmentations.forEach(a => { console.log(` • ${a.id}`); }); return; } // Fetch full details from API try { const response = await fetch(`${CATALOG_API}/api/catalog/augmentation/${id}`); const details = await response.json(); console.log(chalk.cyan.bold(`📦 ${details.name}`)); if (details.popular) console.log(chalk.yellow('⭐ Popular')); console.log(''); console.log(chalk.bold('Category:'), getCategoryName(details.category, catalog.categories)); console.log(chalk.bold('Status:'), getStatusText(details.status)); if (details.eta) console.log(chalk.bold('Expected:'), details.eta); console.log(''); console.log(chalk.bold('Description:')); console.log(details.longDescription || details.description); console.log(''); if (details.features) { console.log(chalk.bold('Features:')); details.features.forEach((f) => console.log(` ✓ ${f}`)); console.log(''); } if (details.example) { console.log(chalk.bold('Example:')); console.log(chalk.gray('─'.repeat(50))); console.log(details.example.code); console.log(chalk.gray('─'.repeat(50))); console.log(''); } if (details.requirements?.config) { console.log(chalk.bold('Required Configuration:')); details.requirements.config.forEach((c) => console.log(` • ${c}`)); console.log(''); } if (details.pricing) { console.log(chalk.bold('Available in:')); details.pricing.tiers.forEach((t) => console.log(` • ${t}`)); console.log(''); } console.log(chalk.dim('To activate: brainy augment activate')); } catch (error) { // Show basic info if API fails console.log(chalk.cyan.bold(`📦 ${aug.name}`)); console.log(aug.description); console.log(''); console.log(chalk.dim('Full details unavailable (API offline)')); } } /** * Show user's available augmentations */ export async function showAvailable(licenseKey) { const key = licenseKey || process.env.BRAINY_LICENSE_KEY || readLicenseFile(); if (!key) { console.log(chalk.yellow('⚠️ No license key found')); console.log(''); console.log('To see your available augmentations:'); console.log(' 1. Sign up at app.soulcraft.com'); console.log(' 2. Run: brainy augment activate'); return; } try { const response = await fetch(`${CATALOG_API}/api/catalog/available`, { headers: { 'x-license-key': key } }); if (!response.ok) { throw new Error('Invalid license'); } const data = await response.json(); console.log(chalk.cyan.bold('🧠 Your Available Augmentations')); console.log(chalk.gray(`Plan: ${data.plan}`)); console.log(''); const grouped = groupByCategory(data.augmentations, []); for (const [category, augs] of Object.entries(grouped)) { console.log(chalk.bold(category)); augs.forEach(aug => { console.log(` ✅ ${aug.name}`); }); console.log(''); } if (data.operations) { const used = data.operations.used || 0; const limit = data.operations.limit; const percent = limit === 'unlimited' ? 0 : Math.round((used / limit) * 100); console.log(chalk.bold('Usage:')); if (limit === 'unlimited') { console.log(` Unlimited operations`); } else { console.log(` ${used.toLocaleString()} / ${limit.toLocaleString()} operations (${percent}%)`); } } } catch (error) { console.log(chalk.red('❌ Could not fetch available augmentations')); console.log(chalk.gray(error.message)); } } // Helper functions function loadCache(ignoreExpiry = false) { try { if (!existsSync(CACHE_PATH)) return null; const data = JSON.parse(readFileSync(CACHE_PATH, 'utf8')); if (!ignoreExpiry && Date.now() - data.timestamp > CACHE_TTL) { return null; } return data.catalog; } catch { return null; } } function saveCache(catalog) { try { const dir = join(homedir(), '.brainy'); if (!existsSync(dir)) { require('fs').mkdirSync(dir, { recursive: true }); } writeFileSync(CACHE_PATH, JSON.stringify({ catalog, timestamp: Date.now() })); } catch { // Ignore cache save errors } } function groupByCategory(augmentations, categories) { const grouped = {}; for (const aug of augmentations) { if (!grouped[aug.category]) { grouped[aug.category] = []; } grouped[aug.category].push(aug); } // Sort by category order const ordered = {}; const categoryOrder = ['memory', 'coordination', 'enterprise', 'perception', 'dialog', 'activation', 'cognition', 'websocket']; for (const cat of categoryOrder) { if (grouped[cat]) { ordered[cat] = grouped[cat]; } } return ordered; } function getStatusIcon(status) { switch (status) { case 'available': return chalk.green('✅'); case 'coming_soon': return chalk.yellow('🔜'); case 'deprecated': return chalk.red('⚠️'); default: return '❓'; } } function getStatusText(status) { switch (status) { case 'available': return chalk.green('Available'); case 'coming_soon': return chalk.yellow('Coming Soon'); case 'deprecated': return chalk.red('Deprecated'); default: return 'Unknown'; } } function getCategoryName(categoryId, categories) { const cat = categories.find(c => c.id === categoryId); return cat ? `${cat.icon} ${cat.name}` : categoryId; } function readLicenseFile() { try { const licensePath = join(homedir(), '.brainy', 'license'); if (existsSync(licensePath)) { return readFileSync(licensePath, 'utf8').trim(); } } catch { } return null; } function getDefaultCatalog() { // Hardcoded fallback catalog return { version: '1.0.0', categories: [ { id: 'memory', name: 'Memory', icon: '🧠', description: 'AI memory and persistence' }, { id: 'coordination', name: 'Coordination', icon: '🤝', description: 'Multi-agent orchestration' }, { id: 'enterprise', name: 'Enterprise', icon: '🏢', description: 'Business integrations' } ], augmentations: [ { id: 'ai-memory', name: 'AI Memory', category: 'memory', description: 'Persistent memory across all AI sessions', status: 'available', popular: true }, { id: 'agent-coordinator', name: 'Agent Coordinator', category: 'coordination', description: 'Multi-agent handoffs and orchestration', status: 'available', popular: true }, { id: 'notion-sync', name: 'Notion Sync', category: 'enterprise', description: 'Bidirectional Notion database sync', status: 'available' } ] }; } //# sourceMappingURL=catalog.js.map