Skip to main content

smcp_computer/
socketio_client.rs

1/*!
2* 文件名: socketio_client
3* 作者: JQQ
4* 创建日期: 2025/12/16
5* 最后修改日期: 2025/12/16
6* 版权: 2023 JQQ. All rights reserved.
7* 依赖: tf_rust_socketio, tokio, serde
8* 描述: SMCP Computer的Socket.IO客户端实现 / Socket.IO client implementation for SMCP Computer
9*/
10
11use crate::errors::{ComputerError, ComputerResult};
12use crate::mcp_clients::manager::MCPServerManager;
13use crate::mcp_clients::model::MCPServerInput;
14use futures_util::FutureExt;
15use serde_json::Value;
16use smcp::{
17    events::{
18        CLIENT_GET_CONFIG, CLIENT_GET_DESKTOP, CLIENT_GET_TOOLS, CLIENT_TOOL_CALL,
19        SERVER_JOIN_OFFICE, SERVER_LEAVE_OFFICE, SERVER_UPDATE_CONFIG, SERVER_UPDATE_DESKTOP,
20        SERVER_UPDATE_TOOL_LIST,
21    },
22    GetComputerConfigReq, GetComputerConfigRet, GetDesktopReq, GetDesktopRet, GetToolsReq,
23    GetToolsRet, ToolCallReq, SMCP_NAMESPACE,
24};
25use std::collections::HashMap;
26use std::sync::Arc;
27use tf_rust_socketio::{
28    asynchronous::{Client, ClientBuilder},
29    Event, Payload, TransportType,
30};
31use tokio::sync::RwLock;
32use tracing::{debug, error, info};
33
34/// SMCP Computer Socket.IO客户端
35/// SMCP Computer Socket.IO client
36pub struct SmcpComputerClient {
37    /// Socket.IO客户端实例 / Socket.IO client instance
38    client: Client,
39    /// Computer名称 / Computer name
40    computer_name: String,
41    /// 当前所在的office ID / Current office ID
42    office_id: Arc<RwLock<Option<String>>>,
43    /// 输入定义映射 / Input definitions map
44    #[allow(dead_code)]
45    inputs: Arc<RwLock<HashMap<String, MCPServerInput>>>,
46}
47
48impl SmcpComputerClient {
49    /// 创建新的Socket.IO客户端
50    /// Create a new Socket.IO client
51    pub async fn new(
52        url: &str,
53        manager: Arc<RwLock<Option<MCPServerManager>>>,
54        computer_name: String,
55        auth_secret: Option<String>,
56        inputs: Arc<RwLock<HashMap<String, MCPServerInput>>>,
57    ) -> ComputerResult<Self> {
58        let office_id = Arc::new(RwLock::new(None));
59        let manager_clone = manager.clone();
60        let computer_name_clone = computer_name.clone();
61        let office_id_clone = office_id.clone();
62        let inputs_clone = inputs.clone();
63
64        // 使用ClientBuilder注册事件处理器
65        // Use ClientBuilder to register event handlers
66        let mut builder = ClientBuilder::new(url)
67            .namespace(SMCP_NAMESPACE)
68            .transport_type(TransportType::Websocket);
69
70        // 如果提供了认证密钥,添加到请求头
71        // If auth secret is provided, add to request headers
72        if let Some(secret) = auth_secret {
73            builder = builder.opening_header("x-api-key", secret.as_str());
74        }
75
76        let client = builder
77            .on_any(move |event, payload, client| {
78                // 只处理自定义事件
79                // Only handle custom events
80                let event_str = match event {
81                    Event::Custom(s) => s,
82                    _ => return async {}.boxed(),
83                };
84
85                match event_str.as_str() {
86                    CLIENT_TOOL_CALL => {
87                        let manager = manager_clone.clone();
88                        let computer_name = computer_name_clone.clone();
89                        let office_id = office_id_clone.clone();
90                        let client_clone = client.clone();
91                        let payload_clone = payload.clone();
92
93                        async move {
94                            match Self::handle_tool_call_with_ack(
95                                payload,
96                                manager,
97                                computer_name,
98                                office_id,
99                                client_clone,
100                            )
101                            .await
102                            {
103                                Ok((ack_id, response)) => {
104                                    if let Some(id) = ack_id {
105                                        if let Err(e) = client.ack_with_id(id, response).await {
106                                            error!("Failed to send ack: {}", e);
107                                        }
108                                    }
109                                }
110                                Err(e) => {
111                                    error!("Error handling tool call: {}", e);
112                                    // 尝试返回错误响应 / Try to return error response
113                                    if let Ok((Some(id), _)) = Self::extract_ack_id(payload_clone) {
114                                        let error_response = serde_json::json!({
115                                            "isError": true,
116                                            "content": [],
117                                            "structuredContent": {
118                                                "error": e.to_string(),
119                                                "error_type": "ComputerError"
120                                            }
121                                        });
122                                        let _ = client.ack_with_id(id, error_response).await;
123                                    }
124                                }
125                            }
126                        }
127                        .boxed()
128                    }
129                    CLIENT_GET_TOOLS => {
130                        let manager = manager_clone.clone();
131                        let computer_name = computer_name_clone.clone();
132                        let office_id = office_id_clone.clone();
133                        let client_clone = client.clone();
134
135                        async move {
136                            match Self::handle_get_tools_with_ack(
137                                payload,
138                                manager,
139                                computer_name,
140                                office_id,
141                                client_clone,
142                            )
143                            .await
144                            {
145                                Ok((ack_id, response)) => {
146                                    if let Some(id) = ack_id {
147                                        if let Err(e) = client.ack_with_id(id, response).await {
148                                            error!("Failed to send ack: {}", e);
149                                        }
150                                    }
151                                }
152                                Err(e) => {
153                                    error!("Error handling get tools: {}", e);
154                                }
155                            }
156                        }
157                        .boxed()
158                    }
159                    CLIENT_GET_CONFIG => {
160                        let manager = manager_clone.clone();
161                        let computer_name = computer_name_clone.clone();
162                        let office_id = office_id_clone.clone();
163                        let client_clone = client.clone();
164                        let inputs = inputs_clone.clone();
165
166                        async move {
167                            match Self::handle_get_config_with_ack(
168                                payload,
169                                manager,
170                                computer_name,
171                                office_id,
172                                client_clone,
173                                inputs,
174                            )
175                            .await
176                            {
177                                Ok((ack_id, response)) => {
178                                    if let Some(id) = ack_id {
179                                        if let Err(e) = client.ack_with_id(id, response).await {
180                                            error!("Failed to send ack: {}", e);
181                                        }
182                                    }
183                                }
184                                Err(e) => {
185                                    error!("Error handling get config: {}", e);
186                                }
187                            }
188                        }
189                        .boxed()
190                    }
191                    CLIENT_GET_DESKTOP => {
192                        let manager = manager_clone.clone();
193                        let computer_name = computer_name_clone.clone();
194                        let office_id = office_id_clone.clone();
195                        let client_clone = client.clone();
196
197                        async move {
198                            match Self::handle_get_desktop_with_ack(
199                                payload,
200                                manager,
201                                computer_name,
202                                office_id,
203                                client_clone,
204                            )
205                            .await
206                            {
207                                Ok((ack_id, response)) => {
208                                    if let Some(id) = ack_id {
209                                        if let Err(e) = client.ack_with_id(id, response).await {
210                                            error!("Failed to send ack: {}", e);
211                                        }
212                                    }
213                                }
214                                Err(e) => {
215                                    error!("Error handling get desktop: {}", e);
216                                }
217                            }
218                        }
219                        .boxed()
220                    }
221                    _ => {
222                        debug!("Unhandled event: {}", event_str);
223                        async {}.boxed()
224                    }
225                }
226            })
227            .connect()
228            .await
229            .map_err(|e| ComputerError::SocketIoError(format!("Failed to connect: {}", e)))?;
230
231        // 等待一小段时间确保 Socket.IO namespace 连接完全建立
232        // Wait a short time to ensure Socket.IO namespace connection is fully established
233        // Socket.IO 有两个连接阶段:Transport 层和 Namespace 层
234        // Socket.IO has two connection phases: Transport layer and Namespace layer
235        // connect() 只保证 Transport 层连接,namespace 连接是异步的
236        // connect() only guarantees Transport layer connection, namespace connection is async
237        tokio::time::sleep(std::time::Duration::from_millis(100)).await;
238
239        info!(
240            "Connected to SMCP server at {} with computer name: {}",
241            url, computer_name
242        );
243
244        Ok(Self {
245            client,
246            computer_name,
247            office_id,
248            inputs,
249        })
250    }
251
252    /// 加入Office(Socket.IO Room)
253    /// Join an Office (Socket.IO Room)
254    pub async fn join_office(&self, office_id: &str) -> ComputerResult<()> {
255        debug!("Joining office: {}", office_id);
256
257        // 先设置office_id
258        // Set office_id first
259        *self.office_id.write().await = Some(office_id.to_string());
260
261        let req_data = serde_json::json!({
262            "office_id": office_id,
263            "role": "computer",
264            "name": self.computer_name
265        });
266
267        // 使用call方法等待服务器响应
268        // Use call method to wait for server response
269        match self.call(SERVER_JOIN_OFFICE, req_data, Some(10)).await {
270            Ok(response) => {
271                // 服务器返回的是 (bool, Option<String>) 元组序列化后的数组
272                // Server returns serialized array of (bool, Option<String>) tuple
273                debug!("Join office response: {:?}", response);
274
275                // 检查响应是否包含嵌套数组
276                // Check if response contains nested array
277                let actual_response = if response.len() == 1 {
278                    if let Some(arr) = response.first().and_then(|v| v.as_array()) {
279                        arr.to_vec()
280                    } else {
281                        response
282                    }
283                } else {
284                    response
285                };
286
287                if !actual_response.is_empty() {
288                    if let Some(success) = actual_response.first().and_then(|v| v.as_bool()) {
289                        if success {
290                            info!("Successfully joined office: {}", office_id);
291                            Ok(())
292                        } else {
293                            // 加入失败,重置office_id / Reset office_id on failure
294                            *self.office_id.write().await = None;
295                            let error_msg = actual_response
296                                .get(1)
297                                .and_then(|v| v.as_str())
298                                .unwrap_or("Unknown error");
299                            Err(ComputerError::SocketIoError(format!(
300                                "Failed to join office: {}",
301                                error_msg
302                            )))
303                        }
304                    } else {
305                        *self.office_id.write().await = None;
306                        Err(ComputerError::SocketIoError(format!(
307                            "Invalid response format from server: {:?}",
308                            actual_response
309                        )))
310                    }
311                } else {
312                    *self.office_id.write().await = None;
313                    Err(ComputerError::SocketIoError(
314                        "Empty response from server".to_string(),
315                    ))
316                }
317            }
318            Err(e) => {
319                *self.office_id.write().await = None;
320                Err(e)
321            }
322        }
323    }
324
325    /// 获取当前Office ID / Get current Office ID
326    pub async fn get_current_office_id(&self) -> ComputerResult<String> {
327        let office_id = self.office_id.read().await;
328        match office_id.as_ref() {
329            Some(id) => Ok(id.clone()),
330            None => Err(ComputerError::InvalidState(
331                "Not currently in any office".to_string(),
332            )),
333        }
334    }
335
336    /// 离开Office
337    /// Leave an Office
338    pub async fn leave_office(&self, office_id: &str) -> ComputerResult<()> {
339        debug!("Leaving office: {}", office_id);
340
341        let req_data = serde_json::json!({
342            "office_id": office_id
343        });
344
345        self.emit(SERVER_LEAVE_OFFICE, req_data).await?;
346        *self.office_id.write().await = None;
347
348        info!("Left office: {}", office_id);
349        Ok(())
350    }
351
352    /// 发送配置更新通知
353    /// Emit config update notification
354    pub async fn emit_update_config(&self) -> ComputerResult<()> {
355        let office_id = self.office_id.read().await;
356        if office_id.is_some() {
357            let req_data = serde_json::json!({
358                "computer": self.computer_name
359            });
360            self.emit(SERVER_UPDATE_CONFIG, req_data).await?;
361            info!("Emitted config update notification");
362        }
363        Ok(())
364    }
365
366    /// 发送工具列表更新通知
367    /// Emit tool list update notification
368    pub async fn emit_update_tool_list(&self) -> ComputerResult<()> {
369        let office_id = self.office_id.read().await;
370        if office_id.is_some() {
371            let req_data = serde_json::json!({
372                "computer": self.computer_name
373            });
374            self.emit(SERVER_UPDATE_TOOL_LIST, req_data).await?;
375            info!("Emitted tool list update notification");
376        }
377        Ok(())
378    }
379
380    /// 发送桌面更新通知
381    /// Emit desktop update notification
382    pub async fn emit_update_desktop(&self) -> ComputerResult<()> {
383        let office_id = self.office_id.read().await;
384        if office_id.is_some() {
385            let req_data = serde_json::json!({
386                "computer": self.computer_name
387            });
388            self.emit(SERVER_UPDATE_DESKTOP, req_data).await?;
389            info!("Emitted desktop update notification");
390        }
391        Ok(())
392    }
393
394    /// 发送事件(不等待响应)
395    /// Emit event without waiting for response
396    async fn emit(&self, event: &str, data: Value) -> ComputerResult<()> {
397        // 检查事件名 policy / Check event name policy
398        if event.starts_with("notify:") || event.starts_with("client:") {
399            return Err(ComputerError::InvalidState(
400                format!(
401                    "Computer 不允许发送 notify:* 或 client:* 事件 / Computer cannot send notify:* or client:* events: {}",
402                    event
403                )
404            ));
405        }
406
407        debug!("Emitting event: {}", event);
408
409        self.client
410            .emit(event, Payload::Text(vec![data], None))
411            .await
412            .map_err(|e| ComputerError::SocketIoError(format!("Failed to emit {}: {}", event, e)))
413    }
414
415    /// 发送事件并等待响应
416    /// Emit event and wait for response
417    async fn call(
418        &self,
419        event: &str,
420        data: Value,
421        timeout_secs: Option<u64>,
422    ) -> ComputerResult<Vec<Value>> {
423        // 检查事件名 policy / Check event name policy
424        if event.starts_with("notify:") || event.starts_with("client:") {
425            return Err(ComputerError::InvalidState(
426                format!(
427                    "Computer 不允许发送 notify:* 或 client:* 事件 / Computer cannot send notify:* or client:* events: {}",
428                    event
429                )
430            ));
431        }
432
433        let timeout = std::time::Duration::from_secs(timeout_secs.unwrap_or(30));
434        debug!("Calling event: {} with timeout {:?}", event, timeout);
435
436        let (tx, rx) = tokio::sync::oneshot::channel();
437        let tx = Arc::new(std::sync::Mutex::new(Some(tx)));
438
439        let callback = move |payload: Payload, _client: Client| {
440            if let Some(tx_opt) = tx.try_lock().ok().and_then(|mut m| m.take()) {
441                let _ = tx_opt.send(payload);
442            }
443            async {}.boxed()
444        };
445
446        self.client
447            .emit_with_ack(event, Payload::Text(vec![data], None), timeout, callback)
448            .await
449            .map_err(|e| {
450                ComputerError::SocketIoError(format!("Failed to call {}: {}", event, e))
451            })?;
452
453        // 使用 tokio::time::timeout 来确保 rx.await 不会无限期等待
454        // Use tokio::time::timeout to ensure rx.await doesn't wait forever
455        match tokio::time::timeout(timeout, rx).await {
456            Ok(Ok(response)) => {
457                // 从响应中提取JSON数据 / Extract JSON data from response
458                match response {
459                    Payload::Text(values, _) => {
460                        debug!("Received response: {:?}", values);
461                        Ok(values)
462                    }
463                    #[allow(deprecated)]
464                    Payload::String(s, _) => {
465                        // 尝试解析字符串为JSON数组
466                        // Try to parse string as JSON array
467                        let parsed: Vec<Value> = serde_json::from_str(&s).map_err(|e| {
468                            ComputerError::SocketIoError(format!("Failed to parse response: {}", e))
469                        })?;
470                        debug!("Received parsed response: {:?}", parsed);
471                        Ok(parsed)
472                    }
473                    Payload::Binary(_, _) => Err(ComputerError::SocketIoError(
474                        "Binary response not supported".to_string(),
475                    )),
476                }
477            }
478            Ok(Err(_)) => {
479                error!("Channel closed while calling event: {}", event);
480                Err(ComputerError::SocketIoError(
481                    "Channel closed while waiting for response".to_string(),
482                ))
483            }
484            Err(_) => {
485                error!("Timeout while calling event: {}", event);
486                Err(ComputerError::SocketIoError(
487                    "Timeout while waiting for response".to_string(),
488                ))
489            }
490        }
491    }
492
493    /// 处理工具调用事件(带ACK响应)
494    /// Handle tool call event (with ACK response)
495    async fn handle_tool_call_with_ack(
496        payload: Payload,
497        manager: Arc<RwLock<Option<MCPServerManager>>>,
498        computer_name: String,
499        _office_id: Arc<RwLock<Option<String>>>,
500        _client: Client,
501    ) -> ComputerResult<(Option<i32>, Value)> {
502        let (ack_id, req) = Self::extract_ack_and_parse::<ToolCallReq>(payload)?;
503
504        // 验证 computer_name(Server 路由已保证请求来自同一 office,无需验证 agent 字段)
505        // Validate computer_name (Server routing ensures request is from same office, no need to validate agent field)
506        if computer_name != req.computer {
507            return Err(ComputerError::ValidationError(format!(
508                "Computer name mismatch: expected {}, got {}",
509                computer_name, req.computer
510            )));
511        }
512
513        // 执行工具调用 / Execute tool call
514        let result = {
515            let manager_guard = manager.read().await;
516            match manager_guard.as_ref() {
517                Some(mgr) => {
518                    mgr.execute_tool(
519                        &req.tool_name,
520                        req.params,
521                        Some(std::time::Duration::from_secs(req.timeout as u64)),
522                    )
523                    .await?
524                }
525                None => {
526                    return Err(ComputerError::InvalidState(
527                        "MCP Manager not initialized".to_string(),
528                    ));
529                }
530            }
531        };
532
533        let result_value =
534            serde_json::to_value(result).map_err(ComputerError::SerializationError)?;
535
536        info!("Tool call executed successfully: {}", req.tool_name);
537        Ok((ack_id, result_value))
538    }
539
540    /// 处理获取工具列表事件(带ACK响应)
541    /// Handle get tools event (with ACK response)
542    async fn handle_get_tools_with_ack(
543        payload: Payload,
544        manager: Arc<RwLock<Option<MCPServerManager>>>,
545        computer_name: String,
546        _office_id: Arc<RwLock<Option<String>>>,
547        _client: Client,
548    ) -> ComputerResult<(Option<i32>, Value)> {
549        let (ack_id, req) = Self::extract_ack_and_parse::<GetToolsReq>(payload)?;
550
551        // 验证 computer_name(Server 路由已保证请求来自同一 office,无需验证 agent 字段)
552        // Validate computer_name (Server routing ensures request is from same office, no need to validate agent field)
553        if computer_name != req.computer {
554            return Err(ComputerError::ValidationError(format!(
555                "Computer name mismatch: expected {}, got {}",
556                computer_name, req.computer
557            )));
558        }
559
560        // 获取工具列表 / Get tools list
561        let tools: Vec<smcp::SMCPTool> = {
562            let manager_guard = manager.read().await;
563            match manager_guard.as_ref() {
564                Some(mgr) => {
565                    // 转换Tool为SMCPTool
566                    // Convert Tool to SMCPTool
567                    let tool_list = mgr.list_available_tools().await;
568                    tool_list
569                        .into_iter()
570                        .map(|tool| smcp::SMCPTool {
571                            name: tool.name,
572                            description: tool.description,
573                            params_schema: tool.input_schema,
574                            return_schema: None,
575                            meta: None,
576                        })
577                        .collect()
578                }
579                None => {
580                    return Err(ComputerError::InvalidState(
581                        "MCP Manager not initialized".to_string(),
582                    ));
583                }
584            }
585        };
586
587        let response = GetToolsRet {
588            tools: tools.clone(),
589            req_id: req.base.req_id,
590        };
591
592        info!(
593            "Returned {} tools for agent {}",
594            tools.len(),
595            req.base.agent
596        );
597        Ok((ack_id, serde_json::to_value(response)?))
598    }
599
600    /// 处理获取配置事件(带ACK响应)
601    /// Handle get config event (with ACK response)
602    async fn handle_get_config_with_ack(
603        payload: Payload,
604        manager: Arc<RwLock<Option<MCPServerManager>>>,
605        computer_name: String,
606        _office_id: Arc<RwLock<Option<String>>>,
607        _client: Client,
608        inputs: Arc<RwLock<HashMap<String, MCPServerInput>>>,
609    ) -> ComputerResult<(Option<i32>, Value)> {
610        let (ack_id, req) = Self::extract_ack_and_parse::<GetComputerConfigReq>(payload)?;
611
612        // 验证 computer_name(Server 路由已保证请求来自同一 office,无需验证 agent 字段)
613        // Validate computer_name (Server routing ensures request is from same office, no need to validate agent field)
614        if computer_name != req.computer {
615            return Err(ComputerError::ValidationError(format!(
616                "Computer name mismatch: expected {}, got {}",
617                computer_name, req.computer
618            )));
619        }
620
621        // 获取配置 / Get config
622        let servers = {
623            let manager_guard = manager.read().await;
624            match manager_guard.as_ref() {
625                Some(mgr) => {
626                    // 获取完整服务器配置(不只是状态)
627                    // Get complete server configurations (not just status)
628                    mgr.get_server_configs().await
629                }
630                None => {
631                    return Err(ComputerError::InvalidState(
632                        "MCP Manager not initialized".to_string(),
633                    ));
634                }
635            }
636        };
637
638        // 获取输入定义 / Get input definitions
639        // 将 HashMap<String, MCPServerInput> 转换为 Vec<serde_json::Value>
640        // Convert HashMap<String, MCPServerInput> to Vec<serde_json::Value>
641        let inputs_data = {
642            let inputs_guard = inputs.read().await;
643            if inputs_guard.is_empty() {
644                None
645            } else {
646                let inputs_vec: Vec<serde_json::Value> = inputs_guard
647                    .values()
648                    .filter_map(|input| serde_json::to_value(input).ok())
649                    .collect();
650                if inputs_vec.is_empty() {
651                    None
652                } else {
653                    Some(inputs_vec)
654                }
655            }
656        };
657
658        let response = GetComputerConfigRet {
659            servers,
660            inputs: inputs_data,
661        };
662
663        info!("Returned config for agent {}", req.base.agent);
664        Ok((ack_id, serde_json::to_value(response)?))
665    }
666
667    /// 处理获取桌面事件(带ACK响应)
668    /// Handle get desktop event (with ACK response)
669    async fn handle_get_desktop_with_ack(
670        payload: Payload,
671        _manager: Arc<RwLock<Option<MCPServerManager>>>,
672        computer_name: String,
673        _office_id: Arc<RwLock<Option<String>>>,
674        _client: Client,
675    ) -> ComputerResult<(Option<i32>, Value)> {
676        let (ack_id, req) = Self::extract_ack_and_parse::<GetDesktopReq>(payload)?;
677
678        // 验证 computer_name(Server 路由已保证请求来自同一 office,无需验证 agent 字段)
679        // Validate computer_name (Server routing ensures request is from same office, no need to validate agent field)
680        if computer_name != req.computer {
681            return Err(ComputerError::ValidationError(format!(
682                "Computer name mismatch: expected {}, got {}",
683                computer_name, req.computer
684            )));
685        }
686
687        // 获取桌面 / Get desktop
688        // TODO: 实现实际的桌面捕获逻辑
689        // TODO: Implement actual desktop capture logic
690        let desktops = Vec::<String>::new(); // 暂时返回空列表 / Return empty list for now
691
692        let response = GetDesktopRet {
693            desktops: Some(desktops),
694            req_id: req.base.req_id,
695        };
696
697        info!("Returned desktop for agent {}", req.base.agent);
698        Ok((ack_id, serde_json::to_value(response)?))
699    }
700
701    /// 从payload中提取ack_id并解析数据
702    /// Extract ack_id from payload and parse data
703    fn extract_ack_and_parse<T: serde::de::DeserializeOwned>(
704        payload: Payload,
705    ) -> ComputerResult<(Option<i32>, T)> {
706        match payload {
707            Payload::Text(mut values, ack_id) => {
708                if let Some(value) = values.pop() {
709                    let req =
710                        serde_json::from_value(value).map_err(ComputerError::SerializationError)?;
711                    Ok((ack_id, req))
712                } else {
713                    Err(ComputerError::ProtocolError("Empty payload".to_string()))
714                }
715            }
716            #[allow(deprecated)]
717            Payload::String(s, ack_id) => {
718                let req = serde_json::from_str(&s).map_err(ComputerError::SerializationError)?;
719                Ok((ack_id, req))
720            }
721            Payload::Binary(_, _) => Err(ComputerError::SocketIoError(
722                "Binary payload not supported".to_string(),
723            )),
724        }
725    }
726
727    /// 仅提取ack_id(用于错误处理)
728    /// Extract ack_id only (for error handling)
729    fn extract_ack_id(payload: Payload) -> ComputerResult<(Option<i32>, ())> {
730        match payload {
731            Payload::Text(_, ack_id) => Ok((ack_id, ())),
732            #[allow(deprecated)]
733            Payload::String(_, ack_id) => Ok((ack_id, ())),
734            Payload::Binary(_, _) => Ok((None, ())),
735        }
736    }
737
738    /// 断开连接
739    /// Disconnect from server
740    pub async fn disconnect(self) -> ComputerResult<()> {
741        debug!("Disconnecting from server");
742        self.client
743            .disconnect()
744            .await
745            .map_err(|e| ComputerError::SocketIoError(format!("Failed to disconnect: {}", e)))?;
746        info!("Disconnected from server");
747        Ok(())
748    }
749
750    /// 获取当前office ID
751    /// Get current office ID
752    pub async fn get_office_id(&self) -> Option<String> {
753        self.office_id.read().await.clone()
754    }
755
756    /// 获取连接的 URL
757    /// Get connected URL
758    pub fn get_url(&self) -> String {
759        // 由于 tf_rust_socketio 的 Client 没有 uri() 方法,返回默认值
760        // Since tf_rust_socketio Client doesn't have uri() method, return default
761        "unknown".to_string()
762    }
763
764    /// 获取连接的 namespace
765    /// Get connected namespace
766    pub fn get_namespace(&self) -> String {
767        // 从 client 中获取 namespace,如果无法获取则返回默认值
768        // Get namespace from client, return default if unable to get
769        "/smcp".to_string()
770    }
771}