Network Anomaly Agent (secubox-network-anomaly): - 5 detection modules: bandwidth, connection flood, port scan, DNS, protocol - EMA-based baseline comparison - LocalAI integration for threat assessment - network-anomalyctl CLI LocalRecall Memory System (secubox-localrecall): - Persistent memory for AI agents - Categories: threats, decisions, patterns, configs, conversations - EMA-based importance scoring - LocalAI integration for summarization - localrecallctl CLI with 13 commands AI Insights Dashboard (luci-app-ai-insights): - Unified view across all AI agents - Security posture scoring (0-100) - Agent status grid with alert counts - Aggregated alerts from all agents - Run All Agents and AI Analysis actions LuCI Dashboards: - luci-app-network-anomaly with real-time stats - luci-app-localrecall with memory management Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
368 lines
7.1 KiB
Bash
368 lines
7.1 KiB
Bash
#!/bin/sh
|
|
# LocalRecall Memory Controller
|
|
# Persistent memory for AI agents
|
|
|
|
VERSION="1.0.0"
|
|
CONFIG="localrecall"
|
|
|
|
# Load libraries
|
|
. /usr/lib/localrecall/memory.sh
|
|
. /usr/lib/localrecall/ai.sh
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
LocalRecall Memory Controller v$VERSION
|
|
|
|
Usage: $(basename "$0") <command> [options]
|
|
|
|
Commands:
|
|
status Show memory status
|
|
add <cat> <content> Add a memory (category: threats|decisions|patterns|configs|conversations)
|
|
get <id> Get memory by ID
|
|
search <query> Search memories by content
|
|
list [category] List memories (optionally by category)
|
|
recent [N] Show N most recent memories (default: 20)
|
|
important [N] Show important memories (importance >= 7)
|
|
delete <id> Delete a memory
|
|
cleanup Cleanup old memories
|
|
export <file> Export memories to file
|
|
import <file> Import memories from file
|
|
summarize [cat] AI-summarize memories
|
|
context <agent> Get context for an agent
|
|
stats Show memory statistics
|
|
|
|
Options:
|
|
-a, --agent <name> Specify agent name (default: user)
|
|
-i, --importance N Set importance (1-10, default: 5)
|
|
-j, --json Output in JSON format
|
|
-q, --quiet Suppress output
|
|
|
|
Examples:
|
|
$(basename "$0") add threats "Detected SQL injection attempt from 192.168.1.100"
|
|
$(basename "$0") search "SQL injection"
|
|
$(basename "$0") context threat_analyst
|
|
$(basename "$0") summarize threats
|
|
EOF
|
|
}
|
|
|
|
uci_get() {
|
|
uci -q get "${CONFIG}.$1"
|
|
}
|
|
|
|
cmd_status() {
|
|
local json_mode="$1"
|
|
|
|
local enabled=$(uci_get main.enabled)
|
|
local storage=$(uci_get main.storage_path)
|
|
local max_mem=$(uci_get main.max_memories)
|
|
|
|
local total=$(count_memories)
|
|
local threats=$(count_category threats)
|
|
local decisions=$(count_category decisions)
|
|
local patterns=$(count_category patterns)
|
|
|
|
local localai_status="offline"
|
|
check_localai && localai_status="online"
|
|
|
|
local storage_size="0"
|
|
[ -d "$storage" ] && storage_size=$(du -sh "$storage" 2>/dev/null | cut -f1)
|
|
|
|
if [ "$json_mode" = "1" ]; then
|
|
cat <<EOF
|
|
{
|
|
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
|
|
"total_memories": $total,
|
|
"threats": $threats,
|
|
"decisions": $decisions,
|
|
"patterns": $patterns,
|
|
"max_memories": ${max_mem:-1000},
|
|
"storage_path": "${storage:-/var/lib/localrecall}",
|
|
"storage_size": "$storage_size",
|
|
"localai_status": "$localai_status"
|
|
}
|
|
EOF
|
|
else
|
|
echo "LocalRecall Memory Status"
|
|
echo "========================="
|
|
echo "Enabled: $([ "$enabled" = "1" ] && echo "Yes" || echo "No")"
|
|
echo "Total: $total memories"
|
|
echo " Threats: $threats"
|
|
echo " Decisions: $decisions"
|
|
echo " Patterns: $patterns"
|
|
echo "Max: ${max_mem:-1000}"
|
|
echo "Storage: ${storage:-/var/lib/localrecall} ($storage_size)"
|
|
echo "LocalAI: $localai_status"
|
|
fi
|
|
}
|
|
|
|
cmd_add() {
|
|
local category="$1"
|
|
local content="$2"
|
|
local agent="${AGENT:-user}"
|
|
local importance="${IMPORTANCE:-5}"
|
|
|
|
if [ -z "$category" ] || [ -z "$content" ]; then
|
|
echo "Error: Category and content required"
|
|
echo "Usage: localrecallctl add <category> <content>"
|
|
return 1
|
|
fi
|
|
|
|
# Validate category
|
|
case "$category" in
|
|
threats|decisions|patterns|configs|conversations) ;;
|
|
*)
|
|
echo "Error: Invalid category. Use: threats|decisions|patterns|configs|conversations"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
local id=$(add_memory "$category" "$agent" "$content" '{}' "$importance")
|
|
echo "Memory added: $id"
|
|
}
|
|
|
|
cmd_get() {
|
|
local id="$1"
|
|
|
|
if [ -z "$id" ]; then
|
|
echo "Error: Memory ID required"
|
|
return 1
|
|
fi
|
|
|
|
local memory=$(get_memory "$id")
|
|
if [ -n "$memory" ]; then
|
|
echo "$memory"
|
|
else
|
|
echo "Memory not found: $id"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
cmd_search() {
|
|
local query="$1"
|
|
local limit="${2:-20}"
|
|
|
|
if [ -z "$query" ]; then
|
|
echo "Error: Search query required"
|
|
return 1
|
|
fi
|
|
|
|
echo "Searching for: $query"
|
|
echo "---"
|
|
search_content "$query" "$limit"
|
|
}
|
|
|
|
cmd_list() {
|
|
local category="$1"
|
|
local limit="${2:-50}"
|
|
|
|
if [ -n "$category" ]; then
|
|
echo "Memories in category: $category"
|
|
echo "---"
|
|
search_category "$category" "$limit"
|
|
else
|
|
echo "All memories (last $limit)"
|
|
echo "---"
|
|
get_recent "$limit"
|
|
fi
|
|
}
|
|
|
|
cmd_recent() {
|
|
local limit="${1:-20}"
|
|
echo "Recent memories (last $limit)"
|
|
echo "---"
|
|
get_recent "$limit"
|
|
}
|
|
|
|
cmd_important() {
|
|
local limit="${1:-50}"
|
|
echo "Important memories (importance >= 7)"
|
|
echo "---"
|
|
get_important "$limit"
|
|
}
|
|
|
|
cmd_delete() {
|
|
local id="$1"
|
|
|
|
if [ -z "$id" ]; then
|
|
echo "Error: Memory ID required"
|
|
return 1
|
|
fi
|
|
|
|
delete_memory "$id"
|
|
echo "Memory deleted: $id"
|
|
}
|
|
|
|
cmd_cleanup() {
|
|
local retention=$(uci_get main.retention_days)
|
|
local keep_imp=$(uci_get cleanup.keep_important)
|
|
|
|
echo "Cleaning up memories older than ${retention:-90} days..."
|
|
local deleted=$(cleanup_old "${retention:-90}" "${keep_imp:-1}")
|
|
echo "Deleted: $deleted memories"
|
|
}
|
|
|
|
cmd_export() {
|
|
local file="$1"
|
|
|
|
if [ -z "$file" ]; then
|
|
echo "Error: Output file required"
|
|
return 1
|
|
fi
|
|
|
|
export_memories "$file"
|
|
echo "Memories exported to: $file"
|
|
}
|
|
|
|
cmd_import() {
|
|
local file="$1"
|
|
|
|
if [ -z "$file" ]; then
|
|
echo "Error: Input file required"
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -f "$file" ]; then
|
|
echo "Error: File not found: $file"
|
|
return 1
|
|
fi
|
|
|
|
import_memories "$file"
|
|
echo "Memories imported from: $file"
|
|
}
|
|
|
|
cmd_summarize() {
|
|
local category="$1"
|
|
|
|
if ! check_localai; then
|
|
echo "Error: LocalAI not available"
|
|
return 1
|
|
fi
|
|
|
|
echo "Summarizing memories..."
|
|
echo "---"
|
|
summarize_memories "$category"
|
|
}
|
|
|
|
cmd_context() {
|
|
local agent="$1"
|
|
|
|
if [ -z "$agent" ]; then
|
|
echo "Error: Agent name required"
|
|
return 1
|
|
fi
|
|
|
|
get_agent_context "$agent" ""
|
|
}
|
|
|
|
cmd_stats() {
|
|
local total=$(count_memories)
|
|
local threats=$(count_category threats)
|
|
local decisions=$(count_category decisions)
|
|
local patterns=$(count_category patterns)
|
|
local configs=$(count_category configs)
|
|
local convs=$(count_category conversations)
|
|
|
|
echo "Memory Statistics"
|
|
echo "================="
|
|
echo "Total: $total"
|
|
echo ""
|
|
echo "By Category:"
|
|
echo " Threats: $threats"
|
|
echo " Decisions: $decisions"
|
|
echo " Patterns: $patterns"
|
|
echo " Configs: $configs"
|
|
echo " Conversations: $convs"
|
|
echo ""
|
|
|
|
# Agent breakdown
|
|
echo "By Agent:"
|
|
for agent in threat_analyst dns_guard network_anomaly cve_triage user; do
|
|
local count=$(search_agent "$agent" 9999 | wc -l)
|
|
[ "$count" -gt 0 ] && echo " $agent: $count"
|
|
done
|
|
}
|
|
|
|
# Parse global options
|
|
AGENT="user"
|
|
IMPORTANCE=5
|
|
JSON=0
|
|
QUIET=0
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-a|--agent)
|
|
AGENT="$2"
|
|
shift 2
|
|
;;
|
|
-i|--importance)
|
|
IMPORTANCE="$2"
|
|
shift 2
|
|
;;
|
|
-j|--json)
|
|
JSON=1
|
|
shift
|
|
;;
|
|
-q|--quiet)
|
|
QUIET=1
|
|
shift
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Parse command
|
|
case "$1" in
|
|
status)
|
|
cmd_status "$JSON"
|
|
;;
|
|
add)
|
|
cmd_add "$2" "$3"
|
|
;;
|
|
get)
|
|
cmd_get "$2"
|
|
;;
|
|
search)
|
|
cmd_search "$2" "$3"
|
|
;;
|
|
list)
|
|
cmd_list "$2" "$3"
|
|
;;
|
|
recent)
|
|
cmd_recent "$2"
|
|
;;
|
|
important)
|
|
cmd_important "$2"
|
|
;;
|
|
delete)
|
|
cmd_delete "$2"
|
|
;;
|
|
cleanup)
|
|
cmd_cleanup
|
|
;;
|
|
export)
|
|
cmd_export "$2"
|
|
;;
|
|
import)
|
|
cmd_import "$2"
|
|
;;
|
|
summarize)
|
|
cmd_summarize "$2"
|
|
;;
|
|
context)
|
|
cmd_context "$2"
|
|
;;
|
|
stats)
|
|
cmd_stats
|
|
;;
|
|
help|--help|-h|"")
|
|
usage
|
|
;;
|
|
*)
|
|
echo "Unknown command: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|