Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
bifrost.go5.45 kB
// Package heimdall provides Heimdall - the cognitive guardian for NornicDB. package heimdall import ( "encoding/json" "fmt" "net/http" "sync" "time" ) // Bifrost implements BifrostBridge for real-time communication with clients. // Named after the rainbow bridge that connects Asgard to other realms. // Bifrost is the communication layer between Heimdall and connected UI clients. type Bifrost struct { mu sync.RWMutex clients map[string]*BifrostClient config Config } // BifrostClient represents a connected client. type BifrostClient struct { ID string Flusher http.Flusher Writer http.ResponseWriter ConnectedAt time.Time LastPing time.Time } // BifrostMessage is a message sent through Bifrost. type BifrostMessage struct { Type string `json:"type"` // "message", "notification", "confirmation" Timestamp int64 `json:"timestamp"` // Unix timestamp Content string `json:"content,omitempty"` Title string `json:"title,omitempty"` Level string `json:"level,omitempty"` // "info", "warning", "error", "success" Data map[string]interface{} `json:"data,omitempty"` } // NewBifrost creates a new Bifrost bridge. // Returns nil if Bifrost is not enabled in config. func NewBifrost(cfg Config) *Bifrost { if !cfg.BifrostEnabled { return nil } return &Bifrost{ clients: make(map[string]*BifrostClient), config: cfg, } } // RegisterClient adds a new connected client. func (b *Bifrost) RegisterClient(id string, w http.ResponseWriter, f http.Flusher) { b.mu.Lock() defer b.mu.Unlock() b.clients[id] = &BifrostClient{ ID: id, Writer: w, Flusher: f, ConnectedAt: time.Now(), LastPing: time.Now(), } } // UnregisterClient removes a disconnected client. func (b *Bifrost) UnregisterClient(id string) { b.mu.Lock() defer b.mu.Unlock() delete(b.clients, id) } // === BifrostBridge Interface Implementation === // SendMessage sends a message to all connected Bifrost clients. // The message appears as a system message in the chat. func (b *Bifrost) SendMessage(msg string) error { return b.broadcast(BifrostMessage{ Type: "message", Timestamp: time.Now().Unix(), Content: msg, }) } // SendNotification sends a notification with a specific type. // Types: "info", "warning", "error", "success" func (b *Bifrost) SendNotification(notifType, title, message string) error { return b.broadcast(BifrostMessage{ Type: "notification", Timestamp: time.Now().Unix(), Level: notifType, Title: title, Content: message, }) } // Broadcast sends a message to all connected clients. // Useful for system-wide announcements. func (b *Bifrost) Broadcast(msg string) error { return b.broadcast(BifrostMessage{ Type: "broadcast", Timestamp: time.Now().Unix(), Content: msg, }) } // RequestConfirmation asks the user to confirm an action. // Returns true if user confirms, false if they decline or timeout. // Note: This is a simplified implementation - real implementation would // need WebSocket for bidirectional communication. func (b *Bifrost) RequestConfirmation(action string) (bool, error) { // For SSE (unidirectional), we can't wait for response // Send notification and return false (require explicit confirmation via API) err := b.broadcast(BifrostMessage{ Type: "confirmation_request", Timestamp: time.Now().Unix(), Content: action, Data: map[string]interface{}{ "action": action, "timeout": 30, // seconds }, }) if err != nil { return false, err } // SSE is unidirectional - confirmation must come via separate API call // Return false to indicate confirmation pending return false, nil } // IsConnected returns true if there are active Bifrost connections. func (b *Bifrost) IsConnected() bool { b.mu.RLock() defer b.mu.RUnlock() return len(b.clients) > 0 } // ConnectionCount returns the number of active Bifrost connections. func (b *Bifrost) ConnectionCount() int { b.mu.RLock() defer b.mu.RUnlock() return len(b.clients) } // === Internal Methods === // broadcast sends a message to all connected clients via SSE. func (b *Bifrost) broadcast(msg BifrostMessage) error { b.mu.RLock() defer b.mu.RUnlock() if len(b.clients) == 0 { // No clients connected - not an error, just no-op return nil } data, err := json.Marshal(msg) if err != nil { return fmt.Errorf("failed to marshal message: %w", err) } // SSE format: "data: <json>\n\n" sseData := fmt.Sprintf("data: %s\n\n", string(data)) var lastErr error for _, client := range b.clients { if _, err := client.Writer.Write([]byte(sseData)); err != nil { lastErr = err continue } client.Flusher.Flush() } return lastErr } // Stats returns current Bifrost statistics. func (b *Bifrost) Stats() map[string]interface{} { b.mu.RLock() defer b.mu.RUnlock() clientInfo := make([]map[string]interface{}, 0, len(b.clients)) for _, c := range b.clients { clientInfo = append(clientInfo, map[string]interface{}{ "id": c.ID, "connected_at": c.ConnectedAt.Unix(), "last_ping": c.LastPing.Unix(), }) } return map[string]interface{}{ "enabled": b.config.BifrostEnabled, "connection_count": len(b.clients), "clients": clientInfo, } } // Ensure Bifrost implements BifrostBridge var _ BifrostBridge = (*Bifrost)(nil)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server