cloneloop 0.1.0__py3-none-any.whl
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.
- cloneloop-0.1.0.dist-info/METADATA +392 -0
- cloneloop-0.1.0.dist-info/RECORD +118 -0
- cloneloop-0.1.0.dist-info/WHEEL +4 -0
- cloneloop-0.1.0.dist-info/entry_points.txt +5 -0
- mcp_ai_supervisor/__init__.py +52 -0
- mcp_ai_supervisor/__main__.py +551 -0
- mcp_ai_supervisor/debug.py +15 -0
- mcp_ai_supervisor/desktop_app/__init__.py +29 -0
- mcp_ai_supervisor/desktop_app/desktop_app.py +390 -0
- mcp_ai_supervisor/helpers/__init__.py +4 -0
- mcp_ai_supervisor/helpers/feedback_helpers.py +140 -0
- mcp_ai_supervisor/helpers/image_helpers.py +73 -0
- mcp_ai_supervisor/i18n.py +14 -0
- mcp_ai_supervisor/log_writer.py +16 -0
- mcp_ai_supervisor/logging/__init__.py +8 -0
- mcp_ai_supervisor/logging/log_parser.py +293 -0
- mcp_ai_supervisor/logging/log_tailer.py +367 -0
- mcp_ai_supervisor/py.typed +0 -0
- mcp_ai_supervisor/server.py +654 -0
- mcp_ai_supervisor/utils/__init__.py +28 -0
- mcp_ai_supervisor/utils/error_handler.py +10 -0
- mcp_ai_supervisor/utils/memory_monitor.py +11 -0
- mcp_ai_supervisor/utils/paths.py +7 -0
- mcp_ai_supervisor/utils/resource_manager.py +15 -0
- mcp_ai_supervisor/utils/structure_checker.py +291 -0
- mcp_ai_supervisor/web/__init__.py +26 -0
- mcp_ai_supervisor/web/constants/__init__.py +8 -0
- mcp_ai_supervisor/web/constants/message_codes.py +173 -0
- mcp_ai_supervisor/web/core/__init__.py +30 -0
- mcp_ai_supervisor/web/core/agent_bridge.py +957 -0
- mcp_ai_supervisor/web/core/auto_responder.py +332 -0
- mcp_ai_supervisor/web/core/context_collector.py +124 -0
- mcp_ai_supervisor/web/core/conversation_recorder.py +751 -0
- mcp_ai_supervisor/web/core/delegate_manager.py +1193 -0
- mcp_ai_supervisor/web/core/feedback_mediator.py +259 -0
- mcp_ai_supervisor/web/core/message_channel.py +551 -0
- mcp_ai_supervisor/web/core/response_builder.py +333 -0
- mcp_ai_supervisor/web/core/scene_detector.py +184 -0
- mcp_ai_supervisor/web/core/task_state.py +194 -0
- mcp_ai_supervisor/web/locales/en/translation.json +643 -0
- mcp_ai_supervisor/web/locales/zh-CN/translation.json +629 -0
- mcp_ai_supervisor/web/locales/zh-TW/translation.json +648 -0
- mcp_ai_supervisor/web/main.py +747 -0
- mcp_ai_supervisor/web/managers/__init__.py +8 -0
- mcp_ai_supervisor/web/managers/desktop_manager.py +228 -0
- mcp_ai_supervisor/web/managers/server_manager.py +170 -0
- mcp_ai_supervisor/web/managers/session_manager.py +515 -0
- mcp_ai_supervisor/web/managers/workbench_connector.py +186 -0
- mcp_ai_supervisor/web/models/__init__.py +13 -0
- mcp_ai_supervisor/web/models/feedback_result.py +16 -0
- mcp_ai_supervisor/web/models/feedback_session.py +938 -0
- mcp_ai_supervisor/web/models/session_cleaner.py +245 -0
- mcp_ai_supervisor/web/models/session_commands.py +213 -0
- mcp_ai_supervisor/web/models/session_resolver.py +158 -0
- mcp_ai_supervisor/web/models/session_timer.py +242 -0
- mcp_ai_supervisor/web/routes/__init__.py +12 -0
- mcp_ai_supervisor/web/routes/main_routes.py +965 -0
- mcp_ai_supervisor/web/routes/settings_routes.py +195 -0
- mcp_ai_supervisor/web/routes/ws_handlers.py +270 -0
- mcp_ai_supervisor/web/static/css/audio-management.css +545 -0
- mcp_ai_supervisor/web/static/css/notification-settings.css +152 -0
- mcp_ai_supervisor/web/static/css/prompt-management.css +566 -0
- mcp_ai_supervisor/web/static/css/session-management.css +1428 -0
- mcp_ai_supervisor/web/static/css/styles.css +2267 -0
- mcp_ai_supervisor/web/static/favicon.ico +0 -0
- mcp_ai_supervisor/web/static/icon-192.png +0 -0
- mcp_ai_supervisor/web/static/icon.svg +11 -0
- mcp_ai_supervisor/web/static/index.html +37 -0
- mcp_ai_supervisor/web/static/js/app.js +1721 -0
- mcp_ai_supervisor/web/static/js/i18n.js +376 -0
- mcp_ai_supervisor/web/static/js/modules/audio/audio-manager.js +610 -0
- mcp_ai_supervisor/web/static/js/modules/audio/audio-settings-ui.js +732 -0
- mcp_ai_supervisor/web/static/js/modules/connection-monitor.js +435 -0
- mcp_ai_supervisor/web/static/js/modules/constants/message-codes.js +168 -0
- mcp_ai_supervisor/web/static/js/modules/countdown-manager.js +273 -0
- mcp_ai_supervisor/web/static/js/modules/drag-drop-handler.js +677 -0
- mcp_ai_supervisor/web/static/js/modules/file-upload-manager.js +555 -0
- mcp_ai_supervisor/web/static/js/modules/image-handler.js +199 -0
- mcp_ai_supervisor/web/static/js/modules/logger.js +404 -0
- mcp_ai_supervisor/web/static/js/modules/notification/notification-manager.js +360 -0
- mcp_ai_supervisor/web/static/js/modules/notification/notification-settings.js +344 -0
- mcp_ai_supervisor/web/static/js/modules/prompt/prompt-input-buttons.js +427 -0
- mcp_ai_supervisor/web/static/js/modules/prompt/prompt-manager.js +414 -0
- mcp_ai_supervisor/web/static/js/modules/prompt/prompt-modal.js +458 -0
- mcp_ai_supervisor/web/static/js/modules/prompt/prompt-settings-ui.js +524 -0
- mcp_ai_supervisor/web/static/js/modules/session/session-data-manager.js +1042 -0
- mcp_ai_supervisor/web/static/js/modules/session/session-details-modal.js +594 -0
- mcp_ai_supervisor/web/static/js/modules/session/session-ui-renderer.js +836 -0
- mcp_ai_supervisor/web/static/js/modules/session-manager.js +1059 -0
- mcp_ai_supervisor/web/static/js/modules/settings-manager.js +1002 -0
- mcp_ai_supervisor/web/static/js/modules/tab-manager.js +235 -0
- mcp_ai_supervisor/web/static/js/modules/textarea-height-manager.js +267 -0
- mcp_ai_supervisor/web/static/js/modules/ui-manager.js +578 -0
- mcp_ai_supervisor/web/static/js/modules/utils/dom-utils.js +392 -0
- mcp_ai_supervisor/web/static/js/modules/utils/status-utils.js +403 -0
- mcp_ai_supervisor/web/static/js/modules/utils/time-utils.js +440 -0
- mcp_ai_supervisor/web/static/js/modules/utils.js +557 -0
- mcp_ai_supervisor/web/static/js/modules/websocket-manager.js +875 -0
- mcp_ai_supervisor/web/static/js/modules/workbench-notification.js +103 -0
- mcp_ai_supervisor/web/static/js/vendor/marked.min.js +6 -0
- mcp_ai_supervisor/web/static/js/vendor/purify.min.js +3 -0
- mcp_ai_supervisor/web/templates/components/image-upload.html +43 -0
- mcp_ai_supervisor/web/templates/components/settings-card.html +58 -0
- mcp_ai_supervisor/web/templates/components/status-indicator.html +31 -0
- mcp_ai_supervisor/web/templates/components/toggle-switch.html +19 -0
- mcp_ai_supervisor/web/templates/feedback.html +1198 -0
- mcp_ai_supervisor/web/templates/index.html +378 -0
- mcp_ai_supervisor/web/utils/__init__.py +12 -0
- mcp_ai_supervisor/web/utils/browser.py +35 -0
- mcp_ai_supervisor/web/utils/compression_config.py +195 -0
- mcp_ai_supervisor/web/utils/compression_monitor.py +314 -0
- mcp_ai_supervisor/web/utils/ide_opener.py +286 -0
- mcp_ai_supervisor/web/utils/network.py +66 -0
- mcp_ai_supervisor/web/utils/port_manager.py +340 -0
- mcp_ai_supervisor/web/utils/session_cleanup_manager.py +543 -0
- mcp_ai_supervisor/web/utils/workbench_client.py +899 -0
- mcp_ai_supervisor/workbench/__init__.py +5 -0
- mcp_ai_supervisor/workbench/__main__.py +5 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MCP AI Supervisor 伺服器主要模組
|
|
4
|
+
|
|
5
|
+
此模組提供 MCP (Model Context Protocol) 的增強回饋收集功能,
|
|
6
|
+
支援智能環境檢測,自動使用 Web UI 介面。
|
|
7
|
+
|
|
8
|
+
主要功能:
|
|
9
|
+
- MCP 工具實現
|
|
10
|
+
- 介面選擇(Web UI)
|
|
11
|
+
- 環境檢測 (SSH Remote, WSL, Local)
|
|
12
|
+
- 國際化支援
|
|
13
|
+
- 圖片處理與上傳
|
|
14
|
+
- 命令執行與結果展示
|
|
15
|
+
- 專案目錄管理
|
|
16
|
+
|
|
17
|
+
主要 MCP 工具:
|
|
18
|
+
- interactive_feedback: 收集用戶互動回饋
|
|
19
|
+
- get_system_info: 獲取系統環境資訊
|
|
20
|
+
|
|
21
|
+
日誌約定:
|
|
22
|
+
- `logger.info` / `warning`:核心鏈路與 Agent 流式(`AgentBridge`)輸出;進程啟動時由
|
|
23
|
+
`_configure_supervisor_logging()` 將 ``mcp_ai_supervisor`` 包掛到 stderr,無需 MCP_DEBUG
|
|
24
|
+
即可在 Cursor MCP 日誌中查看。級別可由 ``MCP_SUPERVISOR_LOG_LEVEL`` 覆寫。
|
|
25
|
+
- `debug_log`:詳情輸出,僅在環境變數 MCP_DEBUG 開啟時寫入 stderr。
|
|
26
|
+
|
|
27
|
+
作者: Fábio Ferreira (原作者)
|
|
28
|
+
增強: Minidoracat (Web UI, 圖片支援, 環境檢測)
|
|
29
|
+
重構: 模塊化設計
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import base64
|
|
33
|
+
import io
|
|
34
|
+
import json
|
|
35
|
+
import logging
|
|
36
|
+
import os
|
|
37
|
+
import sys
|
|
38
|
+
import time
|
|
39
|
+
from collections.abc import AsyncIterator
|
|
40
|
+
from contextlib import asynccontextmanager
|
|
41
|
+
from uuid import uuid4
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
from typing import Annotated, Any, Optional
|
|
44
|
+
|
|
45
|
+
from .log_writer import get_logger as _get_struct_logger
|
|
46
|
+
|
|
47
|
+
# _mcp_logger 延迟初始化:必须在 project_directory 可用之后才能创建
|
|
48
|
+
_mcp_logger = None
|
|
49
|
+
|
|
50
|
+
# 弹窗 request_id 管理:key=project_dir, value=(request_id, summary)
|
|
51
|
+
# summary 相同时复用 request_id,防止重试关闭弹窗
|
|
52
|
+
_project_request_ids: dict[str, tuple[str, str]] = {}
|
|
53
|
+
|
|
54
|
+
# 缓存 MCP roots(Cursor 工作区路径),首次查询后不再重复请求
|
|
55
|
+
_cached_roots: list[str] | None = None
|
|
56
|
+
|
|
57
|
+
# 兼容:旧代码部分仍使用标准 logging(第三方库和 _configure_supervisor_logging)
|
|
58
|
+
logger = logging.getLogger(__name__)
|
|
59
|
+
|
|
60
|
+
from fastmcp import Context, FastMCP
|
|
61
|
+
from mcp.types import ImageContent, TextContent
|
|
62
|
+
from pydantic import Field
|
|
63
|
+
|
|
64
|
+
# 導入統一的調試功能
|
|
65
|
+
from .debug import server_debug_log as debug_log
|
|
66
|
+
|
|
67
|
+
# 導入多語系支援
|
|
68
|
+
# 導入錯誤處理框架
|
|
69
|
+
from .utils.error_handler import ErrorHandler, ErrorType
|
|
70
|
+
|
|
71
|
+
# 導入資源管理器
|
|
72
|
+
from .utils.resource_manager import create_temp_file
|
|
73
|
+
|
|
74
|
+
# 導入提取到 helpers/ 的函數(解決循環 import)
|
|
75
|
+
from .helpers.image_helpers import process_images
|
|
76
|
+
from .helpers.feedback_helpers import create_feedback_text, save_feedback_to_file
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ===== 編碼初始化 =====
|
|
80
|
+
def init_encoding():
|
|
81
|
+
"""初始化編碼設置,確保正確處理中文字符"""
|
|
82
|
+
try:
|
|
83
|
+
# Windows 特殊處理
|
|
84
|
+
if sys.platform == "win32":
|
|
85
|
+
import msvcrt
|
|
86
|
+
|
|
87
|
+
# 設置為二進制模式
|
|
88
|
+
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
|
|
89
|
+
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
|
90
|
+
|
|
91
|
+
# 重新包裝為 UTF-8 文本流,並禁用緩衝
|
|
92
|
+
# 修復 union-attr 錯誤 - 安全獲取 buffer 或 detach
|
|
93
|
+
stdin_buffer = getattr(sys.stdin, "buffer", None)
|
|
94
|
+
if stdin_buffer is None and hasattr(sys.stdin, "detach"):
|
|
95
|
+
stdin_buffer = sys.stdin.detach()
|
|
96
|
+
|
|
97
|
+
stdout_buffer = getattr(sys.stdout, "buffer", None)
|
|
98
|
+
if stdout_buffer is None and hasattr(sys.stdout, "detach"):
|
|
99
|
+
stdout_buffer = sys.stdout.detach()
|
|
100
|
+
|
|
101
|
+
sys.stdin = io.TextIOWrapper(
|
|
102
|
+
stdin_buffer, encoding="utf-8", errors="replace", newline=None
|
|
103
|
+
)
|
|
104
|
+
sys.stdout = io.TextIOWrapper(
|
|
105
|
+
stdout_buffer,
|
|
106
|
+
encoding="utf-8",
|
|
107
|
+
errors="replace",
|
|
108
|
+
newline="",
|
|
109
|
+
write_through=True, # 關鍵:禁用寫入緩衝
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
# 非 Windows 系統的標準設置
|
|
113
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
114
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
115
|
+
if hasattr(sys.stdin, "reconfigure"):
|
|
116
|
+
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
|
|
117
|
+
|
|
118
|
+
# 設置 stderr 編碼(用於調試訊息)
|
|
119
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
120
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
121
|
+
|
|
122
|
+
return True
|
|
123
|
+
except Exception:
|
|
124
|
+
# 如果編碼設置失敗,嘗試基本設置
|
|
125
|
+
try:
|
|
126
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
127
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
128
|
+
if hasattr(sys.stdin, "reconfigure"):
|
|
129
|
+
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
|
|
130
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
131
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
132
|
+
except:
|
|
133
|
+
pass
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# 初始化編碼(在導入時就執行)
|
|
138
|
+
_encoding_initialized = init_encoding()
|
|
139
|
+
|
|
140
|
+
# ===== 常數定義 =====
|
|
141
|
+
SERVER_NAME = "AI 監工 MCP"
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# 初始化 MCP 服務器
|
|
145
|
+
from . import __version__
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# 確保 log_level 設定為正確的大寫格式
|
|
149
|
+
fastmcp_settings = {}
|
|
150
|
+
|
|
151
|
+
# 檢查環境變數並設定正確的 log_level
|
|
152
|
+
env_log_level = os.getenv("FASTMCP_LOG_LEVEL", "").upper()
|
|
153
|
+
if env_log_level in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"):
|
|
154
|
+
fastmcp_settings["log_level"] = env_log_level
|
|
155
|
+
else:
|
|
156
|
+
# 預設使用 INFO 等級
|
|
157
|
+
fastmcp_settings["log_level"] = "INFO"
|
|
158
|
+
|
|
159
|
+
@asynccontextmanager
|
|
160
|
+
async def _mcp_lifespan(server: Any) -> AsyncIterator[dict]:
|
|
161
|
+
"""MCP 生命周期:服务就绪时通知 Workbench,关闭时注销。"""
|
|
162
|
+
_notify_workbench_lifecycle("online")
|
|
163
|
+
try:
|
|
164
|
+
yield {}
|
|
165
|
+
finally:
|
|
166
|
+
_notify_workbench_lifecycle("offline")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _notify_workbench_lifecycle(event: str) -> None:
|
|
170
|
+
"""通知 Workbench MCP 进程状态变化(best-effort)"""
|
|
171
|
+
try:
|
|
172
|
+
from .web.main import get_web_ui_manager
|
|
173
|
+
manager = get_web_ui_manager()
|
|
174
|
+
client = manager._workbench_client
|
|
175
|
+
if client:
|
|
176
|
+
if event == "online":
|
|
177
|
+
client.notify_process_online()
|
|
178
|
+
else:
|
|
179
|
+
client.notify_process_offline()
|
|
180
|
+
except Exception:
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
mcp: Any = FastMCP(SERVER_NAME, lifespan=_mcp_lifespan)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# ===== 工具函數 =====
|
|
188
|
+
def is_wsl_environment() -> bool:
|
|
189
|
+
"""
|
|
190
|
+
檢測是否在 WSL (Windows Subsystem for Linux) 環境中運行
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
bool: True 表示 WSL 環境,False 表示其他環境
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
# 檢查 /proc/version 文件是否包含 WSL 標識
|
|
197
|
+
if os.path.exists("/proc/version"):
|
|
198
|
+
with open("/proc/version") as f:
|
|
199
|
+
version_info = f.read().lower()
|
|
200
|
+
if "microsoft" in version_info or "wsl" in version_info:
|
|
201
|
+
debug_log("检测到 WSL 环境(通过 /proc/version)")
|
|
202
|
+
return True
|
|
203
|
+
|
|
204
|
+
# 檢查 WSL 相關環境變數
|
|
205
|
+
wsl_env_vars = ["WSL_DISTRO_NAME", "WSL_INTEROP", "WSLENV"]
|
|
206
|
+
for env_var in wsl_env_vars:
|
|
207
|
+
if os.getenv(env_var):
|
|
208
|
+
debug_log(f"检测到 WSL 环境变量: {env_var}")
|
|
209
|
+
return True
|
|
210
|
+
|
|
211
|
+
# 檢查是否存在 WSL 特有的路徑
|
|
212
|
+
wsl_paths = ["/mnt/c", "/mnt/d", "/proc/sys/fs/binfmt_misc/WSLInterop"]
|
|
213
|
+
for path in wsl_paths:
|
|
214
|
+
if os.path.exists(path):
|
|
215
|
+
debug_log(f"检测到 WSL 特有路径: {path}")
|
|
216
|
+
return True
|
|
217
|
+
|
|
218
|
+
except Exception as e:
|
|
219
|
+
debug_log(f"WSL 检测过程中发生错误: {e}")
|
|
220
|
+
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
async def _resolve_roots_from_cursor(ctx: Context) -> list[str]:
|
|
225
|
+
"""从 Cursor 获取 MCP roots(工作区路径列表)。
|
|
226
|
+
|
|
227
|
+
Cursor 可能返回裸路径(/Users/...)而非标准 file:// URI,
|
|
228
|
+
导致 MCP SDK 的 Pydantic 验证失败。这里做了兼容处理。
|
|
229
|
+
"""
|
|
230
|
+
# 优先尝试标准 API
|
|
231
|
+
try:
|
|
232
|
+
roots = await ctx.list_roots()
|
|
233
|
+
paths = [
|
|
234
|
+
r.uri[7:] if r.uri.startswith("file://") else r.uri
|
|
235
|
+
for r in roots
|
|
236
|
+
if r.uri.startswith("file://") or r.uri.startswith("/")
|
|
237
|
+
]
|
|
238
|
+
if paths:
|
|
239
|
+
logger.info("[MCP Roots] resolved %d paths via list_roots()", len(paths))
|
|
240
|
+
return paths
|
|
241
|
+
except Exception:
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
# 回退:Cursor 返回裸路径时 Pydantic 验证失败,手动构造 JSONRPC 请求
|
|
245
|
+
try:
|
|
246
|
+
from mcp.types import ListRootsRequest, ServerRequest
|
|
247
|
+
from pydantic import BaseModel
|
|
248
|
+
|
|
249
|
+
class _LooseRoot(BaseModel, extra="allow"):
|
|
250
|
+
uri: str = ""
|
|
251
|
+
name: str | None = None
|
|
252
|
+
|
|
253
|
+
class _LooseListRootsResult(BaseModel, extra="allow"):
|
|
254
|
+
roots: list[_LooseRoot] = []
|
|
255
|
+
|
|
256
|
+
result = await ctx.session.send_request(
|
|
257
|
+
ServerRequest(ListRootsRequest()),
|
|
258
|
+
_LooseListRootsResult,
|
|
259
|
+
)
|
|
260
|
+
paths = []
|
|
261
|
+
for r in result.roots:
|
|
262
|
+
uri = r.uri
|
|
263
|
+
if uri.startswith("file://"):
|
|
264
|
+
paths.append(uri[7:])
|
|
265
|
+
elif uri.startswith("/"):
|
|
266
|
+
paths.append(uri)
|
|
267
|
+
logger.info("[MCP Roots] resolved %d paths via loose fallback: %s", len(paths), paths)
|
|
268
|
+
return paths
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.warning("[MCP Roots] all attempts failed: %s", e)
|
|
271
|
+
return []
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# ===== MCP 工具定義 =====
|
|
275
|
+
#
|
|
276
|
+
# interactive_feedback:主 AI 与监工循环的唯一定型入口(JSON-RPC → 本函数 → DelegateManager)。
|
|
277
|
+
# get_system_info:环境探测辅助;详细 stderr 调试仍由 MCP_DEBUG 控制(见 debug_log)。
|
|
278
|
+
#
|
|
279
|
+
@mcp.tool()
|
|
280
|
+
async def interactive_feedback(
|
|
281
|
+
ctx: Context,
|
|
282
|
+
project_directory: Annotated[str, Field(description="项目目录路径")] = ".",
|
|
283
|
+
summary: Annotated[
|
|
284
|
+
str, Field(description="本轮工作内容的详细摘要:做了哪些修改、分析了什么、解决了什么问题(不少于30字)")
|
|
285
|
+
] = "我已完成了您请求的任务。",
|
|
286
|
+
timeout: Annotated[int, Field(description="等待用户反馈的超时时间(秒)")] = 600,
|
|
287
|
+
# === 新增参数:循环机制与模式支持 ===
|
|
288
|
+
mode: Annotated[
|
|
289
|
+
Optional[str],
|
|
290
|
+
Field(description="运行模式覆盖: semi/manual,未指定时读取环境变量 MCP_DELEGATE_MODE(默认 manual)"),
|
|
291
|
+
] = None,
|
|
292
|
+
intention: Annotated[
|
|
293
|
+
str, Field(description="AI 下一步的计划或意图说明(打算做什么、等待什么确认),注意:工作内容摘要请放在 summary 中")
|
|
294
|
+
] = "",
|
|
295
|
+
original_task: Annotated[
|
|
296
|
+
str, Field(description="用户原始任务(首次调用时传入)")
|
|
297
|
+
] = "",
|
|
298
|
+
task_complete: Annotated[
|
|
299
|
+
bool, Field(description="是否声明任务完成")
|
|
300
|
+
] = False,
|
|
301
|
+
changed_files: Annotated[
|
|
302
|
+
Optional[list], Field(description="本次改动的文件列表")
|
|
303
|
+
] = None,
|
|
304
|
+
) -> list:
|
|
305
|
+
"""Interactive feedback collection tool for LLM agents.
|
|
306
|
+
|
|
307
|
+
USAGE RULES:
|
|
308
|
+
1. During any process, task, or conversation, whether asking, replying, or completing phased tasks, you must call this tool to ask for feedback.
|
|
309
|
+
2. Unless receiving termination instructions, all steps must repeatedly call this tool.
|
|
310
|
+
3. Whenever user feedback is received, if the feedback content is not empty, you must call this tool again and adjust behavior based on the feedback content.
|
|
311
|
+
4. Only when the user explicitly indicates "end" or "no more interaction needed" can you stop calling this tool, and the process is considered complete.
|
|
312
|
+
5. You should summarize what have done, and provide project directory through args to let user know what you have done to provide feedback for next step.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
project_directory: Project directory path for context
|
|
316
|
+
summary: Detailed summary of work done this round: what was modified, analyzed, or resolved (min 30 chars). Put your work content here, NOT in intention.
|
|
317
|
+
timeout: Timeout in seconds for waiting user feedback (default: 600 seconds)
|
|
318
|
+
mode: Override run mode (semi/manual). Falls back to MCP_DELEGATE_MODE env var, then 'manual'.
|
|
319
|
+
intention: AI's next-step plan or intent (what you plan to do next, what confirmation you're waiting for). Do NOT put work summaries here.
|
|
320
|
+
original_task: User's original task description (pass on first call)
|
|
321
|
+
task_complete: Whether AI declares the task as complete
|
|
322
|
+
changed_files: List of files changed in this round
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
list: List containing TextContent and ImageContent objects representing user feedback
|
|
326
|
+
"""
|
|
327
|
+
# [核心节点] MCP 工具入口:之后统一进入 DelegateManager.process(模式 / 完成验证 / Agent)
|
|
328
|
+
global _mcp_logger, _cached_roots
|
|
329
|
+
_call_start = time.monotonic()
|
|
330
|
+
_call_id = uuid4().hex[:8]
|
|
331
|
+
|
|
332
|
+
# 当 project_directory 为默认值 "." 或无效路径时,从 MCP roots 自动推断
|
|
333
|
+
_needs_roots = project_directory == "." or not os.path.exists(project_directory)
|
|
334
|
+
if _needs_roots:
|
|
335
|
+
if _cached_roots is None:
|
|
336
|
+
_cached_roots = await _resolve_roots_from_cursor(ctx)
|
|
337
|
+
if _cached_roots:
|
|
338
|
+
# 优先选择包含当前 MCP 源码的 root(处理多工作区场景)
|
|
339
|
+
_mcp_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
340
|
+
project_directory = _cached_roots[0]
|
|
341
|
+
for _r in _cached_roots:
|
|
342
|
+
if _mcp_src.startswith(_r):
|
|
343
|
+
project_directory = _r
|
|
344
|
+
break
|
|
345
|
+
logger.info("[MCP Roots] using root: %s (from %s)", project_directory, _cached_roots)
|
|
346
|
+
|
|
347
|
+
# 生成 request_id:summary 相同视为同一请求的重试,复用 request_id
|
|
348
|
+
_resolved_dir = os.path.abspath(project_directory) if os.path.exists(project_directory) else os.path.abspath(os.getcwd())
|
|
349
|
+
_prev_req = _project_request_ids.get(_resolved_dir)
|
|
350
|
+
if _prev_req and _prev_req[1] == summary:
|
|
351
|
+
_request_id = _prev_req[0]
|
|
352
|
+
else:
|
|
353
|
+
_request_id = uuid4().hex[:8]
|
|
354
|
+
_project_request_ids[_resolved_dir] = (_request_id, summary)
|
|
355
|
+
if _mcp_logger is None:
|
|
356
|
+
_mcp_logger = _get_struct_logger("agent", project_directory=_resolved_dir)
|
|
357
|
+
|
|
358
|
+
_proj_name = os.path.basename(_resolved_dir)
|
|
359
|
+
_mcp_logger.info(
|
|
360
|
+
f"project={_proj_name} session=- timeout={timeout} call={_call_id}",
|
|
361
|
+
f"[MCP_CALL] mode={mode} task_complete={task_complete} "
|
|
362
|
+
f"summary_chars={len(summary or '')} intention_chars={len(intention or '')} "
|
|
363
|
+
f"changed_files={len(changed_files) if changed_files else 0}",
|
|
364
|
+
)
|
|
365
|
+
cf_list = "\n ".join(changed_files) if changed_files else "(无)"
|
|
366
|
+
_qoder_pid = os.getenv("VSCODE_PID", "(unknown)")
|
|
367
|
+
debug_log(
|
|
368
|
+
f"[MCP入口] ━━━ AI → MCP interactive_feedback ━━━\n"
|
|
369
|
+
f" MCP PID={os.getpid()}, VSCODE_PID={_qoder_pid}\n"
|
|
370
|
+
f" project: {project_directory}\n"
|
|
371
|
+
f" mode: {mode}, task_complete: {task_complete}, timeout: {timeout}\n"
|
|
372
|
+
f" original_task: {original_task or '(未提供)'}\n"
|
|
373
|
+
f" summary: {summary}\n"
|
|
374
|
+
f" intention: {intention}\n"
|
|
375
|
+
f" changed_files ({len(changed_files) if changed_files else 0}个):\n {cf_list}"
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
try:
|
|
379
|
+
if not os.path.exists(project_directory):
|
|
380
|
+
project_directory = os.getcwd()
|
|
381
|
+
project_directory = os.path.abspath(project_directory)
|
|
382
|
+
|
|
383
|
+
# 通过 DelegateManager 编排处理
|
|
384
|
+
from .web.core.delegate_manager import get_delegate_manager
|
|
385
|
+
|
|
386
|
+
delegate = get_delegate_manager()
|
|
387
|
+
|
|
388
|
+
result = await delegate.process(
|
|
389
|
+
project_directory=project_directory,
|
|
390
|
+
summary=summary,
|
|
391
|
+
timeout=timeout,
|
|
392
|
+
mode=mode,
|
|
393
|
+
intention=intention,
|
|
394
|
+
original_task=original_task,
|
|
395
|
+
task_complete=task_complete,
|
|
396
|
+
changed_files=changed_files,
|
|
397
|
+
call_id=_call_id,
|
|
398
|
+
request_id=_request_id,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
total_chars = sum(len(item.text) for item in result if hasattr(item, "text"))
|
|
402
|
+
all_text = "\n---块分隔---\n".join(
|
|
403
|
+
item.text for item in result if hasattr(item, "text")
|
|
404
|
+
) or "(空)"
|
|
405
|
+
debug_log(
|
|
406
|
+
f"[MCP出口] MCP → AI 返回: {len(result)}个内容块, "
|
|
407
|
+
f"总字符数={total_chars}\n"
|
|
408
|
+
f" 完整内容:\n{all_text}"
|
|
409
|
+
)
|
|
410
|
+
# [核心节点] 正常返回主 AI;异常路径见下方 except(仍返回 TextContent,避免 JSON-RPC 失败)
|
|
411
|
+
_duration_ms = int((time.monotonic() - _call_start) * 1000)
|
|
412
|
+
if _mcp_logger:
|
|
413
|
+
_mcp_logger.info(
|
|
414
|
+
f"project={_proj_name} session=- duration_ms={_duration_ms} call={_call_id}",
|
|
415
|
+
f"[MCP_CALL_COMPLETE] result_type=success content_blocks={len(result)} "
|
|
416
|
+
f"total_text_chars={total_chars}",
|
|
417
|
+
)
|
|
418
|
+
return result
|
|
419
|
+
|
|
420
|
+
except Exception as e:
|
|
421
|
+
# 使用統一錯誤處理,但不影響 JSON RPC 響應
|
|
422
|
+
error_id = ErrorHandler.log_error_with_context(
|
|
423
|
+
e,
|
|
424
|
+
context={"operation": "回饋收集", "project_dir": project_directory},
|
|
425
|
+
error_type=ErrorType.SYSTEM,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# 生成用戶友好的錯誤信息
|
|
429
|
+
user_error_msg = ErrorHandler.format_user_error(e, include_technical=False)
|
|
430
|
+
debug_log(f"反馈收集错误 [错误ID: {error_id}]: {e!s}")
|
|
431
|
+
if _mcp_logger:
|
|
432
|
+
_mcp_logger.error(
|
|
433
|
+
f"project={_proj_name} error_id={error_id} call={_call_id}",
|
|
434
|
+
f"[MCP_ERROR] error_type={type(e).__name__} error_message={user_error_msg[:200]}",
|
|
435
|
+
exc_info=True,
|
|
436
|
+
)
|
|
437
|
+
return [TextContent(type="text", text=user_error_msg)]
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
async def launch_web_feedback_ui(project_dir: str, summary: str, timeout: int) -> dict:
|
|
441
|
+
"""
|
|
442
|
+
啟動 Web UI 收集回饋,支援自訂超時時間
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
project_dir: 專案目錄路徑
|
|
446
|
+
summary: AI 工作摘要
|
|
447
|
+
timeout: 超時時間(秒)
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
dict: 收集到的回饋資料
|
|
451
|
+
"""
|
|
452
|
+
# [核心节点] Semi/Manual 用户反馈通道;由 DelegateManager 在适当时机 await(非 MCP 工具直连)
|
|
453
|
+
if _mcp_logger:
|
|
454
|
+
_mcp_logger.info(
|
|
455
|
+
f"project={os.path.basename(project_dir)} timeout={timeout}",
|
|
456
|
+
f"[MCP_CALL] launch_web_feedback_ui summary_chars={len(summary or '')}",
|
|
457
|
+
)
|
|
458
|
+
debug_log(f"启动 Web UI 界面,超时时间: {timeout} 秒")
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
# 使用新的 web 模組
|
|
462
|
+
from .web import launch_web_feedback_ui as web_launch
|
|
463
|
+
|
|
464
|
+
# 傳遞 timeout 參數給 Web UI
|
|
465
|
+
return await web_launch(project_dir, summary, timeout)
|
|
466
|
+
except ImportError as e:
|
|
467
|
+
# 使用統一錯誤處理
|
|
468
|
+
error_id = ErrorHandler.log_error_with_context(
|
|
469
|
+
e,
|
|
470
|
+
context={"operation": "Web UI 模組導入", "module": "web"},
|
|
471
|
+
error_type=ErrorType.DEPENDENCY,
|
|
472
|
+
)
|
|
473
|
+
user_error_msg = ErrorHandler.format_user_error(
|
|
474
|
+
e, ErrorType.DEPENDENCY, include_technical=False
|
|
475
|
+
)
|
|
476
|
+
debug_log(f"Web UI 模块导入失败 [错误ID: {error_id}]: {e}")
|
|
477
|
+
|
|
478
|
+
return {
|
|
479
|
+
"command_logs": "",
|
|
480
|
+
"interactive_feedback": user_error_msg,
|
|
481
|
+
"images": [],
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@mcp.tool()
|
|
486
|
+
def get_system_info() -> str:
|
|
487
|
+
"""
|
|
488
|
+
获取系统环境信息
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
str: JSON 格式的系统信息
|
|
492
|
+
"""
|
|
493
|
+
# [核心节点] 辅助诊断:与 interactive_feedback 编排无关,供主 AI / 人工排查环境
|
|
494
|
+
if _mcp_logger:
|
|
495
|
+
_mcp_logger.info("", "[MCP_CALL] get_system_info")
|
|
496
|
+
system_info = {
|
|
497
|
+
"平台": sys.platform,
|
|
498
|
+
"Python 版本": sys.version.split()[0],
|
|
499
|
+
"WSL 环境": is_wsl_environment(),
|
|
500
|
+
"界面类型": "桌面模式 (Tauri)",
|
|
501
|
+
"环境变量": {
|
|
502
|
+
"MCP_DESKTOP_MODE": os.getenv("MCP_DESKTOP_MODE"),
|
|
503
|
+
"MCP_WEB_PORT": os.getenv("MCP_WEB_PORT"),
|
|
504
|
+
"MCP_DEBUG": os.getenv("MCP_DEBUG"),
|
|
505
|
+
"MCP_SUPERVISOR_LOG_LEVEL": os.getenv("MCP_SUPERVISOR_LOG_LEVEL"),
|
|
506
|
+
"MCP_LANGUAGE": os.getenv("MCP_LANGUAGE"),
|
|
507
|
+
# 循环机制与模式相关环境变量
|
|
508
|
+
"MCP_DELEGATE_MODE": os.getenv("MCP_DELEGATE_MODE", "semi"),
|
|
509
|
+
"MCP_COUNTDOWN_SECONDS": os.getenv("MCP_COUNTDOWN_SECONDS", "15"),
|
|
510
|
+
"MCP_MAX_RETRIES": os.getenv("MCP_MAX_RETRIES", "100"),
|
|
511
|
+
"MCP_AUTO_HIDE": os.getenv("MCP_AUTO_HIDE", "true"),
|
|
512
|
+
},
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return json.dumps(system_info, ensure_ascii=False, indent=2)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _get_log_dir() -> Path:
|
|
519
|
+
"""获取日志目录 ``~/.mcp/logs/``,不存在则自动创建。"""
|
|
520
|
+
log_dir = Path.home() / ".mcp" / "logs"
|
|
521
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
522
|
+
return log_dir
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def _get_daily_log_path() -> Path:
|
|
526
|
+
"""返回按日期命名的日志文件路径,格式: ``~/.mcp/logs/mcp-supervisor-YYYY-MM-DD.log``。"""
|
|
527
|
+
from datetime import datetime
|
|
528
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
529
|
+
return _get_log_dir() / f"mcp-supervisor-{today}.log"
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def _configure_supervisor_logging() -> None:
|
|
533
|
+
"""为 ``mcp_ai_supervisor`` 包挂 stderr + 文件 双 Handler。
|
|
534
|
+
|
|
535
|
+
- **stderr Handler**: 输出到 stderr,供 IDE(Cursor/Qoder)捕获
|
|
536
|
+
- **FileHandler**: 按日期写入 ``~/.mcp/logs/mcp-supervisor-YYYY-MM-DD.log``,
|
|
537
|
+
确保即使 IDE 不捕获 stderr,也能有持久化日志可查
|
|
538
|
+
|
|
539
|
+
环境变量 ``MCP_SUPERVISOR_LOG_LEVEL`` 可覆写级别
|
|
540
|
+
(默认与 ``FASTMCP_LOG_LEVEL`` 或 INFO 一致)。
|
|
541
|
+
"""
|
|
542
|
+
pkg = logging.getLogger("mcp_ai_supervisor")
|
|
543
|
+
if pkg.handlers:
|
|
544
|
+
return
|
|
545
|
+
raw = (
|
|
546
|
+
os.getenv("MCP_SUPERVISOR_LOG_LEVEL")
|
|
547
|
+
or os.getenv("FASTMCP_LOG_LEVEL")
|
|
548
|
+
or "INFO"
|
|
549
|
+
).upper()
|
|
550
|
+
level = getattr(logging, raw, logging.INFO)
|
|
551
|
+
|
|
552
|
+
# === Handler 1: stderr(供 IDE 捕获) ===
|
|
553
|
+
stderr_handler = logging.StreamHandler(sys.stderr)
|
|
554
|
+
stderr_handler.setLevel(logging.DEBUG)
|
|
555
|
+
stderr_handler.setFormatter(
|
|
556
|
+
logging.Formatter("%(levelname)s: %(name)s: %(message)s")
|
|
557
|
+
)
|
|
558
|
+
pkg.addHandler(stderr_handler)
|
|
559
|
+
|
|
560
|
+
# === Handler 2: 按日期文件(持久化,不依赖 IDE) ===
|
|
561
|
+
try:
|
|
562
|
+
file_handler = logging.FileHandler(
|
|
563
|
+
str(_get_daily_log_path()), encoding="utf-8"
|
|
564
|
+
)
|
|
565
|
+
file_handler.setLevel(logging.DEBUG)
|
|
566
|
+
file_handler.setFormatter(
|
|
567
|
+
logging.Formatter(
|
|
568
|
+
"%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
569
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
570
|
+
)
|
|
571
|
+
)
|
|
572
|
+
pkg.addHandler(file_handler)
|
|
573
|
+
except Exception:
|
|
574
|
+
# 文件写入失败(权限等)时静默降级,不影响主流程
|
|
575
|
+
pass
|
|
576
|
+
|
|
577
|
+
pkg.setLevel(level)
|
|
578
|
+
pkg.propagate = False
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
# ===== 主程式入口 =====
|
|
582
|
+
def main():
|
|
583
|
+
"""主要入口點,用於套件執行
|
|
584
|
+
收集用戶的互動回饋,支援文字和圖片
|
|
585
|
+
此工具使用 Web UI 介面收集用戶回饋,支援智能環境檢測。
|
|
586
|
+
|
|
587
|
+
用戶可以:
|
|
588
|
+
1. 執行命令來驗證結果
|
|
589
|
+
2. 提供文字回饋
|
|
590
|
+
3. 上傳圖片作為回饋
|
|
591
|
+
4. 查看 AI 的工作摘要
|
|
592
|
+
|
|
593
|
+
調試模式:
|
|
594
|
+
- 設置環境變數 MCP_DEBUG=true 可啟用詳細調試輸出
|
|
595
|
+
- 生產環境建議關閉調試模式以避免輸出干擾
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
"""
|
|
599
|
+
# 启动时恢复残留的活跃对话记录
|
|
600
|
+
try:
|
|
601
|
+
from .web.core.conversation_recorder import get_conversation_recorder
|
|
602
|
+
get_conversation_recorder().recover_active_sessions()
|
|
603
|
+
except Exception as _recover_err:
|
|
604
|
+
debug_log(f"对话记录恢复失败(不影响主流程): {_recover_err}")
|
|
605
|
+
|
|
606
|
+
# 檢查是否啟用調試模式
|
|
607
|
+
debug_enabled = os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on")
|
|
608
|
+
|
|
609
|
+
if debug_enabled:
|
|
610
|
+
debug_log("🚀 启动互动式反馈收集 MCP 服务器")
|
|
611
|
+
debug_log(f" 服务器名称: {SERVER_NAME}")
|
|
612
|
+
debug_log(f" 版本: {__version__}")
|
|
613
|
+
debug_log(f" 平台: {sys.platform}")
|
|
614
|
+
debug_log(f" 编码初始化: {'成功' if _encoding_initialized else '失败'}")
|
|
615
|
+
debug_log(" 界面类型: 桌面模式 (Tauri)")
|
|
616
|
+
debug_log(" 等待来自 AI 助手的调用...")
|
|
617
|
+
debug_log("准备启动 MCP 服务器...")
|
|
618
|
+
debug_log("调用 mcp.run()...")
|
|
619
|
+
|
|
620
|
+
_start_time = time.time()
|
|
621
|
+
_startup_logger = None
|
|
622
|
+
try:
|
|
623
|
+
_configure_supervisor_logging()
|
|
624
|
+
|
|
625
|
+
# [MCP_START] 记录 MCP 进程启动
|
|
626
|
+
_startup_logger = _get_struct_logger("agent", project_directory=os.getcwd())
|
|
627
|
+
_startup_logger.info(
|
|
628
|
+
f"pid={os.getpid()} version={__version__}",
|
|
629
|
+
"[MCP_START] MCP Server 启动",
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
mcp.run()
|
|
633
|
+
except KeyboardInterrupt:
|
|
634
|
+
# [MCP_SHUTDOWN] 记录正常退出
|
|
635
|
+
_uptime = int(time.time() - _start_time)
|
|
636
|
+
_shutdown_logger = _mcp_logger or _startup_logger
|
|
637
|
+
if _shutdown_logger:
|
|
638
|
+
_shutdown_logger.info(
|
|
639
|
+
f"pid={os.getpid()} uptime_seconds={_uptime}",
|
|
640
|
+
"[MCP_SHUTDOWN] 收到中断信号,正常退出",
|
|
641
|
+
)
|
|
642
|
+
if debug_enabled:
|
|
643
|
+
debug_log("收到中断信号,正常退出")
|
|
644
|
+
sys.exit(0)
|
|
645
|
+
except Exception as e:
|
|
646
|
+
if debug_enabled:
|
|
647
|
+
debug_log(f"MCP 服务器启动失败: {e}")
|
|
648
|
+
import traceback
|
|
649
|
+
debug_log(f"详细错误: {traceback.format_exc()}")
|
|
650
|
+
sys.exit(1)
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
if __name__ == "__main__":
|
|
654
|
+
main()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP AI Supervisor 工具模組
|
|
3
|
+
============================
|
|
4
|
+
|
|
5
|
+
提供各種工具類和函數,包括錯誤處理、資源管理等。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .error_handler import ErrorHandler, ErrorType
|
|
9
|
+
from .resource_manager import (
|
|
10
|
+
ResourceManager,
|
|
11
|
+
cleanup_all_resources,
|
|
12
|
+
create_temp_dir,
|
|
13
|
+
create_temp_file,
|
|
14
|
+
get_resource_manager,
|
|
15
|
+
register_process,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"ErrorHandler",
|
|
21
|
+
"ErrorType",
|
|
22
|
+
"ResourceManager",
|
|
23
|
+
"cleanup_all_resources",
|
|
24
|
+
"create_temp_dir",
|
|
25
|
+
"create_temp_file",
|
|
26
|
+
"get_resource_manager",
|
|
27
|
+
"register_process",
|
|
28
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""代理模块:重定向到 mcp_supervisor_core.error_handler
|
|
2
|
+
|
|
3
|
+
重构过渡期保持向后兼容。
|
|
4
|
+
Phase 3 删除 src/ 时此文件一并删除。
|
|
5
|
+
"""
|
|
6
|
+
from mcp_supervisor_core.error_handler import * # noqa: F401,F403
|
|
7
|
+
from mcp_supervisor_core.error_handler import ( # noqa: F401
|
|
8
|
+
ErrorHandler,
|
|
9
|
+
ErrorType,
|
|
10
|
+
)
|