1use super::transport::{resolve_socket_target, send_message_with_token_target as transport_send};
2use crate::message::Message;
3
4async fn send_agent_command(
9 room_id: &str,
10 token: &str,
11 params: Vec<String>,
12 socket: Option<&std::path::Path>,
13) -> anyhow::Result<()> {
14 let target = resolve_socket_target(room_id, socket);
15 let wire = serde_json::json!({"type": "command", "cmd": "agent", "params": params}).to_string();
16 let msg = transport_send(&target, token, &wire).await.map_err(|e| {
17 if e.to_string().contains("invalid token") {
18 anyhow::anyhow!("invalid token — run: room join <username>")
19 } else {
20 e
21 }
22 })?;
23
24 match &msg {
25 Message::System { content, .. } => println!("{content}"),
26 _ => println!("{}", serde_json::to_string(&msg)?),
27 }
28 Ok(())
29}
30
31pub async fn cmd_agent_spawn(
33 room_id: &str,
34 token: &str,
35 username: &str,
36 model: Option<&str>,
37 issue: Option<&str>,
38 personality: Option<&str>,
39 socket: Option<&std::path::Path>,
40) -> anyhow::Result<()> {
41 let mut params = vec!["spawn".to_owned(), username.to_owned()];
42 if let Some(m) = model {
43 params.push("--model".to_owned());
44 params.push(m.to_owned());
45 }
46 if let Some(i) = issue {
47 params.push("--issue".to_owned());
48 params.push(i.to_owned());
49 }
50 if let Some(p) = personality {
51 params.push("--personality".to_owned());
52 params.push(p.to_owned());
53 }
54 send_agent_command(room_id, token, params, socket).await
55}
56
57pub async fn cmd_agent_list(
59 room_id: &str,
60 token: &str,
61 socket: Option<&std::path::Path>,
62) -> anyhow::Result<()> {
63 send_agent_command(room_id, token, vec!["list".to_owned()], socket).await
64}
65
66pub async fn cmd_agent_stop(
68 room_id: &str,
69 token: &str,
70 username: &str,
71 socket: Option<&std::path::Path>,
72) -> anyhow::Result<()> {
73 send_agent_command(
74 room_id,
75 token,
76 vec!["stop".to_owned(), username.to_owned()],
77 socket,
78 )
79 .await
80}
81
82pub async fn cmd_agent_logs(
84 room_id: &str,
85 token: &str,
86 username: &str,
87 tail: usize,
88 socket: Option<&std::path::Path>,
89) -> anyhow::Result<()> {
90 send_agent_command(
91 room_id,
92 token,
93 vec![
94 "logs".to_owned(),
95 username.to_owned(),
96 "--tail".to_owned(),
97 tail.to_string(),
98 ],
99 socket,
100 )
101 .await
102}
103
104#[cfg(test)]
105mod tests {
106 #[test]
107 fn agent_spawn_wire_payload() {
108 let params = vec!["spawn".to_owned(), "testbot".to_owned()];
109 let wire =
110 serde_json::json!({"type": "command", "cmd": "agent", "params": params}).to_string();
111 let v: serde_json::Value = serde_json::from_str(&wire).unwrap();
112 assert_eq!(v["type"], "command");
113 assert_eq!(v["cmd"], "agent");
114 let p: Vec<&str> = v["params"]
115 .as_array()
116 .unwrap()
117 .iter()
118 .map(|x| x.as_str().unwrap())
119 .collect();
120 assert_eq!(p, vec!["spawn", "testbot"]);
121 }
122
123 #[test]
124 fn agent_spawn_with_flags_wire_payload() {
125 let mut params = vec!["spawn".to_owned(), "mybot".to_owned()];
126 params.push("--model".to_owned());
127 params.push("sonnet".to_owned());
128 params.push("--issue".to_owned());
129 params.push("42".to_owned());
130 let wire =
131 serde_json::json!({"type": "command", "cmd": "agent", "params": params}).to_string();
132 let v: serde_json::Value = serde_json::from_str(&wire).unwrap();
133 let p: Vec<&str> = v["params"]
134 .as_array()
135 .unwrap()
136 .iter()
137 .map(|x| x.as_str().unwrap())
138 .collect();
139 assert_eq!(
140 p,
141 vec!["spawn", "mybot", "--model", "sonnet", "--issue", "42"]
142 );
143 }
144
145 #[test]
146 fn agent_list_wire_payload() {
147 let wire =
148 serde_json::json!({"type": "command", "cmd": "agent", "params": ["list"]}).to_string();
149 let v: serde_json::Value = serde_json::from_str(&wire).unwrap();
150 assert_eq!(v["cmd"], "agent");
151 assert_eq!(v["params"][0], "list");
152 }
153
154 #[test]
155 fn agent_stop_wire_payload() {
156 let wire =
157 serde_json::json!({"type": "command", "cmd": "agent", "params": ["stop", "bot1"]})
158 .to_string();
159 let v: serde_json::Value = serde_json::from_str(&wire).unwrap();
160 assert_eq!(v["cmd"], "agent");
161 let p: Vec<&str> = v["params"]
162 .as_array()
163 .unwrap()
164 .iter()
165 .map(|x| x.as_str().unwrap())
166 .collect();
167 assert_eq!(p, vec!["stop", "bot1"]);
168 }
169
170 #[test]
171 fn agent_logs_wire_payload() {
172 let wire = serde_json::json!({
173 "type": "command",
174 "cmd": "agent",
175 "params": ["logs", "bot1", "--tail", "100"]
176 })
177 .to_string();
178 let v: serde_json::Value = serde_json::from_str(&wire).unwrap();
179 assert_eq!(v["cmd"], "agent");
180 let p: Vec<&str> = v["params"]
181 .as_array()
182 .unwrap()
183 .iter()
184 .map(|x| x.as_str().unwrap())
185 .collect();
186 assert_eq!(p, vec!["logs", "bot1", "--tail", "100"]);
187 }
188}