Initial commit: Split Macha autonomous system into separate flake

Macha is now a standalone NixOS flake that can be imported into other
systems. This provides:

- Independent versioning
- Easier reusability
- Cleaner separation of concerns
- Better development workflow

Includes:
- Complete autonomous system code
- NixOS module with full configuration options
- Queue-based architecture with priority system
- Chunked map-reduce for large outputs
- ChromaDB knowledge base
- Tool calling system
- Multi-host SSH management
- Gotify notification integration

All capabilities from DESIGN.md are preserved.
This commit is contained in:
Lily Miller
2025-10-06 14:32:37 -06:00
commit 22ba493d9e
30 changed files with 10306 additions and 0 deletions

248
notifier.py Normal file
View File

@@ -0,0 +1,248 @@
#!/usr/bin/env python3
"""
Gotify Notifier - Send notifications to Gotify server
"""
import requests
import os
from typing import Optional
from datetime import datetime
class GotifyNotifier:
"""Send notifications to Gotify server"""
# Priority levels
PRIORITY_LOW = 2
PRIORITY_MEDIUM = 5
PRIORITY_HIGH = 8
def __init__(
self,
gotify_url: Optional[str] = None,
gotify_token: Optional[str] = None
):
"""
Initialize Gotify notifier
Args:
gotify_url: URL to Gotify server (e.g. http://rhiannon:8181)
gotify_token: Application token from Gotify
"""
self.gotify_url = gotify_url or os.environ.get("GOTIFY_URL", "")
self.gotify_token = gotify_token or os.environ.get("GOTIFY_TOKEN", "")
self.enabled = bool(self.gotify_url and self.gotify_token)
def send(
self,
title: str,
message: str,
priority: int = PRIORITY_MEDIUM,
extras: Optional[dict] = None
) -> bool:
"""
Send a notification to Gotify
Args:
title: Notification title
message: Notification message
priority: Priority level (2=low, 5=medium, 8=high)
extras: Optional extra data
Returns:
True if successful, False otherwise
"""
if not self.enabled:
return False
try:
url = f"{self.gotify_url}/message"
headers = {
"Authorization": f"Bearer {self.gotify_token}",
"Content-Type": "application/json"
}
data = {
"title": title,
"message": message,
"priority": priority,
}
if extras:
data["extras"] = extras
response = requests.post(
url,
json=data,
headers=headers,
timeout=10
)
return response.status_code == 200
except Exception as e:
# Fail silently - don't crash if Gotify is unavailable
print(f"Warning: Failed to send Gotify notification: {e}")
return False
def notify_critical_issue(self, issue_description: str, details: str = ""):
"""Send high-priority notification for critical issues"""
message = f"⚠️ Critical Issue Detected\n\n{issue_description}"
if details:
message += f"\n\nDetails:\n{details}"
return self.send(
title="🚨 Macha: Critical Issue",
message=message,
priority=self.PRIORITY_HIGH
)
def notify_issue_created(self, issue_id: str, title: str, severity: str):
"""Send notification when a new issue is created"""
severity_icons = {
"low": "",
"medium": "⚠️",
"high": "🚨",
"critical": "🔴"
}
icon = severity_icons.get(severity, "⚠️")
priority_map = {
"low": self.PRIORITY_LOW,
"medium": self.PRIORITY_MEDIUM,
"high": self.PRIORITY_HIGH,
"critical": self.PRIORITY_HIGH
}
priority = priority_map.get(severity, self.PRIORITY_MEDIUM)
message = f"{icon} New Issue Tracked\n\nID: {issue_id}\nSeverity: {severity.upper()}\n\n{title}"
return self.send(
title="📋 Macha: Issue Created",
message=message,
priority=priority
)
def notify_action_queued(self, action_description: str, risk_level: str):
"""Send notification when action is queued for approval"""
emoji = "⚠️" if risk_level == "high" else ""
message = (
f"{emoji} Action Queued for Approval\n\n"
f"Action: {action_description}\n"
f"Risk Level: {risk_level}\n\n"
f"Use 'macha-approve list' to review"
)
priority = self.PRIORITY_HIGH if risk_level == "high" else self.PRIORITY_MEDIUM
return self.send(
title="📋 Macha: Action Needs Approval",
message=message,
priority=priority
)
def notify_action_executed(self, action_description: str, success: bool, output: str = ""):
"""Send notification when action is executed"""
if success:
emoji = ""
title_prefix = "Success"
else:
emoji = ""
title_prefix = "Failed"
message = f"{emoji} Action {title_prefix}\n\n{action_description}"
if output:
message += f"\n\nOutput:\n{output[:500]}" # Limit output length
priority = self.PRIORITY_HIGH if not success else self.PRIORITY_LOW
return self.send(
title=f"{emoji} Macha: Action {title_prefix}",
message=message,
priority=priority
)
def notify_service_failure(self, service_name: str, details: str = ""):
"""Send notification for service failures"""
message = f"🔴 Service Failed: {service_name}"
if details:
message += f"\n\nDetails:\n{details}"
return self.send(
title="🔴 Macha: Service Failure",
message=message,
priority=self.PRIORITY_HIGH
)
def notify_health_summary(self, summary: str, status: str):
"""Send periodic health summary"""
emoji = {
"healthy": "",
"attention_needed": "⚠️",
"intervention_required": "🚨"
}.get(status, "")
priority = {
"healthy": self.PRIORITY_LOW,
"attention_needed": self.PRIORITY_MEDIUM,
"intervention_required": self.PRIORITY_HIGH
}.get(status, self.PRIORITY_MEDIUM)
return self.send(
title=f"{emoji} Macha: Health Check",
message=summary,
priority=priority
)
def send_system_discovered(
self,
hostname: str,
os_type: str,
role: str,
services_count: int
):
"""Send notification when a new system is discovered"""
message = (
f"🔍 New System Auto-Discovered\n\n"
f"Hostname: {hostname}\n"
f"OS: {os_type.upper()}\n"
f"Role: {role}\n"
f"Services: {services_count} detected\n\n"
f"System has been registered and analyzed.\n"
f"Use 'macha-systems' to view all registered systems."
)
return self.send(
title="🌐 Macha: New System Discovered",
message=message,
priority=self.PRIORITY_MEDIUM
)
if __name__ == "__main__":
import sys
# Test the notifier
if len(sys.argv) < 3:
print("Usage: notifier.py <title> <message> [priority]")
print("Example: notifier.py 'Test' 'This is a test message' 5")
sys.exit(1)
title = sys.argv[1]
message = sys.argv[2]
priority = int(sys.argv[3]) if len(sys.argv) > 3 else GotifyNotifier.PRIORITY_MEDIUM
notifier = GotifyNotifier()
if not notifier.enabled:
print("Error: Gotify not configured (GOTIFY_URL and GOTIFY_TOKEN required)")
sys.exit(1)
success = notifier.send(title, message, priority)
if success:
print("✅ Notification sent successfully")
else:
print("❌ Failed to send notification")
sys.exit(1)