Skip to main content

room_cli/oneshot/
agent.rs

1use super::transport::{resolve_socket_target, send_message_with_token_target as transport_send};
2use crate::message::Message;
3
4/// Send an `/agent` command to the broker and print the response.
5///
6/// Builds a command envelope with `cmd: "agent"` and the given params,
7/// sends it via the oneshot transport, and prints the broker's reply.
8async 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
31/// One-shot `room agent spawn` — spawn a ralph agent in the room.
32pub 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
57/// One-shot `room agent list` — list agents in the room.
58pub 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
66/// One-shot `room agent stop` — stop a running agent.
67pub 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
82/// One-shot `room agent logs` — view agent logs.
83pub 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}