openclaw_node/agents/
tools.rs1use napi::bindgen_prelude::*;
4use napi_derive::napi;
5use std::collections::HashMap;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8
9use crate::error::OpenClawError;
10
11#[napi(object)]
13#[derive(Debug, Clone)]
14pub struct JsToolResult {
15 pub success: bool,
17 pub content: String,
19 pub error: Option<String>,
21}
22
23impl JsToolResult {
24 pub fn success(content: impl Into<String>) -> Self {
26 Self {
27 success: true,
28 content: content.into(),
29 error: None,
30 }
31 }
32
33 pub fn error(message: impl Into<String>) -> Self {
35 let msg = message.into();
36 Self {
37 success: false,
38 content: String::new(),
39 error: Some(msg),
40 }
41 }
42}
43
44#[napi(object)]
46#[derive(Debug, Clone)]
47pub struct JsToolDefinition {
48 pub name: String,
50 pub description: String,
52 pub input_schema: serde_json::Value,
54}
55
56struct StoredTool {
58 name: String,
59 description: String,
60 input_schema: serde_json::Value,
61 execute_fn: Option<
63 napi::threadsafe_function::ThreadsafeFunction<
64 serde_json::Value,
65 napi::threadsafe_function::ErrorStrategy::Fatal,
66 >,
67 >,
68}
69
70#[napi]
86pub struct ToolRegistry {
87 tools: Arc<RwLock<HashMap<String, StoredTool>>>,
88}
89
90#[napi]
91impl ToolRegistry {
92 #[napi(constructor)]
94 #[must_use]
95 pub fn new() -> Self {
96 Self {
97 tools: Arc::new(RwLock::new(HashMap::new())),
98 }
99 }
100
101 #[napi]
110 pub fn register_callback(
111 &self,
112 name: String,
113 description: String,
114 input_schema: serde_json::Value,
115 #[napi(ts_arg_type = "(params: any) => Promise<JsToolResult>")] execute_fn: JsFunction,
116 ) -> Result<()> {
117 use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
118
119 let tsfn: ThreadsafeFunction<serde_json::Value, ErrorStrategy::Fatal> =
120 execute_fn.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?;
121
122 let tool = StoredTool {
123 name: name.clone(),
124 description,
125 input_schema,
126 execute_fn: Some(tsfn),
127 };
128
129 let tools = self.tools.clone();
131 std::thread::spawn(move || {
132 let rt = tokio::runtime::Runtime::new().unwrap();
133 rt.block_on(async {
134 let mut guard = tools.write().await;
135 guard.insert(name, tool);
136 });
137 })
138 .join()
139 .map_err(|_| OpenClawError::tool_error("Failed to register tool"))?;
140
141 Ok(())
142 }
143
144 #[napi]
148 pub async fn register_definition(
149 &self,
150 name: String,
151 description: String,
152 input_schema: serde_json::Value,
153 ) -> Result<()> {
154 let tool = StoredTool {
155 name: name.clone(),
156 description,
157 input_schema,
158 execute_fn: None,
159 };
160
161 let mut tools = self.tools.write().await;
162 tools.insert(name, tool);
163 Ok(())
164 }
165
166 #[napi]
168 pub async fn unregister(&self, name: String) -> Result<bool> {
169 let mut tools = self.tools.write().await;
170 Ok(tools.remove(&name).is_some())
171 }
172
173 #[napi]
175 pub async fn list(&self) -> Vec<String> {
176 let tools = self.tools.read().await;
177 tools.keys().cloned().collect()
178 }
179
180 #[napi]
182 pub async fn get(&self, name: String) -> Option<JsToolDefinition> {
183 let tools = self.tools.read().await;
184 tools.get(&name).map(|t| JsToolDefinition {
185 name: t.name.clone(),
186 description: t.description.clone(),
187 input_schema: t.input_schema.clone(),
188 })
189 }
190
191 #[napi]
193 pub async fn get_all(&self) -> Vec<JsToolDefinition> {
194 let tools = self.tools.read().await;
195 tools
196 .values()
197 .map(|t| JsToolDefinition {
198 name: t.name.clone(),
199 description: t.description.clone(),
200 input_schema: t.input_schema.clone(),
201 })
202 .collect()
203 }
204
205 #[napi]
207 pub async fn has(&self, name: String) -> bool {
208 let tools = self.tools.read().await;
209 tools.contains_key(&name)
210 }
211
212 #[napi]
214 pub async fn count(&self) -> u32 {
215 let tools = self.tools.read().await;
216 tools.len() as u32
217 }
218
219 #[napi]
221 pub async fn clear(&self) {
222 let mut tools = self.tools.write().await;
223 tools.clear();
224 }
225}
226
227impl Default for ToolRegistry {
228 fn default() -> Self {
229 Self::new()
230 }
231}