open-brainy/src/cli/catalog.ts
David Snelling 581f9906fd feat: complete VFS with Knowledge Layer integration
- Add importFile() method for single file imports
- Implement entity helper methods (linkEntities, findEntityOccurrences)
- Fix critical embedding tokenizer bug (char.charCodeAt error)
- Fix removeRelationship to actually remove using brain.unrelate()
- Add setMetadata/getMetadata methods
- Fix GitBridge to query real relationships and events
- Enable background Knowledge Layer processing
- Rewrite README to emphasize knowledge over files
- Add comprehensive VFS documentation (core, knowledge layer, examples)
- Add complete test suite covering all VFS methods

This completes the VFS implementation with full Knowledge Layer support,
enabling files as living knowledge that understand themselves, evolve
over time, and connect to everything related.
2025-09-25 10:47:44 -07:00

440 lines
No EOL
13 KiB
TypeScript

/**
* Augmentation Catalog for CLI
*
* Displays available augmentations catalog
* Local catalog with caching support
*/
import chalk from 'chalk'
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs'
import { join } from 'node:path'
import { homedir } from 'node:os'
const CATALOG_API = process.env.BRAINY_CATALOG_URL || null
const CACHE_PATH = join(homedir(), '.brainy', 'catalog-cache.json')
const CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours
interface Augmentation {
id: string
name: string
description: string
category: string
status: 'available' | 'coming_soon' | 'deprecated'
popular?: boolean
eta?: string
}
interface Category {
id: string
name: string
icon: string
description: string
}
interface Catalog {
version: string
categories: Category[]
augmentations: Augmentation[]
}
/**
* Fetch catalog from API with caching
*/
export async function fetchCatalog(): Promise<Catalog | null> {
try {
// Check cache first
const cached = loadCache()
if (cached) return cached
// If external catalog API is configured, try to fetch
if (CATALOG_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
}
// Fall back to local catalog
return getDefaultCatalog()
} catch (error) {
// Try loading from cache even if expired
const cached = loadCache(true)
if (cached) {
console.log(chalk.yellow('📡 Using cached catalog'))
return cached
}
// Fall back to hardcoded catalog
return getDefaultCatalog()
}
}
/**
* Display catalog in CLI
*/
export async function showCatalog(options: {
category?: string
search?: string
detailed?: boolean
}) {
const catalog = await fetchCatalog()
if (!catalog) {
console.log(chalk.red('❌ Could not load augmentation catalog'))
return
}
console.log(chalk.cyan.bold('🧠 Brainy 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('Configure augmentations with "brainy augment"'))
console.log(chalk.dim('Run "brainy augment info <name>" for details'))
}
/**
* Show detailed info about an augmentation
*/
export async function showAugmentationInfo(id: string) {
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 if available
try {
if (!CATALOG_API) throw new Error('No external catalog configured')
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: string) => 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: string) => console.log(`${c}`))
console.log('')
}
if (details.pricing) {
console.log(chalk.bold('Available in:'))
details.pricing.tiers.forEach((t: string) => 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 (no external catalog configured)'))
}
}
/**
* Show user's available augmentations
*/
export async function showAvailable(licenseKey?: string) {
// Show local catalog as default
const catalog = await fetchCatalog()
if (!catalog) {
console.log(chalk.red('❌ Could not load augmentation catalog'))
return
}
console.log(chalk.cyan.bold('🧠 Available Augmentations'))
console.log('')
const available = catalog.augmentations.filter(a => a.status === 'available')
const grouped = groupByCategory(available, catalog.categories)
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}`))
augs.forEach(aug => {
console.log(`${aug.name}`)
console.log(chalk.gray(` ${aug.description}`))
})
console.log('')
}
console.log(chalk.green(`${available.length} augmentations available`))
// If external API is configured and license key provided, try to fetch personalized data
if (CATALOG_API && licenseKey) {
try {
const response = await fetch(`${CATALOG_API}/api/catalog/available`, {
headers: { 'x-license-key': licenseKey }
})
if (response.ok) {
const data = await response.json()
console.log(chalk.gray(`Plan: ${data.plan || 'Standard'}`))
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) {
// Ignore external API errors - local catalog is sufficient
}
}
}
// Helper functions
function loadCache(ignoreExpiry = false): Catalog | null {
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: Catalog): void {
try {
const dir = join(homedir(), '.brainy')
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true })
}
writeFileSync(CACHE_PATH, JSON.stringify({
catalog,
timestamp: Date.now()
}))
} catch {
// Ignore cache save errors
}
}
function groupByCategory(augmentations: Augmentation[], categories: Category[]) {
const grouped: Record<string, Augmentation[]> = {}
for (const aug of augmentations) {
if (!grouped[aug.category]) {
grouped[aug.category] = []
}
grouped[aug.category].push(aug)
}
// Sort by category order
const ordered: Record<string, Augmentation[]> = {}
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: string): string {
switch (status) {
case 'available': return chalk.green('✅')
case 'coming_soon': return chalk.yellow('🔜')
case 'deprecated': return chalk.red('⚠️')
default: return '❓'
}
}
function getStatusText(status: string): string {
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: string, categories: Category[]): string {
const cat = categories.find(c => c.id === categoryId)
return cat ? `${cat.icon} ${cat.name}` : categoryId
}
function readLicenseFile(): string | null {
try {
const licensePath = join(homedir(), '.brainy', 'license')
if (existsSync(licensePath)) {
return readFileSync(licensePath, 'utf8').trim()
}
} catch (error) {
// License file read failed, return null
console.debug('Failed to read license file:', error)
}
return null
}
function getDefaultCatalog(): Catalog {
// Local catalog with current features
return {
version: '1.5.0',
categories: [
{ id: 'core', name: 'Core Features', icon: '🧠', description: 'Essential brainy functionality' },
{ id: 'neural', name: 'Neural API', icon: '🔗', description: 'Semantic similarity and clustering' },
{ id: 'enterprise', name: 'Enterprise', icon: '🏢', description: 'Business integrations' },
{ id: 'storage', name: 'Storage', icon: '💾', description: 'Data persistence and caching' }
],
augmentations: [
{
id: 'vector-search',
name: 'Vector Search',
category: 'core',
description: 'High-performance semantic search with HNSW indexing',
status: 'available',
popular: true
},
{
id: 'neural-similarity',
name: 'Neural Similarity API',
category: 'neural',
description: 'Advanced semantic similarity, clustering, and hierarchy detection',
status: 'available',
popular: true
},
{
id: 'intelligent-verb-scoring',
name: 'Intelligent Verb Scoring',
category: 'neural',
description: 'Smart relationship scoring with taxonomy understanding',
status: 'available'
},
{
id: 'connection-pooling',
name: 'Connection Pooling',
category: 'enterprise',
description: 'Efficient database connection management',
status: 'available'
},
{
id: 'batch-processing',
name: 'Batch Processing',
category: 'enterprise',
description: 'High-throughput batch operations with deduplication',
status: 'available'
},
{
id: 's3-storage',
name: 'S3 Compatible Storage',
category: 'storage',
description: 'Cloud storage with optimized batch operations',
status: 'available'
},
{
id: 'opfs-storage',
name: 'OPFS Storage',
category: 'storage',
description: 'Browser-based persistent storage',
status: 'available'
}
]
}
}