modular_agent_kit/
preset.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::error::AgentError;
5use crate::id::{new_id, update_ids};
6use crate::mak::MAK;
7use crate::spec::PresetSpec;
8use crate::{AgentSpec, ConnectionSpec};
9
10pub struct Preset {
11 id: String,
12
13 name: Option<String>,
14
15 running: bool,
16
17 spec: PresetSpec,
18
19 #[cfg(feature = "file")]
20 dir: Option<String>,
21}
22
23impl Preset {
24 pub fn new(mut spec: PresetSpec) -> Self {
28 let (agents, connections) = update_ids(&spec.agents, &spec.connections);
29 spec.agents = agents;
30 spec.connections = connections;
31
32 Self {
33 id: new_id(),
34 name: None,
35 running: false,
36 spec,
37 #[cfg(feature = "file")]
38 dir: None,
39 }
40 }
41
42 pub fn id(&self) -> &str {
43 &self.id
44 }
45
46 pub fn spec(&self) -> &PresetSpec {
47 &self.spec
48 }
49
50 pub fn update_spec(&mut self, value: &Value) -> Result<(), AgentError> {
51 let update_map = value
52 .as_object()
53 .ok_or_else(|| AgentError::SerializationError("Expected JSON object".to_string()))?;
54
55 for (k, v) in update_map {
56 match k.as_str() {
57 "agents" => {
58 }
60 "connections" => {
61 }
63 _ => {
64 self.spec.extensions.insert(k.clone(), v.clone());
66 }
67 }
68 }
69 Ok(())
70 }
71
72 pub fn running(&self) -> bool {
73 self.running
74 }
75
76 pub fn name(&self) -> Option<&str> {
77 self.name.as_deref()
78 }
79
80 pub fn set_name(&mut self, name: String) {
81 self.name = Some(name);
82 }
83
84 pub fn clear_name(&mut self) {
85 self.name = None;
86 }
87
88 #[cfg(feature = "file")]
89 pub fn dir(&self) -> Option<&str> {
90 self.dir.as_deref()
91 }
92
93 #[cfg(feature = "file")]
94 pub fn set_dir(&mut self, dir: String) {
95 self.dir = Some(dir);
96 }
97
98 #[cfg(feature = "file")]
99 pub fn clear_dir(&mut self) {
100 self.dir = None;
101 }
102
103 pub fn add_agent(&mut self, agent: AgentSpec) {
104 self.spec.add_agent(agent);
105 }
106
107 pub fn remove_agent(&mut self, agent_id: &str) {
108 self.spec.remove_agent(agent_id);
109 }
110
111 pub fn add_connection(&mut self, connection: ConnectionSpec) {
112 self.spec.add_connection(connection);
113 }
114
115 pub fn remove_connection(&mut self, connection: &ConnectionSpec) -> Option<ConnectionSpec> {
116 self.spec.remove_connection(connection)
117 }
118
119 pub async fn start(&mut self, mak: &MAK) -> Result<(), AgentError> {
120 if self.running {
121 return Ok(());
123 }
124 self.running = true;
125
126 for agent in self.spec.agents.iter() {
127 if agent.disabled {
128 continue;
129 }
130 mak.start_agent(&agent.id).await.unwrap_or_else(|e| {
131 log::error!("Failed to start agent {}: {}", agent.id, e);
132 });
133 }
134
135 Ok(())
136 }
137
138 pub async fn stop(&mut self, mak: &MAK) -> Result<(), AgentError> {
139 for agent in self.spec.agents.iter() {
140 mak.stop_agent(&agent.id).await.unwrap_or_else(|e| {
141 log::error!("Failed to stop agent {}: {}", agent.id, e);
142 });
143 }
144 self.running = false;
145 Ok(())
146 }
147}
148
149#[derive(Clone, Debug, Serialize, Deserialize)]
150pub struct PresetInfo {
151 pub id: String,
152 pub name: Option<String>,
153 pub running: bool,
154 #[cfg(feature = "file")]
155 pub dir: Option<String>,
156}
157
158impl From<&Preset> for PresetInfo {
159 fn from(preset: &Preset) -> Self {
160 Self {
161 id: preset.id.clone(),
162 name: preset.name.clone(),
163 running: preset.running,
164 #[cfg(feature = "file")]
165 dir: preset.dir.clone(),
166 }
167 }
168}