feat: add zero-configuration Brainy service template with augmentation-first architecture
- Implement WebSocket augmentation for real-time communication - Implement WebRTC augmentation for peer-to-peer connections - Implement HTTP augmentation as minimal REST fallback - Add auto-discovery augmentation for data pattern analysis - Add adaptive storage augmentation for intelligent resource management - Add environment adapter augmentation for universal compatibility - Template auto-detects environment (browser, Node.js, serverless, containers) - Intelligent transport selection (WebRTC → WebSocket → HTTP) - Automatic storage optimization (memory → filesystem → S3) - Zero configuration required - just npm start - Includes intelligent verb scoring by default - Works in any environment without configuration - Full documentation and examples included
This commit is contained in:
parent
880b8f74e3
commit
ee003a9473
30 changed files with 3548 additions and 0 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,165 @@
|
|||
import { WebSocket, WebSocketServer } from 'ws'
|
||||
import { logger } from '../utils/logger.js'
|
||||
|
||||
/**
|
||||
* WebSocket Augmentation - Real-time communication using Brainy's native augmentation system
|
||||
* Handles real-time queries, updates, and bidirectional communication
|
||||
*/
|
||||
export class WebSocketAugmentation {
|
||||
constructor(options = {}) {
|
||||
this.type = 'WEBSOCKET'
|
||||
this.priority = 2
|
||||
this.options = {
|
||||
port: options.port || 3001,
|
||||
enableRealtime: options.enableRealtime !== false,
|
||||
enableBroadcast: options.enableBroadcast !== false,
|
||||
heartbeatInterval: options.heartbeatInterval || 30000,
|
||||
...options
|
||||
}
|
||||
|
||||
this.server = null
|
||||
this.clients = new Set()
|
||||
this.db = null
|
||||
this.heartbeatTimer = null
|
||||
}
|
||||
|
||||
async augment(brainyData, context) {
|
||||
this.db = brainyData
|
||||
|
||||
try {
|
||||
// Create WebSocket server
|
||||
this.server = new WebSocketServer({
|
||||
port: this.options.port,
|
||||
perMessageDeflate: true
|
||||
})
|
||||
|
||||
this.server.on('connection', (ws, request) => {
|
||||
this.handleConnection(ws, request)
|
||||
})
|
||||
|
||||
// Start heartbeat for connection management
|
||||
if (this.options.heartbeatInterval > 0) {
|
||||
this.startHeartbeat()
|
||||
}
|
||||
|
||||
logger.info('WebSocket augmentation initialized', {
|
||||
port: this.options.port,
|
||||
features: {
|
||||
realtime: this.options.enableRealtime,
|
||||
broadcast: this.options.enableBroadcast
|
||||
}
|
||||
})
|
||||
|
||||
// Hook into Brainy events if real-time is enabled
|
||||
if (this.options.enableRealtime) {
|
||||
this.setupRealtimeHooks()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to initialize WebSocket augmentation:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
handleConnection(ws, request) {
|
||||
const clientId = this.generateClientId()
|
||||
ws.clientId = clientId
|
||||
this.clients.add(ws)
|
||||
|
||||
logger.debug('WebSocket client connected', {
|
||||
clientId,
|
||||
ip: request.socket.remoteAddress,
|
||||
totalClients: this.clients.size
|
||||
})
|
||||
|
||||
// Send welcome message
|
||||
this.send(ws, {
|
||||
type: 'welcome',
|
||||
clientId,
|
||||
capabilities: {
|
||||
realtime: this.options.enableRealtime,
|
||||
broadcast: this.options.enableBroadcast,
|
||||
commands: ['search', 'add', 'addVerb', 'get', 'subscribe', 'unsubscribe']
|
||||
}
|
||||
})
|
||||
|
||||
ws.on('message', (data) => {
|
||||
this.handleMessage(ws, data)
|
||||
})
|
||||
|
||||
ws.on('close', () => {
|
||||
this.clients.delete(ws)
|
||||
logger.debug('WebSocket client disconnected', {
|
||||
clientId,
|
||||
totalClients: this.clients.size
|
||||
})
|
||||
})
|
||||
|
||||
ws.on('error', (error) => {
|
||||
logger.warn('WebSocket client error:', { clientId, error: error.message })
|
||||
})
|
||||
|
||||
// Keep connection alive
|
||||
ws.isAlive = true
|
||||
ws.on('pong', () => {
|
||||
ws.isAlive = true
|
||||
})
|
||||
}
|
||||
|
||||
async handleMessage(ws, data) {
|
||||
try {
|
||||
const message = JSON.parse(data.toString())
|
||||
const { id, type, payload } = message
|
||||
|
||||
let result
|
||||
|
||||
switch (type) {
|
||||
case 'search':
|
||||
result = await this.handleSearch(payload)
|
||||
break
|
||||
|
||||
case 'add':
|
||||
result = await this.handleAdd(payload)
|
||||
break
|
||||
|
||||
case 'addVerb':
|
||||
result = await this.handleAddVerb(payload)
|
||||
break
|
||||
|
||||
case 'get':
|
||||
result = await this.handleGet(payload)
|
||||
break
|
||||
|
||||
case 'subscribe':
|
||||
result = await this.handleSubscribe(ws, payload)
|
||||
break
|
||||
|
||||
case 'unsubscribe':
|
||||
result = await this.handleUnsubscribe(ws, payload)
|
||||
break
|
||||
|
||||
case 'ping':
|
||||
result = { pong: Date.now() }
|
||||
break
|
||||
|
||||
default:\n throw new Error(`Unknown message type: ${type}`)
|
||||
}
|
||||
|
||||
// Send response
|
||||
this.send(ws, {\n id,\n type: 'response',\n payload: result\n })
|
||||
|
||||
} catch (error) {\n logger.warn('WebSocket message handling error:', error)\n this.send(ws, {\n id: message?.id,\n type: 'error',\n payload: {\n message: error.message,\n code: error.code || 'UNKNOWN_ERROR'\n }\n })\n }\n }
|
||||
|
||||
async handleSearch(payload) {\n const { query, limit = 10, threshold = 0.7, options = {} } = payload\n \n if (!query) {\n throw new Error('Query is required for search')\n }\n\n const results = await this.db.search(query, limit, { threshold, ...options })\n \n return {\n query,\n results,\n count: results.length,\n timestamp: new Date().toISOString()\n }\n }
|
||||
|
||||
async handleAdd(payload) {\n const { data, metadata = {}, options = {} } = payload\n \n if (!data) {\n throw new Error('Data is required for add operation')\n }\n\n const enhancedMetadata = {\n ...metadata,\n source: 'websocket',\n timestamp: new Date().toISOString()\n }\n\n const id = await this.db.add(data, enhancedMetadata, options)\n \n const result = {\n id,\n data,\n metadata: enhancedMetadata\n }\n\n // Broadcast to subscribers if enabled\n if (this.options.enableBroadcast) {\n this.broadcast({\n type: 'entityAdded',\n payload: result\n })\n }\n\n return result\n }
|
||||
|
||||
async handleAddVerb(payload) {\n const { sourceId, targetId, verbType, weight, metadata = {}, options = {} } = payload\n \n if (!sourceId || !targetId || !verbType) {\n throw new Error('sourceId, targetId, and verbType are required')\n }\n\n const enhancedMetadata = {\n ...metadata,\n source: 'websocket',\n timestamp: new Date().toISOString()\n }\n\n const verbId = await this.db.addVerb(sourceId, targetId, verbType, {\n weight,\n metadata: enhancedMetadata,\n ...options\n })\n \n const result = {\n id: verbId,\n sourceId,\n targetId,\n verbType,\n weight,\n metadata: enhancedMetadata\n }\n\n // Broadcast to subscribers if enabled\n if (this.options.enableBroadcast) {\n this.broadcast({\n type: 'verbAdded',\n payload: result\n })\n }\n\n return result\n }
|
||||
|
||||
async handleGet(payload) {\n const { id } = payload\n \n if (!id) {\n throw new Error('ID is required for get operation')\n }\n\n const entity = await this.db.get(id)\n \n if (!entity) {\n throw new Error(`Entity with ID ${id} not found`)\n }\n\n return entity\n }
|
||||
|
||||
async handleSubscribe(ws, payload) {\n const { events = ['entityAdded', 'verbAdded'] } = payload\n \n if (!ws.subscriptions) {\n ws.subscriptions = new Set()\n }\n \n events.forEach(event => ws.subscriptions.add(event))\n \n return {\n subscribed: Array.from(ws.subscriptions),\n message: 'Successfully subscribed to events'\n }\n }
|
||||
|
||||
async handleUnsubscribe(ws, payload) {\n const { events = [] } = payload\n \n if (!ws.subscriptions) {\n return { message: 'No active subscriptions' }\n }\n \n if (events.length === 0) {\n // Unsubscribe from all\n ws.subscriptions.clear()\n } else {\n events.forEach(event => ws.subscriptions.delete(event))\n }\n \n return {\n subscribed: Array.from(ws.subscriptions),\n message: 'Successfully unsubscribed from events'\n }\n }
|
||||
|
||||
setupRealtimeHooks() {\n // Hook into Brainy's internal events for real-time updates\n // This would integrate with Brainy's event system once available\n logger.debug('Real-time hooks configured for WebSocket augmentation')\n }\n\n broadcast(message, filter = null) {\n if (!this.options.enableBroadcast) return\n \n const data = JSON.stringify(message)\n \n this.clients.forEach(client => {\n if (client.readyState === WebSocket.OPEN) {\n // Check if client is subscribed to this event type\n if (client.subscriptions && client.subscriptions.has(message.type)) {\n // Apply filter if provided\n if (!filter || filter(client)) {\n client.send(data)\n }\n }\n }\n })\n }\n\n send(ws, message) {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(message))\n }\n }\n\n startHeartbeat() {\n this.heartbeatTimer = setInterval(() => {\n this.clients.forEach(ws => {\n if (!ws.isAlive) {\n ws.terminate()\n return\n }\n \n ws.isAlive = false\n ws.ping()\n })\n }, this.options.heartbeatInterval)\n }\n\n generateClientId() {\n return `ws_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n }\n\n async cleanup() {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n }\n \n if (this.server) {\n this.server.close()\n }\n \n // Close all client connections\n this.clients.forEach(client => {\n client.close()\n })\n this.clients.clear()\n \n logger.info('WebSocket augmentation cleaned up')\n }\n}
|
||||
Loading…
Add table
Add a link
Reference in a new issue