feat: add OpenClaw integration module to miyabi-core
- Add openclaw.rs module for cluster communication - Expand CLI with OpenClaw-related commands (264 lines) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6d5b55ad74
commit
0e37360f8b
3 changed files with 618 additions and 0 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use clap::{Parser, Subcommand};
|
||||
use miyabi_core::{FeatureFlagManager, RulesLoader};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
|
|
@ -94,6 +95,41 @@ enum Commands {
|
|||
#[arg(long)]
|
||||
system: Option<String>,
|
||||
},
|
||||
/// OpenClaw integration - control OpenClaw agents
|
||||
Openclaw {
|
||||
/// OpenClaw subcommand
|
||||
#[command(subcommand)]
|
||||
command: OpenclawCommand,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum OpenclawCommand {
|
||||
/// List all available agents
|
||||
Agents,
|
||||
/// Show OpenClaw status
|
||||
Status,
|
||||
/// Show detailed help for OpenClaw commands
|
||||
Help,
|
||||
/// Send a message to an agent
|
||||
Send {
|
||||
/// Agent name (e.g., maestro, kade, sakura)
|
||||
agent: String,
|
||||
/// Message to send
|
||||
message: String,
|
||||
},
|
||||
/// Broadcast a message to all agents
|
||||
Broadcast {
|
||||
/// Message to broadcast
|
||||
message: String,
|
||||
},
|
||||
/// Broadcast to a specific society
|
||||
BroadcastSociety {
|
||||
/// Society name (core, investment, content, marketing)
|
||||
society: String,
|
||||
/// Message to broadcast
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -554,6 +590,232 @@ async fn main() -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Some(Commands::Openclaw { command }) => {
|
||||
use miyabi_core::openclaw::{OpenClawClient, OpenClawResult};
|
||||
use std::env;
|
||||
|
||||
// Get OpenClaw configuration
|
||||
let gateway_url = env::var("OPENCLAW_GATEWAY_URL")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:18789".to_string());
|
||||
let token = env::var("OPENCLAW_TOKEN")
|
||||
.unwrap_or_else(|_| {
|
||||
// Try to read from openclaw.json
|
||||
#[allow(unused_imports)]
|
||||
use std::fs;
|
||||
#[allow(unused_imports)]
|
||||
use std::path::PathBuf;
|
||||
|
||||
let config_path = PathBuf::from(env::var("HOME").unwrap_or_default())
|
||||
.join(".openclaw")
|
||||
.join("openclaw.json");
|
||||
|
||||
// Fallback: try to read token from config
|
||||
if let Ok(content) = fs::read_to_string(&config_path) {
|
||||
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
|
||||
if let Some(gateway) = json.get("gateway") {
|
||||
if let Some(auth) = gateway.get("auth") {
|
||||
if let Some(t) = auth.get("token") {
|
||||
return t.as_str().unwrap_or("").to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String::new()
|
||||
});
|
||||
|
||||
if token.is_empty() {
|
||||
eprintln!("❌ Error: OPENCLAW_TOKEN not set");
|
||||
eprintln!(" Set environment variable: export OPENCLAW_TOKEN=your_token");
|
||||
eprintln!(" Or add to ~/.miyabi/config.toml");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let client = OpenClawClient::new(gateway_url.clone(), token.clone());
|
||||
|
||||
// Handle Status command separately to avoid borrowing issues
|
||||
if let OpenclawCommand::Status = command {
|
||||
let token_display = if token.len() > 4 {
|
||||
format!("{}***", &token[..4])
|
||||
} else {
|
||||
"***".to_string()
|
||||
};
|
||||
println!("📊 OpenClaw Status:");
|
||||
println!(" Gateway: {}", gateway_url);
|
||||
println!(" Token: {}", token_display);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let client = OpenClawClient::new(gateway_url, token);
|
||||
|
||||
match command {
|
||||
OpenclawCommand::Agents => {
|
||||
// List agents grouped by society
|
||||
let agents = OpenClawClient::get_agents();
|
||||
println!("🎭 Miyabi エージェント一覧 ({} agents):", agents.len());
|
||||
println!();
|
||||
|
||||
// Group by society
|
||||
let mut grouped: HashMap<&str, Vec<_>> = HashMap::new();
|
||||
for agent in &agents {
|
||||
grouped.entry(&agent.society).or_default().push(agent);
|
||||
}
|
||||
|
||||
// Define society order
|
||||
let society_order = vec!["Core", "Investment", "Content", "Marketing"];
|
||||
|
||||
for society in &society_order {
|
||||
if let Some(society_agents) = grouped.get(*society) {
|
||||
println!("【{} Society】", society);
|
||||
for agent in society_agents {
|
||||
println!(" {} {} ({})", agent.emoji, agent.name, agent.id);
|
||||
println!(" Role: {}", agent.role);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
OpenclawCommand::Send { agent, message } => {
|
||||
// Send message
|
||||
let resolved_agent = OpenClawClient::resolve_agent_alias(&agent);
|
||||
match client.send(&resolved_agent, &message).await {
|
||||
Ok(_msg) => {
|
||||
println!("✓ メッセージを送信しました:");
|
||||
println!(" Agent: {}", resolved_agent);
|
||||
println!(" Message: {}", message);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ 送信エラー: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
OpenclawCommand::Broadcast { message } => {
|
||||
// Broadcast message to all core agents
|
||||
match client.broadcast(&message).await {
|
||||
Ok(results) => {
|
||||
println!("✓ ブロードキャストを送信しました:");
|
||||
println!(" Message: {}", message);
|
||||
println!();
|
||||
for result in results {
|
||||
println!(" {}", result);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ ブロードキャストエラー: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
OpenclawCommand::BroadcastSociety { society, message } => {
|
||||
// Broadcast to specific society
|
||||
let society_agents = match society.to_lowercase().as_str() {
|
||||
"core" => vec!["maestro", "kade", "sakura", "tsubaki", "botan", "nagare"],
|
||||
"investment" => vec!["scout", "crystal", "dealer", "sentinel", "architect", "watchman", "chart", "fundy", "scribe"],
|
||||
"content" => vec!["tweeter", "pen", "vidpro", "artist", "optimizer", "scheduler"],
|
||||
"marketing" => vec!["hiro", "kazoeru", "funnel", "adops"],
|
||||
_ => {
|
||||
eprintln!("❌ 不明なSociety: {}", society);
|
||||
eprintln!(" 利用可能: core, investment, content, marketing");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
println!("✓ {} Society にブロードキャスト:", society);
|
||||
println!(" Message: {}", message);
|
||||
println!();
|
||||
|
||||
for agent in society_agents {
|
||||
match client.send(agent, &message).await {
|
||||
Ok(_) => println!(" ✓ {}", agent),
|
||||
Err(e) => eprintln!(" ❌ {}: {}", agent, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
OpenclawCommand::Help => {
|
||||
// Show detailed help
|
||||
println!("📖 Miyabi OpenClaw CLI - 詳細ヘルプ");
|
||||
println!();
|
||||
println!("【基本コマンド】");
|
||||
println!();
|
||||
println!(" miyabi openclaw agents");
|
||||
println!(" → 全エージェント一覧を表示 (Society別)");
|
||||
println!();
|
||||
println!(" miyabi openclaw status");
|
||||
println!(" → OpenClaw Gatewayの状態を確認");
|
||||
println!();
|
||||
println!(" miyabi openclaw send <agent> <message>");
|
||||
println!(" → 特定のエージェントにメッセージを送信");
|
||||
println!();
|
||||
println!(" miyabi openclaw broadcast <message>");
|
||||
println!(" → 全コアエージェントにブロードキャスト");
|
||||
println!();
|
||||
println!(" miyabi openclaw broadcast-society <society> <message>");
|
||||
println!(" → 特定のSocietyにブロードキャスト");
|
||||
println!();
|
||||
println!("【エージェントIDとエイリアス】");
|
||||
println!();
|
||||
println!(" Core Society (6):");
|
||||
println!(" maestro しきるん🎭 - shikirun, conductor, orchestrator");
|
||||
println!(" kade カエデ🍁 - kaede, creator, codegen, developer");
|
||||
println!(" sakura サクラ🌸 - reviewer, qa, critic");
|
||||
println!(" tsubaki ツバキ🌺 - integrator, pr-manager, merge-bot");
|
||||
println!(" botan ボタン🌼 - deployer, release-manager, deployment");
|
||||
println!(" nagare ながれるん🌊 - nagarerun, workflow, automation");
|
||||
println!();
|
||||
println!(" Investment Society (9):");
|
||||
println!(" scout スカウト🔍 - researcher, explorer");
|
||||
println!(" crystal クリスタル💎 - valuer, analyst");
|
||||
println!(" dealer ディーラー🎰 - trader, executor");
|
||||
println!(" sentinel センチネル🛡️ - risk-manager, guardian-rm");
|
||||
println!(" architect アーキテクト🏗️ - portfolio-manager, allocator");
|
||||
println!(" watchman ウォッチマン👁️ - news-monitor, sentinel-news");
|
||||
println!(" chart チャート📈 - technical-analyst, chart-reader");
|
||||
println!(" fundy ファンディ📊 - fundamental-analyst, value-investor");
|
||||
println!(" scribe スクライブ📝 - reporter, documenter");
|
||||
println!();
|
||||
println!(" Content Society (6):");
|
||||
println!(" tweeter ツイーター🐦 - twitter-specialist, x-poster");
|
||||
println!(" pen ペン✒️ - writer, author");
|
||||
println!(" vidpro ビッドプロ🎬 - video-producer, youtuber");
|
||||
println!(" artist アーティスト🎨 - designer, visual-creator");
|
||||
println!(" optimizer オプティマイザー🔧 - seo-specialist, seo-analyst");
|
||||
println!(" scheduler スケジューラー📅 - calendar-manager, planner");
|
||||
println!();
|
||||
println!(" Marketing Society (4):");
|
||||
println!(" hiro ヒロ🚀 - promoter, growth-hacker");
|
||||
println!(" kazoeru カゾエル🔢 - metrics-tracker, data-analyst");
|
||||
println!(" funnel ファネル🌪️ - conversion-optimizer, cro-specialist");
|
||||
println!(" adops アドオプス📢 - ad-manager, media-buyer");
|
||||
println!();
|
||||
println!("【使用例】");
|
||||
println!();
|
||||
println!(" # 個別送信");
|
||||
println!(" miyabi openclaw send maestro \"実装タスクを割り当てて\"");
|
||||
println!(" miyabi openclaw send kade \"コードレビューお願いします\"");
|
||||
println!(" miyabi openclaw send shikirun \"エイリアスでもOK\"");
|
||||
println!();
|
||||
println!(" # ブロードキャスト");
|
||||
println!(" miyabi openclaw broadcast \"システムメンテナンス開始\"");
|
||||
println!(" miyabi openclaw broadcast-society content \"新記事投稿\"");
|
||||
println!();
|
||||
println!("【環境変数】");
|
||||
println!();
|
||||
println!(" OPENCLAW_GATEWAY_URL - Gateway URL (default: http://127.0.0.1:18789)");
|
||||
println!(" OPENCLAW_TOKEN - Gateway認証トークン");
|
||||
println!();
|
||||
println!("【設定ファイル】");
|
||||
println!();
|
||||
println!(" ~/.openclaw/openclaw.json - 設定ファイルから自動読み込み");
|
||||
println!();
|
||||
println!("---
|
||||
🌸 Miyabi Framework - OpenClaw Integration");
|
||||
}
|
||||
OpenclawCommand::Status => {
|
||||
// Already handled above
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ pub use workflow::{
|
|||
pub use mcp::{
|
||||
McpConfig, McpError, McpManager, McpRequest, McpResponse, McpServer, McpServerConfig, McpTool,
|
||||
};
|
||||
pub mod openclaw;
|
||||
pub use openclaw::{AgentInfo, OpenClawClient, OpenClawError, OpenClawResult};
|
||||
pub use dag::{
|
||||
DAGError, Task as DAGTask, TaskGraph, TaskGraphBuilder, TaskId, TaskLevel, TaskNode,
|
||||
};
|
||||
|
|
|
|||
354
crates/miyabi-core/src/openclaw.rs
Normal file
354
crates/miyabi-core/src/openclaw.rs
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
//! Miyabi OpenClaw Integration
|
||||
//!
|
||||
//! This module provides OpenClaw CLI wrapper functionality for Miyabi.
|
||||
|
||||
use reqwest::Client;
|
||||
use serde::Serialize;
|
||||
use std::time::Duration;
|
||||
|
||||
/// OpenClaw client for communicating with the Gateway
|
||||
#[derive(Clone)]
|
||||
pub struct OpenClawClient {
|
||||
gateway_url: String,
|
||||
token: String,
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl OpenClawClient {
|
||||
/// Create a new OpenClaw client
|
||||
pub fn new(gateway_url: String, token: String) -> Self {
|
||||
let client = Client::builder()
|
||||
.timeout(Duration::from_secs(30))
|
||||
.build()
|
||||
.expect("Failed to create HTTP client");
|
||||
|
||||
Self {
|
||||
gateway_url,
|
||||
token,
|
||||
client,
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message to an agent via the Gateway
|
||||
pub async fn send(&self, agent: &str, message: &str) -> Result<String, OpenClawError> {
|
||||
let payload = SendMessageRequest {
|
||||
agent_id: agent.to_string(),
|
||||
message: message.to_string(),
|
||||
};
|
||||
|
||||
let response = self.client
|
||||
.post(format!("{}/api/message", self.gateway_url))
|
||||
.header("Authorization", format!("Bearer {}", self.token))
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| OpenClawError::Network(e.to_string()))?;
|
||||
|
||||
if response.status().is_success() {
|
||||
Ok("Message sent successfully".to_string())
|
||||
} else {
|
||||
let status = response.status().as_u16();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
Err(OpenClawError::Api(status, body))
|
||||
}
|
||||
}
|
||||
|
||||
/// Broadcast a message to all core agents
|
||||
pub async fn broadcast(&self, message: &str) -> Result<Vec<String>, OpenClawError> {
|
||||
let agents = ["maestro", "kade", "sakura", "tsubaki", "botan", "nagare"];
|
||||
let mut results = Vec::new();
|
||||
|
||||
for agent in agents {
|
||||
match self.send(agent, message).await {
|
||||
Ok(msg) => results.push(format!("{}: {}", agent, msg)),
|
||||
Err(e) => results.push(format!("{}: {}", agent, e)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
/// Get agent list
|
||||
pub fn get_agents() -> Vec<AgentInfo> {
|
||||
vec![
|
||||
// Core Society
|
||||
AgentInfo {
|
||||
id: "maestro".to_string(),
|
||||
name: "しきるん".to_string(),
|
||||
emoji: "🎭".to_string(),
|
||||
role: "Conductor".to_string(),
|
||||
society: "Core".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "kade".to_string(),
|
||||
name: "カエデ".to_string(),
|
||||
emoji: "🍁".to_string(),
|
||||
role: "CodeGen".to_string(),
|
||||
society: "Core".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "sakura".to_string(),
|
||||
name: "サクラ".to_string(),
|
||||
emoji: "🌸".to_string(),
|
||||
role: "Review".to_string(),
|
||||
society: "Core".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "tsubaki".to_string(),
|
||||
name: "ツバキ".to_string(),
|
||||
emoji: "🌺".to_string(),
|
||||
role: "PR".to_string(),
|
||||
society: "Core".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "botan".to_string(),
|
||||
name: "ボタン".to_string(),
|
||||
emoji: "🌼".to_string(),
|
||||
role: "Deploy".to_string(),
|
||||
society: "Core".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "nagare".to_string(),
|
||||
name: "ながれるん".to_string(),
|
||||
emoji: "🌊".to_string(),
|
||||
role: "Workflow".to_string(),
|
||||
society: "Core".to_string(),
|
||||
},
|
||||
// Investment Society
|
||||
AgentInfo {
|
||||
id: "scout".to_string(),
|
||||
name: "スカウト".to_string(),
|
||||
emoji: "🔍".to_string(),
|
||||
role: "Explorer".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "crystal".to_string(),
|
||||
name: "クリスタル".to_string(),
|
||||
emoji: "💎".to_string(),
|
||||
role: "Valuer".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "dealer".to_string(),
|
||||
name: "ディーラー".to_string(),
|
||||
emoji: "🎰".to_string(),
|
||||
role: "Trader".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "sentinel".to_string(),
|
||||
name: "センチネル".to_string(),
|
||||
emoji: "🛡️".to_string(),
|
||||
role: "Risk Manager".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "architect".to_string(),
|
||||
name: "アーキテクト".to_string(),
|
||||
emoji: "🏗️".to_string(),
|
||||
role: "Portfolio Manager".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "watchman".to_string(),
|
||||
name: "ウォッチマン".to_string(),
|
||||
emoji: "👁️".to_string(),
|
||||
role: "News Monitor".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "chart".to_string(),
|
||||
name: "チャート".to_string(),
|
||||
emoji: "📈".to_string(),
|
||||
role: "Technical Analyst".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "fundy".to_string(),
|
||||
name: "ファンディ".to_string(),
|
||||
emoji: "📊".to_string(),
|
||||
role: "Fundamental Analyst".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "scribe".to_string(),
|
||||
name: "スクライブ".to_string(),
|
||||
emoji: "📝".to_string(),
|
||||
role: "Reporter".to_string(),
|
||||
society: "Investment".to_string(),
|
||||
},
|
||||
// Content Society
|
||||
AgentInfo {
|
||||
id: "tweeter".to_string(),
|
||||
name: "ツイーター".to_string(),
|
||||
emoji: "🐦".to_string(),
|
||||
role: "X Specialist".to_string(),
|
||||
society: "Content".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "pen".to_string(),
|
||||
name: "ペン".to_string(),
|
||||
emoji: "✒️".to_string(),
|
||||
role: "Writer".to_string(),
|
||||
society: "Content".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "vidpro".to_string(),
|
||||
name: "ビッドプロ".to_string(),
|
||||
emoji: "🎬".to_string(),
|
||||
role: "Video Producer".to_string(),
|
||||
society: "Content".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "artist".to_string(),
|
||||
name: "アーティスト".to_string(),
|
||||
emoji: "🎨".to_string(),
|
||||
role: "Designer".to_string(),
|
||||
society: "Content".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "optimizer".to_string(),
|
||||
name: "オプティマイザー".to_string(),
|
||||
emoji: "🔧".to_string(),
|
||||
role: "SEO Specialist".to_string(),
|
||||
society: "Content".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "scheduler".to_string(),
|
||||
name: "スケジューラー".to_string(),
|
||||
emoji: "📅".to_string(),
|
||||
role: "Calendar Manager".to_string(),
|
||||
society: "Content".to_string(),
|
||||
},
|
||||
// Marketing Society
|
||||
AgentInfo {
|
||||
id: "hiro".to_string(),
|
||||
name: "ヒロ".to_string(),
|
||||
emoji: "🚀".to_string(),
|
||||
role: "Promoter".to_string(),
|
||||
society: "Marketing".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "kazoeru".to_string(),
|
||||
name: "カゾエル".to_string(),
|
||||
emoji: "🔢".to_string(),
|
||||
role: "Metrics Tracker".to_string(),
|
||||
society: "Marketing".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "funnel".to_string(),
|
||||
name: "ファネル".to_string(),
|
||||
emoji: "🌪️".to_string(),
|
||||
role: "CRO Specialist".to_string(),
|
||||
society: "Marketing".to_string(),
|
||||
},
|
||||
AgentInfo {
|
||||
id: "adops".to_string(),
|
||||
name: "アドオプス".to_string(),
|
||||
emoji: "📢".to_string(),
|
||||
role: "Ad Manager".to_string(),
|
||||
society: "Marketing".to_string(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Resolve agent alias to canonical ID
|
||||
pub fn resolve_agent_alias(alias: &str) -> String {
|
||||
match alias {
|
||||
// Core Society - maestro
|
||||
"shikirun" | "conductor" | "orchestrator" => "maestro".to_string(),
|
||||
// Core Society - kade
|
||||
"kaede" | "creator" | "codegen" | "developer" => "kade".to_string(),
|
||||
// Core Society - sakura
|
||||
"reviewer" | "qa" | "critic" => "sakura".to_string(),
|
||||
// Core Society - tsubaki
|
||||
"integrator" | "pr-manager" | "merge-bot" => "tsubaki".to_string(),
|
||||
// Core Society - botan
|
||||
"deployer" | "release-manager" | "deployment" => "botan".to_string(),
|
||||
// Core Society - nagare
|
||||
"nagarerun" | "workflow" | "automation" | "n8n-specialist" => "nagare".to_string(),
|
||||
|
||||
// Investment Society - scout
|
||||
"researcher" | "explorer" => "scout".to_string(),
|
||||
// Investment Society - crystal
|
||||
"valuer" | "analyst" => "crystal".to_string(),
|
||||
// Investment Society - dealer
|
||||
"trader" | "executor" => "dealer".to_string(),
|
||||
// Investment Society - sentinel
|
||||
"risk-manager" | "guardian-rm" => "sentinel".to_string(),
|
||||
// Investment Society - architect
|
||||
"portfolio-manager" | "allocator" | "architect-inv" => "architect".to_string(),
|
||||
// Investment Society - watchman
|
||||
"news-monitor" | "sentinel-news" => "watchman".to_string(),
|
||||
// Investment Society - chart
|
||||
"technical-analyst" | "chart-reader" => "chart".to_string(),
|
||||
// Investment Society - fundy
|
||||
"fundamental-analyst" | "value-investor" => "fundy".to_string(),
|
||||
// Investment Society - scribe
|
||||
"reporter" | "documenter" | "scribe-inv" => "scribe".to_string(),
|
||||
|
||||
// Content Society - tweeter
|
||||
"twitter-specialist" | "x-poster" => "tweeter".to_string(),
|
||||
// Content Society - pen
|
||||
"writer" | "author" => "pen".to_string(),
|
||||
// Content Society - vidpro
|
||||
"video-producer" | "youtuber" => "vidpro".to_string(),
|
||||
// Content Society - artist
|
||||
"designer" | "visual-creator" => "artist".to_string(),
|
||||
// Content Society - optimizer
|
||||
"seo-specialist" | "seo-analyst" => "optimizer".to_string(),
|
||||
// Content Society - scheduler
|
||||
"calendar-manager" | "planner" => "scheduler".to_string(),
|
||||
|
||||
// Marketing Society - hiro
|
||||
"promoter" | "growth-hacker" => "hiro".to_string(),
|
||||
// Marketing Society - kazoeru
|
||||
"metrics-tracker" | "data-analyst" => "kazoeru".to_string(),
|
||||
// Marketing Society - funnel
|
||||
"conversion-optimizer" | "cro-specialist" => "funnel".to_string(),
|
||||
// Marketing Society - adops
|
||||
"ad-manager" | "media-buyer" => "adops".to_string(),
|
||||
|
||||
// default - return as-is
|
||||
_ => alias.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Agent information
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AgentInfo {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub emoji: String,
|
||||
pub role: String,
|
||||
pub society: String,
|
||||
}
|
||||
|
||||
/// Request for sending a message
|
||||
#[derive(Serialize)]
|
||||
struct SendMessageRequest {
|
||||
#[serde(rename = "agentId")]
|
||||
agent_id: String,
|
||||
message: String,
|
||||
}
|
||||
|
||||
/// OpenClaw error types
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum OpenClawError {
|
||||
#[error("Network error: {0}")]
|
||||
Network(String),
|
||||
|
||||
#[error("API error (status {0}): {1}")]
|
||||
Api(u16, String),
|
||||
|
||||
#[error("Configuration error: {0}")]
|
||||
Config(String),
|
||||
|
||||
#[error("Agent '{0}' not found")]
|
||||
AgentNotFound(String),
|
||||
}
|
||||
|
||||
/// Result type for OpenClaw operations
|
||||
pub type OpenClawResult<T> = Result<T, OpenClawError>;
|
||||
Loading…
Add table
Add a link
Reference in a new issue