brainy/dist/cli/catalog.js

325 lines
12 KiB
JavaScript
Raw Normal View History

/**
* 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 <name>" 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