zeroclaw_plugins/
wasm_tool.rs1use crate::PluginPermission;
4use crate::runtime;
5use async_trait::async_trait;
6use serde_json::Value;
7use std::path::PathBuf;
8use zeroclaw_api::attribution::ToolKind;
9use zeroclaw_api::tool::{Tool, ToolResult};
10use zeroclaw_api::tool_attribution;
11
12tool_attribution!(WasmTool, ToolKind::Plugin);
13
14pub struct WasmTool {
16 name: String,
17 description: String,
18 parameters_schema: Value,
19 wasm_path: PathBuf,
20 permissions: Vec<PluginPermission>,
21}
22
23impl WasmTool {
24 pub fn new(
25 name: String,
26 description: String,
27 parameters_schema: Value,
28 wasm_path: PathBuf,
29 permissions: Vec<PluginPermission>,
30 ) -> Self {
31 Self {
32 name,
33 description,
34 parameters_schema,
35 wasm_path,
36 permissions,
37 }
38 }
39
40 pub fn from_wasm(
43 wasm_path: PathBuf,
44 permissions: Vec<PluginPermission>,
45 fallback_name: String,
46 fallback_description: String,
47 ) -> Self {
48 let (name, description, schema) = match runtime::create_plugin(&wasm_path, &permissions) {
50 Ok(mut plugin) => match runtime::call_tool_metadata(&mut plugin) {
51 Ok(meta) => (meta.name, meta.description, meta.parameters_schema),
52 Err(e) => {
53 ::zeroclaw_log::record!(
54 DEBUG,
55 ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note),
56 &format!(
57 "plugin at {} has no tool_metadata export ({e}), using fallback",
58 wasm_path.display()
59 )
60 );
61 (
62 fallback_name.clone(),
63 fallback_description.clone(),
64 default_schema(),
65 )
66 }
67 },
68 Err(e) => {
69 ::zeroclaw_log::record!(
70 WARN,
71 ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
72 .with_outcome(::zeroclaw_log::EventOutcome::Unknown),
73 &format!(
74 "failed to load WASM plugin at {} for metadata: {e}",
75 wasm_path.display()
76 )
77 );
78 (
79 fallback_name.clone(),
80 fallback_description.clone(),
81 default_schema(),
82 )
83 }
84 };
85
86 Self {
87 name,
88 description,
89 parameters_schema: schema,
90 wasm_path,
91 permissions,
92 }
93 }
94}
95
96fn default_schema() -> Value {
100 serde_json::json!({
101 "type": "object",
102 "properties": {
103 "input": {
104 "type": "string",
105 "description": "Input for the plugin"
106 }
107 },
108 "required": ["input"]
109 })
110}
111
112#[async_trait]
113impl Tool for WasmTool {
114 fn name(&self) -> &str {
115 &self.name
116 }
117
118 fn description(&self) -> &str {
119 &self.description
120 }
121
122 fn parameters_schema(&self) -> Value {
123 self.parameters_schema.clone()
124 }
125
126 async fn execute(&self, args: Value) -> anyhow::Result<ToolResult> {
127 let wasm_path = self.wasm_path.clone();
128 let permissions = self.permissions.clone();
129 let args_json = serde_json::to_vec(&args)?;
130
131 tokio::task::spawn_blocking(move || {
133 let mut plugin = runtime::create_plugin(&wasm_path, &permissions)?;
134 runtime::call_execute(&mut plugin, &args_json)
135 })
136 .await?
137 }
138}