From 0e0b3644a8c76a795b41f2531ed60ec6be278e2f Mon Sep 17 00:00:00 2001 From: Argenis Date: Mon, 16 Feb 2026 00:16:04 -0500 Subject: [PATCH] feat(config): add Lark/Feishu channel config support * feat(config): add Lark/Feishu channel config support - Add LarkConfig struct with app_id, app_secret, encrypt_key, verification_token, allowed_users, use_feishu fields - Add lark field to ChannelsConfig - Export LarkConfig in config/mod.rs - Add 5 tests for LarkConfig serialization/deserialization Related to #164 Co-Authored-By: Claude Opus 4.6 * fix: apply cargo fmt formatting Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .cargo/config.toml | 5 +++ src/config/mod.rs | 2 +- src/config/schema.rs | 93 +++++++++++++++++++++++++++++++++++++++++++ src/onboard/wizard.rs | 1 + 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e1f508b --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.x86_64-unknown-linux-musl] +rustflags = ["-C", "link-arg=-static"] + +[target.aarch64-unknown-linux-musl] +rustflags = ["-C", "link-arg=-static"] diff --git a/src/config/mod.rs b/src/config/mod.rs index b18a699..bd520a8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -3,7 +3,7 @@ pub mod schema; pub use schema::{ AutonomyConfig, BrowserConfig, ChannelsConfig, ComposioConfig, Config, DelegateAgentConfig, DiscordConfig, DockerRuntimeConfig, GatewayConfig, HeartbeatConfig, IMessageConfig, - IdentityConfig, MatrixConfig, MemoryConfig, ModelRouteConfig, ObservabilityConfig, + IdentityConfig, LarkConfig, MatrixConfig, MemoryConfig, ModelRouteConfig, ObservabilityConfig, ReliabilityConfig, RuntimeConfig, SecretsConfig, SlackConfig, TelegramConfig, TunnelConfig, WebhookConfig, }; diff --git a/src/config/schema.rs b/src/config/schema.rs index 7b4a198..f4d5ccd 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -741,6 +741,7 @@ pub struct ChannelsConfig { pub whatsapp: Option, pub email: Option, pub irc: Option, + pub lark: Option, } impl Default for ChannelsConfig { @@ -756,6 +757,7 @@ impl Default for ChannelsConfig { whatsapp: None, email: None, irc: None, + lark: None, } } } @@ -850,6 +852,28 @@ fn default_irc_port() -> u16 { 6697 } +/// Lark/Feishu configuration for messaging integration +/// Lark is the international version, Feishu is the Chinese version +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LarkConfig { + /// App ID from Lark/Feishu developer console + pub app_id: String, + /// App Secret from Lark/Feishu developer console + pub app_secret: String, + /// Encrypt key for webhook message decryption (optional) + #[serde(default)] + pub encrypt_key: Option, + /// Verification token for webhook validation (optional) + #[serde(default)] + pub verification_token: Option, + /// Allowed user IDs or union IDs (empty = deny all, "*" = allow all) + #[serde(default)] + pub allowed_users: Vec, + /// Whether to use the Feishu (Chinese) endpoint instead of Lark (International) + #[serde(default)] + pub use_feishu: bool, +} + // ── Config impl ────────────────────────────────────────────────── impl Default for Config { @@ -1216,6 +1240,7 @@ mod tests { whatsapp: None, email: None, irc: None, + lark: None, }, memory: MemoryConfig::default(), tunnel: TunnelConfig::default(), @@ -1464,6 +1489,7 @@ default_temperature = 0.7 whatsapp: None, email: None, irc: None, + lark: None, }; let toml_str = toml::to_string_pretty(&c).unwrap(); let parsed: ChannelsConfig = toml::from_str(&toml_str).unwrap(); @@ -1622,6 +1648,7 @@ channel_id = "C123" }), email: None, irc: None, + lark: None, }; let toml_str = toml::to_string_pretty(&c).unwrap(); let parsed: ChannelsConfig = toml::from_str(&toml_str).unwrap(); @@ -2052,6 +2079,72 @@ default_temperature = 0.7 assert!(g.paired_tokens.is_empty()); } + // ── Lark config ─────────────────────────────────────────────── + + #[test] + fn lark_config_serde() { + let lc = LarkConfig { + app_id: "cli_123456".into(), + app_secret: "secret_abc".into(), + encrypt_key: Some("encrypt_key".into()), + verification_token: Some("verify_token".into()), + allowed_users: vec!["user_123".into(), "user_456".into()], + use_feishu: true, + }; + let json = serde_json::to_string(&lc).unwrap(); + let parsed: LarkConfig = serde_json::from_str(&json).unwrap(); + assert_eq!(parsed.app_id, "cli_123456"); + assert_eq!(parsed.app_secret, "secret_abc"); + assert_eq!(parsed.encrypt_key.as_deref(), Some("encrypt_key")); + assert_eq!(parsed.verification_token.as_deref(), Some("verify_token")); + assert_eq!(parsed.allowed_users.len(), 2); + assert!(parsed.use_feishu); + } + + #[test] + fn lark_config_toml_roundtrip() { + let lc = LarkConfig { + app_id: "cli_123456".into(), + app_secret: "secret_abc".into(), + encrypt_key: Some("encrypt_key".into()), + verification_token: Some("verify_token".into()), + allowed_users: vec!["*".into()], + use_feishu: false, + }; + let toml_str = toml::to_string(&lc).unwrap(); + let parsed: LarkConfig = toml::from_str(&toml_str).unwrap(); + assert_eq!(parsed.app_id, "cli_123456"); + assert_eq!(parsed.app_secret, "secret_abc"); + assert!(!parsed.use_feishu); + } + + #[test] + fn lark_config_deserializes_without_optional_fields() { + let json = r#"{"app_id":"cli_123","app_secret":"secret"}"#; + let parsed: LarkConfig = serde_json::from_str(json).unwrap(); + assert!(parsed.encrypt_key.is_none()); + assert!(parsed.verification_token.is_none()); + assert!(parsed.allowed_users.is_empty()); + assert!(!parsed.use_feishu); + } + + #[test] + fn lark_config_defaults_to_lark_endpoint() { + let json = r#"{"app_id":"cli_123","app_secret":"secret"}"#; + let parsed: LarkConfig = serde_json::from_str(json).unwrap(); + assert!( + !parsed.use_feishu, + "use_feishu should default to false (Lark)" + ); + } + + #[test] + fn lark_config_with_wildcard_allowed_users() { + let json = r#"{"app_id":"cli_123","app_secret":"secret","allowed_users":["*"]}"#; + let parsed: LarkConfig = serde_json::from_str(json).unwrap(); + assert_eq!(parsed.allowed_users, vec!["*"]); + } + // ══════════════════════════════════════════════════════════ // AGENT DELEGATION CONFIG TESTS // ══════════════════════════════════════════════════════════ diff --git a/src/onboard/wizard.rs b/src/onboard/wizard.rs index 28ae154..5b66e17 100644 --- a/src/onboard/wizard.rs +++ b/src/onboard/wizard.rs @@ -1126,6 +1126,7 @@ fn setup_channels() -> Result { whatsapp: None, email: None, irc: None, + lark: None, }; loop {