@agentunion/kite 1.3.2 → 1.5.0
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.
- package/CHANGELOG.md +302 -0
- package/cli.js +119 -4
- package/core/dependency_checker.py +250 -0
- package/core/env_checker.py +490 -0
- package/dependencies_lock.json +128 -0
- package/extensions/agents/assistant/entry.py +111 -1
- package/extensions/agents/assistant/server.py +279 -215
- package/extensions/channels/acp_channel/entry.py +111 -1
- package/extensions/channels/acp_channel/module.md +23 -22
- package/extensions/channels/acp_channel/server.py +279 -215
- package/extensions/event_hub_bench/entry.py +107 -1
- package/extensions/services/backup/entry.py +306 -21
- package/extensions/services/backup/module.md +24 -22
- package/extensions/services/evol/auth_manager.py +443 -0
- package/extensions/services/evol/config.yaml +149 -0
- package/extensions/services/evol/config_loader.py +117 -0
- package/extensions/services/evol/entry.py +406 -0
- package/extensions/services/evol/evol_api.py +173 -0
- package/extensions/services/evol/evol_config.json5 +29 -0
- package/extensions/services/evol/migrate_tokens.py +122 -0
- package/extensions/services/evol/module.md +32 -0
- package/extensions/services/evol/pairing.py +250 -0
- package/extensions/services/evol/pairing_codes.jsonl +1 -0
- package/extensions/services/evol/relay.py +682 -0
- package/extensions/services/evol/relay_config.json5 +67 -0
- package/extensions/services/evol/routes/__init__.py +1 -0
- package/extensions/services/evol/routes/routes_management_ws.py +127 -0
- package/extensions/services/evol/routes/routes_rpc.py +89 -0
- package/extensions/services/evol/routes/routes_test.py +61 -0
- package/extensions/services/evol/server.py +875 -0
- package/extensions/services/evol/static/css/style.css +1200 -0
- package/extensions/services/evol/static/index.html +781 -0
- package/extensions/services/evol/static/index_evol.html +14 -0
- package/extensions/services/evol/static/js/app.js +6304 -0
- package/extensions/services/evol/static/js/auth.js +326 -0
- package/extensions/services/evol/static/js/dialog.js +285 -0
- package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
- package/extensions/services/evol/static/js/evol-app.js +1949 -0
- package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
- package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
- package/extensions/services/evol/static/js/kernel-client.js +396 -0
- package/extensions/services/evol/static/js/main.js +141 -0
- package/extensions/services/evol/static/js/registry-tests.js +585 -0
- package/extensions/services/evol/static/js/stats.js +217 -0
- package/extensions/services/evol/static/js/token-manager.js +175 -0
- package/extensions/services/evol/static/pairing.html +248 -0
- package/extensions/services/evol/static/test_registry.html +262 -0
- package/extensions/services/evol/static/test_relay.html +462 -0
- package/extensions/services/evol/stats_manager.py +240 -0
- package/extensions/services/model_service/entry.py +167 -19
- package/extensions/services/model_service/module.md +21 -22
- package/extensions/services/proxy/.claude/settings.local.json +13 -0
- package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
- package/extensions/services/proxy/_fix_prints.py +133 -0
- package/extensions/services/proxy/_fix_prints2.py +87 -0
- package/extensions/services/proxy/agentcp/LICENCE +178 -0
- package/extensions/services/proxy/agentcp/README copy.md +85 -0
- package/extensions/services/proxy/agentcp/README.md +260 -0
- package/extensions/services/proxy/agentcp/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/agent.py +4 -0
- package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
- package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
- package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
- package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
- package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
- package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
- package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
- package/extensions/services/proxy/agentcp/base/client.py +112 -0
- package/extensions/services/proxy/agentcp/base/env.py +34 -0
- package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
- package/extensions/services/proxy/agentcp/base/log.py +98 -0
- package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
- package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
- package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
- package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
- package/extensions/services/proxy/agentcp/context/context.py +73 -0
- package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
- package/extensions/services/proxy/agentcp/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
- package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
- package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
- package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
- package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
- package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
- package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
- package/extensions/services/proxy/agentcp/hcp.py +299 -0
- package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
- package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
- package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
- package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
- package/extensions/services/proxy/agentcp/llm_server.py +172 -0
- package/extensions/services/proxy/agentcp/mermaid.py +210 -0
- package/extensions/services/proxy/agentcp/message.py +149 -0
- package/extensions/services/proxy/agentcp/metrics.py +256 -0
- package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
- package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
- package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
- package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
- package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
- package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
- package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
- package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
- package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
- package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
- package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
- package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
- package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
- package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
- package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
- package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
- package/extensions/services/proxy/agentcp/requirements.txt +7 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
- package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
- package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
- package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
- package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
- package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
- package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
- package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
- package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
- package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
- package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
- package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
- package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
- package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
- package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
- package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
- package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
- package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
- package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
- package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
- package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
- package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
- package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
- package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
- package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
- package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
- package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
- package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
- package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
- package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
- package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
- package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
- package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
- package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
- package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
- package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
- package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
- package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
- package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
- package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
- package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
- package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
- package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
- package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
- package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
- package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
- package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
- package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
- package/extensions/services/proxy/agentcp/workflow.py +203 -0
- package/extensions/services/proxy/console_auth.py +109 -0
- package/extensions/services/proxy/evol/__init__.py +1 -0
- package/extensions/services/proxy/evol/config.py +37 -0
- package/extensions/services/proxy/evol/http/__init__.py +1 -0
- package/extensions/services/proxy/evol/http/async_http.py +551 -0
- package/extensions/services/proxy/evol/log.py +28 -0
- package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
- package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
- package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +106 -0
- package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
- package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
- package/extensions/services/proxy/evol/server/__init__.py +1 -0
- package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -0
- package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
- package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
- package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
- package/extensions/services/proxy/evol/version.py +24 -0
- package/extensions/services/proxy/logs/websocket.log +260 -0
- package/extensions/services/proxy/main.py +240 -0
- package/extensions/services/proxy/requirements.txt +13 -0
- package/extensions/services/proxy/server.py +271 -0
- package/extensions/services/watchdog/entry.py +215 -26
- package/extensions/services/watchdog/module.md +1 -0
- package/extensions/services/watchdog/monitor.py +178 -38
- package/extensions/services/web/WEBSOCKET_STATUS.md +143 -0
- package/extensions/services/web/config_example.py +35 -0
- package/extensions/services/web/config_loader.py +110 -0
- package/extensions/services/web/entry.py +114 -26
- package/extensions/services/web/module.md +35 -24
- package/extensions/services/web/pairing.py +250 -0
- package/extensions/services/web/pairing_codes.jsonl +16 -0
- package/extensions/services/web/relay.py +643 -0
- package/extensions/services/web/relay_config.json5 +67 -0
- package/extensions/services/web/routes/routes_management_ws.py +127 -0
- package/extensions/services/web/routes/routes_rpc.py +89 -0
- package/extensions/services/web/routes/routes_test.py +61 -0
- package/extensions/services/web/routes/schemas.py +0 -22
- package/extensions/services/web/server.py +434 -99
- package/extensions/services/web/static/css/style.css +67 -28
- package/extensions/services/web/static/index.html +234 -44
- package/extensions/services/web/static/js/app.js +1335 -48
- package/extensions/services/web/static/js/kernel-client-example.js +161 -0
- package/extensions/services/web/static/js/kernel-client.js +383 -0
- package/extensions/services/web/static/js/registry-tests.js +558 -0
- package/extensions/services/web/static/js/token-manager.js +175 -0
- package/extensions/services/web/static/pairing.html +248 -0
- package/extensions/services/web/static/test_registry.html +262 -0
- package/extensions/services/web/web_config.json5 +29 -0
- package/kernel/entry.py +120 -32
- package/kernel/event_hub.py +141 -16
- package/kernel/module.md +60 -33
- package/kernel/registry_store.py +45 -36
- package/kernel/rpc_router.py +152 -59
- package/kernel/server.py +322 -26
- package/kite_cli/__init__.py +3 -0
- package/kite_cli/__main__.py +5 -0
- package/kite_cli/commands/__init__.py +1 -0
- package/kite_cli/commands/clean.py +101 -0
- package/kite_cli/commands/deps_install.py +67 -0
- package/kite_cli/commands/doctor.py +35 -0
- package/kite_cli/commands/env_check.py +45 -0
- package/kite_cli/commands/history.py +111 -0
- package/kite_cli/commands/info.py +96 -0
- package/kite_cli/commands/install.py +313 -0
- package/kite_cli/commands/list.py +143 -0
- package/kite_cli/commands/log.py +81 -0
- package/kite_cli/commands/prepare.py +49 -0
- package/kite_cli/commands/rollback.py +88 -0
- package/kite_cli/commands/search.py +73 -0
- package/kite_cli/commands/uninstall.py +85 -0
- package/kite_cli/commands/update.py +118 -0
- package/kite_cli/commands/venv_setup.py +56 -0
- package/kite_cli/core/__init__.py +1 -0
- package/kite_cli/core/checker.py +142 -0
- package/kite_cli/core/dependency.py +229 -0
- package/kite_cli/core/downloader.py +209 -0
- package/kite_cli/core/install_info.py +40 -0
- package/kite_cli/core/tool_installer.py +397 -0
- package/kite_cli/core/validator.py +78 -0
- package/kite_cli/main.py +317 -0
- package/kite_cli/utils/__init__.py +1 -0
- package/kite_cli/utils/i18n.py +252 -0
- package/kite_cli/utils/interactive.py +63 -0
- package/kite_cli/utils/operation_log.py +77 -0
- package/kite_cli/utils/paths.py +34 -0
- package/kite_cli/utils/version.py +308 -0
- package/launcher/entry.py +1124 -178
- package/launcher/logging_setup.py +104 -0
- package/launcher/module.md +46 -37
- package/launcher/module_scanner.py +11 -1
- package/main.py +4 -1
- package/package.json +9 -1
- package/python_version.json +4 -0
- package/requirements.txt +38 -0
- package/scripts/env-manager.js +328 -0
- package/scripts/plan_manager.py +315 -0
- package/scripts/python-env.js +79 -0
- package/scripts/scan_dependencies.py +461 -0
- package/scripts/setup-python-env.js +191 -0
- package/extensions/services/web/routes/routes_modules.py +0 -249
|
@@ -0,0 +1,1281 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from ..log import logger
|
|
7
|
+
|
|
8
|
+
class configPresenter:
|
|
9
|
+
_lock = threading.Lock()
|
|
10
|
+
_config_dir_initialized = False
|
|
11
|
+
searchPreviewEnabled = False
|
|
12
|
+
userName = "default"
|
|
13
|
+
KEY_CLAUDE_AGENT_NAME = "claude_agent_name" # Claude代理Agent名称
|
|
14
|
+
KEY_GEMINI_AGENT_NAME = "gemini_agent_name" # Gemini代理Agent名称
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def get_config_dir():
|
|
18
|
+
"""获取配置文件目录,确保PyInstaller打包后也能正常工作"""
|
|
19
|
+
import platform
|
|
20
|
+
|
|
21
|
+
if platform.system() == "Windows":
|
|
22
|
+
app_path = os.path.join(os.getenv("APPDATA", "~"), "evol",configPresenter.userName)
|
|
23
|
+
else:
|
|
24
|
+
home_dir = os.path.expanduser("~")
|
|
25
|
+
if os.access(home_dir, os.W_OK):
|
|
26
|
+
app_path = os.path.join(home_dir, ".evol",configPresenter.userName)
|
|
27
|
+
|
|
28
|
+
else:
|
|
29
|
+
import tempfile
|
|
30
|
+
app_path = os.path.join(tempfile.gettempdir(), "evol",configPresenter.userName)
|
|
31
|
+
logger.debug(f"using config dir: {app_path}")
|
|
32
|
+
return app_path
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def ensure_config_dir():
|
|
36
|
+
"""确保配置目录存在,只在需要时创建一次"""
|
|
37
|
+
if not configPresenter._config_dir_initialized:
|
|
38
|
+
config_dir = configPresenter.get_config_dir()
|
|
39
|
+
try:
|
|
40
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
41
|
+
configPresenter._config_dir_initialized = True
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"创建配置目录失败: {e}")
|
|
44
|
+
# 降级到临时目录
|
|
45
|
+
import tempfile
|
|
46
|
+
|
|
47
|
+
config_dir = tempfile.gettempdir()
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def get_claude_agent_name():
|
|
52
|
+
"""获取Claude智能体名称"""
|
|
53
|
+
"""获取Claude代理Agent名称,如果未设置则返回默认值"""
|
|
54
|
+
#return "claude_code_proxy.aid.pub"
|
|
55
|
+
agent_name = configPresenter.getSetting(configPresenter.KEY_CLAUDE_AGENT_NAME)
|
|
56
|
+
if agent_name is None:
|
|
57
|
+
return "claude_code_proxy_1.aid.pub" # 默认值
|
|
58
|
+
#return "claude_code_proxy.aid.pub"
|
|
59
|
+
return agent_name
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def get_gemini_agent_name():
|
|
63
|
+
"""获取Gemini智能体名称"""
|
|
64
|
+
"""获取Gemini代理Agent名称,如果未设置则返回默认值"""
|
|
65
|
+
agent_name = configPresenter.getSetting(configPresenter.KEY_GEMINI_AGENT_NAME)
|
|
66
|
+
if agent_name is None:
|
|
67
|
+
return "gemini_proxy.aid.pub" # 默认值
|
|
68
|
+
return agent_name
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def get_config_file_path(workspaceName = ""):
|
|
73
|
+
"""获取配置文件的完整路径"""
|
|
74
|
+
if not workspaceName:
|
|
75
|
+
configPresenter.ensure_config_dir() # 确保目录存在
|
|
76
|
+
config_dir = configPresenter.get_config_dir()
|
|
77
|
+
return os.path.join(config_dir, "key_value_store.json")
|
|
78
|
+
else:
|
|
79
|
+
from evol.presenter.workPresenter import workPresenter
|
|
80
|
+
workspace = workPresenter.get_evol_workspace_path(workspaceName)
|
|
81
|
+
return os.path.join(workspace, "key_value_store.json")
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def getSetting(key,workspaceName = ""):
|
|
85
|
+
"""
|
|
86
|
+
从本地文件读取指定键的值
|
|
87
|
+
"""
|
|
88
|
+
filepath = configPresenter.get_config_file_path(workspaceName)
|
|
89
|
+
try:
|
|
90
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
91
|
+
content = f.read().strip()
|
|
92
|
+
if not content: # 如果文件为空
|
|
93
|
+
return None
|
|
94
|
+
data = json.loads(content)
|
|
95
|
+
value = data.get(key)
|
|
96
|
+
return value
|
|
97
|
+
except FileNotFoundError:
|
|
98
|
+
return None
|
|
99
|
+
except json.JSONDecodeError as e:
|
|
100
|
+
logger.error(f"JSON格式错误: {e}")
|
|
101
|
+
# 如果JSON格式错误,重新创建一个空的配置文件
|
|
102
|
+
try:
|
|
103
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
104
|
+
json.dump({}, f, indent=4, ensure_ascii=False)
|
|
105
|
+
logger.info("已重新创建配置文件")
|
|
106
|
+
except Exception as write_error:
|
|
107
|
+
logger.error(f"重新创建配置文件失败: {write_error}")
|
|
108
|
+
return None
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.exception(f"读取设置时出错: {e}")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def setSetting(key, value,workspaceName = ""):
|
|
115
|
+
"""
|
|
116
|
+
将键值对存储到本地文件(线程安全版本)
|
|
117
|
+
"""
|
|
118
|
+
logger.info(f"开始保存设置: {key} = {value}")
|
|
119
|
+
try:
|
|
120
|
+
with configPresenter._lock: # 使用线程锁保护临界区
|
|
121
|
+
filepath = configPresenter.get_config_file_path(workspaceName)
|
|
122
|
+
if not os.path.exists(filepath):
|
|
123
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
124
|
+
json.dump({}, f, indent=4, ensure_ascii=False)
|
|
125
|
+
|
|
126
|
+
# 读取现有数据
|
|
127
|
+
try:
|
|
128
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
129
|
+
data = json.load(f)
|
|
130
|
+
except (json.JSONDecodeError, FileNotFoundError):
|
|
131
|
+
data = {}
|
|
132
|
+
|
|
133
|
+
# 更新数据
|
|
134
|
+
old_value = data.get(key, "未设置")
|
|
135
|
+
data[key] = value
|
|
136
|
+
|
|
137
|
+
# 写回文件
|
|
138
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
139
|
+
json.dump(data, f, indent=4, ensure_ascii=False)
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.exception(f"保存设置时出错: {e}")
|
|
142
|
+
return False
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def setCurrentModelInfo(workspaceName,modelId,providerId):
|
|
147
|
+
configPresenter.setSetting("currentModel", modelId,workspaceName)
|
|
148
|
+
configPresenter.setSetting("currentProvider", providerId,workspaceName)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def getCurrentModelInfo(workspaceName):
|
|
152
|
+
currentModel = configPresenter.getSetting("currentModel",workspaceName)
|
|
153
|
+
currentProvider = configPresenter.getSetting("currentProvider",workspaceName)
|
|
154
|
+
return {"modelId": currentModel, "providerId": currentProvider}
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def setProviderById(key,value):
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def deleteProviders(id):
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def addProviders(value):
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def get_fsss2_file_path():
|
|
170
|
+
"""获取fsss2文件的完整路径(用于存储token)"""
|
|
171
|
+
configPresenter.ensure_config_dir()
|
|
172
|
+
config_dir = configPresenter.get_config_dir()
|
|
173
|
+
return os.path.join(config_dir, "fsss2")
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def get_token():
|
|
177
|
+
"""
|
|
178
|
+
获取token(优先从fsss2文件读取)
|
|
179
|
+
读取逻辑:
|
|
180
|
+
1. 先读取fsss2文件
|
|
181
|
+
2. 如果没读取到,再读取原来位置的token
|
|
182
|
+
3. 如果原位置有token,写入fsss2文件再返回
|
|
183
|
+
"""
|
|
184
|
+
fsss2_path = configPresenter.get_fsss2_file_path()
|
|
185
|
+
|
|
186
|
+
# 1. 先尝试从fsss2文件读取
|
|
187
|
+
try:
|
|
188
|
+
with open(fsss2_path, "r", encoding="utf-8") as f:
|
|
189
|
+
token = f.read().strip()
|
|
190
|
+
if token:
|
|
191
|
+
return token
|
|
192
|
+
except FileNotFoundError:
|
|
193
|
+
pass
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(f"读取fsss2文件时出错: {e}")
|
|
196
|
+
|
|
197
|
+
# 2. fsss2没读到,尝试从原位置读取
|
|
198
|
+
old_token = configPresenter.getSetting("token")
|
|
199
|
+
if old_token:
|
|
200
|
+
# 3. 原位置有token,写入fsss2文件
|
|
201
|
+
try:
|
|
202
|
+
with open(fsss2_path, "w", encoding="utf-8") as f:
|
|
203
|
+
f.write(old_token)
|
|
204
|
+
logger.info("已将token从原位置迁移到fsss2文件")
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.error(f"迁移token到fsss2文件时出错: {e}")
|
|
207
|
+
return old_token
|
|
208
|
+
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def set_token(token: str):
|
|
213
|
+
"""
|
|
214
|
+
设置token(写入fsss2文件)
|
|
215
|
+
如果token为空或None,删除fsss2文件以清除token
|
|
216
|
+
"""
|
|
217
|
+
fsss2_path = configPresenter.get_fsss2_file_path()
|
|
218
|
+
|
|
219
|
+
# 如果token为空,删除文件以清除token
|
|
220
|
+
if not token:
|
|
221
|
+
try:
|
|
222
|
+
if os.path.exists(fsss2_path):
|
|
223
|
+
os.remove(fsss2_path)
|
|
224
|
+
logger.info("token已清除(fsss2文件已删除)")
|
|
225
|
+
return True
|
|
226
|
+
except Exception as e:
|
|
227
|
+
logger.exception(f"清除token时出错: {e}")
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
with open(fsss2_path, "w", encoding="utf-8") as f:
|
|
232
|
+
f.write(token)
|
|
233
|
+
logger.info("token已保存到fsss2文件")
|
|
234
|
+
return True
|
|
235
|
+
except Exception as e:
|
|
236
|
+
logger.exception(f"保存token到fsss2文件时出错: {e}")
|
|
237
|
+
return False
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def set_account_info(username: str, password: str):
|
|
241
|
+
account_info = {"username": username, "password": password}
|
|
242
|
+
return configPresenter.setSetting("account_info", json.dumps(account_info))
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def get_account_info():
|
|
246
|
+
try:
|
|
247
|
+
user_info = configPresenter.getSetting("account_info")
|
|
248
|
+
if user_info is None:
|
|
249
|
+
return None
|
|
250
|
+
return json.loads(user_info)
|
|
251
|
+
except:
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def set_user_info(aid: str, password: str):
|
|
256
|
+
user_info = {"aid": aid, "password": password}
|
|
257
|
+
return configPresenter.setSetting("user_info", json.dumps(user_info))
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def get_user_info():
|
|
261
|
+
try:
|
|
262
|
+
user_info = configPresenter.getSetting("user_info")
|
|
263
|
+
if user_info is None:
|
|
264
|
+
return None
|
|
265
|
+
return json.loads(user_info)
|
|
266
|
+
except:
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
@staticmethod
|
|
270
|
+
def getSearchPreviewEnabled():
|
|
271
|
+
return configPresenter.searchPreviewEnabled
|
|
272
|
+
|
|
273
|
+
@staticmethod
|
|
274
|
+
def getCustomSearchEngines():
|
|
275
|
+
return 'baidu'
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def getProviders():
|
|
279
|
+
return []
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def getDefaultProviders():
|
|
283
|
+
return ""
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def getLanguage():
|
|
287
|
+
return "zh-CN"
|
|
288
|
+
|
|
289
|
+
@staticmethod
|
|
290
|
+
def getModelDefaultConfig(modal):
|
|
291
|
+
return "";
|
|
292
|
+
|
|
293
|
+
@staticmethod
|
|
294
|
+
def getArtifactsEffectEnabled():
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
@staticmethod
|
|
298
|
+
def setProviderById(key,value):
|
|
299
|
+
pass
|
|
300
|
+
|
|
301
|
+
@staticmethod
|
|
302
|
+
def deleteProviders(id):
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
@staticmethod
|
|
306
|
+
def addProviders(value):
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
@staticmethod
|
|
310
|
+
def is_autostart_enabled():
|
|
311
|
+
return False
|
|
312
|
+
|
|
313
|
+
@staticmethod
|
|
314
|
+
def disable_autostart():
|
|
315
|
+
return False
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def enable_autostart():
|
|
319
|
+
return False
|
|
320
|
+
|
|
321
|
+
@staticmethod
|
|
322
|
+
def get_statistics_cache(workspace_name: str):
|
|
323
|
+
"""
|
|
324
|
+
获取工作空间统计数据缓存
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
workspace_name (str): 工作空间名称
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
dict: 缓存数据,包含timestamp和data字段,如果没有缓存则返回None
|
|
331
|
+
"""
|
|
332
|
+
cache_key = f"statistics_cache_{workspace_name}"
|
|
333
|
+
return configPresenter.getSetting(cache_key, workspace_name)
|
|
334
|
+
|
|
335
|
+
@staticmethod
|
|
336
|
+
def set_statistics_cache(workspace_name: str, statistics_data: dict):
|
|
337
|
+
"""
|
|
338
|
+
设置工作空间统计数据缓存
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
workspace_name (str): 工作空间名称
|
|
342
|
+
statistics_data (dict): 统计数据
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
bool: 是否设置成功
|
|
346
|
+
"""
|
|
347
|
+
cache_key = f"statistics_cache_{workspace_name}"
|
|
348
|
+
cache_data = {
|
|
349
|
+
"timestamp": time.time(),
|
|
350
|
+
"data": statistics_data
|
|
351
|
+
}
|
|
352
|
+
return configPresenter.setSetting(cache_key, cache_data, workspace_name)
|
|
353
|
+
|
|
354
|
+
@staticmethod
|
|
355
|
+
def is_statistics_cache_valid(workspace_name: str, cache_timeout: int = 300):
|
|
356
|
+
"""
|
|
357
|
+
检查统计数据缓存是否有效(默认5分钟过期)
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
workspace_name (str): 工作空间名称
|
|
361
|
+
cache_timeout (int): 缓存超时时间(秒),默认300秒(5分钟)
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
bool: 缓存是否有效
|
|
365
|
+
"""
|
|
366
|
+
cache_data = configPresenter.get_statistics_cache(workspace_name)
|
|
367
|
+
if not cache_data or not isinstance(cache_data, dict):
|
|
368
|
+
return False
|
|
369
|
+
|
|
370
|
+
timestamp = cache_data.get("timestamp")
|
|
371
|
+
if not timestamp:
|
|
372
|
+
return False
|
|
373
|
+
|
|
374
|
+
current_time = time.time()
|
|
375
|
+
return (current_time - timestamp) < cache_timeout
|
|
376
|
+
|
|
377
|
+
@staticmethod
|
|
378
|
+
def get_default_download_path():
|
|
379
|
+
"""
|
|
380
|
+
获取系统默认下载路径
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
str: 默认下载路径
|
|
384
|
+
"""
|
|
385
|
+
import platform
|
|
386
|
+
|
|
387
|
+
if platform.system() == "Windows":
|
|
388
|
+
# Windows系统下载路径
|
|
389
|
+
downloads_path = os.path.join(os.path.expanduser("~"), "Downloads")
|
|
390
|
+
elif platform.system() == "Darwin": # macOS
|
|
391
|
+
downloads_path = os.path.join(os.path.expanduser("~"), "Downloads")
|
|
392
|
+
else: # Linux
|
|
393
|
+
downloads_path = os.path.join(os.path.expanduser("~"), "Downloads")
|
|
394
|
+
|
|
395
|
+
# 确保路径存在
|
|
396
|
+
if not os.path.exists(downloads_path):
|
|
397
|
+
try:
|
|
398
|
+
os.makedirs(downloads_path, exist_ok=True)
|
|
399
|
+
except Exception as e:
|
|
400
|
+
logger.error(f"创建下载目录失败: {e}")
|
|
401
|
+
# 降级到用户主目录
|
|
402
|
+
downloads_path = os.path.expanduser("~")
|
|
403
|
+
|
|
404
|
+
return downloads_path
|
|
405
|
+
|
|
406
|
+
@staticmethod
|
|
407
|
+
def get_download_path():
|
|
408
|
+
"""
|
|
409
|
+
获取下载路径设置,如果没有设置则返回系统默认下载路径
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
str: 下载路径
|
|
413
|
+
"""
|
|
414
|
+
download_path = configPresenter.getSetting("download_path")
|
|
415
|
+
if not download_path:
|
|
416
|
+
# 如果没有设置,获取系统默认下载路径并保存
|
|
417
|
+
default_path = configPresenter.get_default_download_path()
|
|
418
|
+
configPresenter.set_download_path(default_path)
|
|
419
|
+
return default_path
|
|
420
|
+
return download_path
|
|
421
|
+
|
|
422
|
+
@staticmethod
|
|
423
|
+
def set_download_path(path: str):
|
|
424
|
+
"""
|
|
425
|
+
设置下载路径
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
path (str): 下载路径
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
bool: 是否设置成功
|
|
432
|
+
"""
|
|
433
|
+
return configPresenter.setSetting("download_path", path)
|
|
434
|
+
|
|
435
|
+
# ============ 默认工作空间路径配置 ============
|
|
436
|
+
|
|
437
|
+
@staticmethod
|
|
438
|
+
def get_default_workspace_path():
|
|
439
|
+
"""
|
|
440
|
+
获取默认工作空间路径
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
str: 默认工作空间路径,如果未设置则返回None
|
|
444
|
+
"""
|
|
445
|
+
return configPresenter.getSetting("default_workspace_path")
|
|
446
|
+
|
|
447
|
+
@staticmethod
|
|
448
|
+
def get_default_workspace_base_path():
|
|
449
|
+
"""
|
|
450
|
+
获取默认工作空间基础路径(系统默认路径)
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
str: 默认工作空间基础路径
|
|
454
|
+
"""
|
|
455
|
+
return os.path.join(configPresenter.get_config_dir(), "workspace")
|
|
456
|
+
|
|
457
|
+
@staticmethod
|
|
458
|
+
def set_default_workspace_path(path: str):
|
|
459
|
+
"""
|
|
460
|
+
设置默认工作空间路径
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
path (str): 默认工作空间路径
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
bool: 是否设置成功
|
|
467
|
+
"""
|
|
468
|
+
return configPresenter.setSetting("default_workspace_path", path)
|
|
469
|
+
|
|
470
|
+
# ============ AI 网关配置管理 ============
|
|
471
|
+
|
|
472
|
+
KEY_AI_GATEWAY_TYPE = "ai_gateway_type"
|
|
473
|
+
KEY_AI_GATEWAY_CONFIGS = "ai_gateway_configs"
|
|
474
|
+
|
|
475
|
+
# 默认网关配置
|
|
476
|
+
DEFAULT_GATEWAY_CONFIGS = {
|
|
477
|
+
"local": {
|
|
478
|
+
"type": "local",
|
|
479
|
+
"name": "本地网关",
|
|
480
|
+
"description": "使用本地 ModelGate 服务",
|
|
481
|
+
"baseUrl": "http://127.0.0.1:13148",
|
|
482
|
+
"apiVersion": "/v1",
|
|
483
|
+
"timeout": 30000,
|
|
484
|
+
"retryCount": 3,
|
|
485
|
+
"enabled": True
|
|
486
|
+
},
|
|
487
|
+
"cloud": {
|
|
488
|
+
"type": "cloud",
|
|
489
|
+
"name": "Evol 网关",
|
|
490
|
+
"description": "使用 Evol 云端服务",
|
|
491
|
+
# 兜底配置,实际云端地址会由 userPresenter 的网关信息覆盖
|
|
492
|
+
"baseUrl": "https://mg-new.evolai.cn",
|
|
493
|
+
"apiVersion": "/v1",
|
|
494
|
+
"timeout": 30000,
|
|
495
|
+
"retryCount": 3,
|
|
496
|
+
"enabled": True
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
@staticmethod
|
|
501
|
+
def get_gateway_type():
|
|
502
|
+
"""
|
|
503
|
+
获取当前网关类型
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
str: 'local' 或 'cloud',默认 'cloud'
|
|
507
|
+
"""
|
|
508
|
+
gateway_type = configPresenter.getSetting(configPresenter.KEY_AI_GATEWAY_TYPE)
|
|
509
|
+
if not gateway_type:
|
|
510
|
+
# 默认使用云端网关
|
|
511
|
+
configPresenter.set_gateway_type("cloud")
|
|
512
|
+
return "cloud"
|
|
513
|
+
return gateway_type
|
|
514
|
+
|
|
515
|
+
@staticmethod
|
|
516
|
+
def set_gateway_type(gateway_type: str):
|
|
517
|
+
"""
|
|
518
|
+
设置当前网关类型
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
gateway_type (str): 'local', 'cloud', 或 'custom-{id}'
|
|
522
|
+
|
|
523
|
+
Returns:
|
|
524
|
+
bool: 是否设置成功
|
|
525
|
+
"""
|
|
526
|
+
# 验证网关类型
|
|
527
|
+
if gateway_type in ["local", "cloud"]:
|
|
528
|
+
# 预设网关,直接设置
|
|
529
|
+
logger.info(f"Setting gateway type to: {gateway_type}")
|
|
530
|
+
return configPresenter.setSetting(configPresenter.KEY_AI_GATEWAY_TYPE, gateway_type)
|
|
531
|
+
elif gateway_type.startswith("custom-"):
|
|
532
|
+
# 自定义网关,验证是否存在
|
|
533
|
+
gateway_id = gateway_type.replace("custom-", "")
|
|
534
|
+
custom_gateway = configPresenter.get_custom_gateway(gateway_id)
|
|
535
|
+
if custom_gateway:
|
|
536
|
+
logger.info(f"Setting gateway type to custom: {gateway_id}")
|
|
537
|
+
return configPresenter.setSetting(configPresenter.KEY_AI_GATEWAY_TYPE, gateway_type)
|
|
538
|
+
else:
|
|
539
|
+
logger.error(f"Custom gateway not found: {gateway_id}")
|
|
540
|
+
return False
|
|
541
|
+
else:
|
|
542
|
+
logger.error(f"Invalid gateway type: {gateway_type}")
|
|
543
|
+
return False
|
|
544
|
+
|
|
545
|
+
@staticmethod
|
|
546
|
+
def get_gateway_configs():
|
|
547
|
+
"""
|
|
548
|
+
获取所有网关配置
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
dict: 所有网关配置
|
|
552
|
+
"""
|
|
553
|
+
#configs = configPresenter.getSetting(configPresenter.KEY_AI_GATEWAY_CONFIGS)
|
|
554
|
+
#if not configs:
|
|
555
|
+
# 首次使用,保存默认配置
|
|
556
|
+
configPresenter.set_gateway_configs(configPresenter.DEFAULT_GATEWAY_CONFIGS)
|
|
557
|
+
return configPresenter.DEFAULT_GATEWAY_CONFIGS
|
|
558
|
+
#return configs
|
|
559
|
+
|
|
560
|
+
@staticmethod
|
|
561
|
+
def _apply_cloud_gateway_info(config: dict) -> dict:
|
|
562
|
+
if not isinstance(config, dict):
|
|
563
|
+
return config
|
|
564
|
+
|
|
565
|
+
try:
|
|
566
|
+
from .userPresenter import userPresenter
|
|
567
|
+
|
|
568
|
+
result = userPresenter.get_cloud_gateway_info()
|
|
569
|
+
gateway_info = None
|
|
570
|
+
if isinstance(result, dict):
|
|
571
|
+
gateway_info = result.get("gateway_info")
|
|
572
|
+
|
|
573
|
+
if isinstance(gateway_info, dict):
|
|
574
|
+
base_url = gateway_info.get("modelBaseUrl") or gateway_info.get("apiUrl")
|
|
575
|
+
if base_url:
|
|
576
|
+
updated = dict(config)
|
|
577
|
+
updated["baseUrl"] = base_url
|
|
578
|
+
return updated
|
|
579
|
+
except Exception:
|
|
580
|
+
return config
|
|
581
|
+
|
|
582
|
+
return config
|
|
583
|
+
|
|
584
|
+
@staticmethod
|
|
585
|
+
def set_gateway_configs(configs: dict):
|
|
586
|
+
"""
|
|
587
|
+
设置所有网关配置
|
|
588
|
+
|
|
589
|
+
Args:
|
|
590
|
+
configs (dict): 网关配置字典
|
|
591
|
+
|
|
592
|
+
Returns:
|
|
593
|
+
bool: 是否设置成功
|
|
594
|
+
"""
|
|
595
|
+
return configPresenter.setSetting(configPresenter.KEY_AI_GATEWAY_CONFIGS, configs)
|
|
596
|
+
|
|
597
|
+
@staticmethod
|
|
598
|
+
def get_gateway_config(gateway_type: str = None):
|
|
599
|
+
"""
|
|
600
|
+
获取指定网关的配置(支持自定义网关)
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
gateway_type (str): 网关类型,不传则使用当前网关
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
dict: 网关配置
|
|
607
|
+
"""
|
|
608
|
+
if not gateway_type:
|
|
609
|
+
gateway_type = configPresenter.get_gateway_type()
|
|
610
|
+
|
|
611
|
+
# 检查是否为自定义网关
|
|
612
|
+
if gateway_type and gateway_type.startswith("custom-"):
|
|
613
|
+
gateway_id = gateway_type.replace("custom-", "")
|
|
614
|
+
custom_config = configPresenter.get_custom_gateway(gateway_id)
|
|
615
|
+
if custom_config:
|
|
616
|
+
logger.debug(f"Using custom gateway config: {gateway_id}")
|
|
617
|
+
return custom_config
|
|
618
|
+
else:
|
|
619
|
+
logger.warning(f"Custom gateway not found: {gateway_id}, using default cloud gateway")
|
|
620
|
+
return configPresenter.DEFAULT_GATEWAY_CONFIGS.get("cloud", {})
|
|
621
|
+
|
|
622
|
+
# 原有逻辑:本地或云端网关
|
|
623
|
+
configs = configPresenter.get_gateway_configs()
|
|
624
|
+
config = configs.get(gateway_type)
|
|
625
|
+
|
|
626
|
+
if not config:
|
|
627
|
+
logger.warning(f"Gateway config not found: {gateway_type}, using default")
|
|
628
|
+
return configPresenter.DEFAULT_GATEWAY_CONFIGS.get(gateway_type, {})
|
|
629
|
+
|
|
630
|
+
if gateway_type == "cloud":
|
|
631
|
+
return configPresenter._apply_cloud_gateway_info(config)
|
|
632
|
+
|
|
633
|
+
return config
|
|
634
|
+
|
|
635
|
+
@staticmethod
|
|
636
|
+
def get_current_gateway_base_url():
|
|
637
|
+
"""
|
|
638
|
+
获取当前网关的 Base URL
|
|
639
|
+
|
|
640
|
+
Returns:
|
|
641
|
+
str: 当前网关的基础 URL
|
|
642
|
+
"""
|
|
643
|
+
config = configPresenter.get_gateway_config()
|
|
644
|
+
return config.get("baseUrl", "http://127.0.0.1:13148")
|
|
645
|
+
|
|
646
|
+
@staticmethod
|
|
647
|
+
def get_current_gateway_api_url():
|
|
648
|
+
"""
|
|
649
|
+
获取当前网关的 API URL(含版本)
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
str: 当前网关的 API URL
|
|
653
|
+
"""
|
|
654
|
+
config = configPresenter.get_gateway_config()
|
|
655
|
+
base_url = config.get("baseUrl", "http://127.0.0.1:13148")
|
|
656
|
+
api_version = config.get("apiVersion", "/v1")
|
|
657
|
+
return f"{base_url}{api_version}"
|
|
658
|
+
|
|
659
|
+
@staticmethod
|
|
660
|
+
def get_current_gateway_api_key():
|
|
661
|
+
"""
|
|
662
|
+
获取当前网关的 API Key
|
|
663
|
+
|
|
664
|
+
Returns:
|
|
665
|
+
str: 当前网关的 API Key,如果没有则返回空字符串
|
|
666
|
+
"""
|
|
667
|
+
config = configPresenter.get_gateway_config()
|
|
668
|
+
return config.get("apiKey", "")
|
|
669
|
+
|
|
670
|
+
@staticmethod
|
|
671
|
+
def build_gateway_url(endpoint: str, include_version: bool = True, gateway_type: str = None):
|
|
672
|
+
"""
|
|
673
|
+
构建完整的网关端点 URL
|
|
674
|
+
|
|
675
|
+
Args:
|
|
676
|
+
endpoint (str): 端点路径
|
|
677
|
+
include_version (bool): 是否包含版本号
|
|
678
|
+
gateway_type (str): 网关类型,不传则使用当前网关
|
|
679
|
+
|
|
680
|
+
Returns:
|
|
681
|
+
str: 完整的 URL
|
|
682
|
+
"""
|
|
683
|
+
config = configPresenter.get_gateway_config(gateway_type)
|
|
684
|
+
base_url = config.get("baseUrl", "http://127.0.0.1:13148")
|
|
685
|
+
|
|
686
|
+
if include_version:
|
|
687
|
+
api_version = config.get("apiVersion", "/v1")
|
|
688
|
+
base = f"{base_url}{api_version}"
|
|
689
|
+
else:
|
|
690
|
+
base = base_url
|
|
691
|
+
|
|
692
|
+
# 确保 endpoint 以 / 开头
|
|
693
|
+
if not endpoint.startswith("/"):
|
|
694
|
+
endpoint = f"/{endpoint}"
|
|
695
|
+
|
|
696
|
+
return f"{base}{endpoint}"
|
|
697
|
+
|
|
698
|
+
# ============ 自定义网关管理 ============
|
|
699
|
+
|
|
700
|
+
KEY_CUSTOM_GATEWAYS = "custom_gateways"
|
|
701
|
+
|
|
702
|
+
@staticmethod
|
|
703
|
+
def get_custom_gateways():
|
|
704
|
+
"""
|
|
705
|
+
获取所有自定义网关配置
|
|
706
|
+
|
|
707
|
+
Returns:
|
|
708
|
+
dict: 自定义网关配置字典,格式为 {gateway_id: config}
|
|
709
|
+
"""
|
|
710
|
+
gateways = configPresenter.getSetting(configPresenter.KEY_CUSTOM_GATEWAYS)
|
|
711
|
+
return gateways if gateways else {}
|
|
712
|
+
|
|
713
|
+
@staticmethod
|
|
714
|
+
def add_custom_gateway(config=None):
|
|
715
|
+
"""
|
|
716
|
+
添加自定义网关
|
|
717
|
+
|
|
718
|
+
Args:
|
|
719
|
+
config (dict): 网关配置,包含 name, baseUrl, apiKey, apiType 等字段
|
|
720
|
+
|
|
721
|
+
Returns:
|
|
722
|
+
dict: {"success": bool, "id": str} 添加结果
|
|
723
|
+
"""
|
|
724
|
+
import uuid
|
|
725
|
+
|
|
726
|
+
try:
|
|
727
|
+
logger.info(f"add_custom_gateway called with config: {config}")
|
|
728
|
+
logger.info(f"config type: {type(config)}")
|
|
729
|
+
|
|
730
|
+
if config is None:
|
|
731
|
+
logger.error("Config is None!")
|
|
732
|
+
return {"success": False, "id": "", "error": "Config is None"}
|
|
733
|
+
|
|
734
|
+
if not isinstance(config, dict):
|
|
735
|
+
logger.error(f"Config is not a dict, it's {type(config)}")
|
|
736
|
+
return {"success": False, "id": "", "error": f"Config must be dict, got {type(config)}"}
|
|
737
|
+
|
|
738
|
+
# 生成唯一ID
|
|
739
|
+
gateway_id = str(uuid.uuid4())[:8]
|
|
740
|
+
|
|
741
|
+
gateways = configPresenter.get_custom_gateways()
|
|
742
|
+
|
|
743
|
+
# 根据API类型确定API版本
|
|
744
|
+
api_type = config.get("apiType", "openai")
|
|
745
|
+
api_version = "/v1" # OpenAI风格默认使用 /v1
|
|
746
|
+
|
|
747
|
+
# 构建完整的网关配置
|
|
748
|
+
gateways[gateway_id] = {
|
|
749
|
+
"id": gateway_id,
|
|
750
|
+
"type": f"custom-{gateway_id}",
|
|
751
|
+
"name": config.get("name", "自定义网关"),
|
|
752
|
+
"description": config.get("description", f"自定义网关 - {config.get('baseUrl', '')}"),
|
|
753
|
+
"baseUrl": config.get("baseUrl", ""),
|
|
754
|
+
"apiKey": config.get("apiKey", ""),
|
|
755
|
+
"apiType": api_type,
|
|
756
|
+
"apiVersion": api_version, # 根据API类型自动设置
|
|
757
|
+
"timeout": config.get("timeout", 30000),
|
|
758
|
+
"retryCount": config.get("retryCount", 3),
|
|
759
|
+
"enabled": True,
|
|
760
|
+
"isCustom": True,
|
|
761
|
+
"createdAt": time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
success = configPresenter.setSetting(configPresenter.KEY_CUSTOM_GATEWAYS, gateways)
|
|
765
|
+
|
|
766
|
+
if success:
|
|
767
|
+
logger.info(f"Added custom gateway: {gateway_id} - {config.get('name')} (API Type: {api_type})")
|
|
768
|
+
else:
|
|
769
|
+
logger.error(f"Failed to add custom gateway: {config.get('name')}")
|
|
770
|
+
|
|
771
|
+
return {"success": success, "id": gateway_id}
|
|
772
|
+
except Exception as e:
|
|
773
|
+
logger.exception(f"Exception in add_custom_gateway: {e}")
|
|
774
|
+
return {"success": False, "id": "", "error": str(e)}
|
|
775
|
+
|
|
776
|
+
@staticmethod
|
|
777
|
+
def update_custom_gateway(gateway_id: str, config: dict):
|
|
778
|
+
"""
|
|
779
|
+
更新自定义网关配置
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
gateway_id (str): 网关ID
|
|
783
|
+
config (dict): 要更新的配置字段
|
|
784
|
+
|
|
785
|
+
Returns:
|
|
786
|
+
bool: 是否更新成功
|
|
787
|
+
"""
|
|
788
|
+
gateways = configPresenter.get_custom_gateways()
|
|
789
|
+
|
|
790
|
+
if gateway_id not in gateways:
|
|
791
|
+
logger.error(f"Custom gateway not found: {gateway_id}")
|
|
792
|
+
return False
|
|
793
|
+
|
|
794
|
+
# 保留原有的ID、类型和创建时间
|
|
795
|
+
original = gateways[gateway_id]
|
|
796
|
+
|
|
797
|
+
# 如果更新了apiType,需要更新对应的apiVersion
|
|
798
|
+
if "apiType" in config:
|
|
799
|
+
api_type = config.get("apiType", "openai")
|
|
800
|
+
config["apiVersion"] = "/v1" # OpenAI风格使用 /v1
|
|
801
|
+
|
|
802
|
+
# 更新配置(合并新旧配置)
|
|
803
|
+
updated_config = {
|
|
804
|
+
**original,
|
|
805
|
+
**config,
|
|
806
|
+
"id": gateway_id, # 确保ID不变
|
|
807
|
+
"type": f"custom-{gateway_id}", # 确保type不变
|
|
808
|
+
"createdAt": original.get("createdAt"), # 确保创建时间不变
|
|
809
|
+
"isCustom": True # 确保isCustom标记不变
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
gateways[gateway_id] = updated_config
|
|
813
|
+
|
|
814
|
+
success = configPresenter.setSetting(configPresenter.KEY_CUSTOM_GATEWAYS, gateways)
|
|
815
|
+
|
|
816
|
+
if success:
|
|
817
|
+
logger.info(f"Updated custom gateway: {gateway_id} (API Type: {updated_config.get('apiType', 'openai')})")
|
|
818
|
+
else:
|
|
819
|
+
logger.error(f"Failed to update custom gateway: {gateway_id}")
|
|
820
|
+
|
|
821
|
+
return success
|
|
822
|
+
|
|
823
|
+
@staticmethod
|
|
824
|
+
def delete_custom_gateway(gateway_id: str):
|
|
825
|
+
"""
|
|
826
|
+
删除自定义网关
|
|
827
|
+
|
|
828
|
+
Args:
|
|
829
|
+
gateway_id (str): 网关ID
|
|
830
|
+
|
|
831
|
+
Returns:
|
|
832
|
+
bool: 是否删除成功
|
|
833
|
+
"""
|
|
834
|
+
gateways = configPresenter.get_custom_gateways()
|
|
835
|
+
|
|
836
|
+
if gateway_id not in gateways:
|
|
837
|
+
logger.warning(f"Custom gateway not found for deletion: {gateway_id}")
|
|
838
|
+
return False
|
|
839
|
+
|
|
840
|
+
# 检查是否正在使用该网关
|
|
841
|
+
current_gateway_type = configPresenter.get_gateway_type()
|
|
842
|
+
if current_gateway_type == f"custom-{gateway_id}":
|
|
843
|
+
logger.warning(f"Cannot delete custom gateway {gateway_id}: currently in use")
|
|
844
|
+
# 自动切换到云端网关
|
|
845
|
+
configPresenter.set_gateway_type("cloud")
|
|
846
|
+
logger.info("Switched to cloud gateway")
|
|
847
|
+
|
|
848
|
+
del gateways[gateway_id]
|
|
849
|
+
|
|
850
|
+
success = configPresenter.setSetting(configPresenter.KEY_CUSTOM_GATEWAYS, gateways)
|
|
851
|
+
|
|
852
|
+
if success:
|
|
853
|
+
logger.info(f"Deleted custom gateway: {gateway_id}")
|
|
854
|
+
else:
|
|
855
|
+
logger.error(f"Failed to delete custom gateway: {gateway_id}")
|
|
856
|
+
|
|
857
|
+
return success
|
|
858
|
+
|
|
859
|
+
@staticmethod
|
|
860
|
+
def get_custom_gateway(gateway_id: str):
|
|
861
|
+
"""
|
|
862
|
+
获取指定的自定义网关配置
|
|
863
|
+
|
|
864
|
+
Args:
|
|
865
|
+
gateway_id (str): 网关ID
|
|
866
|
+
|
|
867
|
+
Returns:
|
|
868
|
+
dict: 网关配置,如果不存在则返回None
|
|
869
|
+
"""
|
|
870
|
+
gateways = configPresenter.get_custom_gateways()
|
|
871
|
+
return gateways.get(gateway_id)
|
|
872
|
+
|
|
873
|
+
# ============ 应用设置配置管理 ============
|
|
874
|
+
|
|
875
|
+
KEY_APP_SETTINGS_HTTP_RULE = "app_settings_http_rule"
|
|
876
|
+
|
|
877
|
+
@staticmethod
|
|
878
|
+
def get_app_settings_http_rule():
|
|
879
|
+
"""
|
|
880
|
+
获取应用设置 - 允许局域网访问配置
|
|
881
|
+
|
|
882
|
+
Returns:
|
|
883
|
+
bool: True 允许局域网访问, False 不允许(默认)
|
|
884
|
+
"""
|
|
885
|
+
result = configPresenter.getSetting(configPresenter.KEY_APP_SETTINGS_HTTP_RULE)
|
|
886
|
+
if result is None:
|
|
887
|
+
return False
|
|
888
|
+
# 处理字符串类型的返回值
|
|
889
|
+
if isinstance(result, str):
|
|
890
|
+
return result.lower() == 'true'
|
|
891
|
+
return bool(result)
|
|
892
|
+
|
|
893
|
+
@staticmethod
|
|
894
|
+
def set_app_settings_http_rule(enabled: bool):
|
|
895
|
+
"""
|
|
896
|
+
设置应用设置 - 允许局域网访问配置
|
|
897
|
+
|
|
898
|
+
Args:
|
|
899
|
+
enabled (bool): True 允许局域网访问, False 不允许
|
|
900
|
+
|
|
901
|
+
Returns:
|
|
902
|
+
bool: 是否设置成功
|
|
903
|
+
"""
|
|
904
|
+
return configPresenter.setSetting(configPresenter.KEY_APP_SETTINGS_HTTP_RULE, enabled)
|
|
905
|
+
|
|
906
|
+
# ============ 网络代理配置管理 ============
|
|
907
|
+
|
|
908
|
+
# 类变量:内存缓存
|
|
909
|
+
_proxy_cache = None # None 表示未加载,True/False 表示配置值
|
|
910
|
+
KEY_USE_SYSTEM_PROXY = "use_system_proxy"
|
|
911
|
+
|
|
912
|
+
@staticmethod
|
|
913
|
+
def get_use_system_proxy():
|
|
914
|
+
"""
|
|
915
|
+
获取是否使用系统代理配置(优先从内存缓存读取)
|
|
916
|
+
|
|
917
|
+
Returns:
|
|
918
|
+
bool: True 表示使用系统代理, False 表示不使用(默认)
|
|
919
|
+
"""
|
|
920
|
+
# 优先从缓存读取
|
|
921
|
+
if configPresenter._proxy_cache is not None:
|
|
922
|
+
return configPresenter._proxy_cache
|
|
923
|
+
|
|
924
|
+
# 从硬盘加载
|
|
925
|
+
result = configPresenter.getSetting(configPresenter.KEY_USE_SYSTEM_PROXY)
|
|
926
|
+
if result is None:
|
|
927
|
+
value = False # 默认不使用代理
|
|
928
|
+
elif isinstance(result, str):
|
|
929
|
+
value = result.lower() == 'true'
|
|
930
|
+
else:
|
|
931
|
+
value = bool(result)
|
|
932
|
+
|
|
933
|
+
# 更新缓存
|
|
934
|
+
configPresenter._proxy_cache = value
|
|
935
|
+
return value
|
|
936
|
+
|
|
937
|
+
@staticmethod
|
|
938
|
+
def set_use_system_proxy(use_proxy: bool):
|
|
939
|
+
"""
|
|
940
|
+
设置是否使用系统代理配置(先更新内存,再写硬盘)
|
|
941
|
+
|
|
942
|
+
Args:
|
|
943
|
+
use_proxy (bool): True 表示使用系统代理, False 表示不使用
|
|
944
|
+
|
|
945
|
+
Returns:
|
|
946
|
+
bool: 是否设置成功
|
|
947
|
+
"""
|
|
948
|
+
# 1. 先更新内存缓存
|
|
949
|
+
configPresenter._proxy_cache = use_proxy
|
|
950
|
+
|
|
951
|
+
# 2. 再写硬盘
|
|
952
|
+
return configPresenter.setSetting(configPresenter.KEY_USE_SYSTEM_PROXY, use_proxy)
|
|
953
|
+
|
|
954
|
+
@staticmethod
|
|
955
|
+
def load_proxy_config():
|
|
956
|
+
"""
|
|
957
|
+
启动时加载代理配置到内存(可选调用,懒加载也可以)
|
|
958
|
+
|
|
959
|
+
Returns:
|
|
960
|
+
bool: 当前的代理配置值
|
|
961
|
+
"""
|
|
962
|
+
return configPresenter.get_use_system_proxy()
|
|
963
|
+
|
|
964
|
+
# ============ Tab-MCP 绑定配置管理 ============
|
|
965
|
+
|
|
966
|
+
KEY_TAB_MCP_BINDINGS = "tab_mcp_bindings"
|
|
967
|
+
|
|
968
|
+
# 默认Tab-MCP绑定配置(硬编码回退值)
|
|
969
|
+
# V1格式:字符串表示单个MCP名称
|
|
970
|
+
# 后续V2格式:对象可支持多MCP或SubAgent
|
|
971
|
+
DEFAULT_TAB_MCP_BINDINGS = {
|
|
972
|
+
"files": "Evol记忆",
|
|
973
|
+
"web": "Evol浏览器自动化",
|
|
974
|
+
"pdf": "PDF助手",
|
|
975
|
+
"excel": "Evol记忆",
|
|
976
|
+
"word": "Evol记忆",
|
|
977
|
+
"terminal": "Evol终端控制器",
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
@staticmethod
|
|
981
|
+
def get_tab_mcp_bindings():
|
|
982
|
+
"""
|
|
983
|
+
获取Tab-MCP绑定配置
|
|
984
|
+
|
|
985
|
+
Returns:
|
|
986
|
+
dict: Tab类型到MCP服务名称的映射,格式为 {tabType: mcpServerName}
|
|
987
|
+
"""
|
|
988
|
+
bindings = configPresenter.getSetting(configPresenter.KEY_TAB_MCP_BINDINGS)
|
|
989
|
+
if not bindings or not isinstance(bindings, dict):
|
|
990
|
+
# 返回默认配置
|
|
991
|
+
return dict(configPresenter.DEFAULT_TAB_MCP_BINDINGS)
|
|
992
|
+
# 合并默认值,确保新增的tabType有默认配置
|
|
993
|
+
return {**configPresenter.DEFAULT_TAB_MCP_BINDINGS, **bindings}
|
|
994
|
+
|
|
995
|
+
@staticmethod
|
|
996
|
+
def set_tab_mcp_bindings(bindings: dict):
|
|
997
|
+
"""
|
|
998
|
+
设置Tab-MCP绑定配置
|
|
999
|
+
|
|
1000
|
+
Args:
|
|
1001
|
+
bindings (dict): Tab类型到MCP服务名称的映射
|
|
1002
|
+
|
|
1003
|
+
Returns:
|
|
1004
|
+
bool: 是否设置成功
|
|
1005
|
+
"""
|
|
1006
|
+
return configPresenter.setSetting(configPresenter.KEY_TAB_MCP_BINDINGS, bindings)
|
|
1007
|
+
|
|
1008
|
+
@staticmethod
|
|
1009
|
+
def get_mcp_for_tab_type(tab_type: str):
|
|
1010
|
+
"""
|
|
1011
|
+
获取指定Tab类型的默认MCP服务名称
|
|
1012
|
+
|
|
1013
|
+
Args:
|
|
1014
|
+
tab_type (str): Tab类型
|
|
1015
|
+
|
|
1016
|
+
Returns:
|
|
1017
|
+
str: MCP服务名称,如果未配置返回空字符串
|
|
1018
|
+
"""
|
|
1019
|
+
bindings = configPresenter.get_tab_mcp_bindings()
|
|
1020
|
+
return bindings.get(tab_type, "")
|
|
1021
|
+
|
|
1022
|
+
@staticmethod
|
|
1023
|
+
def set_mcp_for_tab_type(tab_type: str, mcp_server_name: str):
|
|
1024
|
+
"""
|
|
1025
|
+
设置指定Tab类型的MCP服务
|
|
1026
|
+
|
|
1027
|
+
Args:
|
|
1028
|
+
tab_type (str): Tab类型
|
|
1029
|
+
mcp_server_name (str): MCP服务名称,空字符串表示重置为默认值
|
|
1030
|
+
|
|
1031
|
+
Returns:
|
|
1032
|
+
bool: 是否设置成功
|
|
1033
|
+
"""
|
|
1034
|
+
bindings = configPresenter.get_tab_mcp_bindings()
|
|
1035
|
+
|
|
1036
|
+
if mcp_server_name == '':
|
|
1037
|
+
# 空字符串表示重置:删除该key,让默认值生效
|
|
1038
|
+
bindings.pop(tab_type, None)
|
|
1039
|
+
else:
|
|
1040
|
+
# 设置用户自定义的MCP服务
|
|
1041
|
+
bindings[tab_type] = mcp_server_name
|
|
1042
|
+
|
|
1043
|
+
return configPresenter.set_tab_mcp_bindings(bindings)
|
|
1044
|
+
|
|
1045
|
+
# ============ 远程访问控制配置管理 ============
|
|
1046
|
+
|
|
1047
|
+
KEY_REMOTE_ACCESS_ENABLED = "remote_access_enabled"
|
|
1048
|
+
KEY_DISABLED_PAIRING_CODES = "disabled_pairing_codes"
|
|
1049
|
+
|
|
1050
|
+
@staticmethod
|
|
1051
|
+
def get_remote_access_enabled():
|
|
1052
|
+
"""
|
|
1053
|
+
获取远程访问总开关状态
|
|
1054
|
+
|
|
1055
|
+
Returns:
|
|
1056
|
+
bool: True 允许远程访问, False 禁止远程访问(默认)
|
|
1057
|
+
"""
|
|
1058
|
+
result = configPresenter.getSetting(configPresenter.KEY_REMOTE_ACCESS_ENABLED)
|
|
1059
|
+
if result is None:
|
|
1060
|
+
return False
|
|
1061
|
+
if isinstance(result, str):
|
|
1062
|
+
return result.lower() == 'true'
|
|
1063
|
+
return bool(result)
|
|
1064
|
+
|
|
1065
|
+
@staticmethod
|
|
1066
|
+
def set_remote_access_enabled(enabled: bool):
|
|
1067
|
+
"""
|
|
1068
|
+
设置远程访问总开关
|
|
1069
|
+
|
|
1070
|
+
Args:
|
|
1071
|
+
enabled (bool): True 允许远程访问, False 禁止远程访问
|
|
1072
|
+
|
|
1073
|
+
Returns:
|
|
1074
|
+
bool: 是否设置成功
|
|
1075
|
+
"""
|
|
1076
|
+
return configPresenter.setSetting(configPresenter.KEY_REMOTE_ACCESS_ENABLED, enabled)
|
|
1077
|
+
|
|
1078
|
+
@staticmethod
|
|
1079
|
+
def get_disabled_pairing_codes():
|
|
1080
|
+
"""
|
|
1081
|
+
获取禁用的配对码列表
|
|
1082
|
+
|
|
1083
|
+
Returns:
|
|
1084
|
+
list: 禁用的配对码列表
|
|
1085
|
+
"""
|
|
1086
|
+
result = configPresenter.getSetting(configPresenter.KEY_DISABLED_PAIRING_CODES)
|
|
1087
|
+
if result is None:
|
|
1088
|
+
return []
|
|
1089
|
+
if isinstance(result, str):
|
|
1090
|
+
# 支持逗号分隔的字符串格式
|
|
1091
|
+
if not result.strip():
|
|
1092
|
+
return []
|
|
1093
|
+
return [code.strip() for code in result.split(',') if code.strip()]
|
|
1094
|
+
if isinstance(result, list):
|
|
1095
|
+
return result
|
|
1096
|
+
return []
|
|
1097
|
+
|
|
1098
|
+
@staticmethod
|
|
1099
|
+
def set_disabled_pairing_codes(codes: list):
|
|
1100
|
+
"""
|
|
1101
|
+
设置禁用的配对码列表
|
|
1102
|
+
|
|
1103
|
+
Args:
|
|
1104
|
+
codes (list): 禁用的配对码列表
|
|
1105
|
+
|
|
1106
|
+
Returns:
|
|
1107
|
+
bool: 是否设置成功
|
|
1108
|
+
"""
|
|
1109
|
+
return configPresenter.setSetting(configPresenter.KEY_DISABLED_PAIRING_CODES, codes)
|
|
1110
|
+
|
|
1111
|
+
@staticmethod
|
|
1112
|
+
def add_disabled_pairing_code(code: str):
|
|
1113
|
+
"""
|
|
1114
|
+
添加一个禁用的配对码
|
|
1115
|
+
|
|
1116
|
+
Args:
|
|
1117
|
+
code (str): 要禁用的配对码
|
|
1118
|
+
|
|
1119
|
+
Returns:
|
|
1120
|
+
bool: 是否添加成功
|
|
1121
|
+
"""
|
|
1122
|
+
if not code or not code.strip():
|
|
1123
|
+
return False
|
|
1124
|
+
codes = configPresenter.get_disabled_pairing_codes()
|
|
1125
|
+
code = code.strip()
|
|
1126
|
+
if code not in codes:
|
|
1127
|
+
codes.append(code)
|
|
1128
|
+
return configPresenter.set_disabled_pairing_codes(codes)
|
|
1129
|
+
return True
|
|
1130
|
+
|
|
1131
|
+
@staticmethod
|
|
1132
|
+
def remove_disabled_pairing_code(code: str):
|
|
1133
|
+
"""
|
|
1134
|
+
移除一个禁用的配对码
|
|
1135
|
+
|
|
1136
|
+
Args:
|
|
1137
|
+
code (str): 要移除的配对码
|
|
1138
|
+
|
|
1139
|
+
Returns:
|
|
1140
|
+
bool: 是否移除成功
|
|
1141
|
+
"""
|
|
1142
|
+
if not code or not code.strip():
|
|
1143
|
+
return False
|
|
1144
|
+
codes = configPresenter.get_disabled_pairing_codes()
|
|
1145
|
+
code = code.strip()
|
|
1146
|
+
if code in codes:
|
|
1147
|
+
codes.remove(code)
|
|
1148
|
+
return configPresenter.set_disabled_pairing_codes(codes)
|
|
1149
|
+
return True
|
|
1150
|
+
|
|
1151
|
+
@staticmethod
|
|
1152
|
+
def is_pairing_code_disabled(code: str):
|
|
1153
|
+
"""
|
|
1154
|
+
检查配对码是否被禁用
|
|
1155
|
+
|
|
1156
|
+
Args:
|
|
1157
|
+
code (str): 配对码
|
|
1158
|
+
|
|
1159
|
+
Returns:
|
|
1160
|
+
bool: True 表示被禁用, False 表示未被禁用
|
|
1161
|
+
"""
|
|
1162
|
+
if not code or not code.strip():
|
|
1163
|
+
return False
|
|
1164
|
+
codes = configPresenter.get_disabled_pairing_codes()
|
|
1165
|
+
return code.strip() in codes
|
|
1166
|
+
|
|
1167
|
+
@staticmethod
|
|
1168
|
+
def get_remote_access_config():
|
|
1169
|
+
"""
|
|
1170
|
+
获取远程访问配置(总开关 + 禁用的配对码)
|
|
1171
|
+
|
|
1172
|
+
Returns:
|
|
1173
|
+
dict: {
|
|
1174
|
+
"enabled": bool,
|
|
1175
|
+
"disabled_codes": list
|
|
1176
|
+
}
|
|
1177
|
+
"""
|
|
1178
|
+
return {
|
|
1179
|
+
"enabled": configPresenter.get_remote_access_enabled(),
|
|
1180
|
+
"disabled_codes": configPresenter.get_disabled_pairing_codes()
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
@staticmethod
|
|
1184
|
+
def set_remote_access_config(enabled: bool = None, disabled_codes: list = None):
|
|
1185
|
+
"""
|
|
1186
|
+
设置远程访问配置
|
|
1187
|
+
|
|
1188
|
+
Args:
|
|
1189
|
+
enabled (bool): 总开关状态(可选)
|
|
1190
|
+
disabled_codes (list): 禁用的配对码列表(可选)
|
|
1191
|
+
|
|
1192
|
+
Returns:
|
|
1193
|
+
dict: {"success": bool, "message": str}
|
|
1194
|
+
"""
|
|
1195
|
+
try:
|
|
1196
|
+
if enabled is not None:
|
|
1197
|
+
configPresenter.set_remote_access_enabled(enabled)
|
|
1198
|
+
if disabled_codes is not None:
|
|
1199
|
+
configPresenter.set_disabled_pairing_codes(disabled_codes)
|
|
1200
|
+
return {"success": True, "message": "配置已更新"}
|
|
1201
|
+
except Exception as e:
|
|
1202
|
+
return {"success": False, "message": str(e)}
|
|
1203
|
+
|
|
1204
|
+
# ============ 用户默认系统提示词配置管理 ============
|
|
1205
|
+
|
|
1206
|
+
KEY_USER_DEFAULT_SYSTEM_PROMPT = "user_default_system_prompt"
|
|
1207
|
+
|
|
1208
|
+
@staticmethod
|
|
1209
|
+
def get_user_default_system_prompt(workspace_name: str):
|
|
1210
|
+
"""
|
|
1211
|
+
获取用户默认系统提示词(workspace级别)
|
|
1212
|
+
|
|
1213
|
+
Args:
|
|
1214
|
+
workspace_name (str): 任务空间名称
|
|
1215
|
+
|
|
1216
|
+
Returns:
|
|
1217
|
+
dict: 提示词数据字典 {'userCustom': str, 'version': str},如果未设置返回默认值
|
|
1218
|
+
"""
|
|
1219
|
+
try:
|
|
1220
|
+
prompt_data = configPresenter.getSetting(
|
|
1221
|
+
configPresenter.KEY_USER_DEFAULT_SYSTEM_PROMPT,
|
|
1222
|
+
workspace_name
|
|
1223
|
+
)
|
|
1224
|
+
|
|
1225
|
+
if not prompt_data:
|
|
1226
|
+
# 返回默认值
|
|
1227
|
+
return {'userCustom': '', 'version': '1.0'}
|
|
1228
|
+
|
|
1229
|
+
# 兼容旧格式(字符串)
|
|
1230
|
+
if isinstance(prompt_data, str):
|
|
1231
|
+
try:
|
|
1232
|
+
return json.loads(prompt_data)
|
|
1233
|
+
except json.JSONDecodeError:
|
|
1234
|
+
# 如果不是JSON,当作纯文本处理
|
|
1235
|
+
return {'userCustom': prompt_data, 'version': '1.0'}
|
|
1236
|
+
|
|
1237
|
+
# 新格式(字典)
|
|
1238
|
+
return prompt_data
|
|
1239
|
+
except Exception as e:
|
|
1240
|
+
logger.exception(f"获取用户默认系统提示词失败: {e}")
|
|
1241
|
+
return {'userCustom': '', 'version': '1.0'}
|
|
1242
|
+
|
|
1243
|
+
@staticmethod
|
|
1244
|
+
def set_user_default_system_prompt(workspace_name: str, prompt_data: dict):
|
|
1245
|
+
"""
|
|
1246
|
+
设置用户默认系统提示词(workspace级别)
|
|
1247
|
+
|
|
1248
|
+
Args:
|
|
1249
|
+
workspace_name (str): 任务空间名称
|
|
1250
|
+
prompt_data (dict): 提示词数据 {'userCustom': str, 'version': str}
|
|
1251
|
+
|
|
1252
|
+
Returns:
|
|
1253
|
+
bool: 是否设置成功
|
|
1254
|
+
"""
|
|
1255
|
+
try:
|
|
1256
|
+
if not isinstance(prompt_data, dict):
|
|
1257
|
+
logger.error(f"prompt_data必须是字典类型,当前类型: {type(prompt_data)}")
|
|
1258
|
+
return False
|
|
1259
|
+
|
|
1260
|
+
# 确保包含必要字段
|
|
1261
|
+
if 'userCustom' not in prompt_data:
|
|
1262
|
+
prompt_data['userCustom'] = ''
|
|
1263
|
+
if 'version' not in prompt_data:
|
|
1264
|
+
prompt_data['version'] = '1.0'
|
|
1265
|
+
|
|
1266
|
+
success = configPresenter.setSetting(
|
|
1267
|
+
configPresenter.KEY_USER_DEFAULT_SYSTEM_PROMPT,
|
|
1268
|
+
prompt_data,
|
|
1269
|
+
workspace_name
|
|
1270
|
+
)
|
|
1271
|
+
|
|
1272
|
+
if success:
|
|
1273
|
+
logger.info(f"已保存用户默认系统提示词到workspace: {workspace_name}")
|
|
1274
|
+
else:
|
|
1275
|
+
logger.error(f"保存用户默认系统提示词失败: {workspace_name}")
|
|
1276
|
+
|
|
1277
|
+
return success
|
|
1278
|
+
except Exception as e:
|
|
1279
|
+
logger.exception(f"设置用户默认系统提示词失败: {e}")
|
|
1280
|
+
return False
|
|
1281
|
+
|