roboticus_plugin_sdk/
lib.rs1pub mod archive;
22pub mod catalog;
23pub mod loader;
24pub mod manifest;
25pub mod registry;
26pub mod script;
27
28use async_trait::async_trait;
29use serde::{Deserialize, Serialize};
30use serde_json::Value;
31
32use roboticus_core::{Result, RiskLevel};
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ToolDef {
36 pub name: String,
37 pub description: String,
38 pub parameters: Value,
39 #[serde(default = "default_tool_risk_level")]
40 pub risk_level: RiskLevel,
41 #[serde(default)]
42 pub permissions: Vec<String>,
43}
44
45fn default_tool_risk_level() -> RiskLevel {
46 RiskLevel::Caution
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ToolResult {
51 pub success: bool,
52 pub output: String,
53 pub metadata: Option<Value>,
54}
55
56#[async_trait]
57pub trait Plugin: Send + Sync {
58 fn name(&self) -> &str;
59 fn version(&self) -> &str;
60 fn tools(&self) -> Vec<ToolDef>;
61 async fn init(&mut self) -> Result<()>;
62 async fn execute_tool(&self, tool_name: &str, input: &Value) -> Result<ToolResult>;
63 async fn shutdown(&mut self) -> Result<()>;
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
67pub enum PluginStatus {
68 Loaded,
69 Active,
70 Disabled,
71 Error,
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn tool_def_serde() {
80 let tool = ToolDef {
81 name: "test_tool".into(),
82 description: "A test tool".into(),
83 parameters: serde_json::json!({"type": "object"}),
84 risk_level: RiskLevel::Safe,
85 permissions: vec![],
86 };
87 let json = serde_json::to_string(&tool).unwrap();
88 let back: ToolDef = serde_json::from_str(&json).unwrap();
89 assert_eq!(back.name, "test_tool");
90 }
91
92 #[test]
93 fn tool_def_defaults_to_caution_when_risk_missing() {
94 let raw = r#"{"name":"t","description":"d","parameters":{"type":"object"}}"#;
95 let back: ToolDef = serde_json::from_str(raw).unwrap();
96 assert_eq!(back.risk_level, RiskLevel::Caution);
97 }
98
99 #[test]
100 fn tool_result_serde() {
101 let result = ToolResult {
102 success: true,
103 output: "done".into(),
104 metadata: Some(serde_json::json!({"elapsed_ms": 42})),
105 };
106 let json = serde_json::to_string(&result).unwrap();
107 let back: ToolResult = serde_json::from_str(&json).unwrap();
108 assert!(back.success);
109 assert_eq!(back.output, "done");
110 }
111
112 #[test]
113 fn plugin_status_roundtrip() {
114 for status in [
115 PluginStatus::Loaded,
116 PluginStatus::Active,
117 PluginStatus::Disabled,
118 PluginStatus::Error,
119 ] {
120 let json = serde_json::to_string(&status).unwrap();
121 let back: PluginStatus = serde_json::from_str(&json).unwrap();
122 assert_eq!(status, back);
123 }
124 }
125}