agentos-native-sidecar-core 0.2.6

Shared Secure Exec sidecar logic for native and browser shells
Documentation
use agentos_kernel::process_table::{ProcessInfo, ProcessStatus};
use agentos_sidecar_protocol::protocol::{ProcessSnapshotEntry, ProcessSnapshotStatus};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SharedProcessSnapshotStatus {
    Running,
    Stopped,
    Exited,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SharedProcessSnapshotEntry {
    pub process_id: String,
    pub pid: u32,
    pub ppid: u32,
    pub pgid: u32,
    pub sid: u32,
    pub driver: String,
    pub command: String,
    pub args: Vec<String>,
    pub cwd: String,
    pub status: SharedProcessSnapshotStatus,
    pub exit_code: Option<i32>,
}

pub fn process_status_from_kernel(status: ProcessStatus) -> SharedProcessSnapshotStatus {
    match status {
        ProcessStatus::Running => SharedProcessSnapshotStatus::Running,
        ProcessStatus::Stopped => SharedProcessSnapshotStatus::Stopped,
        ProcessStatus::Exited => SharedProcessSnapshotStatus::Exited,
    }
}

pub fn process_snapshot_entry_from_kernel(
    process_id: &str,
    info: &ProcessInfo,
    cwd: impl Into<String>,
    exit_code: Option<i32>,
) -> SharedProcessSnapshotEntry {
    SharedProcessSnapshotEntry {
        process_id: process_id.to_owned(),
        pid: info.pid,
        ppid: info.ppid,
        pgid: info.pgid,
        sid: info.sid,
        driver: info.driver.clone(),
        command: info.command.clone(),
        args: Vec::new(),
        cwd: cwd.into(),
        status: if exit_code.is_some() {
            SharedProcessSnapshotStatus::Exited
        } else {
            process_status_from_kernel(info.status)
        },
        exit_code: exit_code.or(info.exit_code),
    }
}

pub fn protocol_process_snapshot_entry(entry: SharedProcessSnapshotEntry) -> ProcessSnapshotEntry {
    ProcessSnapshotEntry {
        process_id: entry.process_id,
        pid: entry.pid,
        ppid: entry.ppid,
        pgid: entry.pgid,
        sid: entry.sid,
        driver: entry.driver,
        command: entry.command,
        args: entry.args,
        cwd: entry.cwd,
        status: match entry.status {
            SharedProcessSnapshotStatus::Running => ProcessSnapshotStatus::Running,
            SharedProcessSnapshotStatus::Stopped => ProcessSnapshotStatus::Stopped,
            SharedProcessSnapshotStatus::Exited => ProcessSnapshotStatus::Exited,
        },
        exit_code: entry.exit_code,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use agentos_kernel::user::ProcessIdentity;

    fn process_info(status: ProcessStatus, exit_code: Option<i32>) -> ProcessInfo {
        ProcessInfo {
            pid: 42,
            ppid: 1,
            pgid: 42,
            sid: 42,
            driver: "javascript".to_owned(),
            command: "node".to_owned(),
            status,
            exit_code,
            identity: ProcessIdentity::default(),
        }
    }

    #[test]
    fn maps_kernel_process_statuses() {
        assert_eq!(
            process_status_from_kernel(ProcessStatus::Running),
            SharedProcessSnapshotStatus::Running
        );
        assert_eq!(
            process_status_from_kernel(ProcessStatus::Stopped),
            SharedProcessSnapshotStatus::Stopped
        );
        assert_eq!(
            process_status_from_kernel(ProcessStatus::Exited),
            SharedProcessSnapshotStatus::Exited
        );
    }

    #[test]
    fn builds_process_snapshot_from_kernel_info() {
        let entry = process_snapshot_entry_from_kernel(
            "exec-1",
            &process_info(ProcessStatus::Running, None),
            "/workspace",
            None,
        );

        assert_eq!(entry.process_id, "exec-1");
        assert_eq!(entry.pid, 42);
        assert_eq!(entry.ppid, 1);
        assert_eq!(entry.pgid, 42);
        assert_eq!(entry.sid, 42);
        assert_eq!(entry.driver, "javascript");
        assert_eq!(entry.command, "node");
        assert_eq!(entry.args, Vec::<String>::new());
        assert_eq!(entry.cwd, "/workspace");
        assert_eq!(entry.status, SharedProcessSnapshotStatus::Running);
        assert_eq!(entry.exit_code, None);
    }

    #[test]
    fn explicit_exit_code_marks_snapshot_exited() {
        let entry = process_snapshot_entry_from_kernel(
            "exec-1",
            &process_info(ProcessStatus::Running, Some(9)),
            "/workspace",
            Some(7),
        );

        assert_eq!(entry.status, SharedProcessSnapshotStatus::Exited);
        assert_eq!(entry.exit_code, Some(7));
    }

    #[test]
    fn maps_shared_process_snapshot_to_protocol_entry() {
        let entry = protocol_process_snapshot_entry(SharedProcessSnapshotEntry {
            process_id: String::from("proc-1"),
            pid: 42,
            ppid: 1,
            pgid: 42,
            sid: 42,
            driver: String::from("javascript"),
            command: String::from("node"),
            args: vec![String::from("app.js")],
            cwd: String::from("/workspace"),
            status: SharedProcessSnapshotStatus::Stopped,
            exit_code: None,
        });

        assert_eq!(entry.process_id, "proc-1");
        assert_eq!(entry.pid, 42);
        assert_eq!(entry.args, vec![String::from("app.js")]);
        assert_eq!(entry.cwd, "/workspace");
        assert_eq!(entry.status, ProcessSnapshotStatus::Stopped);
        assert_eq!(entry.exit_code, None);
    }
}