- 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.
440 lines
No EOL
13 KiB
TypeScript
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'
|
|
}
|
|
]
|
|
}
|
|
} |