open-swarm 0.1.1745274976__py3-none-any.whl → 0.1.1748636259__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.
- open_swarm-0.1.1748636259.dist-info/METADATA +188 -0
- open_swarm-0.1.1748636259.dist-info/RECORD +82 -0
- {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1748636259.dist-info}/WHEEL +2 -1
- open_swarm-0.1.1748636259.dist-info/entry_points.txt +3 -0
- open_swarm-0.1.1748636259.dist-info/top_level.txt +1 -0
- swarm/agent/agent.py +49 -0
- swarm/auth.py +48 -113
- swarm/consumers.py +0 -19
- swarm/extensions/blueprint/__init__.py +16 -30
- swarm/{core → extensions/blueprint}/agent_utils.py +1 -1
- swarm/extensions/blueprint/blueprint_base.py +458 -0
- swarm/extensions/blueprint/blueprint_discovery.py +112 -0
- swarm/extensions/blueprint/output_utils.py +95 -0
- swarm/{core → extensions/blueprint}/spinner.py +21 -30
- swarm/extensions/cli/cli_args.py +0 -6
- swarm/extensions/cli/commands/blueprint_management.py +9 -47
- swarm/extensions/cli/commands/config_management.py +6 -5
- swarm/extensions/cli/commands/edit_config.py +7 -16
- swarm/extensions/cli/commands/list_blueprints.py +1 -1
- swarm/extensions/cli/commands/validate_env.py +4 -11
- swarm/extensions/cli/commands/validate_envvars.py +6 -6
- swarm/extensions/cli/interactive_shell.py +2 -16
- swarm/extensions/config/config_loader.py +201 -107
- swarm/{core → extensions/config}/config_manager.py +38 -50
- swarm/{core → extensions/config}/server_config.py +0 -32
- swarm/extensions/launchers/build_launchers.py +14 -0
- swarm/{core → extensions/launchers}/build_swarm_wrapper.py +0 -0
- swarm/extensions/launchers/swarm_api.py +64 -8
- swarm/extensions/launchers/swarm_cli.py +300 -8
- swarm/llm/chat_completion.py +195 -0
- swarm/serializers.py +5 -96
- swarm/settings.py +111 -99
- swarm/urls.py +74 -57
- swarm/utils/context_utils.py +4 -10
- swarm/utils/general_utils.py +0 -21
- swarm/utils/redact.py +36 -23
- swarm/views/api_views.py +39 -48
- swarm/views/chat_views.py +70 -237
- swarm/views/core_views.py +87 -80
- swarm/views/model_views.py +121 -64
- swarm/views/utils.py +441 -65
- swarm/views/web_views.py +2 -2
- open_swarm-0.1.1745274976.dist-info/METADATA +0 -874
- open_swarm-0.1.1745274976.dist-info/RECORD +0 -318
- open_swarm-0.1.1745274976.dist-info/entry_points.txt +0 -4
- swarm/blueprints/README.md +0 -68
- swarm/blueprints/blueprint_audit_status.json +0 -27
- swarm/blueprints/chatbot/README.md +0 -40
- swarm/blueprints/chatbot/blueprint_chatbot.py +0 -471
- swarm/blueprints/chatbot/metadata.json +0 -23
- swarm/blueprints/chatbot/templates/chatbot/chatbot.html +0 -33
- swarm/blueprints/chucks_angels/README.md +0 -11
- swarm/blueprints/chucks_angels/blueprint_chucks_angels.py +0 -7
- swarm/blueprints/chucks_angels/test_basic.py +0 -3
- swarm/blueprints/codey/CODEY.md +0 -15
- swarm/blueprints/codey/README.md +0 -115
- swarm/blueprints/codey/blueprint_codey.py +0 -1072
- swarm/blueprints/codey/codey_cli.py +0 -373
- swarm/blueprints/codey/instructions.md +0 -17
- swarm/blueprints/codey/metadata.json +0 -23
- swarm/blueprints/common/operation_box_utils.py +0 -83
- swarm/blueprints/digitalbutlers/README.md +0 -11
- swarm/blueprints/digitalbutlers/__init__.py +0 -1
- swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +0 -7
- swarm/blueprints/digitalbutlers/test_basic.py +0 -3
- swarm/blueprints/divine_code/README.md +0 -3
- swarm/blueprints/divine_code/__init__.py +0 -10
- swarm/blueprints/divine_code/apps.py +0 -11
- swarm/blueprints/divine_code/blueprint_divine_code.py +0 -270
- swarm/blueprints/django_chat/apps.py +0 -6
- swarm/blueprints/django_chat/blueprint_django_chat.py +0 -268
- swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +0 -37
- swarm/blueprints/django_chat/urls.py +0 -8
- swarm/blueprints/django_chat/views.py +0 -32
- swarm/blueprints/echocraft/blueprint_echocraft.py +0 -384
- swarm/blueprints/flock/README.md +0 -11
- swarm/blueprints/flock/__init__.py +0 -8
- swarm/blueprints/flock/blueprint_flock.py +0 -7
- swarm/blueprints/flock/test_basic.py +0 -3
- swarm/blueprints/geese/README.md +0 -97
- swarm/blueprints/geese/blueprint_geese.py +0 -803
- swarm/blueprints/geese/geese_cli.py +0 -102
- swarm/blueprints/jeeves/README.md +0 -41
- swarm/blueprints/jeeves/blueprint_jeeves.py +0 -722
- swarm/blueprints/jeeves/jeeves_cli.py +0 -55
- swarm/blueprints/jeeves/metadata.json +0 -24
- swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +0 -473
- swarm/blueprints/messenger/templates/messenger/messenger.html +0 -46
- swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +0 -423
- swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +0 -340
- swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +0 -265
- swarm/blueprints/omniplex/blueprint_omniplex.py +0 -298
- swarm/blueprints/poets/blueprint_poets.py +0 -546
- swarm/blueprints/poets/poets_cli.py +0 -23
- swarm/blueprints/rue_code/README.md +0 -8
- swarm/blueprints/rue_code/blueprint_rue_code.py +0 -448
- swarm/blueprints/rue_code/rue_code_cli.py +0 -43
- swarm/blueprints/stewie/apps.py +0 -12
- swarm/blueprints/stewie/blueprint_family_ties.py +0 -349
- swarm/blueprints/stewie/models.py +0 -19
- swarm/blueprints/stewie/serializers.py +0 -10
- swarm/blueprints/stewie/settings.py +0 -17
- swarm/blueprints/stewie/urls.py +0 -11
- swarm/blueprints/stewie/views.py +0 -26
- swarm/blueprints/suggestion/blueprint_suggestion.py +0 -222
- swarm/blueprints/whinge_surf/README.md +0 -22
- swarm/blueprints/whinge_surf/__init__.py +0 -1
- swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +0 -565
- swarm/blueprints/whinge_surf/whinge_surf_cli.py +0 -99
- swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
- swarm/blueprints/whiskeytango_foxtrot/apps.py +0 -11
- swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +0 -339
- swarm/blueprints/zeus/__init__.py +0 -2
- swarm/blueprints/zeus/apps.py +0 -4
- swarm/blueprints/zeus/blueprint_zeus.py +0 -270
- swarm/blueprints/zeus/zeus_cli.py +0 -13
- swarm/cli/async_input.py +0 -65
- swarm/cli/async_input_demo.py +0 -32
- swarm/core/blueprint_base.py +0 -769
- swarm/core/blueprint_discovery.py +0 -125
- swarm/core/blueprint_runner.py +0 -59
- swarm/core/blueprint_ux.py +0 -109
- swarm/core/build_launchers.py +0 -15
- swarm/core/cli/__init__.py +0 -1
- swarm/core/cli/commands/__init__.py +0 -1
- swarm/core/cli/commands/blueprint_management.py +0 -7
- swarm/core/cli/interactive_shell.py +0 -14
- swarm/core/cli/main.py +0 -50
- swarm/core/cli/utils/__init__.py +0 -1
- swarm/core/cli/utils/discover_commands.py +0 -18
- swarm/core/config_loader.py +0 -122
- swarm/core/output_utils.py +0 -193
- swarm/core/session_logger.py +0 -42
- swarm/core/slash_commands.py +0 -89
- swarm/core/swarm_api.py +0 -68
- swarm/core/swarm_cli.py +0 -216
- swarm/core/utils/__init__.py +0 -0
- swarm/extensions/blueprint/cli_handler.py +0 -197
- swarm/extensions/blueprint/runnable_blueprint.py +0 -42
- swarm/extensions/cli/utils/__init__.py +0 -1
- swarm/extensions/cli/utils/async_input.py +0 -46
- swarm/extensions/cli/utils/prompt_user.py +0 -3
- swarm/management/__init__.py +0 -0
- swarm/management/commands/__init__.py +0 -0
- swarm/management/commands/runserver.py +0 -58
- swarm/middleware.py +0 -65
- swarm/permissions.py +0 -38
- swarm/static/contrib/fonts/fontawesome-webfont.ttf +0 -7
- swarm/static/contrib/fonts/fontawesome-webfont.woff +0 -7
- swarm/static/contrib/fonts/fontawesome-webfont.woff2 +0 -7
- swarm/static/contrib/markedjs/marked.min.js +0 -6
- swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +0 -27
- swarm/static/contrib/tabler-icons/alert-triangle.svg +0 -21
- swarm/static/contrib/tabler-icons/archive.svg +0 -21
- swarm/static/contrib/tabler-icons/artboard.svg +0 -27
- swarm/static/contrib/tabler-icons/automatic-gearbox.svg +0 -23
- swarm/static/contrib/tabler-icons/box-multiple.svg +0 -19
- swarm/static/contrib/tabler-icons/carambola.svg +0 -19
- swarm/static/contrib/tabler-icons/copy.svg +0 -20
- swarm/static/contrib/tabler-icons/download.svg +0 -21
- swarm/static/contrib/tabler-icons/edit.svg +0 -21
- swarm/static/contrib/tabler-icons/filled/carambola.svg +0 -13
- swarm/static/contrib/tabler-icons/filled/paint.svg +0 -13
- swarm/static/contrib/tabler-icons/headset.svg +0 -22
- swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +0 -21
- swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +0 -21
- swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +0 -21
- swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +0 -21
- swarm/static/contrib/tabler-icons/message-chatbot.svg +0 -22
- swarm/static/contrib/tabler-icons/message-star.svg +0 -22
- swarm/static/contrib/tabler-icons/message-x.svg +0 -23
- swarm/static/contrib/tabler-icons/message.svg +0 -21
- swarm/static/contrib/tabler-icons/paperclip.svg +0 -18
- swarm/static/contrib/tabler-icons/playlist-add.svg +0 -22
- swarm/static/contrib/tabler-icons/robot.svg +0 -26
- swarm/static/contrib/tabler-icons/search.svg +0 -19
- swarm/static/contrib/tabler-icons/settings.svg +0 -20
- swarm/static/contrib/tabler-icons/thumb-down.svg +0 -19
- swarm/static/contrib/tabler-icons/thumb-up.svg +0 -19
- swarm/static/css/dropdown.css +0 -22
- swarm/static/htmx/htmx.min.js +0 -0
- swarm/static/js/dropdown.js +0 -23
- swarm/static/rest_mode/css/base.css +0 -470
- swarm/static/rest_mode/css/chat-history.css +0 -286
- swarm/static/rest_mode/css/chat.css +0 -251
- swarm/static/rest_mode/css/chatbot.css +0 -74
- swarm/static/rest_mode/css/chatgpt.css +0 -62
- swarm/static/rest_mode/css/colors/corporate.css +0 -74
- swarm/static/rest_mode/css/colors/pastel.css +0 -81
- swarm/static/rest_mode/css/colors/tropical.css +0 -82
- swarm/static/rest_mode/css/general.css +0 -142
- swarm/static/rest_mode/css/layout.css +0 -167
- swarm/static/rest_mode/css/layouts/messenger-layout.css +0 -17
- swarm/static/rest_mode/css/layouts/minimalist-layout.css +0 -57
- swarm/static/rest_mode/css/layouts/mobile-layout.css +0 -8
- swarm/static/rest_mode/css/messages.css +0 -84
- swarm/static/rest_mode/css/messenger.css +0 -135
- swarm/static/rest_mode/css/settings.css +0 -91
- swarm/static/rest_mode/css/simple.css +0 -44
- swarm/static/rest_mode/css/slack.css +0 -58
- swarm/static/rest_mode/css/style.css +0 -156
- swarm/static/rest_mode/css/theme.css +0 -30
- swarm/static/rest_mode/css/toast.css +0 -40
- swarm/static/rest_mode/js/auth.js +0 -9
- swarm/static/rest_mode/js/blueprint.js +0 -41
- swarm/static/rest_mode/js/blueprintUtils.js +0 -12
- swarm/static/rest_mode/js/chatLogic.js +0 -79
- swarm/static/rest_mode/js/debug.js +0 -63
- swarm/static/rest_mode/js/events.js +0 -98
- swarm/static/rest_mode/js/main.js +0 -19
- swarm/static/rest_mode/js/messages.js +0 -264
- swarm/static/rest_mode/js/messengerLogic.js +0 -355
- swarm/static/rest_mode/js/modules/apiService.js +0 -84
- swarm/static/rest_mode/js/modules/blueprintManager.js +0 -162
- swarm/static/rest_mode/js/modules/chatHistory.js +0 -110
- swarm/static/rest_mode/js/modules/debugLogger.js +0 -14
- swarm/static/rest_mode/js/modules/eventHandlers.js +0 -107
- swarm/static/rest_mode/js/modules/messageProcessor.js +0 -120
- swarm/static/rest_mode/js/modules/state.js +0 -7
- swarm/static/rest_mode/js/modules/userInteractions.js +0 -29
- swarm/static/rest_mode/js/modules/validation.js +0 -23
- swarm/static/rest_mode/js/rendering.js +0 -119
- swarm/static/rest_mode/js/settings.js +0 -130
- swarm/static/rest_mode/js/sidebar.js +0 -94
- swarm/static/rest_mode/js/simpleLogic.js +0 -37
- swarm/static/rest_mode/js/slackLogic.js +0 -66
- swarm/static/rest_mode/js/splash.js +0 -76
- swarm/static/rest_mode/js/theme.js +0 -111
- swarm/static/rest_mode/js/toast.js +0 -36
- swarm/static/rest_mode/js/ui.js +0 -265
- swarm/static/rest_mode/js/validation.js +0 -57
- swarm/static/rest_mode/svg/animated_spinner.svg +0 -12
- swarm/static/rest_mode/svg/arrow_down.svg +0 -5
- swarm/static/rest_mode/svg/arrow_left.svg +0 -5
- swarm/static/rest_mode/svg/arrow_right.svg +0 -5
- swarm/static/rest_mode/svg/arrow_up.svg +0 -5
- swarm/static/rest_mode/svg/attach.svg +0 -8
- swarm/static/rest_mode/svg/avatar.svg +0 -7
- swarm/static/rest_mode/svg/canvas.svg +0 -6
- swarm/static/rest_mode/svg/chat_history.svg +0 -4
- swarm/static/rest_mode/svg/close.svg +0 -5
- swarm/static/rest_mode/svg/copy.svg +0 -4
- swarm/static/rest_mode/svg/dark_mode.svg +0 -3
- swarm/static/rest_mode/svg/edit.svg +0 -5
- swarm/static/rest_mode/svg/layout.svg +0 -9
- swarm/static/rest_mode/svg/logo.svg +0 -29
- swarm/static/rest_mode/svg/logout.svg +0 -5
- swarm/static/rest_mode/svg/mobile.svg +0 -5
- swarm/static/rest_mode/svg/new_chat.svg +0 -4
- swarm/static/rest_mode/svg/not_visible.svg +0 -5
- swarm/static/rest_mode/svg/plus.svg +0 -7
- swarm/static/rest_mode/svg/run_code.svg +0 -6
- swarm/static/rest_mode/svg/save.svg +0 -4
- swarm/static/rest_mode/svg/search.svg +0 -6
- swarm/static/rest_mode/svg/settings.svg +0 -4
- swarm/static/rest_mode/svg/speaker.svg +0 -5
- swarm/static/rest_mode/svg/stop.svg +0 -6
- swarm/static/rest_mode/svg/thumbs_down.svg +0 -3
- swarm/static/rest_mode/svg/thumbs_up.svg +0 -3
- swarm/static/rest_mode/svg/toggle_off.svg +0 -6
- swarm/static/rest_mode/svg/toggle_on.svg +0 -6
- swarm/static/rest_mode/svg/trash.svg +0 -10
- swarm/static/rest_mode/svg/undo.svg +0 -3
- swarm/static/rest_mode/svg/visible.svg +0 -8
- swarm/static/rest_mode/svg/voice.svg +0 -10
- swarm/templates/account/login.html +0 -22
- swarm/templates/account/signup.html +0 -32
- swarm/templates/base.html +0 -30
- swarm/templates/chat.html +0 -43
- swarm/templates/index.html +0 -35
- swarm/templates/rest_mode/components/chat_sidebar.html +0 -55
- swarm/templates/rest_mode/components/header.html +0 -45
- swarm/templates/rest_mode/components/main_chat_pane.html +0 -41
- swarm/templates/rest_mode/components/settings_dialog.html +0 -97
- swarm/templates/rest_mode/components/splash_screen.html +0 -7
- swarm/templates/rest_mode/components/top_bar.html +0 -28
- swarm/templates/rest_mode/message_ui.html +0 -50
- swarm/templates/rest_mode/slackbot.html +0 -30
- swarm/templates/simple_blueprint_page.html +0 -24
- swarm/templates/websocket_partials/final_system_message.html +0 -3
- swarm/templates/websocket_partials/system_message.html +0 -4
- swarm/templates/websocket_partials/user_message.html +0 -5
- swarm/utils/ansi_box.py +0 -34
- swarm/utils/disable_tracing.py +0 -38
- swarm/utils/log_utils.py +0 -63
- swarm/utils/openai_patch.py +0 -33
- swarm/ux/ansi_box.py +0 -43
- swarm/ux/spinner.py +0 -53
- {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1748636259.dist-info}/licenses/LICENSE +0 -0
- /swarm/{core → extensions/blueprint}/blueprint_utils.py +0 -0
- /swarm/{core → extensions/blueprint}/common_utils.py +0 -0
- /swarm/{core → extensions/config}/setup_wizard.py +0 -0
- /swarm/{blueprints/rue_code → extensions/config/utils}/__init__.py +0 -0
- /swarm/{core → extensions/config}/utils/logger.py +0 -0
- /swarm/{core → extensions/launchers}/swarm_wrapper.py +0 -0
@@ -1,114 +1,208 @@
|
|
1
|
-
|
1
|
+
"""
|
2
|
+
Configuration Loader for Open Swarm MCP Framework.
|
3
|
+
"""
|
4
|
+
|
2
5
|
import os
|
3
|
-
|
6
|
+
import json
|
7
|
+
import re
|
4
8
|
import logging
|
5
|
-
from typing import Dict,
|
9
|
+
from typing import Any, Dict, List, Tuple, Optional
|
10
|
+
from pathlib import Path
|
11
|
+
from dotenv import load_dotenv
|
12
|
+
try: from .server_config import save_server_config
|
13
|
+
except ImportError: save_server_config = None
|
14
|
+
|
15
|
+
SWARM_DEBUG = os.getenv("SWARM_DEBUG", "False").lower() in ("true", "1", "yes")
|
16
|
+
try: from swarm.settings import BASE_DIR
|
17
|
+
except ImportError: BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
18
|
+
|
19
|
+
from swarm.utils.redact import redact_sensitive_data
|
6
20
|
|
7
21
|
logger = logging.getLogger(__name__)
|
22
|
+
logger.setLevel(logging.DEBUG if SWARM_DEBUG else logging.INFO)
|
8
23
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
if
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
current = start_dir.resolve()
|
26
|
-
while current != current.parent:
|
27
|
-
if (cp := current / DEFAULT_CONFIG_FILENAME).is_file():
|
28
|
-
logger.debug(f"Found config upwards: {cp}")
|
29
|
-
return cp.resolve()
|
30
|
-
current = current.parent
|
31
|
-
if (cp := current / DEFAULT_CONFIG_FILENAME).is_file():
|
32
|
-
logger.debug(f"Found config at root: {cp}")
|
33
|
-
return cp.resolve()
|
34
|
-
# 4. Default dir
|
35
|
-
if default_dir and (cp := default_dir.resolve() / DEFAULT_CONFIG_FILENAME).is_file():
|
36
|
-
logger.debug(f"Found config default: {cp}")
|
37
|
-
return cp.resolve()
|
38
|
-
# 5. CWD
|
39
|
-
cwd = Path.cwd()
|
40
|
-
if start_dir is None or cwd != start_dir.resolve():
|
41
|
-
if (cp := cwd / DEFAULT_CONFIG_FILENAME).is_file():
|
42
|
-
logger.debug(f"Found config cwd: {cp}")
|
43
|
-
return cp.resolve()
|
44
|
-
logger.debug(f"Config '{DEFAULT_CONFIG_FILENAME}' not found.")
|
45
|
-
return None
|
46
|
-
|
47
|
-
def load_config(config_path: Path) -> Dict[str, Any]:
|
48
|
-
logger.debug(f"Loading config from {config_path}")
|
24
|
+
# Add handler only if needed, DO NOT set handler level conditionally here
|
25
|
+
if not logger.handlers and not logging.getLogger().hasHandlers():
|
26
|
+
stream_handler = logging.StreamHandler()
|
27
|
+
formatter = logging.Formatter("[%(levelname)s] %(asctime)s - %(name)s:%(lineno)d - %(message)s")
|
28
|
+
stream_handler.setFormatter(formatter)
|
29
|
+
logger.addHandler(stream_handler)
|
30
|
+
# --- REMOVED CONDITIONAL HANDLER LEVEL SETTING ---
|
31
|
+
# if not SWARM_DEBUG:
|
32
|
+
# stream_handler.setLevel(logging.WARNING)
|
33
|
+
|
34
|
+
config: Dict[str, Any] = {}
|
35
|
+
load_dotenv()
|
36
|
+
logger.debug("Environment variables potentially loaded from .env file.")
|
37
|
+
|
38
|
+
def process_config(config_dict: dict) -> dict:
|
39
|
+
"""Processes config: resolves placeholders, merges external MCP."""
|
49
40
|
try:
|
50
|
-
|
51
|
-
logger.
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
return
|
73
|
-
|
74
|
-
def
|
75
|
-
|
76
|
-
if isinstance(
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
41
|
+
resolved_config = resolve_placeholders(config_dict)
|
42
|
+
if logger.isEnabledFor(logging.DEBUG): logger.debug("Config after resolving placeholders: " + json.dumps(redact_sensitive_data(resolved_config), indent=2))
|
43
|
+
disable_merge = os.getenv("DISABLE_MCP_MERGE", "false").lower() in ("true", "1", "yes")
|
44
|
+
if not disable_merge:
|
45
|
+
if os.name == "nt": external_mcp_path = Path(os.getenv("APPDATA", Path.home())) / "Claude" / "claude_desktop_config.json"
|
46
|
+
else: external_mcp_path = Path.home() / ".vscode-server" / "data" / "User" / "globalStorage" / "rooveterinaryinc.roo-cline" / "settings" / "cline_mcp_settings.json"
|
47
|
+
if external_mcp_path.exists():
|
48
|
+
logger.info(f"Found external MCP settings file at: {external_mcp_path}")
|
49
|
+
try:
|
50
|
+
with open(external_mcp_path, "r", encoding='utf-8') as mcp_file: external_mcp_config = json.load(mcp_file)
|
51
|
+
if logger.isEnabledFor(logging.DEBUG): logger.debug("Loaded external MCP settings: " + json.dumps(redact_sensitive_data(external_mcp_config), indent=2))
|
52
|
+
main_mcp_servers = resolved_config.get("mcpServers", {}); external_mcp_servers = external_mcp_config.get("mcpServers", {})
|
53
|
+
merged_mcp_servers = main_mcp_servers.copy(); servers_added_count = 0
|
54
|
+
for name, server_cfg in external_mcp_servers.items():
|
55
|
+
if name not in merged_mcp_servers and not server_cfg.get("disabled", False): merged_mcp_servers[name] = server_cfg; servers_added_count += 1
|
56
|
+
if servers_added_count > 0: resolved_config["mcpServers"] = merged_mcp_servers; logger.info(f"Merged {servers_added_count} MCP servers.");
|
57
|
+
else: logger.debug("No new MCP servers added from external settings.")
|
58
|
+
except Exception as merge_err: logger.error(f"Failed to load/merge MCP settings from '{external_mcp_path}': {merge_err}", exc_info=logger.isEnabledFor(logging.DEBUG))
|
59
|
+
else: logger.debug(f"External MCP settings file not found at {external_mcp_path}. Skipping merge.")
|
60
|
+
else: logger.debug("MCP settings merge disabled.")
|
61
|
+
except Exception as e: logger.error(f"Failed during config processing: {e}", exc_info=logger.isEnabledFor(logging.DEBUG)); raise
|
62
|
+
globals()["config"] = resolved_config
|
63
|
+
return resolved_config
|
64
|
+
|
65
|
+
def resolve_placeholders(obj: Any) -> Any:
|
66
|
+
"""Recursively resolve ${VAR_NAME} placeholders."""
|
67
|
+
if isinstance(obj, dict): return {k: resolve_placeholders(v) for k, v in obj.items()}
|
68
|
+
elif isinstance(obj, list): return [resolve_placeholders(item) for item in obj]
|
69
|
+
elif isinstance(obj, str):
|
70
|
+
pattern = re.compile(r'\$\{(\w+(?:[_-]\w+)*)\}')
|
71
|
+
resolved_string = obj; any_unresolved = False
|
72
|
+
for var_name in pattern.findall(obj):
|
73
|
+
env_value = os.getenv(var_name); placeholder = f'${{{var_name}}}'
|
74
|
+
if env_value is None:
|
75
|
+
log_level = logging.DEBUG
|
76
|
+
if resolved_string == placeholder:
|
77
|
+
log_level = logging.WARNING
|
78
|
+
resolved_string = None
|
79
|
+
any_unresolved = True
|
80
|
+
logger.log(log_level, f"Env var '{var_name}' not set for placeholder '{placeholder}'. Resolving to None.")
|
81
|
+
return None
|
82
|
+
else:
|
83
|
+
resolved_string = resolved_string.replace(placeholder, "")
|
84
|
+
any_unresolved = True
|
85
|
+
logger.log(log_level, f"Env var '{var_name}' not set for placeholder '{placeholder}'. Removing from string.")
|
86
|
+
else:
|
87
|
+
resolved_string = resolved_string.replace(placeholder, env_value)
|
88
|
+
if logger.isEnabledFor(logging.DEBUG): logger.debug(f"Resolved placeholder '{placeholder}' using env var '{var_name}'.")
|
89
|
+
if any_unresolved and resolved_string is not None:
|
90
|
+
logger.debug(f"String '{obj}' contained unresolved placeholders. Result: '{resolved_string}'")
|
91
|
+
return resolved_string
|
92
|
+
else: return obj
|
93
|
+
|
94
|
+
def load_server_config(file_path: Optional[str] = None) -> dict:
|
95
|
+
"""Loads, resolves, and merges server config from JSON file."""
|
96
|
+
config_path: Optional[Path] = None
|
97
|
+
if file_path:
|
98
|
+
path_obj = Path(file_path)
|
99
|
+
if path_obj.is_file(): config_path = path_obj; logger.info(f"Using provided config file path: {config_path}")
|
100
|
+
else: logger.warning(f"Provided path '{file_path}' not found. Searching standard locations.")
|
101
|
+
if not config_path:
|
102
|
+
standard_paths = [ Path.cwd() / "swarm_config.json", Path(BASE_DIR) / "swarm_config.json", Path.home() / ".swarm" / "swarm_config.json" ]
|
103
|
+
config_path = next((p for p in standard_paths if p.is_file()), None)
|
104
|
+
if not config_path: raise FileNotFoundError(f"Config file 'swarm_config.json' not found. Checked: {[str(p) for p in standard_paths]}")
|
105
|
+
logger.info(f"Using config file found at: {config_path}")
|
109
106
|
try:
|
110
|
-
|
111
|
-
logger.debug("
|
112
|
-
|
113
|
-
|
114
|
-
|
107
|
+
raw_config = json.loads(config_path.read_text(encoding='utf-8'))
|
108
|
+
if logger.isEnabledFor(logging.DEBUG): logger.debug(f"Raw config loaded: {redact_sensitive_data(raw_config)}")
|
109
|
+
processed_config = process_config(raw_config)
|
110
|
+
globals()["config"] = processed_config
|
111
|
+
logger.info(f"Config loaded and processed from {config_path}")
|
112
|
+
return processed_config
|
113
|
+
except json.JSONDecodeError as e: logger.critical(f"Invalid JSON in {config_path}: {e}"); raise ValueError(f"Invalid JSON") from e
|
114
|
+
except Exception as e: logger.critical(f"Failed to read/process config {config_path}: {e}"); raise ValueError("Failed to load/process config") from e
|
115
|
+
|
116
|
+
def load_llm_config(config_dict: Optional[Dict[str, Any]] = None, llm_name: Optional[str] = None) -> Dict[str, Any]:
|
117
|
+
"""Loads, validates, and resolves API keys for a specific LLM profile."""
|
118
|
+
if config_dict is None:
|
119
|
+
global_config = globals().get("config")
|
120
|
+
if not global_config:
|
121
|
+
try: config_dict = load_server_config(); globals()["config"] = config_dict
|
122
|
+
except Exception as e: raise ValueError("Global config not loaded and no config_dict provided.") from e
|
123
|
+
else: config_dict = global_config
|
124
|
+
|
125
|
+
target_llm_name = llm_name or os.getenv("DEFAULT_LLM", "default")
|
126
|
+
logger.debug(f"LOAD_LLM: Loading profile: '{target_llm_name}'.")
|
127
|
+
|
128
|
+
resolved_config = resolve_placeholders(config_dict)
|
129
|
+
llm_profiles = resolved_config.get("llm", {})
|
130
|
+
if not isinstance(llm_profiles, dict): raise ValueError("'llm' section must be a dictionary.")
|
131
|
+
|
132
|
+
llm_config = llm_profiles.get(target_llm_name)
|
133
|
+
config_source = f"config file ('{target_llm_name}')"
|
134
|
+
logger.debug(f"LOAD_LLM: Initial lookup for '{target_llm_name}': {'Found' if llm_config else 'Missing'}")
|
135
|
+
|
136
|
+
if not llm_config:
|
137
|
+
logger.warning(f"LOAD_LLM: Config for '{target_llm_name}' not found. Generating fallback.")
|
138
|
+
config_source = "fallback generation"
|
139
|
+
fb_provider = os.getenv("DEFAULT_LLM_PROVIDER", "openai"); fb_model = os.getenv("DEFAULT_LLM_MODEL", "gpt-4o")
|
140
|
+
llm_config = { "provider": fb_provider, "model": fb_model, "api_key": None, "base_url": None }
|
141
|
+
logger.debug(f"LOAD_LLM: Generated fallback core config: {llm_config}")
|
142
|
+
|
143
|
+
if not isinstance(llm_config, dict): raise ValueError(f"LLM profile '{target_llm_name}' must be a dictionary.")
|
144
|
+
|
145
|
+
final_api_key = llm_config.get("api_key"); key_log_source = f"{config_source} (resolved)" if final_api_key else config_source
|
146
|
+
provider = llm_config.get("provider"); api_key_required = llm_config.get("api_key_required", True)
|
147
|
+
logger.debug(f"LOAD_LLM: Initial key from {key_log_source}: {'****' if final_api_key else 'None'}. Required={api_key_required}")
|
148
|
+
|
149
|
+
logger.debug(f"LOAD_LLM: Checking ENV vars for potential override.")
|
150
|
+
specific_env_var_name = f"{provider.upper()}_API_KEY" if provider else "PROVIDER_API_KEY"
|
151
|
+
common_fallback_var = "OPENAI_API_KEY"
|
152
|
+
specific_key_from_env = os.getenv(specific_env_var_name); fallback_key_from_env = os.getenv(common_fallback_var)
|
153
|
+
logger.debug(f"LOAD_LLM: Env Check: Specific ('{specific_env_var_name}')={'****' if specific_key_from_env else 'None'}, Fallback ('{common_fallback_var}')={'****' if fallback_key_from_env else 'None'}")
|
154
|
+
|
155
|
+
if specific_key_from_env:
|
156
|
+
if final_api_key != specific_key_from_env: logger.info(f"LOAD_LLM: Overriding key with env var '{specific_env_var_name}'.")
|
157
|
+
final_api_key = specific_key_from_env; key_log_source = f"env var '{specific_env_var_name}'"
|
158
|
+
elif fallback_key_from_env:
|
159
|
+
if not specific_key_from_env or specific_env_var_name == common_fallback_var:
|
160
|
+
if final_api_key != fallback_key_from_env: logger.info(f"LOAD_LLM: Overriding key with fallback env var '{common_fallback_var}'.")
|
161
|
+
final_api_key = fallback_key_from_env; key_log_source = f"env var '{common_fallback_var}'"
|
162
|
+
else: logger.debug(f"LOAD_LLM: Specific env key '{specific_env_var_name}' unset, NOT using fallback.")
|
163
|
+
else: logger.debug(f"LOAD_LLM: No relevant API key found in environment variables.")
|
164
|
+
|
165
|
+
key_is_still_missing_or_empty = final_api_key is None or (isinstance(final_api_key, str) and not final_api_key.strip())
|
166
|
+
logger.debug(f"LOAD_LLM: Key after env check: {'****' if final_api_key else 'None'}. Source: {key_log_source}. Still MissingOrEmpty={key_is_still_missing_or_empty}")
|
167
|
+
|
168
|
+
if key_is_still_missing_or_empty:
|
169
|
+
if api_key_required and not os.getenv("SUPPRESS_DUMMY_KEY"):
|
170
|
+
final_api_key = "sk-DUMMYKEY"; key_log_source = "dummy key"; logger.warning(f"LOAD_LLM: Applying dummy key for '{target_llm_name}'.")
|
171
|
+
elif api_key_required:
|
172
|
+
key_log_source = "MISSING - ERROR"; raise ValueError(f"Required API key for LLM profile '{target_llm_name}' is missing.")
|
173
|
+
else: key_log_source = "Not Required/Not Found"
|
174
|
+
|
175
|
+
final_llm_config = llm_config.copy(); final_llm_config["api_key"] = final_api_key; final_llm_config["_log_key_source"] = key_log_source
|
176
|
+
logger.debug(f"LOAD_LLM: Returning final config for '{target_llm_name}': {redact_sensitive_data(final_llm_config)}")
|
177
|
+
return final_llm_config
|
178
|
+
|
179
|
+
def get_llm_model(config_dict: Dict[str, Any], llm_name: Optional[str] = None) -> str:
|
180
|
+
target_llm_name = llm_name or os.getenv("DEFAULT_LLM", "default")
|
181
|
+
try: llm_config = load_llm_config(config_dict, target_llm_name)
|
182
|
+
except ValueError as e: raise ValueError(f"Could not load config for LLM '{target_llm_name}': {e}") from e
|
183
|
+
model_name = llm_config.get("model")
|
184
|
+
if not model_name or not isinstance(model_name, str): raise ValueError(f"'model' name missing/invalid for LLM '{target_llm_name}'.")
|
185
|
+
logger.debug(f"Retrieved model name '{model_name}' for LLM '{target_llm_name}'")
|
186
|
+
return model_name
|
187
|
+
|
188
|
+
def load_and_validate_llm(config_dict: Dict[str, Any], llm_name: Optional[str] = None) -> Dict[str, Any]:
|
189
|
+
target_llm_name = llm_name or os.getenv("DEFAULT_LLM", "default")
|
190
|
+
logger.debug(f"Loading and validating LLM (via load_llm_config) for profile: {target_llm_name}")
|
191
|
+
return load_llm_config(config_dict, target_llm_name)
|
192
|
+
|
193
|
+
def get_server_params(server_config: Dict[str, Any], server_name: str) -> Optional[Dict[str, Any]]:
|
194
|
+
"""Extracts and validates parameters needed to start an MCP server."""
|
195
|
+
command = server_config.get("command"); args = server_config.get("args", []); config_env = server_config.get("env", {})
|
196
|
+
if not command: logger.error(f"MCP server '{server_name}' missing 'command'."); return None
|
197
|
+
if not isinstance(args, list): logger.error(f"MCP server '{server_name}' 'args' must be list."); return None
|
198
|
+
if not isinstance(config_env, dict): logger.error(f"MCP server '{server_name}' 'env' must be dict."); return None
|
199
|
+
env = {**os.environ.copy(), **config_env}; valid_env = {}
|
200
|
+
for k, v in env.items():
|
201
|
+
if v is None: logger.warning(f"Env var '{k}' for MCP server '{server_name}' resolved to None. Omitting.")
|
202
|
+
else: valid_env[k] = str(v)
|
203
|
+
return {"command": command, "args": args, "env": valid_env}
|
204
|
+
|
205
|
+
def list_mcp_servers(config_dict: Dict[str, Any]) -> List[str]:
|
206
|
+
"""Returns a list of configured MCP server names."""
|
207
|
+
return list(config_dict.get("mcpServers", {}).keys())
|
208
|
+
|
@@ -6,11 +6,17 @@ import sys
|
|
6
6
|
import logging
|
7
7
|
from typing import Any, Dict
|
8
8
|
|
9
|
-
from swarm.
|
9
|
+
from swarm.extensions.config.config_loader import (
|
10
|
+
load_server_config,
|
11
|
+
resolve_placeholders
|
12
|
+
)
|
10
13
|
from swarm.utils.color_utils import color_text
|
11
14
|
from swarm.settings import DEBUG
|
12
|
-
from swarm.
|
13
|
-
|
15
|
+
from swarm.extensions.cli.utils import (
|
16
|
+
prompt_user,
|
17
|
+
log_and_exit,
|
18
|
+
display_message
|
19
|
+
)
|
14
20
|
|
15
21
|
# Initialize logger for this module
|
16
22
|
logger = logging.getLogger(__name__)
|
@@ -23,24 +29,6 @@ if not logger.handlers:
|
|
23
29
|
|
24
30
|
CONFIG_BACKUP_SUFFIX = ".backup"
|
25
31
|
|
26
|
-
def resolve_placeholders(config):
|
27
|
-
# Recursively resolve placeholders in the config dict (env vars, etc.)
|
28
|
-
import os
|
29
|
-
import re
|
30
|
-
pattern = re.compile(r'\$\{([^}]+)\}')
|
31
|
-
def _resolve(val):
|
32
|
-
if isinstance(val, str):
|
33
|
-
def replacer(match):
|
34
|
-
var = match.group(1)
|
35
|
-
return os.environ.get(var, match.group(0))
|
36
|
-
return pattern.sub(replacer, val)
|
37
|
-
elif isinstance(val, dict):
|
38
|
-
return {k: _resolve(v) for k, v in val.items()}
|
39
|
-
elif isinstance(val, list):
|
40
|
-
return [_resolve(v) for v in val]
|
41
|
-
return val
|
42
|
-
return _resolve(config)
|
43
|
-
|
44
32
|
def backup_configuration(config_path: str) -> None:
|
45
33
|
"""
|
46
34
|
Create a backup of the existing configuration file.
|
@@ -52,10 +40,10 @@ def backup_configuration(config_path: str) -> None:
|
|
52
40
|
try:
|
53
41
|
shutil.copy(config_path, backup_path)
|
54
42
|
logger.info(f"Configuration backup created at '{backup_path}'")
|
55
|
-
|
43
|
+
display_message(f"Backup of configuration created at '{backup_path}'", "info")
|
56
44
|
except Exception as e:
|
57
45
|
logger.error(f"Failed to create configuration backup: {e}")
|
58
|
-
|
46
|
+
display_message(f"Failed to create backup: {e}", "error")
|
59
47
|
sys.exit(1)
|
60
48
|
|
61
49
|
def load_config(config_path: str) -> Dict[str, Any]:
|
@@ -78,11 +66,11 @@ def load_config(config_path: str) -> Dict[str, Any]:
|
|
78
66
|
logger.debug(f"Raw configuration loaded: {config}")
|
79
67
|
except FileNotFoundError:
|
80
68
|
logger.error(f"Configuration file not found at {config_path}")
|
81
|
-
|
69
|
+
display_message(f"Configuration file not found at {config_path}", "error")
|
82
70
|
sys.exit(1)
|
83
71
|
except json.JSONDecodeError as e:
|
84
72
|
logger.error(f"Invalid JSON in configuration file {config_path}: {e}")
|
85
|
-
|
73
|
+
display_message(f"Invalid JSON in configuration file {config_path}: {e}", "error")
|
86
74
|
sys.exit(1)
|
87
75
|
|
88
76
|
# Resolve placeholders recursively
|
@@ -91,7 +79,7 @@ def load_config(config_path: str) -> Dict[str, Any]:
|
|
91
79
|
logger.debug(f"Configuration after resolving placeholders: {resolved_config}")
|
92
80
|
except Exception as e:
|
93
81
|
logger.error(f"Failed to resolve placeholders in configuration: {e}")
|
94
|
-
|
82
|
+
display_message(f"Failed to resolve placeholders in configuration: {e}", "error")
|
95
83
|
sys.exit(1)
|
96
84
|
|
97
85
|
return resolved_config
|
@@ -111,10 +99,10 @@ def save_config(config_path: str, config: Dict[str, Any]) -> None:
|
|
111
99
|
with open(config_path, "w") as f:
|
112
100
|
json.dump(config, f, indent=4)
|
113
101
|
logger.info(f"Configuration saved to '{config_path}'")
|
114
|
-
|
102
|
+
display_message(f"Configuration saved to '{config_path}'", "info")
|
115
103
|
except Exception as e:
|
116
104
|
logger.error(f"Failed to save configuration: {e}")
|
117
|
-
|
105
|
+
display_message(f"Failed to save configuration: {e}", "error")
|
118
106
|
sys.exit(1)
|
119
107
|
|
120
108
|
def add_llm(config_path: str) -> None:
|
@@ -125,20 +113,20 @@ def add_llm(config_path: str) -> None:
|
|
125
113
|
config_path (str): Path to the configuration file.
|
126
114
|
"""
|
127
115
|
config = load_config(config_path)
|
128
|
-
|
116
|
+
display_message("Starting the process to add a new LLM.", "info")
|
129
117
|
|
130
118
|
while True:
|
131
119
|
llm_name = prompt_user("Enter the name of the new LLM (or type 'done' to finish)").strip()
|
132
|
-
|
120
|
+
display_message(f"User entered LLM name: {llm_name}", "info")
|
133
121
|
if llm_name.lower() == 'done':
|
134
|
-
|
122
|
+
display_message("Finished adding LLMs.", "info")
|
135
123
|
break
|
136
124
|
if not llm_name:
|
137
|
-
|
125
|
+
display_message("LLM name cannot be empty.", "error")
|
138
126
|
continue
|
139
127
|
|
140
128
|
if llm_name in config.get("llm", {}):
|
141
|
-
|
129
|
+
display_message(f"LLM '{llm_name}' already exists.", "warning")
|
142
130
|
continue
|
143
131
|
|
144
132
|
llm = {}
|
@@ -154,16 +142,16 @@ def add_llm(config_path: str) -> None:
|
|
154
142
|
temperature_input = prompt_user("Enter the temperature (e.g., 0.7)").strip()
|
155
143
|
llm["temperature"] = float(temperature_input)
|
156
144
|
except ValueError:
|
157
|
-
|
145
|
+
display_message("Invalid temperature value. Using default 0.7.", "warning")
|
158
146
|
llm["temperature"] = 0.7
|
159
147
|
|
160
148
|
config.setdefault("llm", {})[llm_name] = llm
|
161
149
|
logger.info(f"Added LLM '{llm_name}' to configuration.")
|
162
|
-
|
150
|
+
display_message(f"LLM '{llm_name}' added.", "info")
|
163
151
|
|
164
152
|
backup_configuration(config_path)
|
165
153
|
save_config(config_path, config)
|
166
|
-
|
154
|
+
display_message("LLM configuration process completed.", "info")
|
167
155
|
|
168
156
|
def remove_llm(config_path: str, llm_name: str) -> None:
|
169
157
|
"""
|
@@ -176,18 +164,18 @@ def remove_llm(config_path: str, llm_name: str) -> None:
|
|
176
164
|
config = load_config(config_path)
|
177
165
|
|
178
166
|
if llm_name not in config.get("llm", {}):
|
179
|
-
|
167
|
+
display_message(f"LLM '{llm_name}' does not exist.", "error")
|
180
168
|
return
|
181
169
|
|
182
170
|
confirm = prompt_user(f"Are you sure you want to remove LLM '{llm_name}'? (yes/no)").strip().lower()
|
183
171
|
if confirm not in ['yes', 'y']:
|
184
|
-
|
172
|
+
display_message("Operation cancelled.", "warning")
|
185
173
|
return
|
186
174
|
|
187
175
|
del config["llm"][llm_name]
|
188
176
|
backup_configuration(config_path)
|
189
177
|
save_config(config_path, config)
|
190
|
-
|
178
|
+
display_message(f"LLM '{llm_name}' has been removed.", "info")
|
191
179
|
logger.info(f"Removed LLM '{llm_name}' from configuration.")
|
192
180
|
|
193
181
|
def add_mcp_server(config_path: str) -> None:
|
@@ -198,20 +186,20 @@ def add_mcp_server(config_path: str) -> None:
|
|
198
186
|
config_path (str): Path to the configuration file.
|
199
187
|
"""
|
200
188
|
config = load_config(config_path)
|
201
|
-
|
189
|
+
display_message("Starting the process to add a new MCP server.", "info")
|
202
190
|
|
203
191
|
while True:
|
204
192
|
server_name = prompt_user("Enter the name of the new MCP server (or type 'done' to finish)").strip()
|
205
|
-
|
193
|
+
display_message(f"User entered MCP server name: {server_name}", "info")
|
206
194
|
if server_name.lower() == 'done':
|
207
|
-
|
195
|
+
display_message("Finished adding MCP servers.", "info")
|
208
196
|
break
|
209
197
|
if not server_name:
|
210
|
-
|
198
|
+
display_message("Server name cannot be empty.", "error")
|
211
199
|
continue
|
212
200
|
|
213
201
|
if server_name in config.get("mcpServers", {}):
|
214
|
-
|
202
|
+
display_message(f"MCP server '{server_name}' already exists.", "warning")
|
215
203
|
continue
|
216
204
|
|
217
205
|
server = {}
|
@@ -222,7 +210,7 @@ def add_mcp_server(config_path: str) -> None:
|
|
222
210
|
if not isinstance(server["args"], list):
|
223
211
|
raise ValueError
|
224
212
|
except ValueError:
|
225
|
-
|
213
|
+
display_message("Invalid arguments format. Using an empty list.", "warning")
|
226
214
|
server["args"] = []
|
227
215
|
|
228
216
|
env_vars = {}
|
@@ -238,11 +226,11 @@ def add_mcp_server(config_path: str) -> None:
|
|
238
226
|
|
239
227
|
config.setdefault("mcpServers", {})[server_name] = server
|
240
228
|
logger.info(f"Added MCP server '{server_name}' to configuration.")
|
241
|
-
|
229
|
+
display_message(f"MCP server '{server_name}' added.", "info")
|
242
230
|
|
243
231
|
backup_configuration(config_path)
|
244
232
|
save_config(config_path, config)
|
245
|
-
|
233
|
+
display_message("MCP server configuration process completed.", "info")
|
246
234
|
|
247
235
|
def remove_mcp_server(config_path: str, server_name: str) -> None:
|
248
236
|
"""
|
@@ -255,16 +243,16 @@ def remove_mcp_server(config_path: str, server_name: str) -> None:
|
|
255
243
|
config = load_config(config_path)
|
256
244
|
|
257
245
|
if server_name not in config.get("mcpServers", {}):
|
258
|
-
|
246
|
+
display_message(f"MCP server '{server_name}' does not exist.", "error")
|
259
247
|
return
|
260
248
|
|
261
249
|
confirm = prompt_user(f"Are you sure you want to remove MCP server '{server_name}'? (yes/no)").strip().lower()
|
262
250
|
if confirm not in ['yes', 'y']:
|
263
|
-
|
251
|
+
display_message("Operation cancelled.", "warning")
|
264
252
|
return
|
265
253
|
|
266
254
|
del config["mcpServers"][server_name]
|
267
255
|
backup_configuration(config_path)
|
268
256
|
save_config(config_path, config)
|
269
|
-
|
257
|
+
display_message(f"MCP server '{server_name}' has been removed.", "info")
|
270
258
|
logger.info(f"Removed MCP server '{server_name}' from configuration.")
|
@@ -47,35 +47,3 @@ def save_server_config(config: dict, file_path: str = None) -> None:
|
|
47
47
|
except OSError as e:
|
48
48
|
logger.error(f"Error saving configuration to {file_path}: {e}")
|
49
49
|
raise
|
50
|
-
|
51
|
-
def load_server_config(file_path: str = None) -> dict:
|
52
|
-
"""
|
53
|
-
Loads the server configuration from a JSON file.
|
54
|
-
|
55
|
-
Args:
|
56
|
-
file_path (str): The path to the configuration file. Defaults to 'swarm_settings.json' in the current directory.
|
57
|
-
|
58
|
-
Returns:
|
59
|
-
dict: The loaded configuration dictionary.
|
60
|
-
|
61
|
-
Raises:
|
62
|
-
FileNotFoundError: If the configuration file does not exist.
|
63
|
-
ValueError: If the configuration is not a valid dictionary or JSON is invalid.
|
64
|
-
"""
|
65
|
-
if file_path is None:
|
66
|
-
file_path = os.path.join(os.getcwd(), "swarm_settings.json")
|
67
|
-
logger.debug(f"Loading server configuration from {file_path}")
|
68
|
-
try:
|
69
|
-
with open(file_path, "r") as file:
|
70
|
-
config = json.load(file)
|
71
|
-
if not isinstance(config, dict):
|
72
|
-
logger.error("Loaded configuration is not a dictionary.")
|
73
|
-
raise ValueError("Configuration must be a dictionary.")
|
74
|
-
logger.debug(f"Loaded configuration: {redact_sensitive_data(config)}")
|
75
|
-
return config
|
76
|
-
except FileNotFoundError as e:
|
77
|
-
logger.error(f"Configuration file not found: {file_path}")
|
78
|
-
raise
|
79
|
-
except json.JSONDecodeError as e:
|
80
|
-
logger.error(f"Invalid JSON in configuration file {file_path}: {e}")
|
81
|
-
raise ValueError(f"Invalid JSON in configuration file: {e}")
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
import PyInstaller.__main__
|
3
|
+
|
4
|
+
def build_executable(script, output_name):
|
5
|
+
PyInstaller.__main__.run([
|
6
|
+
script,
|
7
|
+
"--onefile",
|
8
|
+
"--name", output_name,
|
9
|
+
"--add-data", "swarm_config.json:." # Adjust if additional data is needed
|
10
|
+
])
|
11
|
+
|
12
|
+
if __name__ == "__main__":
|
13
|
+
build_executable("launchers/swarm_cli.py", "swarm-cli")
|
14
|
+
build_executable("launchers/swarm_rest.py", "swarm-rest")
|
File without changes
|