a2c-smcp 0.1.1rc0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. a2c_smcp-0.1.1rc0/LICENSE +21 -0
  2. a2c_smcp-0.1.1rc0/PKG-INFO +43 -0
  3. a2c_smcp-0.1.1rc0/README.md +2 -0
  4. a2c_smcp-0.1.1rc0/a2c_smcp/__init__.py +7 -0
  5. a2c_smcp-0.1.1rc0/a2c_smcp/agent/__init__.py +56 -0
  6. a2c_smcp-0.1.1rc0/a2c_smcp/agent/auth.py +140 -0
  7. a2c_smcp-0.1.1rc0/a2c_smcp/agent/base.py +288 -0
  8. a2c_smcp-0.1.1rc0/a2c_smcp/agent/client.py +342 -0
  9. a2c_smcp-0.1.1rc0/a2c_smcp/agent/sync_client.py +297 -0
  10. a2c_smcp-0.1.1rc0/a2c_smcp/agent/types.py +134 -0
  11. a2c_smcp-0.1.1rc0/a2c_smcp/computer/__init__.py +10 -0
  12. a2c_smcp-0.1.1rc0/a2c_smcp/computer/base.py +153 -0
  13. a2c_smcp-0.1.1rc0/a2c_smcp/computer/cli/__init__.py +6 -0
  14. a2c_smcp-0.1.1rc0/a2c_smcp/computer/cli/interactive_impl.py +490 -0
  15. a2c_smcp-0.1.1rc0/a2c_smcp/computer/cli/main.py +289 -0
  16. a2c_smcp-0.1.1rc0/a2c_smcp/computer/cli/utils.py +177 -0
  17. a2c_smcp-0.1.1rc0/a2c_smcp/computer/computer.py +738 -0
  18. a2c_smcp-0.1.1rc0/a2c_smcp/computer/desktop/__init__.py +6 -0
  19. a2c_smcp-0.1.1rc0/a2c_smcp/computer/desktop/organize.py +142 -0
  20. a2c_smcp-0.1.1rc0/a2c_smcp/computer/inputs/__init__.py +6 -0
  21. a2c_smcp-0.1.1rc0/a2c_smcp/computer/inputs/base.py +135 -0
  22. a2c_smcp-0.1.1rc0/a2c_smcp/computer/inputs/cli_io.py +182 -0
  23. a2c_smcp-0.1.1rc0/a2c_smcp/computer/inputs/render.py +96 -0
  24. a2c_smcp-0.1.1rc0/a2c_smcp/computer/inputs/resolver.py +88 -0
  25. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/__init__.py +5 -0
  26. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/base_client.py +461 -0
  27. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/base_client.pyi +91 -0
  28. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/http_client.py +49 -0
  29. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/manager.py +552 -0
  30. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/model.py +192 -0
  31. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/sse_client.py +44 -0
  32. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/stdio_client.py +42 -0
  33. a2c_smcp-0.1.1rc0/a2c_smcp/computer/mcp_clients/utils.py +28 -0
  34. a2c_smcp-0.1.1rc0/a2c_smcp/computer/socketio/__init__.py +5 -0
  35. a2c_smcp-0.1.1rc0/a2c_smcp/computer/socketio/client.py +227 -0
  36. a2c_smcp-0.1.1rc0/a2c_smcp/computer/types.py +34 -0
  37. a2c_smcp-0.1.1rc0/a2c_smcp/computer/utils/__init__.py +6 -0
  38. a2c_smcp-0.1.1rc0/a2c_smcp/computer/utils/console.py +23 -0
  39. a2c_smcp-0.1.1rc0/a2c_smcp/server/__init__.py +50 -0
  40. a2c_smcp-0.1.1rc0/a2c_smcp/server/auth.py +134 -0
  41. a2c_smcp-0.1.1rc0/a2c_smcp/server/base.py +129 -0
  42. a2c_smcp-0.1.1rc0/a2c_smcp/server/namespace.py +410 -0
  43. a2c_smcp-0.1.1rc0/a2c_smcp/server/sync_auth.py +90 -0
  44. a2c_smcp-0.1.1rc0/a2c_smcp/server/sync_base.py +91 -0
  45. a2c_smcp-0.1.1rc0/a2c_smcp/server/sync_namespace.py +304 -0
  46. a2c_smcp-0.1.1rc0/a2c_smcp/server/types.py +48 -0
  47. a2c_smcp-0.1.1rc0/a2c_smcp/server/utils.py +139 -0
  48. a2c_smcp-0.1.1rc0/a2c_smcp/smcp.py +299 -0
  49. a2c_smcp-0.1.1rc0/a2c_smcp/types.py +29 -0
  50. a2c_smcp-0.1.1rc0/a2c_smcp/utils/__init__.py +18 -0
  51. a2c_smcp-0.1.1rc0/a2c_smcp/utils/async_property/__init__.py +9 -0
  52. a2c_smcp-0.1.1rc0/a2c_smcp/utils/async_property/base.py +51 -0
  53. a2c_smcp-0.1.1rc0/a2c_smcp/utils/async_property/proxy.py +463 -0
  54. a2c_smcp-0.1.1rc0/a2c_smcp/utils/logger.py +67 -0
  55. a2c_smcp-0.1.1rc0/a2c_smcp/utils/window_uri.py +232 -0
  56. a2c_smcp-0.1.1rc0/pyproject.toml +86 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 A2C-SMCP
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: a2c-smcp
3
+ Version: 0.1.1rc0
4
+ Summary: The official Python Computer Client SDK for A2C-SMCP
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: JQQ
8
+ Author-email: jqq1716@gmail.com
9
+ Requires-Python: >=3.11,<4.0
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Provides-Extra: cli
17
+ Provides-Extra: dev
18
+ Provides-Extra: test
19
+ Requires-Dist: aiohttp (>=3.12.15,<4.0.0) ; extra == "test"
20
+ Requires-Dist: inline-snapshot (>=0.27.2,<0.28.0) ; extra == "test"
21
+ Requires-Dist: mcp (==1.15.0)
22
+ Requires-Dist: mypy (>=1.17.1,<2.0.0) ; extra == "dev"
23
+ Requires-Dist: poethepoet (>=0.37.0,<0.38.0) ; extra == "dev"
24
+ Requires-Dist: polyfactory (>=2.22.2,<3.0.0) ; extra == "test"
25
+ Requires-Dist: prompt-toolkit (>=3.0.52,<4.0.0) ; extra == "cli"
26
+ Requires-Dist: pydantic (>=2.11.7,<3.0.0)
27
+ Requires-Dist: pytest (>=8.4.1,<9.0.0) ; extra == "dev"
28
+ Requires-Dist: pytest-asyncio (>=1.1.0,<2.0.0) ; extra == "test"
29
+ Requires-Dist: pytest-cov (>=6.2.1,<7.0.0) ; extra == "test"
30
+ Requires-Dist: pytest-dotenv (>=0.5.2,<0.6.0) ; extra == "test"
31
+ Requires-Dist: pytest-mock (>=3.14.1,<4.0.0) ; extra == "test"
32
+ Requires-Dist: python-socketio (>=5.13.0,<6.0.0)
33
+ Requires-Dist: rich (>=14.1.0,<15.0.0) ; extra == "cli"
34
+ Requires-Dist: ruff (>=0.12.9,<0.13.0) ; extra == "dev"
35
+ Requires-Dist: transitions (>=0.9.3,<0.10.0)
36
+ Requires-Dist: typer (>=0.17.4,<1.0.0) ; extra == "cli"
37
+ Requires-Dist: tzlocal (>=5.0.0,<6.0.0)
38
+ Requires-Dist: vrl-python (>=0.1.0,<0.2.0)
39
+ Description-Content-Type: text/markdown
40
+
41
+ # python-sdk
42
+ The official Python Client SDK for A2C-SMCP
43
+
@@ -0,0 +1,2 @@
1
+ # python-sdk
2
+ The official Python Client SDK for A2C-SMCP
@@ -0,0 +1,7 @@
1
+ # -*- coding: utf-8 -*-
2
+ # filename: __init__.py.py
3
+ # @Time : 2025/9/28 15:33
4
+ # @Author : JQQ
5
+ # @Email : jqq1716@gmail.com
6
+ # @Software: PyCharm
7
+ __version__: str = "0.1.1-rc0"
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ # filename: __init__.py
3
+ # @Time : 2025/9/30 13:56
4
+ # @Author : JQQ
5
+ # @Email : jqq1716@gmail.com
6
+ # @Software: PyCharm
7
+
8
+ """
9
+ A2C-SMCP Agent模块
10
+ A2C-SMCP Agent module
11
+
12
+ 提供Agent端的SMCP协议客户端实现,包括同步和异步两种模式
13
+ Provides Agent-side SMCP protocol client implementation, including both synchronous and asynchronous modes
14
+ """
15
+
16
+ from a2c_smcp.agent.auth import AgentAuthProvider, DefaultAgentAuthProvider
17
+ from a2c_smcp.agent.base import BaseAgentClient
18
+ from a2c_smcp.agent.client import AsyncSMCPAgentClient
19
+ from a2c_smcp.agent.sync_client import SMCPAgentClient
20
+ from a2c_smcp.agent.types import (
21
+ AgentConfig,
22
+ AgentEventHandler,
23
+ AgentID,
24
+ AgentIDGetter,
25
+ AsyncAgentEventHandler,
26
+ AsyncAgentIDGetter,
27
+ AsyncToolCallCallback,
28
+ ComputerID,
29
+ RequestID,
30
+ ToolCallCallback,
31
+ ToolCallContext,
32
+ )
33
+
34
+ __all__ = [
35
+ # 认证相关 / Authentication related
36
+ "AgentAuthProvider",
37
+ "DefaultAgentAuthProvider",
38
+
39
+ # 客户端实现 / Client implementations
40
+ "BaseAgentClient",
41
+ "SMCPAgentClient",
42
+ "AsyncSMCPAgentClient",
43
+
44
+ # 类型定义 / Type definitions
45
+ "AgentConfig",
46
+ "AgentEventHandler",
47
+ "AsyncAgentEventHandler",
48
+ "AgentID",
49
+ "ComputerID",
50
+ "RequestID",
51
+ "ToolCallContext",
52
+ "ToolCallCallback",
53
+ "AsyncToolCallCallback",
54
+ "AgentIDGetter",
55
+ "AsyncAgentIDGetter",
56
+ ]
@@ -0,0 +1,140 @@
1
+ """
2
+ * 文件名: auth
3
+ * 作者: JQQ
4
+ * 创建日期: 2025/9/30
5
+ * 最后修改日期: 2025/9/30
6
+ * 版权: 2023 JQQ. All rights reserved.
7
+ * 依赖: None
8
+ * 描述: Agent端认证接口抽象定义 / Agent-side authentication interface abstract definition
9
+ """
10
+
11
+ from abc import ABC, abstractmethod
12
+ from typing import Any
13
+
14
+ from a2c_smcp.agent.types import AgentConfig
15
+
16
+
17
+ class AgentAuthProvider(ABC):
18
+ """
19
+ Agent认证提供者抽象基类,用于处理Agent连接的认证逻辑
20
+ Abstract base class for Agent authentication providers, handles Agent connection authentication logic
21
+ """
22
+
23
+ @abstractmethod
24
+ def get_agent_id(self) -> str:
25
+ """
26
+ 获取agent_id,由用户实现具体逻辑
27
+ Get agent_id, implemented by user with specific logic
28
+
29
+ Returns:
30
+ str: agent_id
31
+ """
32
+ pass
33
+
34
+ @abstractmethod
35
+ def get_connection_auth(self) -> dict[str, Any] | None:
36
+ """
37
+ 获取连接认证信息,用于Socket.IO连接时的auth参数
38
+ Get connection authentication info, used for Socket.IO connection auth parameter
39
+
40
+ Returns:
41
+ dict[str, Any] | None: 认证信息字典 / Authentication info dictionary
42
+ """
43
+ pass
44
+
45
+ @abstractmethod
46
+ def get_connection_headers(self) -> dict[str, str]:
47
+ """
48
+ 获取连接请求头,用于Socket.IO连接时的headers参数
49
+ Get connection headers, used for Socket.IO connection headers parameter
50
+
51
+ Returns:
52
+ dict[str, str]: 请求头字典 / Headers dictionary
53
+ """
54
+ pass
55
+
56
+ @abstractmethod
57
+ def get_agent_config(self) -> AgentConfig:
58
+ """
59
+ 获取Agent配置信息
60
+ Get Agent configuration information
61
+
62
+ Returns:
63
+ AgentConfig: Agent配置 / Agent configuration
64
+ """
65
+ pass
66
+
67
+
68
+ class DefaultAgentAuthProvider(AgentAuthProvider):
69
+ """
70
+ 默认Agent认证提供者,提供基础的认证逻辑实现
71
+ Default Agent authentication provider, provides basic authentication logic implementation
72
+ """
73
+
74
+ def __init__(
75
+ self,
76
+ agent_id: str,
77
+ office_id: str,
78
+ api_key: str | None = None,
79
+ api_key_header: str = "x-api-key",
80
+ extra_headers: dict[str, str] | None = None,
81
+ auth_data: dict[str, Any] | None = None,
82
+ ) -> None:
83
+ """
84
+ 初始化默认Agent认证提供者
85
+ Initialize default Agent authentication provider
86
+
87
+ Args:
88
+ agent_id (str): Agent唯一标识 / Agent unique identifier
89
+ office_id (str): 办公室ID / Office ID
90
+ api_key (str | None): API密钥 / API key
91
+ api_key_header (str): API密钥请求头名称 / API key header name
92
+ extra_headers (dict[str, str] | None): 额外请求头 / Extra headers
93
+ auth_data (dict[str, Any] | None): 额外认证数据 / Extra auth data
94
+ """
95
+ self._agent_id = agent_id
96
+ self._office_id = office_id
97
+ self._api_key = api_key
98
+ self._api_key_header = api_key_header
99
+ self._extra_headers = extra_headers or {}
100
+ self._auth_data = auth_data or {}
101
+
102
+ def get_agent_id(self) -> str:
103
+ """
104
+ 获取Agent ID
105
+ Get Agent ID
106
+ """
107
+ return self._agent_id
108
+
109
+ def get_connection_auth(self) -> dict[str, Any] | None:
110
+ """
111
+ 获取连接认证信息
112
+ Get connection authentication info
113
+ """
114
+ if not self._auth_data:
115
+ return None
116
+ return self._auth_data.copy()
117
+
118
+ def get_connection_headers(self) -> dict[str, str]:
119
+ """
120
+ 获取连接请求头
121
+ Get connection headers
122
+ """
123
+ headers = self._extra_headers.copy()
124
+
125
+ # 添加API密钥到请求头
126
+ # Add API key to headers
127
+ if self._api_key:
128
+ headers[self._api_key_header] = self._api_key
129
+
130
+ return headers
131
+
132
+ def get_agent_config(self) -> AgentConfig:
133
+ """
134
+ 获取Agent配置信息
135
+ Get Agent configuration information
136
+ """
137
+ return AgentConfig(
138
+ agent_id=self._agent_id,
139
+ office_id=self._office_id,
140
+ )
@@ -0,0 +1,288 @@
1
+ """
2
+ * 文件名: base
3
+ * 作者: JQQ
4
+ * 创建日期: 2025/9/30
5
+ * 最后修改日期: 2025/9/30
6
+ * 版权: 2023 JQQ. All rights reserved.
7
+ * 依赖: socketio, loguru
8
+ * 描述: Agent基础客户端抽象类 / Agent base client abstract class
9
+ """
10
+
11
+ import uuid
12
+ from abc import ABC, abstractmethod
13
+ from typing import Any
14
+
15
+ from mcp.types import CallToolResult, TextContent
16
+
17
+ from a2c_smcp.agent.auth import AgentAuthProvider
18
+ from a2c_smcp.agent.types import AgentEventHandler
19
+ from a2c_smcp.smcp import (
20
+ EnterOfficeNotification,
21
+ GetDeskTopReq,
22
+ GetDeskTopRet,
23
+ GetToolsReq,
24
+ GetToolsRet,
25
+ LeaveOfficeNotification,
26
+ ToolCallReq,
27
+ UpdateMCPConfigNotification,
28
+ )
29
+ from a2c_smcp.utils.logger import logger
30
+
31
+
32
+ class BaseAgentClient(ABC):
33
+ """
34
+ Agent基础客户端抽象类,提供通用的SMCP协议处理逻辑
35
+ Agent base client abstract class, provides common SMCP protocol handling logic
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ auth_provider: AgentAuthProvider,
41
+ event_handler: AgentEventHandler | None = None,
42
+ ) -> None:
43
+ """
44
+ 初始化基础Agent客户端
45
+ Initialize base Agent client
46
+
47
+ Args:
48
+ auth_provider (AgentAuthProvider): 认证提供者 / Authentication provider
49
+ event_handler (AgentEventHandler | None): 事件处理器 / Event handler
50
+ """
51
+ self.auth_provider = auth_provider
52
+ self.event_handler = event_handler
53
+
54
+ @abstractmethod
55
+ def emit(self, event: str, data: Any = None, namespace: str | None = None, callback: Any = None) -> Any:
56
+ """
57
+ 发送事件的抽象方法
58
+ Abstract method for sending events
59
+ """
60
+ pass
61
+
62
+ @abstractmethod
63
+ def call(self, event: str, data: Any = None, namespace: str | None = None, timeout: int = 60) -> Any:
64
+ """
65
+ 调用事件并等待响应的抽象方法
66
+ Abstract method for calling events and waiting for response
67
+ """
68
+ pass
69
+
70
+ def validate_emit_event(self, event: str) -> None:
71
+ """
72
+ 验证发送事件的合法性
73
+ Validate the legality of emitted events
74
+
75
+ Args:
76
+ event (str): 事件名称 / Event name
77
+
78
+ Raises:
79
+ ValueError: 当事件不合法时 / When event is invalid
80
+ """
81
+ if event.startswith("notify:"):
82
+ raise ValueError("AgentClient不允许使用notify:*事件 / AgentClient is not allowed to use notify:* events")
83
+ if event.startswith("agent:"):
84
+ raise ValueError("AgentClient不允许发起agent:*事件 / AgentClient is not allowed to initiate agent:* events")
85
+
86
+ def create_tool_call_request(self, computer: str, tool_name: str, params: dict, timeout: int) -> ToolCallReq:
87
+ """
88
+ 创建工具调用请求对象
89
+ Create tool call request object
90
+
91
+ Args:
92
+ computer (str): 目标计算机ID / Target computer ID
93
+ tool_name (str): 工具名称 / Tool name
94
+ params (dict): 调用参数 / Call parameters
95
+ timeout (int): 超时时间 / Timeout duration
96
+
97
+ Returns:
98
+ ToolCallReq: 工具调用请求 / Tool call request
99
+ """
100
+ agent_config = self.auth_provider.get_agent_config()
101
+ return ToolCallReq(
102
+ computer=computer,
103
+ tool_name=tool_name,
104
+ params=params,
105
+ robot_id=agent_config["agent_id"],
106
+ req_id=uuid.uuid4().hex,
107
+ timeout=timeout,
108
+ )
109
+
110
+ def create_get_tools_request(self, computer: str) -> GetToolsReq:
111
+ """
112
+ 创建获取工具请求对象
113
+ Create get tools request object
114
+
115
+ Args:
116
+ computer (str): 目标计算机ID / Target computer ID
117
+
118
+ Returns:
119
+ GetToolsReq: 获取工具请求 / Get tools request
120
+ """
121
+ agent_config = self.auth_provider.get_agent_config()
122
+ return GetToolsReq(
123
+ computer=computer,
124
+ robot_id=agent_config["agent_id"],
125
+ req_id=uuid.uuid4().hex,
126
+ )
127
+
128
+ def create_get_desktop_request(self, computer: str, *, size: int | None = None, window: str | None = None) -> GetDeskTopReq:
129
+ """
130
+ 创建获取桌面请求对象
131
+ Create get desktop request object
132
+
133
+ Args:
134
+ computer (str): 目标计算机ID / Target computer ID
135
+ size (int | None): 桌面窗口数量上限 / Max number of windows
136
+ window (str | None): 指定窗口URI / Specific window URI
137
+
138
+ Returns:
139
+ GetDeskTopReq: 获取桌面请求 / Get desktop request
140
+ """
141
+ agent_config = self.auth_provider.get_agent_config()
142
+ req: GetDeskTopReq = {
143
+ "computer": computer,
144
+ "robot_id": agent_config["agent_id"],
145
+ "req_id": uuid.uuid4().hex,
146
+ }
147
+ if size is not None:
148
+ req["desktop_size"] = size
149
+ if window is not None:
150
+ req["window"] = window
151
+ return req
152
+
153
+ def process_desktop_response(self, response: GetDeskTopRet, computer: str) -> None:
154
+ """
155
+ 处理桌面响应(默认仅记录日志;留作后续扩展回调)。
156
+ Process desktop response (log only by default; placeholder for future callbacks).
157
+ """
158
+ try:
159
+ desktops = response.get("desktops", []) if isinstance(response, dict) else []
160
+ logger.info(f"Received desktop from computer {computer}, windows={len(desktops)}")
161
+ except Exception as e:
162
+ logger.error(f"Error processing desktop response: {e}")
163
+
164
+ def handle_tool_call_timeout(self, req_id: str) -> CallToolResult:
165
+ """
166
+ 处理工具调用超时情况
167
+ Handle tool call timeout situation
168
+
169
+ Args:
170
+ req_id (str): 请求ID / Request ID
171
+
172
+ Returns:
173
+ CallToolResult: 超时错误结果 / Timeout error result
174
+ """
175
+ return CallToolResult(
176
+ content=[TextContent(text=f"工具调用超时 / Tool call timeout, req_id={req_id}", type="text")],
177
+ isError=True,
178
+ )
179
+
180
+ def validate_office_data(self, data: EnterOfficeNotification | LeaveOfficeNotification) -> str:
181
+ """
182
+ 验证办公室数据并返回计算机ID
183
+ Validate office data and return computer ID
184
+
185
+ Args:
186
+ data: 办公室通知数据 / Office notification data
187
+
188
+ Returns:
189
+ str: 计算机ID / Computer ID
190
+
191
+ Raises:
192
+ AssertionError: 当数据无效时 / When data is invalid
193
+ """
194
+ agent_config = self.auth_provider.get_agent_config()
195
+ assert data["office_id"] == agent_config["office_id"], "无效的办公室ID / Invalid office ID"
196
+ assert data.get("computer"), "无效的计算机ID / Invalid computer ID"
197
+ return data["computer"]
198
+
199
+ def handle_computer_enter_office(self, data: EnterOfficeNotification) -> None:
200
+ """
201
+ 处理Computer加入办公室事件
202
+ Handle Computer enter office event
203
+
204
+ Args:
205
+ data: 加入办公室通知数据 / Enter office notification data
206
+ """
207
+ try:
208
+ computer = self.validate_office_data(data)
209
+ logger.info(f"Computer {computer} entered office {data['office_id']}")
210
+
211
+ # 调用事件处理器
212
+ # Call event handler
213
+ if self.event_handler:
214
+ self.event_handler.on_computer_enter_office(data)
215
+
216
+ except Exception as e:
217
+ logger.error(f"Error handling computer enter office: {e}")
218
+
219
+ def handle_computer_leave_office(self, data: LeaveOfficeNotification) -> None:
220
+ """
221
+ 处理Computer离开办公室事件
222
+ Handle Computer leave office event
223
+
224
+ Args:
225
+ data: 离开办公室通知数据 / Leave office notification data
226
+ """
227
+ try:
228
+ computer = self.validate_office_data(data)
229
+ logger.info(f"Computer {computer} left office {data['office_id']}")
230
+
231
+ # 调用事件处理器
232
+ # Call event handler
233
+ if self.event_handler:
234
+ self.event_handler.on_computer_leave_office(data)
235
+
236
+ except Exception as e:
237
+ logger.error(f"Error handling computer leave office: {e}")
238
+
239
+ def handle_computer_update_config(self, data: UpdateMCPConfigNotification) -> None:
240
+ """
241
+ 处理Computer更新配置事件
242
+ Handle Computer update config event
243
+
244
+ Args:
245
+ data: 配置更新通知数据 / Config update notification data
246
+ """
247
+ try:
248
+ computer = data["computer"]
249
+ logger.info(f"Computer {computer} updated config")
250
+
251
+ # 调用事件处理器
252
+ # Call event handler
253
+ if self.event_handler:
254
+ self.event_handler.on_computer_update_config(data)
255
+
256
+ except Exception as e:
257
+ logger.error(f"Error handling computer update config: {e}")
258
+
259
+ def process_tools_response(self, response: GetToolsRet, computer: str) -> None:
260
+ """
261
+ 处理工具响应
262
+ Process tools response
263
+
264
+ Args:
265
+ response: 工具响应数据 / Tools response data
266
+ computer: 计算机ID / Computer ID
267
+ """
268
+ try:
269
+ if tools := response.get("tools"):
270
+ logger.info(f"Received {len(tools)} tools from computer {computer}")
271
+
272
+ # 调用事件处理器
273
+ # Call event handler
274
+ if self.event_handler:
275
+ self.event_handler.on_tools_received(computer, tools)
276
+
277
+ except Exception as e:
278
+ logger.error(f"Error processing tools response: {e}")
279
+
280
+ @abstractmethod
281
+ def register_event_handlers(self) -> None:
282
+ """
283
+ 注册事件处理器,子类需要实现具体的注册逻辑
284
+ Register event handlers, subclasses need to implement specific registration logic
285
+ """
286
+ # 这个方法需要在子类中实现具体的事件注册逻辑
287
+ # This method needs to implement specific event registration logic in subclasses
288
+ pass