feat: New MC with left sidebar + Horus AI Management

This commit is contained in:
root
2026-02-16 12:21:12 +00:00
parent 98dfe1a442
commit 604b4b7db4
3 changed files with 481 additions and 191 deletions
+183
View File
@@ -0,0 +1,183 @@
"use client";
import { useState, useEffect } from "react";
import { AISkill, ApiConfig, CronJob, defaultSkills, defaultApis, defaultCronJobs } from "@/lib/ai-management/types";
const STORAGE_KEYS = {
skills: "horus:skills",
apis: "horus:apis",
crons: "horus:crons",
};
export default function AIManagement() {
const [skills, setSkills] = useState<AISkill[]>(defaultSkills);
const [apis, setApis] = useState<ApiConfig[]>(defaultApis);
const [crons, setCrons] = useState<CronJob[]>(defaultCronJobs);
const [activeTab, setActiveTab] = useState<"skills" | "apis" | "crons">("skills");
// Load from localStorage
useEffect(() => {
if (typeof window === "undefined") return;
const savedSkills = localStorage.getItem(STORAGE_KEYS.skills);
if (savedSkills) setSkills(JSON.parse(savedSkills));
const savedApis = localStorage.getItem(STORAGE_KEYS.apis);
if (savedApis) setApis(JSON.parse(savedApis));
const savedCrons = localStorage.getItem(STORAGE_KEYS.crons);
if (savedCrons) setCrons(JSON.parse(savedCrons));
}, []);
// Save changes
const toggleSkill = (id: string) => {
const updated = skills.map(s => s.id === id ? { ...s, enabled: !s.enabled } : s);
setSkills(updated);
localStorage.setItem(STORAGE_KEYS.skills, JSON.stringify(updated));
};
const toggleCron = (id: string) => {
const updated = crons.map(c => c.id === id ? { ...c, enabled: !c.enabled } : c);
setCrons(updated);
localStorage.setItem(STORAGE_KEYS.crons, JSON.stringify(updated));
};
const categoryColors: Record<string, string> = {
development: "bg-blue-500/20 text-blue-400",
research: "bg-purple-500/20 text-purple-400",
automation: "bg-green-500/20 text-green-400",
communication: "bg-yellow-500/20 text-yellow-400",
};
return (
<div className="space-y-6">
{/* Header */}
<div>
<h2 className="text-2xl font-bold">🤖 Horus AI Management</h2>
<p className="text-white/60 mt-1">Configure skills, APIs, and automation for your AI assistant</p>
</div>
{/* Tabs */}
<div className="flex gap-2">
{[
{ id: "skills", label: "Skills", icon: "🧩" },
{ id: "apis", label: "APIs", icon: "🔑" },
{ id: "crons", label: "Automation", icon: "⏰" },
].map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id as any)}
className={`px-4 py-2 rounded-lg text-sm font-medium transition ${
activeTab === tab.id
? "bg-brand-pink text-white"
: "bg-white/10 text-white/70 hover:bg-white/20"
}`}
>
{tab.icon} {tab.label}
</button>
))}
</div>
{/* Skills Tab */}
{activeTab === "skills" && (
<div className="grid gap-3">
{skills.map((skill) => (
<div
key={skill.id}
className="flex items-center justify-between p-4 rounded-xl border border-white/10 bg-white/5"
>
<div className="flex items-center gap-3">
<div className={`px-2 py-1 rounded text-xs font-medium ${categoryColors[skill.category]}`}>
{skill.category}
</div>
<div>
<p className="font-medium">{skill.name}</p>
<p className="text-sm text-white/50">{skill.description}</p>
</div>
</div>
<button
onClick={() => toggleSkill(skill.id)}
className={`w-12 h-6 rounded-full transition relative ${
skill.enabled ? "bg-green-500" : "bg-white/20"
}`}
>
<span
className={`absolute top-1 w-4 h-4 rounded-full bg-white transition ${
skill.enabled ? "left-7" : "left-1"
}`}
/>
</button>
</div>
))}
</div>
)}
{/* APIs Tab */}
{activeTab === "apis" && (
<div className="grid gap-3">
{apis.map((api) => (
<div
key={api.id}
className="flex items-center justify-between p-4 rounded-xl border border-white/10 bg-white/5"
>
<div>
<p className="font-medium flex items-center gap-2">
{api.name}
{api.configured && <span className="text-green-400 text-xs"> Configured</span>}
</p>
<p className="text-sm text-white/50">{api.description}</p>
</div>
<button
className={`px-3 py-1.5 rounded-lg text-xs font-medium ${
api.configured
? "bg-green-500/20 text-green-400"
: "bg-white/10 text-white/60 hover:bg-white/20"
}`}
>
{api.configured ? "Active" : "Configure"}
</button>
</div>
))}
<div className="mt-4 p-4 rounded-xl border border-yellow-500/30 bg-yellow-500/10">
<p className="text-sm text-yellow-200">
💡 To configure APIs, give me the keys or run: <code className="bg-black/30 px-2 py-1 rounded">openclaw configure --section auth --set KEY=value</code>
</p>
</div>
</div>
)}
{/* Automation/Crons Tab */}
{activeTab === "crons" && (
<div className="grid gap-3">
{crons.map((cron) => (
<div
key={cron.id}
className="flex items-center justify-between p-4 rounded-xl border border-white/10 bg-white/5"
>
<div>
<p className="font-medium">{cron.name}</p>
<p className="text-sm text-white/50">
{cron.schedule}
{cron.nextRun && <span className="ml-2 text-brand-pink">Next: {cron.nextRun}</span>}
</p>
</div>
<button
onClick={() => toggleCron(cron.id)}
className={`w-12 h-6 rounded-full transition relative ${
cron.enabled ? "bg-green-500" : "bg-white/20"
}`}
>
<span
className={`absolute top-1 w-4 h-4 rounded-full bg-white transition ${
cron.enabled ? "left-7" : "left-1"
}`}
/>
</button>
</div>
))}
</div>
)}
</div>
);
}
@@ -4,6 +4,7 @@ import { useState } from "react";
import { useMissionControl } from "@/lib/mission-control/store";
import { TaskStatus } from "@/lib/mission-control/types";
import VoiceChat from "./VoiceChat";
import AIManagement from "@/components/ai-management/AIManagement";
interface ProjectSummary {
id: string;
@@ -11,6 +12,7 @@ interface ProjectSummary {
description: string;
status: "active" | "paused" | "completed";
color: string;
icon: string;
}
const projects: ProjectSummary[] = [
@@ -20,6 +22,7 @@ const projects: ProjectSummary[] = [
description: "AI website platform for local businesses (B2B)",
status: "active",
color: "#ff7bc0",
icon: "🌐",
},
{
id: "holacompi",
@@ -27,6 +30,7 @@ const projects: ProjectSummary[] = [
description: "AI ally for immigrants/consumers (B2C)",
status: "paused",
color: "#6366f1",
icon: "🤝",
},
{
id: "infrastructure",
@@ -34,22 +38,35 @@ const projects: ProjectSummary[] = [
description: "Security, backups, APIs, and system config",
status: "active",
color: "#10b981",
icon: "🔒",
},
{
id: "horus",
name: "Horus AI",
description: "Manage my skills, APIs, and automation",
status: "active",
color: "#f59e0b",
icon: "🤖",
},
];
const statusConfig: Record<TaskStatus, { label: string; color: string; bg: string }> = {
todo: { label: "To Do", color: "text-white/70", bg: "bg-white/10" },
in_progress: { label: "In Progress", color: "text-yellow-400", bg: "bg-yellow-500/20" },
done: { label: "Done", color: "text-green-400", bg: "bg-green-500/20" },
blocked: { label: "Blocked", color: "text-red-400", bg: "bg-red-500/20" },
paused: { label: "Paused", color: "text-gray-400", bg: "bg-gray-500/20" },
const statusConfig: Record<TaskStatus, { label: string; color: string }> = {
todo: { label: "To Do", color: "text-white/70" },
in_progress: { label: "In Progress", color: "text-yellow-400" },
done: { label: "Done", color: "text-green-400" },
blocked: { label: "Blocked", color: "text-red-400" },
paused: { label: "Paused", color: "text-gray-400" },
};
type ViewType = "tasks" | "horus";
export default function MissionControlDashboard() {
const { tasks, toggleTask, updateTaskStatus, getProjectProgress, getTasksByProject } =
useMissionControl();
const [selectedProject, setSelectedProject] = useState<string>("sitemente");
const [filter, setFilter] = useState<TaskStatus | "all">("all");
const [view, setView] = useState<ViewType>("tasks");
const projectTasks = getTasksByProject(selectedProject as any);
const filteredTasks = filter === "all" ? projectTasks : projectTasks.filter((t) => t.status === filter);
@@ -57,6 +74,45 @@ export default function MissionControlDashboard() {
const selectedProjectData = projects.find((p) => p.id === selectedProject)!;
// If viewing Horus AI management
if (view === "horus") {
return (
<div className="min-h-screen bg-[#1a1625] text-white">
{/* Header */}
<header className="border-b border-white/10 bg-[#2d2640] px-6 py-4">
<div className="mx-auto flex max-w-7xl items-center justify-between">
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-brand-pink text-xl">
👁
</div>
<div>
<h1 className="text-xl font-bold">Mission Control</h1>
<p className="text-xs text-white/60">Horus AI Management</p>
</div>
</div>
<nav className="flex items-center gap-2">
<button
onClick={() => setView("tasks")}
className="px-3 py-1.5 rounded-lg text-sm font-medium text-white/70 hover:bg-white/10 transition"
>
Back
</button>
</nav>
</div>
</header>
<main className="mx-auto max-w-4xl px-6 py-8">
<AIManagement />
{/* Voice Chat */}
<div className="mt-8">
<VoiceChat />
</div>
</main>
</div>
);
}
return (
<div className="min-h-screen bg-[#1a1625] text-white">
{/* Header */}
@@ -68,223 +124,208 @@ export default function MissionControlDashboard() {
</div>
<div>
<h1 className="text-xl font-bold">Mission Control</h1>
<p className="text-xs text-white/60">SiteMente + HolaCompi</p>
<p className="text-xs text-white/60">SiteMente + HolaCompi + Infrastructure</p>
</div>
</div>
<div className="flex items-center gap-4">
{/* Navigation */}
<nav className="flex items-center gap-2 mr-4">
<a
href="/mission-control"
className="px-3 py-1.5 rounded-lg bg-white/10 text-sm font-medium hover:bg-white/20 transition"
>
Tasks
</a>
<a
href="/morning-brief"
className="px-3 py-1.5 rounded-lg text-sm font-medium text-white/70 hover:bg-white/10 transition"
>
Brief
</a>
</nav>
<div className="text-right">
<p className="text-sm text-white/60">Total Progress</p>
<p className="text-2xl font-bold">{progress}%</p>
</div>
<div className="h-12 w-12 rounded-full border-4 border-brand-pink bg-white/10">
<svg className="h-full w-full -rotate-90" viewBox="0 0 36 36">
<circle
cx="18"
cy="18"
r="16"
fill="none"
stroke="currentColor"
strokeWidth="3"
className="text-white/20"
/>
<circle
cx="18"
cy="18"
r="16"
fill="none"
stroke="currentColor"
strokeWidth="3"
strokeDasharray={`${progress}, 100`}
className="text-brand-pink"
/>
<circle cx="18" cy="18" r="16" fill="none" stroke="currentColor" strokeWidth="3" className="text-white/20" />
<circle cx="18" cy="18" r="16" fill="none" stroke="currentColor" strokeWidth="3" strokeDasharray={`${progress}, 100`} className="text-brand-pink" />
</svg>
</div>
</div>
</div>
</header>
<main className="mx-auto max-w-7xl px-6 py-8">
{/* Project Tabs */}
<div className="mb-8 flex gap-4">
{projects.map((project) => {
const p = getProjectProgress(project.id as any);
return (
<button
key={project.id}
onClick={() => setSelectedProject(project.id)}
className={`relative flex-1 rounded-xl border p-4 text-left transition ${
selectedProject === project.id
? "border-white/30 bg-white/10"
: "border-white/10 bg-white/5 hover:border-white/20"
}`}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div
className="h-3 w-3 rounded-full"
style={{ backgroundColor: project.color }}
/>
<span className="font-semibold">{project.name}</span>
<span
className={`rounded-full px-2 py-0.5 text-xs ${
project.status === "active"
? "bg-green-500/20 text-green-400"
: "bg-yellow-500/20 text-yellow-400"
}`}
>
{project.status}
</span>
</div>
<span className="text-sm font-bold">{p}%</span>
</div>
<div className="mt-3 h-1.5 w-full rounded-full bg-white/10">
<div
className="h-full rounded-full transition-all duration-500"
style={{ width: `${p}%`, backgroundColor: project.color }}
/>
</div>
</button>
);
})}
</div>
<div className="mx-auto max-w-7xl px-6 py-8">
<div className="flex gap-8">
{/* Left Sidebar */}
<aside className="w-64 flex-shrink-0">
<nav className="space-y-2">
{projects.map((project) => {
const p = getProjectProgress(project.id as any);
const isSelected = selectedProject === project.id;
const isHorus = project.id === "horus";
{/* Stats Row */}
<div className="mb-8 grid grid-cols-4 gap-4">
{(["todo", "in_progress", "done", "blocked"] as TaskStatus[]).map((status) => {
const count = projectTasks.filter((t) => t.status === status).length;
const config = statusConfig[status];
return (
<button
key={status}
onClick={() => setFilter(filter === status ? "all" : status)}
className={`rounded-xl border p-4 text-center transition ${
filter === status
? "border-white/40 bg-white/10"
: "border-white/10 bg-white/5 hover:border-white/20"
}`}
>
<p className={`text-2xl font-bold ${config.color}`}>{count}</p>
<p className="text-xs text-white/60">{config.label}</p>
</button>
);
})}
</div>
{/* Task List */}
<div className="rounded-xl border border-white/10 bg-white/5">
<div className="border-b border-white/10 px-6 py-4">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold">
{selectedProjectData?.name} Tasks
</h2>
<p className="text-sm text-white/60">
{filteredTasks.filter((t) => t.status === "done").length} /{" "}
{filteredTasks.length} completed
</p>
</div>
</div>
<div className="divide-y divide-white/5">
{filteredTasks.map((task) => {
const config = statusConfig[task.status];
return (
<div
key={task.id}
className={`flex items-center gap-4 px-6 py-4 transition hover:bg-white/5 ${
task.status === "done" ? "opacity-50" : ""
}`}
>
return (
<button
onClick={() => toggleTask(task.id)}
className={`flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full border-2 transition ${
task.status === "done"
? "border-green-500 bg-green-500 text-white"
: "border-white/30 hover:border-white/50"
key={project.id}
onClick={() => {
if (isHorus) {
setView("horus");
} else {
setSelectedProject(project.id);
setView("tasks");
}
}}
className={`w-full flex items-center gap-3 p-3 rounded-xl text-left transition ${
isSelected && !isHorus
? "bg-white/10 border border-white/20"
: "hover:bg-white/5"
}`}
>
{task.status === "done" && (
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
)}
<span className="text-xl">{project.icon}</span>
<div className="flex-1 min-w-0">
<p className="font-medium text-sm truncate">{project.name}</p>
<div className="mt-1 h-1 w-full rounded-full bg-white/10">
<div
className="h-full rounded-full transition-all"
style={{ width: `${p}%`, backgroundColor: project.color }}
/>
</div>
</div>
<span className="text-xs text-white/50">{p}%</span>
</button>
);
})}
</nav>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<p
className={`font-medium ${
task.status === "done" ? "line-through text-white/50" : ""
{/* Quick Links */}
<div className="mt-8 pt-6 border-t border-white/10">
<p className="text-xs text-white/50 uppercase mb-3">Quick Links</p>
<div className="space-y-2">
<a href="/morning-brief" className="flex items-center gap-2 p-2 rounded-lg text-sm text-white/70 hover:bg-white/5 hover:text-white transition">
Morning Brief
</a>
<a href="/" className="flex items-center gap-2 p-2 rounded-lg text-sm text-white/70 hover:bg-white/5 hover:text-white transition">
🏠 SiteMente Site
</a>
</div>
</div>
</aside>
{/* Main Content */}
<main className="flex-1">
{/* Project Tabs */}
<div className="mb-6 flex gap-2 overflow-x-auto pb-2">
{projects.filter(p => p.id !== "horus").map((project) => {
const p = getProjectProgress(project.id as any);
return (
<button
key={project.id}
onClick={() => setSelectedProject(project.id)}
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium whitespace-nowrap transition ${
selectedProject === project.id
? "bg-white/10 border border-white/20"
: "hover:bg-white/5"
}`}
>
<span>{project.icon}</span>
<span>{project.name}</span>
<span className={`px-1.5 py-0.5 rounded text-xs ${
project.status === "active" ? "bg-green-500/20 text-green-400" : "bg-yellow-500/20 text-yellow-400"
}`}>
{project.status}
</span>
</button>
);
})}
</div>
{/* Stats Row */}
<div className="mb-6 grid grid-cols-4 gap-3">
{(["todo", "in_progress", "done", "blocked"] as TaskStatus[]).map((status) => {
const count = projectTasks.filter((t) => t.status === status).length;
const config = statusConfig[status];
return (
<button
key={status}
onClick={() => setFilter(filter === status ? "all" : status)}
className={`rounded-xl border p-3 text-center transition ${
filter === status
? "border-white/40 bg-white/10"
: "border-white/10 bg-white/5 hover:border-white/20"
}`}
>
<p className={`text-xl font-bold ${config.color}`}>{count}</p>
<p className="text-xs text-white/50">{config.label}</p>
</button>
);
})}
</div>
{/* Task List */}
<div className="rounded-xl border border-white/10 bg-white/5">
<div className="border-b border-white/10 px-4 py-3">
<div className="flex items-center justify-between">
<h2 className="font-semibold">{selectedProjectData?.name} Tasks</h2>
<p className="text-sm text-white/50">
{filteredTasks.filter((t) => t.status === "done").length} / {filteredTasks.length} done
</p>
</div>
</div>
<div className="divide-y divide-white/5 max-h-[400px] overflow-y-auto">
{filteredTasks.map((task) => {
const config = statusConfig[task.status];
return (
<div
key={task.id}
className={`flex items-center gap-3 px-4 py-3 transition hover:bg-white/5 ${
task.status === "done" ? "opacity-50" : ""
}`}
>
<button
onClick={() => toggleTask(task.id)}
className={`flex-shrink-0 w-5 h-5 rounded-full border-2 flex items-center justify-center transition ${
task.status === "done"
? "border-green-500 bg-green-500 text-white"
: "border-white/30 hover:border-white/50"
}`}
>
{task.title}
</p>
{task.status === "done" && (
<svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
)}
</button>
<div className="flex-1 min-w-0">
<p className={`text-sm font-medium truncate ${task.status === "done" ? "line-through text-white/50" : ""}`}>
{task.title}
</p>
</div>
{task.priority === "critical" && (
<span className="rounded-full bg-red-500/20 px-2 py-0.5 text-xs text-red-400">
<span className="px-2 py-0.5 rounded-full bg-red-500/20 text-red-400 text-xs">
CRITICAL
</span>
)}
<select
value={task.status}
onChange={(e) => updateTaskStatus(task.id, e.target.value as TaskStatus)}
className="text-xs bg-transparent border border-white/20 rounded px-2 py-1"
>
<option value="todo">To Do</option>
<option value="in_progress">In Progress</option>
<option value="done">Done</option>
<option value="blocked">Blocked</option>
</select>
</div>
<p className="text-sm text-white/50 truncate">{task.description}</p>
</div>
);
})}
</div>
<select
value={task.status}
onChange={(e) => updateTaskStatus(task.id, e.target.value as TaskStatus)}
className={`rounded-lg border border-white/20 bg-white/10 px-3 py-1.5 text-xs ${config.color} focus:border-white/40 focus:outline-none`}
>
<option value="todo">To Do</option>
<option value="in_progress">In Progress</option>
<option value="done">Done</option>
<option value="blocked">Blocked</option>
</select>
{filteredTasks.length === 0 && (
<div className="px-4 py-8 text-center text-white/50 text-sm">
No tasks match the current filter.
</div>
);
})}
</div>
{filteredTasks.length === 0 && (
<div className="px-6 py-12 text-center text-white/50">
No tasks match the current filter.
)}
</div>
)}
</div>
{/* Voice Chat */}
<div className="mt-8">
<VoiceChat />
{/* Voice Chat */}
<div className="mt-6">
<VoiceChat />
</div>
</main>
</div>
{/* Quick Actions */}
<div className="mt-8 flex gap-4">
<button
onClick={() => {
const confirmed = window.confirm("Reset all tasks? This cannot be undone.");
if (confirmed && typeof window !== "undefined") {
localStorage.removeItem("sitemente:mission-control");
window.location.reload();
}
}}
className="rounded-lg border border-white/20 bg-white/5 px-4 py-2 text-sm text-white/70 hover:bg-white/10"
>
Reset All Tasks
</button>
</div>
</main>
</div>
</div>
);
}
+66
View File
@@ -0,0 +1,66 @@
// AI Management Types
export interface AISkill {
id: string;
name: string;
description: string;
enabled: boolean;
category: "communication" | "research" | "development" | "automation";
}
export interface AIModel {
id: string;
name: string;
provider: string;
enabled: boolean;
isDefault: boolean;
}
export interface ApiConfig {
id: string;
name: string;
key: string;
configured: boolean;
description: string;
}
export interface CronJob {
id: string;
name: string;
schedule: string;
enabled: boolean;
lastRun?: string;
nextRun?: string;
}
// Current configuration
export const defaultSkills: AISkill[] = [
{ id: "github", name: "GitHub", description: "Manage GitHub issues, PRs, and repos", enabled: true, category: "development" },
{ id: "healthcheck", name: "Health Check", description: "VPS security and hardening", enabled: true, category: "automation" },
{ id: "skill-creator", name: "Skill Creator", description: "Create and manage agent skills", enabled: false, category: "development" },
{ id: "tmux", name: "Tmux", description: "Remote terminal sessions", enabled: true, category: "development" },
{ id: "weather", name: "Weather", description: "Get weather and forecasts", enabled: true, category: "research" },
{ id: "automation-workflows", name: "Automation", description: "Design automation workflows", enabled: false, category: "automation" },
{ id: "docker-sandbox", name: "Docker Sandbox", description: "Safe code execution environment", enabled: false, category: "development" },
];
export const defaultModels: AIModel[] = [
{ id: "minimax", name: "MiniMax M2.5", provider: "minimax", enabled: true, isDefault: true },
{ id: "sonnet", name: "Claude Sonnet 4.5", provider: "anthropic", enabled: true, isDefault: false },
{ id: "gemini-flash", name: "Gemini 3 Flash", provider: "google", enabled: false, isDefault: false },
];
export const defaultApis: ApiConfig[] = [
{ id: "perplexity", name: "Perplexity AI", key: "PERPLEXITY_API_KEY", configured: false, description: "Web research and live information" },
{ id: "openweather", name: "OpenWeatherMap", key: "OPENWEATHER_API_KEY", configured: false, description: "Weather data for morning brief" },
{ id: "newsapi", name: "News API", key: "NEWS_API_KEY", configured: false, description: "AI and market news" },
{ id: "coingecko", name: "CoinGecko", key: "", configured: true, description: "Crypto prices (free, no key needed)" },
{ id: "alphavantage", name: "Alpha Vantage", key: "ALPHA_VANTAGE_API_KEY", configured: false, description: "Stock market data" },
{ id: "things3", name: "Things 3", key: "", configured: false, description: "Todoist as alternative" },
];
export const defaultCronJobs: CronJob[] = [
{ id: "morning-brief", name: "Morning Brief", schedule: "6:00 AM CET", enabled: true, nextRun: "2026-02-17 06:00" },
{ id: "backup", name: "Daily Backup", schedule: "2:00 AM CET", enabled: false },
{ id: "healthcheck", name: "Weekly Security Audit", schedule: "Sunday 3:00 AM", enabled: false },
];