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,551 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MCP AI Supervisor - 主程式入口
|
|
4
|
+
================================
|
|
5
|
+
|
|
6
|
+
此檔案允許套件透過 `python -m mcp_ai_supervisor` 執行。
|
|
7
|
+
|
|
8
|
+
使用方法:
|
|
9
|
+
python -m mcp_ai_supervisor # 啟動 MCP 伺服器
|
|
10
|
+
python -m mcp_ai_supervisor test # 執行測試
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import asyncio
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
import warnings
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# 抑制 Windows 上的 asyncio ResourceWarning
|
|
21
|
+
if sys.platform == "win32":
|
|
22
|
+
warnings.filterwarnings(
|
|
23
|
+
"ignore", category=ResourceWarning, message=".*unclosed transport.*"
|
|
24
|
+
)
|
|
25
|
+
warnings.filterwarnings("ignore", category=ResourceWarning, message=".*unclosed.*")
|
|
26
|
+
|
|
27
|
+
# 設置 asyncio 事件循環策略以減少警告
|
|
28
|
+
try:
|
|
29
|
+
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
|
|
30
|
+
except AttributeError:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main():
|
|
35
|
+
"""主程式入口點"""
|
|
36
|
+
parser = argparse.ArgumentParser(
|
|
37
|
+
description="MCP AI Supervisor - AI 監工互動式回饋收集 MCP 伺服器"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
|
41
|
+
|
|
42
|
+
# 伺服器命令(預設)
|
|
43
|
+
subparsers.add_parser("server", help="啟動 MCP 伺服器(預設)")
|
|
44
|
+
|
|
45
|
+
# Workbench 命令
|
|
46
|
+
dash_parser = subparsers.add_parser("workbench", help="管理 Workbench Server")
|
|
47
|
+
dash_sub = dash_parser.add_subparsers(dest="dash_action", help="Workbench 操作")
|
|
48
|
+
|
|
49
|
+
dash_start = dash_sub.add_parser("start", help="启动 Workbench(后台)")
|
|
50
|
+
dash_start.add_argument("--port", type=int, default=None, help="端口号")
|
|
51
|
+
dash_start.add_argument("--host", default="127.0.0.1", help="绑定地址")
|
|
52
|
+
dash_start.add_argument("--fg", action="store_true", help="前台模式运行")
|
|
53
|
+
|
|
54
|
+
dash_sub.add_parser("stop", help="停止 Workbench")
|
|
55
|
+
|
|
56
|
+
dash_status = dash_sub.add_parser("status", help="查看 Workbench 状态")
|
|
57
|
+
dash_status.add_argument("--port", type=int, default=None, help="端口号")
|
|
58
|
+
dash_status.add_argument("--host", default="127.0.0.1", help="绑定地址")
|
|
59
|
+
|
|
60
|
+
dash_restart = dash_sub.add_parser("restart", help="重启 Workbench")
|
|
61
|
+
dash_restart.add_argument("--port", type=int, default=None, help="端口号")
|
|
62
|
+
dash_restart.add_argument("--host", default="127.0.0.1", help="绑定地址")
|
|
63
|
+
|
|
64
|
+
# 測試命令
|
|
65
|
+
test_parser = subparsers.add_parser("test", help="執行測試")
|
|
66
|
+
test_parser.add_argument(
|
|
67
|
+
"--web", action="store_true", help="測試 Web UI (自動持續運行)"
|
|
68
|
+
)
|
|
69
|
+
test_parser.add_argument(
|
|
70
|
+
"--desktop", action="store_true", help="啟動桌面應用程式模式"
|
|
71
|
+
)
|
|
72
|
+
test_parser.add_argument(
|
|
73
|
+
"--timeout", type=int, default=60, help="測試超時時間 (秒)"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# 版本命令
|
|
77
|
+
subparsers.add_parser("version", help="顯示版本資訊")
|
|
78
|
+
|
|
79
|
+
args = parser.parse_args()
|
|
80
|
+
|
|
81
|
+
if args.command == "workbench":
|
|
82
|
+
run_workbench(args)
|
|
83
|
+
elif args.command == "test":
|
|
84
|
+
run_tests(args)
|
|
85
|
+
elif args.command == "version":
|
|
86
|
+
show_version()
|
|
87
|
+
elif args.command == "server" or args.command is None:
|
|
88
|
+
run_server()
|
|
89
|
+
else:
|
|
90
|
+
parser.print_help()
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def run_server():
|
|
95
|
+
"""啟動 MCP 伺服器"""
|
|
96
|
+
from .server import main as server_main
|
|
97
|
+
|
|
98
|
+
return server_main()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run_workbench(args):
|
|
102
|
+
"""Workbench 生命周期管理"""
|
|
103
|
+
import signal
|
|
104
|
+
import subprocess
|
|
105
|
+
import time
|
|
106
|
+
from .workbench import pid_manager
|
|
107
|
+
from .workbench.config import DEFAULT_PORT, DEFAULT_HOST
|
|
108
|
+
|
|
109
|
+
action = args.dash_action
|
|
110
|
+
if not action:
|
|
111
|
+
print("用法: mcp-ai-supervisor workbench {start|stop|status|restart}")
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
|
|
114
|
+
port = getattr(args, "port", None) or int(os.environ.get("MCP_WORKBENCH_PORT", str(DEFAULT_PORT)))
|
|
115
|
+
host = getattr(args, "host", DEFAULT_HOST)
|
|
116
|
+
|
|
117
|
+
if action == "start":
|
|
118
|
+
pid_manager.cleanup_stale()
|
|
119
|
+
if pid_manager.is_running():
|
|
120
|
+
existing_pid = pid_manager.read_pid()
|
|
121
|
+
print(f"Workbench 已在运行 (PID: {existing_pid})")
|
|
122
|
+
sys.exit(0)
|
|
123
|
+
|
|
124
|
+
if getattr(args, "fg", False):
|
|
125
|
+
pid_manager.write_pid()
|
|
126
|
+
try:
|
|
127
|
+
from .workbench import WorkbenchServer
|
|
128
|
+
server = WorkbenchServer(host=host, port=port)
|
|
129
|
+
print(f"Workbench (前台): http://{host}:{port}")
|
|
130
|
+
server.run()
|
|
131
|
+
finally:
|
|
132
|
+
pid_manager.remove_pid()
|
|
133
|
+
else:
|
|
134
|
+
pid_manager.PID_DIR.mkdir(parents=True, exist_ok=True)
|
|
135
|
+
log_path = pid_manager.LOG_FILE
|
|
136
|
+
log_fd = open(log_path, "a")
|
|
137
|
+
|
|
138
|
+
env = os.environ.copy()
|
|
139
|
+
if port != DEFAULT_PORT:
|
|
140
|
+
env["MCP_WORKBENCH_PORT"] = str(port)
|
|
141
|
+
|
|
142
|
+
proc = subprocess.Popen(
|
|
143
|
+
[sys.executable, "-m", "mcp_supervisor_workbench",
|
|
144
|
+
"--host", host, "--port", str(port)],
|
|
145
|
+
stdout=log_fd,
|
|
146
|
+
stderr=log_fd,
|
|
147
|
+
start_new_session=True,
|
|
148
|
+
)
|
|
149
|
+
pid_manager.write_pid(proc.pid)
|
|
150
|
+
log_fd.close()
|
|
151
|
+
|
|
152
|
+
for _ in range(30):
|
|
153
|
+
time.sleep(0.2)
|
|
154
|
+
alive = pid_manager.get_port_from_health(host, port)
|
|
155
|
+
if alive:
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
if pid_manager.get_port_from_health(host, port):
|
|
159
|
+
print(f"Workbench started (PID: {proc.pid}, port: {port})")
|
|
160
|
+
else:
|
|
161
|
+
print(f"Workbench started (PID: {proc.pid}), but health check pending")
|
|
162
|
+
print(f" Log: {log_path}")
|
|
163
|
+
|
|
164
|
+
elif action == "stop":
|
|
165
|
+
pid_manager.cleanup_stale()
|
|
166
|
+
pid = pid_manager.read_pid()
|
|
167
|
+
|
|
168
|
+
# PID 文件无效时,通过端口查找实际进程(兜底)
|
|
169
|
+
if pid is None or not pid_manager._process_alive(pid):
|
|
170
|
+
actual_pid = pid_manager.find_pid_on_port(port)
|
|
171
|
+
if actual_pid:
|
|
172
|
+
pid = actual_pid
|
|
173
|
+
else:
|
|
174
|
+
print("Workbench 未在运行")
|
|
175
|
+
pid_manager.remove_pid()
|
|
176
|
+
sys.exit(0)
|
|
177
|
+
|
|
178
|
+
os.kill(pid, signal.SIGTERM)
|
|
179
|
+
for _ in range(50):
|
|
180
|
+
time.sleep(0.1)
|
|
181
|
+
if not pid_manager._process_alive(pid):
|
|
182
|
+
break
|
|
183
|
+
|
|
184
|
+
if pid_manager._process_alive(pid):
|
|
185
|
+
os.kill(pid, signal.SIGKILL)
|
|
186
|
+
time.sleep(0.5)
|
|
187
|
+
|
|
188
|
+
pid_manager.remove_pid()
|
|
189
|
+
print("Workbench stopped")
|
|
190
|
+
|
|
191
|
+
elif action == "status":
|
|
192
|
+
pid_manager.cleanup_stale()
|
|
193
|
+
pid = pid_manager.read_pid()
|
|
194
|
+
if pid is None:
|
|
195
|
+
print("Workbench: not running")
|
|
196
|
+
sys.exit(1)
|
|
197
|
+
|
|
198
|
+
if not pid_manager._process_alive(pid):
|
|
199
|
+
print(f"Workbench: not running (stale PID: {pid})")
|
|
200
|
+
pid_manager.remove_pid()
|
|
201
|
+
sys.exit(1)
|
|
202
|
+
|
|
203
|
+
health_port = pid_manager.get_port_from_health(host, port)
|
|
204
|
+
if health_port:
|
|
205
|
+
print(f"Workbench: running (PID: {pid}, port: {port})")
|
|
206
|
+
else:
|
|
207
|
+
print(f"Workbench: running (PID: {pid}, health check failed)")
|
|
208
|
+
|
|
209
|
+
elif action == "restart":
|
|
210
|
+
print("Stopping Workbench...")
|
|
211
|
+
args.dash_action = "stop"
|
|
212
|
+
try:
|
|
213
|
+
run_workbench(args)
|
|
214
|
+
except SystemExit:
|
|
215
|
+
pass
|
|
216
|
+
time.sleep(0.5)
|
|
217
|
+
print("Starting Workbench...")
|
|
218
|
+
args.dash_action = "start"
|
|
219
|
+
run_workbench(args)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def run_tests(args):
|
|
223
|
+
"""執行測試"""
|
|
224
|
+
# 啟用調試模式以顯示測試過程
|
|
225
|
+
os.environ["MCP_DEBUG"] = "true"
|
|
226
|
+
|
|
227
|
+
# 在 Windows 上抑制 asyncio 警告
|
|
228
|
+
if sys.platform == "win32":
|
|
229
|
+
import warnings
|
|
230
|
+
|
|
231
|
+
# 設置更全面的警告抑制
|
|
232
|
+
os.environ["PYTHONWARNINGS"] = (
|
|
233
|
+
"ignore::ResourceWarning,ignore::DeprecationWarning"
|
|
234
|
+
)
|
|
235
|
+
warnings.filterwarnings("ignore", category=ResourceWarning)
|
|
236
|
+
warnings.filterwarnings("ignore", message=".*unclosed transport.*")
|
|
237
|
+
warnings.filterwarnings("ignore", message=".*I/O operation on closed pipe.*")
|
|
238
|
+
warnings.filterwarnings("ignore", message=".*unclosed.*")
|
|
239
|
+
# 抑制 asyncio 相關的所有警告
|
|
240
|
+
warnings.filterwarnings("ignore", module="asyncio.*")
|
|
241
|
+
|
|
242
|
+
if args.web:
|
|
243
|
+
print("🧪 執行 Web UI 測試...")
|
|
244
|
+
success = test_web_ui_simple()
|
|
245
|
+
if not success:
|
|
246
|
+
sys.exit(1)
|
|
247
|
+
elif args.desktop:
|
|
248
|
+
print("🖥️ 啟動桌面應用程式...")
|
|
249
|
+
success = test_desktop_app()
|
|
250
|
+
if not success:
|
|
251
|
+
sys.exit(1)
|
|
252
|
+
else:
|
|
253
|
+
print("❌ 測試功能已簡化")
|
|
254
|
+
print("💡 可用的測試選項:")
|
|
255
|
+
print(" --web 測試 Web UI")
|
|
256
|
+
print(" --desktop 啟動桌面應用程式")
|
|
257
|
+
print("💡 對於開發者:使用 'uv run pytest' 執行完整測試")
|
|
258
|
+
sys.exit(1)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_web_ui_simple():
|
|
262
|
+
"""簡單的 Web UI 測試"""
|
|
263
|
+
try:
|
|
264
|
+
import tempfile
|
|
265
|
+
import time
|
|
266
|
+
from .web.main import WebUIManager
|
|
267
|
+
|
|
268
|
+
# 設置測試模式,禁用自動清理避免權限問題
|
|
269
|
+
os.environ["MCP_TEST_MODE"] = "true"
|
|
270
|
+
os.environ["MCP_WEB_HOST"] = "127.0.0.1"
|
|
271
|
+
# 設置更高的端口範圍避免系統保留端口
|
|
272
|
+
os.environ["MCP_WEB_PORT"] = "9765"
|
|
273
|
+
|
|
274
|
+
print("🔧 創建 Web UI 管理器...")
|
|
275
|
+
manager = WebUIManager() # 使用環境變數控制主機和端口
|
|
276
|
+
|
|
277
|
+
# 顯示最終使用的端口(可能因端口佔用而自動切換)
|
|
278
|
+
if manager.port != 9765:
|
|
279
|
+
print(f"💡 端口 9765 被佔用,已自動切換到端口 {manager.port}")
|
|
280
|
+
|
|
281
|
+
print("🔧 創建測試會話...")
|
|
282
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
283
|
+
markdown_test_content = """# Web UI 測試 - Markdown 渲染功能
|
|
284
|
+
|
|
285
|
+
## 🎯 測試目標
|
|
286
|
+
驗證 **combinedSummaryContent** 區域的 Markdown 語法顯示功能
|
|
287
|
+
|
|
288
|
+
### ✨ 支援的語法特性
|
|
289
|
+
|
|
290
|
+
#### 文字格式
|
|
291
|
+
- **粗體文字** 使用雙星號
|
|
292
|
+
- *斜體文字* 使用單星號
|
|
293
|
+
- ~~刪除線文字~~ 使用雙波浪號
|
|
294
|
+
- `行內程式碼` 使用反引號
|
|
295
|
+
|
|
296
|
+
#### 程式碼區塊
|
|
297
|
+
```javascript
|
|
298
|
+
// JavaScript 範例
|
|
299
|
+
function renderMarkdown(content) {
|
|
300
|
+
return marked.parse(content);
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
# Python 範例
|
|
306
|
+
def process_feedback(data):
|
|
307
|
+
return {"status": "success", "data": data}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### 列表功能
|
|
311
|
+
**無序列表:**
|
|
312
|
+
- 第一個項目
|
|
313
|
+
- 第二個項目
|
|
314
|
+
- 巢狀項目 1
|
|
315
|
+
- 巢狀項目 2
|
|
316
|
+
- 第三個項目
|
|
317
|
+
|
|
318
|
+
**有序列表:**
|
|
319
|
+
1. 初始化 Markdown 渲染器
|
|
320
|
+
2. 載入 marked.js 和 DOMPurify
|
|
321
|
+
3. 配置安全選項
|
|
322
|
+
4. 渲染內容
|
|
323
|
+
|
|
324
|
+
#### 連結和引用
|
|
325
|
+
- 專案連結:[MCP AI Supervisor](https://github.com/mcp-ai-supervisor)
|
|
326
|
+
- 文檔連結:[Marked.js 官方文檔](https://marked.js.org/)
|
|
327
|
+
|
|
328
|
+
> **重要提示:** 所有 HTML 輸出都經過 DOMPurify 清理,確保安全性。
|
|
329
|
+
|
|
330
|
+
#### 表格範例
|
|
331
|
+
| 功能 | 狀態 | 說明 |
|
|
332
|
+
|------|------|------|
|
|
333
|
+
| 標題渲染 | ✅ | 支援 H1-H6 |
|
|
334
|
+
| 程式碼高亮 | ✅ | 基本語法高亮 |
|
|
335
|
+
| 列表功能 | ✅ | 有序/無序列表 |
|
|
336
|
+
| 連結處理 | ✅ | 安全連結渲染 |
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### 🔒 安全特性
|
|
341
|
+
- XSS 防護:使用 DOMPurify 清理
|
|
342
|
+
- 白名單標籤:僅允許安全的 HTML 標籤
|
|
343
|
+
- URL 驗證:限制允許的 URL 協議
|
|
344
|
+
|
|
345
|
+
### 📝 測試結果
|
|
346
|
+
如果您能看到上述內容以正確的格式顯示,表示 Markdown 渲染功能運作正常!"""
|
|
347
|
+
|
|
348
|
+
created_session_id, _ = manager.create_session(temp_dir, markdown_test_content)
|
|
349
|
+
|
|
350
|
+
if created_session_id:
|
|
351
|
+
print("✅ 會話創建成功")
|
|
352
|
+
|
|
353
|
+
print("🚀 啟動 Web 服務器...")
|
|
354
|
+
manager.start_server()
|
|
355
|
+
time.sleep(5) # 等待服務器完全啟動
|
|
356
|
+
|
|
357
|
+
if (
|
|
358
|
+
manager.server_thread is not None
|
|
359
|
+
and manager.server_thread.is_alive()
|
|
360
|
+
):
|
|
361
|
+
print("✅ Web 服務器啟動成功")
|
|
362
|
+
url = f"http://{manager.host}:{manager.port}"
|
|
363
|
+
print(f"🌐 服務器運行在: {url}")
|
|
364
|
+
|
|
365
|
+
# 如果端口有變更,額外提醒
|
|
366
|
+
if manager.port != 9765:
|
|
367
|
+
print(
|
|
368
|
+
f"📌 注意:由於端口 9765 被佔用,服務已切換到端口 {manager.port}"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
# AI Supervisor 模式:不自動開啟瀏覽器
|
|
372
|
+
print(f"💡 請使用桌面應用或手動訪問: {url}")
|
|
373
|
+
|
|
374
|
+
print("📝 Web UI 測試完成,進入持續模式...")
|
|
375
|
+
print("💡 提示:服務器將持續運行,可在瀏覽器中測試互動功能")
|
|
376
|
+
print("💡 按 Ctrl+C 停止服務器")
|
|
377
|
+
|
|
378
|
+
try:
|
|
379
|
+
# 保持服務器運行
|
|
380
|
+
while True:
|
|
381
|
+
time.sleep(1)
|
|
382
|
+
except KeyboardInterrupt:
|
|
383
|
+
print("\n🛑 停止服務器...")
|
|
384
|
+
return True
|
|
385
|
+
else:
|
|
386
|
+
print("❌ Web 服務器啟動失敗")
|
|
387
|
+
return False
|
|
388
|
+
else:
|
|
389
|
+
print("❌ 會話創建失敗")
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
except Exception as e:
|
|
393
|
+
print(f"❌ Web UI 測試失敗: {e}")
|
|
394
|
+
import traceback
|
|
395
|
+
|
|
396
|
+
traceback.print_exc()
|
|
397
|
+
return False
|
|
398
|
+
finally:
|
|
399
|
+
# 清理測試環境變數
|
|
400
|
+
os.environ.pop("MCP_TEST_MODE", None)
|
|
401
|
+
os.environ.pop("MCP_WEB_HOST", None)
|
|
402
|
+
os.environ.pop("MCP_WEB_PORT", None)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def test_desktop_app():
|
|
406
|
+
"""測試桌面應用程式"""
|
|
407
|
+
try:
|
|
408
|
+
print("🔧 檢查桌面應用程式依賴...")
|
|
409
|
+
|
|
410
|
+
# 檢查是否有 Tauri 桌面模組
|
|
411
|
+
try:
|
|
412
|
+
import os
|
|
413
|
+
import sys
|
|
414
|
+
|
|
415
|
+
# 嘗試導入桌面應用程式模組
|
|
416
|
+
def import_desktop_app():
|
|
417
|
+
# 首先嘗試從發佈包位置導入
|
|
418
|
+
try:
|
|
419
|
+
from .desktop_app import launch_desktop_app as desktop_func
|
|
420
|
+
|
|
421
|
+
print("✅ 找到發佈包中的桌面應用程式模組")
|
|
422
|
+
return desktop_func
|
|
423
|
+
except ImportError:
|
|
424
|
+
print("🔍 發佈包中未找到桌面應用程式模組,嘗試開發環境...")
|
|
425
|
+
|
|
426
|
+
# 回退到開發環境路徑
|
|
427
|
+
tauri_python_path = os.path.join(
|
|
428
|
+
os.path.dirname(__file__), "..", "..", "src-tauri", "python"
|
|
429
|
+
)
|
|
430
|
+
if os.path.exists(tauri_python_path):
|
|
431
|
+
sys.path.insert(0, tauri_python_path)
|
|
432
|
+
print(f"✅ 找到 Tauri Python 模組路徑: {tauri_python_path}")
|
|
433
|
+
try:
|
|
434
|
+
from mcp_ai_supervisor_desktop import ( # type: ignore
|
|
435
|
+
launch_desktop_app as dev_func,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
return dev_func
|
|
439
|
+
except ImportError:
|
|
440
|
+
print("❌ 無法從開發環境路徑導入桌面應用程式模組")
|
|
441
|
+
return None
|
|
442
|
+
else:
|
|
443
|
+
print(f"⚠️ 開發環境路徑不存在: {tauri_python_path}")
|
|
444
|
+
print("💡 這可能是 PyPI 安裝的版本,桌面應用功能不可用")
|
|
445
|
+
return None
|
|
446
|
+
|
|
447
|
+
launch_desktop_app_func = import_desktop_app()
|
|
448
|
+
if launch_desktop_app_func is None:
|
|
449
|
+
print("❌ 桌面應用程式不可用")
|
|
450
|
+
print("💡 可能的原因:")
|
|
451
|
+
print(" 1. 此版本不包含桌面應用程式二進制檔案")
|
|
452
|
+
print(" 2. 請使用包含桌面應用的版本,或使用 Web 模式")
|
|
453
|
+
print(" 3. Web 模式指令:uvx mcp-ai-supervisor test --web")
|
|
454
|
+
return False
|
|
455
|
+
|
|
456
|
+
print("✅ 桌面應用程式模組導入成功")
|
|
457
|
+
|
|
458
|
+
except ImportError as e:
|
|
459
|
+
print(f"❌ 無法導入桌面應用程式模組: {e}")
|
|
460
|
+
print(
|
|
461
|
+
"💡 請確保已執行 'make build-desktop' 或 'python scripts/build_desktop.py'"
|
|
462
|
+
)
|
|
463
|
+
return False
|
|
464
|
+
|
|
465
|
+
print("🚀 啟動桌面應用程式...")
|
|
466
|
+
|
|
467
|
+
# 設置桌面模式環境變數
|
|
468
|
+
os.environ["MCP_DESKTOP_MODE"] = "true"
|
|
469
|
+
|
|
470
|
+
# 使用 asyncio 啟動桌面應用程式
|
|
471
|
+
if sys.platform == "win32":
|
|
472
|
+
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
|
|
473
|
+
|
|
474
|
+
loop = asyncio.new_event_loop()
|
|
475
|
+
asyncio.set_event_loop(loop)
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
# 使用 WebUIManager 來管理桌面應用實例
|
|
479
|
+
from .web.main import get_web_ui_manager
|
|
480
|
+
|
|
481
|
+
manager = get_web_ui_manager()
|
|
482
|
+
|
|
483
|
+
# 啟動桌面應用並保存實例到 manager
|
|
484
|
+
app = loop.run_until_complete(launch_desktop_app_func(test_mode=True))
|
|
485
|
+
manager.desktop_app_instance = app
|
|
486
|
+
|
|
487
|
+
print("✅ 桌面應用程式啟動成功")
|
|
488
|
+
print("💡 桌面應用程式正在運行,按 Ctrl+C 停止...")
|
|
489
|
+
|
|
490
|
+
# 保持應用程式運行
|
|
491
|
+
try:
|
|
492
|
+
while True:
|
|
493
|
+
import time
|
|
494
|
+
|
|
495
|
+
time.sleep(1)
|
|
496
|
+
except KeyboardInterrupt:
|
|
497
|
+
print("\n🛑 停止桌面應用程式...")
|
|
498
|
+
app.stop()
|
|
499
|
+
return True
|
|
500
|
+
|
|
501
|
+
except Exception as e:
|
|
502
|
+
print(f"❌ 桌面應用程式啟動失敗: {e}")
|
|
503
|
+
import traceback
|
|
504
|
+
|
|
505
|
+
traceback.print_exc()
|
|
506
|
+
return False
|
|
507
|
+
finally:
|
|
508
|
+
loop.close()
|
|
509
|
+
|
|
510
|
+
except Exception as e:
|
|
511
|
+
print(f"❌ 桌面應用程式測試失敗: {e}")
|
|
512
|
+
import traceback
|
|
513
|
+
|
|
514
|
+
traceback.print_exc()
|
|
515
|
+
return False
|
|
516
|
+
finally:
|
|
517
|
+
# 清理環境變數
|
|
518
|
+
os.environ.pop("MCP_DESKTOP_MODE", None)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
async def wait_for_process(process):
|
|
522
|
+
"""等待進程結束"""
|
|
523
|
+
try:
|
|
524
|
+
# 等待進程自然結束
|
|
525
|
+
await process.wait()
|
|
526
|
+
|
|
527
|
+
# 確保管道正確關閉
|
|
528
|
+
try:
|
|
529
|
+
if hasattr(process, "stdout") and process.stdout:
|
|
530
|
+
process.stdout.close()
|
|
531
|
+
if hasattr(process, "stderr") and process.stderr:
|
|
532
|
+
process.stderr.close()
|
|
533
|
+
if hasattr(process, "stdin") and process.stdin:
|
|
534
|
+
process.stdin.close()
|
|
535
|
+
except Exception as close_error:
|
|
536
|
+
print(f"關閉進程管道時出錯: {close_error}")
|
|
537
|
+
|
|
538
|
+
except Exception as e:
|
|
539
|
+
print(f"等待進程時出錯: {e}")
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def show_version():
|
|
543
|
+
"""顯示版本資訊"""
|
|
544
|
+
from . import __author__, __version__
|
|
545
|
+
|
|
546
|
+
print(f"CloneLoop v{__version__}")
|
|
547
|
+
print(f"作者: {__author__}")
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
if __name__ == "__main__":
|
|
551
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""代理模块:重定向到 mcp_supervisor_core.debug
|
|
2
|
+
|
|
3
|
+
重构过渡期保持向后兼容。
|
|
4
|
+
Phase 3 删除 src/ 时此文件一并删除。
|
|
5
|
+
"""
|
|
6
|
+
from mcp_supervisor_core.debug import * # noqa: F401,F403
|
|
7
|
+
from mcp_supervisor_core.debug import ( # noqa: F401
|
|
8
|
+
debug_log,
|
|
9
|
+
debug_log_throttled,
|
|
10
|
+
i18n_debug_log,
|
|
11
|
+
is_debug_enabled,
|
|
12
|
+
server_debug_log,
|
|
13
|
+
set_debug_mode,
|
|
14
|
+
web_debug_log,
|
|
15
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MCP AI Supervisor Desktop Application
|
|
4
|
+
======================================
|
|
5
|
+
|
|
6
|
+
基於 Tauri 的桌面應用程式包裝器,為 MCP AI Supervisor 提供原生桌面體驗。
|
|
7
|
+
|
|
8
|
+
主要功能:
|
|
9
|
+
- 原生桌面應用程式界面
|
|
10
|
+
- 整合現有的 Web UI 功能
|
|
11
|
+
- 跨平台支援(Windows、macOS、Linux)
|
|
12
|
+
- 無需瀏覽器的獨立運行環境
|
|
13
|
+
|
|
14
|
+
版本: 0.1.0
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
__author__ = "AI Supervisor Team"
|
|
19
|
+
__email__ = ""
|
|
20
|
+
|
|
21
|
+
from .desktop_app import DesktopApp, launch_desktop_app
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"DesktopApp",
|
|
26
|
+
"__author__",
|
|
27
|
+
"__version__",
|
|
28
|
+
"launch_desktop_app",
|
|
29
|
+
]
|