zeroclaw/src/integrations/registry.rs
Edvard Schøyen e057bf4128
fix: remove unused import and correct WhatsApp/Email registry status
- Remove unused `std::fmt::Write` import in `load_openclaw_bootstrap_files`
  (eliminates compiler warning)
- Update WhatsApp integration status from ComingSoon to config-driven
  (implementation exists in channels/whatsapp.rs)
- Update Email integration status from ComingSoon to config-driven
  (implementation exists in channels/email_channel.rs)
- Update tests to reflect corrected integration statuses

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:28:44 -05:00

890 lines
31 KiB
Rust

use super::{IntegrationCategory, IntegrationEntry, IntegrationStatus};
/// Returns the full catalog of integrations
#[allow(clippy::too_many_lines)]
pub fn all_integrations() -> Vec<IntegrationEntry> {
vec![
// ── Chat Providers ──────────────────────────────────────
IntegrationEntry {
name: "Telegram",
description: "Bot API — long-polling",
category: IntegrationCategory::Chat,
status_fn: |c| {
if c.channels_config.telegram.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Discord",
description: "Servers, channels & DMs",
category: IntegrationCategory::Chat,
status_fn: |c| {
if c.channels_config.discord.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Slack",
description: "Workspace apps via Web API",
category: IntegrationCategory::Chat,
status_fn: |c| {
if c.channels_config.slack.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Webhooks",
description: "HTTP endpoint for triggers",
category: IntegrationCategory::Chat,
status_fn: |c| {
if c.channels_config.webhook.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "WhatsApp",
description: "Meta Cloud API via webhook",
category: IntegrationCategory::Chat,
status_fn: |c| {
if c.channels_config.whatsapp.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Signal",
description: "Privacy-focused via signal-cli",
category: IntegrationCategory::Chat,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "iMessage",
description: "macOS AppleScript bridge",
category: IntegrationCategory::Chat,
status_fn: |c| {
if c.channels_config.imessage.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Microsoft Teams",
description: "Enterprise chat support",
category: IntegrationCategory::Chat,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Matrix",
description: "Matrix protocol (Element)",
category: IntegrationCategory::Chat,
status_fn: |c| {
if c.channels_config.matrix.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Nostr",
description: "Decentralized DMs (NIP-04)",
category: IntegrationCategory::Chat,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "WebChat",
description: "Browser-based chat UI",
category: IntegrationCategory::Chat,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Nextcloud Talk",
description: "Self-hosted Nextcloud chat",
category: IntegrationCategory::Chat,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Zalo",
description: "Zalo Bot API",
category: IntegrationCategory::Chat,
status_fn: |_| IntegrationStatus::ComingSoon,
},
// ── AI Models ───────────────────────────────────────────
IntegrationEntry {
name: "OpenRouter",
description: "200+ models, 1 API key",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("openrouter") && c.api_key.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Anthropic",
description: "Claude 3.5/4 Sonnet & Opus",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("anthropic") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "OpenAI",
description: "GPT-4o, GPT-5, o1",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("openai") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Google",
description: "Gemini 2.5 Pro/Flash",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_model
.as_deref()
.is_some_and(|m| m.starts_with("google/"))
{
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "DeepSeek",
description: "DeepSeek V3 & R1",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_model
.as_deref()
.is_some_and(|m| m.starts_with("deepseek/"))
{
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "xAI",
description: "Grok 3 & 4",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_model
.as_deref()
.is_some_and(|m| m.starts_with("x-ai/"))
{
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Mistral",
description: "Mistral Large & Codestral",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_model
.as_deref()
.is_some_and(|m| m.starts_with("mistral"))
{
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Ollama",
description: "Local models (Llama, etc.)",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("ollama") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Perplexity",
description: "Search-augmented AI",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("perplexity") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Hugging Face",
description: "Open-source models",
category: IntegrationCategory::AiModel,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "LM Studio",
description: "Local model server",
category: IntegrationCategory::AiModel,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Venice",
description: "Privacy-first inference (Llama, Opus)",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("venice") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Vercel AI",
description: "Vercel AI Gateway",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("vercel") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Cloudflare AI",
description: "Cloudflare AI Gateway",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("cloudflare") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Moonshot",
description: "Kimi & Kimi Coding",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("moonshot") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Synthetic",
description: "Synthetic AI models",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("synthetic") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "OpenCode Zen",
description: "Code-focused AI models",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("opencode") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Z.AI",
description: "Z.AI inference",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("zai") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "GLM",
description: "ChatGLM / Zhipu models",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("glm") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "MiniMax",
description: "MiniMax AI models",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("minimax") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Amazon Bedrock",
description: "AWS managed model access",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("bedrock") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Qianfan",
description: "Baidu AI models",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("qianfan") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Groq",
description: "Ultra-fast LPU inference",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("groq") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Together AI",
description: "Open-source model hosting",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("together") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Fireworks AI",
description: "Fast open-source inference",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("fireworks") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Cohere",
description: "Command R+ & embeddings",
category: IntegrationCategory::AiModel,
status_fn: |c| {
if c.default_provider.as_deref() == Some("cohere") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
// ── Productivity ────────────────────────────────────────
IntegrationEntry {
name: "GitHub",
description: "Code, issues, PRs",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Notion",
description: "Workspace & databases",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Apple Notes",
description: "Native macOS/iOS notes",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Apple Reminders",
description: "Task management",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Obsidian",
description: "Knowledge graph notes",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Things 3",
description: "GTD task manager",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Bear Notes",
description: "Markdown notes",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Trello",
description: "Kanban boards",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Linear",
description: "Issue tracking",
category: IntegrationCategory::Productivity,
status_fn: |_| IntegrationStatus::ComingSoon,
},
// ── Music & Audio ───────────────────────────────────────
IntegrationEntry {
name: "Spotify",
description: "Music playback control",
category: IntegrationCategory::MusicAudio,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Sonos",
description: "Multi-room audio",
category: IntegrationCategory::MusicAudio,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Shazam",
description: "Song recognition",
category: IntegrationCategory::MusicAudio,
status_fn: |_| IntegrationStatus::ComingSoon,
},
// ── Smart Home ──────────────────────────────────────────
IntegrationEntry {
name: "Home Assistant",
description: "Home automation hub",
category: IntegrationCategory::SmartHome,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Philips Hue",
description: "Smart lighting",
category: IntegrationCategory::SmartHome,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "8Sleep",
description: "Smart mattress",
category: IntegrationCategory::SmartHome,
status_fn: |_| IntegrationStatus::ComingSoon,
},
// ── Tools & Automation ──────────────────────────────────
IntegrationEntry {
name: "Browser",
description: "Chrome/Chromium control",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::Available,
},
IntegrationEntry {
name: "Shell",
description: "Terminal command execution",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::Active,
},
IntegrationEntry {
name: "File System",
description: "Read/write files",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::Active,
},
IntegrationEntry {
name: "Cron",
description: "Scheduled tasks",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::Available,
},
IntegrationEntry {
name: "Voice",
description: "Voice wake + talk mode",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Gmail",
description: "Email triggers & send",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "1Password",
description: "Secure credentials",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Weather",
description: "Forecasts & conditions",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Canvas",
description: "Visual workspace + A2UI",
category: IntegrationCategory::ToolsAutomation,
status_fn: |_| IntegrationStatus::ComingSoon,
},
// ── Media & Creative ────────────────────────────────────
IntegrationEntry {
name: "Image Gen",
description: "AI image generation",
category: IntegrationCategory::MediaCreative,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "GIF Search",
description: "Find the perfect GIF",
category: IntegrationCategory::MediaCreative,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Screen Capture",
description: "Screenshot & screen control",
category: IntegrationCategory::MediaCreative,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Camera",
description: "Photo/video capture",
category: IntegrationCategory::MediaCreative,
status_fn: |_| IntegrationStatus::ComingSoon,
},
// ── Social ──────────────────────────────────────────────
IntegrationEntry {
name: "Twitter/X",
description: "Tweet, reply, search",
category: IntegrationCategory::Social,
status_fn: |_| IntegrationStatus::ComingSoon,
},
IntegrationEntry {
name: "Email",
description: "IMAP/SMTP email channel",
category: IntegrationCategory::Social,
status_fn: |c| {
if c.channels_config.email.is_some() {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
// ── Platforms ───────────────────────────────────────────
IntegrationEntry {
name: "macOS",
description: "Native support + AppleScript",
category: IntegrationCategory::Platform,
status_fn: |_| {
if cfg!(target_os = "macos") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Linux",
description: "Native support",
category: IntegrationCategory::Platform,
status_fn: |_| {
if cfg!(target_os = "linux") {
IntegrationStatus::Active
} else {
IntegrationStatus::Available
}
},
},
IntegrationEntry {
name: "Windows",
description: "WSL2 recommended",
category: IntegrationCategory::Platform,
status_fn: |_| IntegrationStatus::Available,
},
IntegrationEntry {
name: "iOS",
description: "Chat via Telegram/Discord",
category: IntegrationCategory::Platform,
status_fn: |_| IntegrationStatus::Available,
},
IntegrationEntry {
name: "Android",
description: "Chat via Telegram/Discord",
category: IntegrationCategory::Platform,
status_fn: |_| IntegrationStatus::Available,
},
]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::schema::{IMessageConfig, MatrixConfig, TelegramConfig};
use crate::config::Config;
#[test]
fn registry_has_entries() {
let entries = all_integrations();
assert!(
entries.len() >= 50,
"Expected 50+ integrations, got {}",
entries.len()
);
}
#[test]
fn all_categories_represented() {
let entries = all_integrations();
for cat in IntegrationCategory::all() {
let count = entries.iter().filter(|e| e.category == *cat).count();
assert!(count > 0, "Category {cat:?} has no entries");
}
}
#[test]
fn status_functions_dont_panic() {
let config = Config::default();
let entries = all_integrations();
for entry in &entries {
let _ = (entry.status_fn)(&config);
}
}
#[test]
fn no_duplicate_names() {
let entries = all_integrations();
let mut seen = std::collections::HashSet::new();
for entry in &entries {
assert!(
seen.insert(entry.name),
"Duplicate integration name: {}",
entry.name
);
}
}
#[test]
fn no_empty_names_or_descriptions() {
let entries = all_integrations();
for entry in &entries {
assert!(!entry.name.is_empty(), "Found integration with empty name");
assert!(
!entry.description.is_empty(),
"Integration '{}' has empty description",
entry.name
);
}
}
#[test]
fn telegram_active_when_configured() {
let mut config = Config::default();
config.channels_config.telegram = Some(TelegramConfig {
bot_token: "123:ABC".into(),
allowed_users: vec!["user".into()],
});
let entries = all_integrations();
let tg = entries.iter().find(|e| e.name == "Telegram").unwrap();
assert!(matches!((tg.status_fn)(&config), IntegrationStatus::Active));
}
#[test]
fn telegram_available_when_not_configured() {
let config = Config::default();
let entries = all_integrations();
let tg = entries.iter().find(|e| e.name == "Telegram").unwrap();
assert!(matches!(
(tg.status_fn)(&config),
IntegrationStatus::Available
));
}
#[test]
fn imessage_active_when_configured() {
let mut config = Config::default();
config.channels_config.imessage = Some(IMessageConfig {
allowed_contacts: vec!["*".into()],
});
let entries = all_integrations();
let im = entries.iter().find(|e| e.name == "iMessage").unwrap();
assert!(matches!((im.status_fn)(&config), IntegrationStatus::Active));
}
#[test]
fn imessage_available_when_not_configured() {
let config = Config::default();
let entries = all_integrations();
let im = entries.iter().find(|e| e.name == "iMessage").unwrap();
assert!(matches!(
(im.status_fn)(&config),
IntegrationStatus::Available
));
}
#[test]
fn matrix_active_when_configured() {
let mut config = Config::default();
config.channels_config.matrix = Some(MatrixConfig {
homeserver: "https://m.org".into(),
access_token: "tok".into(),
room_id: "!r:m".into(),
allowed_users: vec![],
});
let entries = all_integrations();
let mx = entries.iter().find(|e| e.name == "Matrix").unwrap();
assert!(matches!((mx.status_fn)(&config), IntegrationStatus::Active));
}
#[test]
fn matrix_available_when_not_configured() {
let config = Config::default();
let entries = all_integrations();
let mx = entries.iter().find(|e| e.name == "Matrix").unwrap();
assert!(matches!(
(mx.status_fn)(&config),
IntegrationStatus::Available
));
}
#[test]
fn coming_soon_integrations_stay_coming_soon() {
let config = Config::default();
let entries = all_integrations();
for name in ["Signal", "Nostr", "Spotify", "Home Assistant"] {
let entry = entries.iter().find(|e| e.name == name).unwrap();
assert!(
matches!((entry.status_fn)(&config), IntegrationStatus::ComingSoon),
"{name} should be ComingSoon"
);
}
}
#[test]
fn whatsapp_available_when_not_configured() {
let config = Config::default();
let entries = all_integrations();
let wa = entries.iter().find(|e| e.name == "WhatsApp").unwrap();
assert!(matches!(
(wa.status_fn)(&config),
IntegrationStatus::Available
));
}
#[test]
fn email_available_when_not_configured() {
let config = Config::default();
let entries = all_integrations();
let email = entries.iter().find(|e| e.name == "Email").unwrap();
assert!(matches!(
(email.status_fn)(&config),
IntegrationStatus::Available
));
}
#[test]
fn shell_and_filesystem_always_active() {
let config = Config::default();
let entries = all_integrations();
for name in ["Shell", "File System"] {
let entry = entries.iter().find(|e| e.name == name).unwrap();
assert!(
matches!((entry.status_fn)(&config), IntegrationStatus::Active),
"{name} should always be Active"
);
}
}
#[test]
fn macos_active_on_macos() {
let config = Config::default();
let entries = all_integrations();
let macos = entries.iter().find(|e| e.name == "macOS").unwrap();
let status = (macos.status_fn)(&config);
if cfg!(target_os = "macos") {
assert!(matches!(status, IntegrationStatus::Active));
} else {
assert!(matches!(status, IntegrationStatus::Available));
}
}
#[test]
fn category_counts_reasonable() {
let entries = all_integrations();
let chat_count = entries
.iter()
.filter(|e| e.category == IntegrationCategory::Chat)
.count();
let ai_count = entries
.iter()
.filter(|e| e.category == IntegrationCategory::AiModel)
.count();
assert!(
chat_count >= 5,
"Expected 5+ chat integrations, got {chat_count}"
);
assert!(
ai_count >= 5,
"Expected 5+ AI model integrations, got {ai_count}"
);
}
}