Skip to main content

mur_core/remote/
mod.rs

1//! Cross-machine coordination for remote workflow execution.
2//!
3//! Supports SSH-based direct execution and mur.run relay-based
4//! tunneled execution across machines.
5
6pub mod inventory;
7pub mod relay;
8pub mod ssh;
9
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13/// Result of a remote command execution.
14#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
15pub struct RemoteOutput {
16    pub stdout: String,
17    pub stderr: String,
18    pub exit_code: i32,
19    pub duration_ms: u64,
20    pub machine_id: String,
21}
22
23impl RemoteOutput {
24    /// Whether the command succeeded (exit code 0).
25    pub fn success(&self) -> bool {
26        self.exit_code == 0
27    }
28}
29
30/// Trait for remote execution backends.
31pub trait RemoteExecutor: Send + Sync {
32    /// Execute a command on a remote machine.
33    fn execute(
34        &self,
35        machine_id: &str,
36        command: &str,
37        working_dir: Option<&str>,
38    ) -> anyhow::Result<RemoteOutput>;
39
40    /// Check if the remote machine is reachable.
41    fn ping(&self, machine_id: &str) -> anyhow::Result<bool>;
42
43    /// Get the executor type name (e.g., "ssh", "relay").
44    fn executor_type(&self) -> &str;
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn test_remote_output_success() {
53        let output = RemoteOutput {
54            stdout: "hello".into(),
55            stderr: String::new(),
56            exit_code: 0,
57            duration_ms: 100,
58            machine_id: "m1".into(),
59        };
60        assert!(output.success());
61    }
62
63    #[test]
64    fn test_remote_output_failure() {
65        let output = RemoteOutput {
66            stdout: String::new(),
67            stderr: "not found".into(),
68            exit_code: 1,
69            duration_ms: 50,
70            machine_id: "m1".into(),
71        };
72        assert!(!output.success());
73    }
74
75    #[test]
76    fn test_remote_output_serialization() {
77        let output = RemoteOutput {
78            stdout: "ok".into(),
79            stderr: String::new(),
80            exit_code: 0,
81            duration_ms: 42,
82            machine_id: "test-machine".into(),
83        };
84        let json = serde_json::to_string(&output).unwrap();
85        let back: RemoteOutput = serde_json::from_str(&json).unwrap();
86        assert_eq!(output.exit_code, back.exit_code);
87        assert_eq!(output.machine_id, back.machine_id);
88    }
89}