feat: Add Brain Cloud integration and catalog command
- Add brainy cloud setup command for auto-provisioning - Add brainy catalog command for viewing augmentations - Update CLI help with Brain Cloud commands - Improve cloud connection flow
This commit is contained in:
parent
b9ce67f64c
commit
6de6adec17
3 changed files with 896 additions and 12 deletions
481
bin/brainy.js
481
bin/brainy.js
|
|
@ -204,24 +204,138 @@ program
|
|||
console.log(chalk.cyan('Restart Claude Code to activate memory.'))
|
||||
} else {
|
||||
console.log(chalk.yellow('No Brain Cloud found. Setting up:'))
|
||||
console.log('\n1. Visit: ' + chalk.cyan('https://soulcraft.com'))
|
||||
console.log('2. Sign up for Brain Cloud')
|
||||
console.log('3. Run ' + chalk.green('brainy connect') + ' again')
|
||||
console.log('\n1. Visit: ' + chalk.cyan('https://soulcraft.com/brain-cloud'))
|
||||
console.log('2. Get your Early Access license key')
|
||||
console.log('3. Run ' + chalk.green('brainy cloud setup') + ' for auto-configuration')
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(chalk.red('❌ Setup failed:'), error.message)
|
||||
}
|
||||
}))
|
||||
|
||||
// Moved to brainy cloud setup command below for better separation
|
||||
|
||||
// ========================================
|
||||
// BRAIN CLOUD COMMANDS (Premium Features)
|
||||
// ========================================
|
||||
|
||||
const cloud = program
|
||||
.command('cloud')
|
||||
.description('Brain Cloud premium features and management')
|
||||
|
||||
cloud
|
||||
.command('setup')
|
||||
.description('🚀 Auto-setup Brain Cloud (provisions cloud instance + configures locally)')
|
||||
.option('--email <email>', 'Your email address')
|
||||
.action(wrapInteractive(async (options) => {
|
||||
console.log(chalk.cyan('🧠☁️ Brain Cloud Auto-Setup'))
|
||||
console.log(chalk.gray('═'.repeat(50)))
|
||||
console.log(chalk.yellow('Perfect for non-coders! One-click setup.\n'))
|
||||
|
||||
try {
|
||||
// Step 1: Validate license
|
||||
await validateLicense()
|
||||
|
||||
// Step 2: Check if Brainy is installed
|
||||
await ensureBrainyInstalled()
|
||||
|
||||
// Step 3: Provision cloud instance
|
||||
const instance = await provisionCloudInstance(options.email)
|
||||
|
||||
// Step 4: Configure local Brainy
|
||||
await configureBrainy(instance)
|
||||
|
||||
// Step 5: Install Brain Cloud package
|
||||
await installBrainCloudPackage()
|
||||
|
||||
// Step 6: Test connection
|
||||
await testConnection(instance)
|
||||
|
||||
console.log('\n✅ Setup Complete!')
|
||||
console.log(chalk.gray('═'.repeat(30)))
|
||||
console.log('\nYour Brain Cloud instance is ready:')
|
||||
console.log(`📱 Dashboard: ${chalk.cyan(instance.endpoints.dashboard)}`)
|
||||
console.log(`🔗 API: ${chalk.gray(instance.endpoints.api)}`)
|
||||
console.log('\n🚀 What\'s next?')
|
||||
console.log('• Your AI now has persistent memory across all conversations')
|
||||
console.log('• All devices sync automatically to your cloud instance')
|
||||
console.log('• Agents coordinate seamlessly through handoffs')
|
||||
console.log('\n💡 Try asking Claude: "Remember that I prefer TypeScript"')
|
||||
console.log(' Then in a new conversation: "What do you know about my preferences?"')
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Setup failed:', error.message)
|
||||
console.log('\n🆘 Need help? Contact support@soulcraft.com')
|
||||
}
|
||||
}))
|
||||
|
||||
cloud
|
||||
.command('connect [id]')
|
||||
.description('Connect to existing Brain Cloud instance')
|
||||
.action(wrapInteractive(async (id) => {
|
||||
if (id) {
|
||||
console.log(chalk.green(`✅ Connecting to Brain Cloud instance: ${id}`))
|
||||
// Connect to specific instance
|
||||
} else {
|
||||
// Show connection instructions
|
||||
console.log(chalk.cyan('\n🔗 Brain Cloud Connection'))
|
||||
console.log(chalk.gray('━'.repeat(40)))
|
||||
console.log('\nOptions:')
|
||||
console.log('1. ' + chalk.green('brainy cloud setup') + ' - Auto-setup with provisioning')
|
||||
console.log('2. ' + chalk.green('brainy cloud connect <id>') + ' - Connect to existing instance')
|
||||
console.log('\nGet started: ' + chalk.cyan('https://soulcraft.com/brain-cloud'))
|
||||
}
|
||||
}))
|
||||
|
||||
cloud
|
||||
.command('status [id]')
|
||||
.description('Check Brain Cloud instance status')
|
||||
.action(wrapInteractive(async (id) => {
|
||||
// Implementation moved from old cloud command
|
||||
console.log('Checking Brain Cloud status...')
|
||||
}))
|
||||
|
||||
cloud
|
||||
.command('dashboard [id]')
|
||||
.description('Open Brain Cloud dashboard')
|
||||
.action(wrapInteractive(async (id) => {
|
||||
const dashboardUrl = id
|
||||
? `https://brainy-${id}.soulcraft-brain.workers.dev/dashboard`
|
||||
: 'https://app.soulcraft.com'
|
||||
|
||||
console.log(chalk.cyan(`\n🌐 Opening Brain Cloud Dashboard: ${dashboardUrl}`))
|
||||
|
||||
try {
|
||||
const { exec } = await import('child_process')
|
||||
const { promisify } = await import('util')
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
const command = process.platform === 'win32' ? 'start' :
|
||||
process.platform === 'darwin' ? 'open' : 'xdg-open'
|
||||
|
||||
await execAsync(`${command} "${dashboardUrl}"`)
|
||||
console.log(chalk.green('✅ Dashboard opened!'))
|
||||
} catch (error) {
|
||||
console.log(chalk.yellow('💡 Copy the URL above to open in your browser'))
|
||||
}
|
||||
}))
|
||||
|
||||
// Legacy cloud command (for backward compatibility)
|
||||
program
|
||||
.command('cloud [action]')
|
||||
.description('Manage Brain Cloud connection')
|
||||
.command('cloud-legacy [action]')
|
||||
.description('Legacy Brain Cloud connection (deprecated - use "brainy cloud")')
|
||||
.option('--connect <id>', 'Connect to existing Brain Cloud instance')
|
||||
.option('--export <id>', 'Export all data from Brain Cloud instance')
|
||||
.option('--status <id>', 'Check status of Brain Cloud instance')
|
||||
.option('--dashboard <id>', 'Open dashboard for Brain Cloud instance')
|
||||
.option('--migrate', 'Migrate between local and cloud')
|
||||
.action(wrapInteractive(async (action, options) => {
|
||||
console.log(chalk.yellow('⚠️ Deprecated: Use "brainy cloud" commands instead'))
|
||||
console.log(chalk.cyan('Examples:'))
|
||||
console.log(' brainy cloud setup')
|
||||
console.log(' brainy cloud connect <id>')
|
||||
console.log(' brainy cloud dashboard')
|
||||
console.log('')
|
||||
// For now, show connection instructions
|
||||
console.log(chalk.cyan('\n⚛️ BRAIN CLOUD - AI Memory That Never Forgets'))
|
||||
console.log(chalk.gray('━'.repeat(50)))
|
||||
|
|
@ -383,9 +497,47 @@ augment
|
|||
}
|
||||
} catch {}
|
||||
|
||||
console.log(chalk.dim('📦 Available:'))
|
||||
console.log(chalk.dim(' • notion-sync (Premium)'))
|
||||
console.log(chalk.dim(' • 40+ more at app.soulcraft.com'))
|
||||
// Fetch from catalog API
|
||||
try {
|
||||
const response = await fetch('http://localhost:3001/api/catalog/cli')
|
||||
if (response.ok) {
|
||||
const catalog = await response.json()
|
||||
|
||||
// Show available augmentations
|
||||
const available = catalog.augmentations.filter(aug => aug.status === 'available')
|
||||
if (available.length > 0) {
|
||||
console.log(chalk.cyan('🌟 Available (Brain Cloud):'))
|
||||
available.forEach(aug => {
|
||||
const popular = aug.popular ? chalk.yellow(' ⭐ Popular') : ''
|
||||
console.log(` • ${aug.id} - ${aug.description}${popular}`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
|
||||
// Show coming soon
|
||||
const comingSoon = catalog.augmentations.filter(aug => aug.status === 'coming-soon')
|
||||
if (comingSoon.length > 0) {
|
||||
console.log(chalk.dim('📦 Coming Soon:'))
|
||||
comingSoon.forEach(aug => {
|
||||
const eta = aug.eta ? ` (${aug.eta})` : ''
|
||||
console.log(chalk.dim(` • ${aug.id} - ${aug.description}${eta}`))
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
} else {
|
||||
throw new Error('API unavailable')
|
||||
}
|
||||
} catch (error) {
|
||||
// Fallback to static list if API is unavailable
|
||||
console.log(chalk.cyan('🌟 Available (Brain Cloud):'))
|
||||
console.log(' • ai-memory - ' + chalk.yellow('⭐ Popular') + ' - Persistent AI memory')
|
||||
console.log(' • agent-coordinator - ' + chalk.yellow('⭐ Popular') + ' - Multi-agent handoffs')
|
||||
console.log(' • notion-sync - Enterprise connector')
|
||||
console.log('')
|
||||
}
|
||||
|
||||
console.log(chalk.dim('🚀 Join Early Access: https://soulcraft.com/brain-cloud'))
|
||||
console.log(chalk.dim('📦 Install: npm install @soulcraft/brain-cloud'))
|
||||
}))
|
||||
|
||||
augment
|
||||
|
|
@ -719,9 +871,10 @@ if (!process.argv.slice(2).length) {
|
|||
console.log(' brainy search "query" # Search across all dimensions')
|
||||
console.log(' brainy chat # AI chat with full context')
|
||||
console.log('')
|
||||
console.log(chalk.bold('AI Memory:'))
|
||||
console.log(chalk.green(' brainy connect # Connect to Brain Cloud'))
|
||||
console.log(' brainy cloud --status <id> # Check cloud status')
|
||||
console.log(chalk.bold('Brain Cloud (Premium):'))
|
||||
console.log(chalk.green(' brainy cloud setup # Auto-setup with provisioning'))
|
||||
console.log(' brainy cloud connect <id> # Connect to existing instance')
|
||||
console.log(' brainy cloud dashboard # Open Brain Cloud dashboard')
|
||||
console.log('')
|
||||
console.log(chalk.bold('AI Coordination:'))
|
||||
console.log(' brainy install brain-jar # Install coordination')
|
||||
|
|
@ -857,4 +1010,310 @@ When working with multiple AI assistants, we automatically coordinate:
|
|||
} catch (error) {
|
||||
console.log(chalk.yellow('⚠️ Could not save brainy config:', error.message))
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// AUTO-SETUP HELPER FUNCTIONS
|
||||
// ========================================
|
||||
|
||||
const PROVISIONING_API = 'https://provisioning.soulcraft.com'
|
||||
|
||||
let spinner = null
|
||||
|
||||
function startSpinner(message) {
|
||||
stopSpinner()
|
||||
process.stdout.write(`${message} `)
|
||||
|
||||
const spinnerChars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
||||
let i = 0
|
||||
|
||||
spinner = setInterval(() => {
|
||||
process.stdout.write(`\r${message} ${spinnerChars[i]}`)
|
||||
i = (i + 1) % spinnerChars.length
|
||||
}, 100)
|
||||
}
|
||||
|
||||
function stopSpinner() {
|
||||
if (spinner) {
|
||||
clearInterval(spinner)
|
||||
spinner = null
|
||||
process.stdout.write('\r')
|
||||
}
|
||||
}
|
||||
|
||||
async function validateLicense() {
|
||||
startSpinner('Validating Early Access license...')
|
||||
|
||||
const licenseKey = process.env.BRAINY_LICENSE_KEY
|
||||
|
||||
if (!licenseKey) {
|
||||
stopSpinner()
|
||||
console.log('\n❌ No license key found')
|
||||
console.log('\n🔑 Please set your Early Access license key:')
|
||||
console.log(' export BRAINY_LICENSE_KEY="lic_early_access_your_key"')
|
||||
console.log('\n📝 Don\'t have a key? Get one free at: https://soulcraft.com/brain-cloud')
|
||||
throw new Error('License key required')
|
||||
}
|
||||
|
||||
if (!licenseKey.startsWith('lic_early_access_')) {
|
||||
stopSpinner()
|
||||
throw new Error('Invalid license key format. Early Access keys start with "lic_early_access_"')
|
||||
}
|
||||
|
||||
stopSpinner()
|
||||
console.log('✅ License validated')
|
||||
}
|
||||
|
||||
async function ensureBrainyInstalled() {
|
||||
startSpinner('Checking Brainy installation...')
|
||||
|
||||
try {
|
||||
const { exec } = await import('child_process')
|
||||
const { promisify } = await import('util')
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
await execAsync('brainy --version')
|
||||
stopSpinner()
|
||||
console.log('✅ Brainy CLI found')
|
||||
} catch (error) {
|
||||
stopSpinner()
|
||||
console.log('📦 Installing Brainy CLI...')
|
||||
|
||||
try {
|
||||
await execWithProgress('npm install -g @soulcraft/brainy')
|
||||
console.log('✅ Brainy CLI installed')
|
||||
} catch (installError) {
|
||||
throw new Error('Failed to install Brainy CLI. Please install manually: npm install -g @soulcraft/brainy')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function provisionCloudInstance(userEmail) {
|
||||
const licenseKey = process.env.BRAINY_LICENSE_KEY
|
||||
startSpinner('Provisioning your cloud Brainy instance...')
|
||||
|
||||
try {
|
||||
const response = await fetch(`${PROVISIONING_API}/provision`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
licenseKey,
|
||||
userEmail: userEmail || 'user@example.com'
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.message || 'Provisioning failed')
|
||||
}
|
||||
|
||||
const result = await response.json()
|
||||
stopSpinner()
|
||||
|
||||
if (result.instance.status === 'active') {
|
||||
console.log('✅ Cloud instance already active')
|
||||
return result.instance
|
||||
}
|
||||
|
||||
console.log('🚀 Provisioning started (2-3 minutes)')
|
||||
|
||||
// Wait for provisioning to complete
|
||||
return await waitForProvisioning(licenseKey)
|
||||
|
||||
} catch (error) {
|
||||
stopSpinner()
|
||||
throw new Error(`Provisioning failed: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForProvisioning(licenseKey) {
|
||||
const maxWaitTime = 5 * 60 * 1000 // 5 minutes
|
||||
const checkInterval = 15 * 1000 // 15 seconds
|
||||
const startTime = Date.now()
|
||||
|
||||
while (Date.now() - startTime < maxWaitTime) {
|
||||
startSpinner('Waiting for cloud instance to be ready...')
|
||||
|
||||
try {
|
||||
const response = await fetch(`${PROVISIONING_API}/status?licenseKey=${encodeURIComponent(licenseKey)}`)
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json()
|
||||
|
||||
if (result.instance.status === 'active') {
|
||||
stopSpinner()
|
||||
console.log('✅ Cloud instance is ready')
|
||||
return result.instance
|
||||
}
|
||||
|
||||
if (result.instance.status === 'failed') {
|
||||
stopSpinner()
|
||||
throw new Error('Instance provisioning failed')
|
||||
}
|
||||
}
|
||||
|
||||
stopSpinner()
|
||||
await new Promise(resolve => setTimeout(resolve, checkInterval))
|
||||
|
||||
} catch (error) {
|
||||
stopSpinner()
|
||||
console.log('⏳ Still provisioning...')
|
||||
await new Promise(resolve => setTimeout(resolve, checkInterval))
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Provisioning timeout. Please check your dashboard or contact support.')
|
||||
}
|
||||
|
||||
async function configureBrainy(instance) {
|
||||
const { writeFile, mkdir } = await import('fs/promises')
|
||||
const { join } = await import('path')
|
||||
const { homedir } = await import('os')
|
||||
const { existsSync } = await import('fs')
|
||||
|
||||
startSpinner('Configuring local Brainy to use cloud instance...')
|
||||
|
||||
// Ensure config directory exists
|
||||
const BRAINY_CONFIG_DIR = join(homedir(), '.brainy')
|
||||
const BRAINY_CONFIG_FILE = join(BRAINY_CONFIG_DIR, 'config.json')
|
||||
|
||||
if (!existsSync(BRAINY_CONFIG_DIR)) {
|
||||
await mkdir(BRAINY_CONFIG_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
// Create or update Brainy config
|
||||
let config = {}
|
||||
if (existsSync(BRAINY_CONFIG_FILE)) {
|
||||
try {
|
||||
const { readFile } = await import('fs/promises')
|
||||
const existing = await readFile(BRAINY_CONFIG_FILE, 'utf8')
|
||||
config = JSON.parse(existing)
|
||||
} catch (error) {
|
||||
console.log('⚠️ Could not read existing config, creating new one')
|
||||
}
|
||||
}
|
||||
|
||||
// Update config with cloud instance details
|
||||
config.cloudSync = {
|
||||
enabled: true,
|
||||
endpoint: instance.endpoints.api,
|
||||
instanceId: instance.id,
|
||||
licenseKey: process.env.BRAINY_LICENSE_KEY
|
||||
}
|
||||
|
||||
config.aiMemory = {
|
||||
enabled: true,
|
||||
storage: 'cloud',
|
||||
endpoint: instance.endpoints.api
|
||||
}
|
||||
|
||||
config.agentCoordination = {
|
||||
enabled: true,
|
||||
endpoint: instance.endpoints.api
|
||||
}
|
||||
|
||||
await writeFile(BRAINY_CONFIG_FILE, JSON.stringify(config, null, 2))
|
||||
|
||||
stopSpinner()
|
||||
console.log('✅ Local Brainy configured for cloud sync')
|
||||
}
|
||||
|
||||
async function installBrainCloudPackage() {
|
||||
startSpinner('Installing Brain Cloud augmentations...')
|
||||
|
||||
try {
|
||||
const { existsSync } = await import('fs')
|
||||
|
||||
// Check if we're in a project directory
|
||||
const hasPackageJson = existsSync('package.json')
|
||||
|
||||
if (hasPackageJson) {
|
||||
await execWithProgress('npm install @soulcraft/brain-cloud')
|
||||
console.log('✅ Brain Cloud package installed in current project')
|
||||
} else {
|
||||
// Install globally for non-project usage
|
||||
await execWithProgress('npm install -g @soulcraft/brain-cloud')
|
||||
console.log('✅ Brain Cloud package installed globally')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
stopSpinner()
|
||||
console.log('⚠️ Could not auto-install Brain Cloud package')
|
||||
console.log(' You can install it manually: npm install @soulcraft/brain-cloud')
|
||||
// Don't throw error, this is optional
|
||||
}
|
||||
}
|
||||
|
||||
async function testConnection(instance) {
|
||||
startSpinner('Testing cloud connection...')
|
||||
|
||||
try {
|
||||
const response = await fetch(`${instance.endpoints.api}/health`)
|
||||
|
||||
if (response.ok) {
|
||||
const health = await response.json()
|
||||
stopSpinner()
|
||||
console.log('✅ Cloud instance connection verified')
|
||||
|
||||
// Test memory storage
|
||||
const testMemory = {
|
||||
content: 'Test memory from auto-setup',
|
||||
source: 'brain-cloud-setup',
|
||||
importance: 'low',
|
||||
tags: ['setup', 'test']
|
||||
}
|
||||
|
||||
const memoryResponse = await fetch(`${instance.endpoints.api}/api/memories`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(testMemory)
|
||||
})
|
||||
|
||||
if (memoryResponse.ok) {
|
||||
console.log('✅ Memory storage working')
|
||||
}
|
||||
|
||||
} else {
|
||||
stopSpinner()
|
||||
console.log('⚠️ Cloud instance not responding yet (this is normal)')
|
||||
console.log(' Your instance may need a few more minutes to fully initialize')
|
||||
}
|
||||
} catch (error) {
|
||||
stopSpinner()
|
||||
console.log('⚠️ Could not test connection (this is usually fine)')
|
||||
}
|
||||
}
|
||||
|
||||
async function execWithProgress(command) {
|
||||
const { spawn } = await import('child_process')
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn('sh', ['-c', command], {
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
shell: true
|
||||
})
|
||||
|
||||
let stdout = ''
|
||||
let stderr = ''
|
||||
|
||||
child.stdout?.on('data', (data) => {
|
||||
stdout += data.toString()
|
||||
process.stdout.write('.')
|
||||
})
|
||||
|
||||
child.stderr?.on('data', (data) => {
|
||||
stderr += data.toString()
|
||||
})
|
||||
|
||||
child.on('close', (code) => {
|
||||
process.stdout.write('\n')
|
||||
if (code === 0) {
|
||||
resolve(stdout)
|
||||
} else {
|
||||
reject(new Error(stderr || `Command failed with code ${code}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue