open-swarm 0.1.1745275181__py3-none-any.whl → 0.1.1748636295__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.1748636295.dist-info/METADATA +257 -0
- open_swarm-0.1.1748636295.dist-info/RECORD +89 -0
- {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/WHEEL +2 -1
- open_swarm-0.1.1748636295.dist-info/entry_points.txt +3 -0
- open_swarm-0.1.1748636295.dist-info/top_level.txt +1 -0
- swarm/__init__.py +2 -0
- swarm/agent/agent.py +49 -0
- swarm/auth.py +48 -113
- swarm/consumers.py +0 -19
- swarm/core.py +411 -0
- swarm/extensions/blueprint/__init__.py +16 -30
- swarm/extensions/blueprint/agent_utils.py +45 -0
- swarm/extensions/blueprint/blueprint_base.py +562 -0
- swarm/extensions/blueprint/blueprint_discovery.py +112 -0
- swarm/extensions/blueprint/django_utils.py +79 -181
- swarm/extensions/blueprint/interactive_mode.py +72 -67
- swarm/extensions/blueprint/output_utils.py +82 -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 +345 -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/extensions/mcp/__init__.py +1 -0
- swarm/extensions/mcp/cache_utils.py +32 -0
- swarm/extensions/mcp/mcp_client.py +233 -0
- swarm/extensions/mcp/mcp_tool_provider.py +135 -0
- swarm/extensions/mcp/mcp_utils.py +260 -0
- swarm/llm/chat_completion.py +166 -0
- swarm/serializers.py +5 -96
- swarm/settings.py +133 -85
- swarm/types.py +91 -0
- 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 +76 -236
- swarm/views/core_views.py +87 -80
- swarm/views/model_views.py +121 -64
- swarm/views/utils.py +439 -65
- swarm/views/web_views.py +2 -2
- open_swarm-0.1.1745275181.dist-info/METADATA +0 -874
- open_swarm-0.1.1745275181.dist-info/RECORD +0 -319
- open_swarm-0.1.1745275181.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 -10
- swarm/blueprints/geese/__init__.py +0 -8
- swarm/blueprints/geese/blueprint_geese.py +0 -384
- 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/agent_utils.py +0 -21
- 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.1745275181.dist-info → open_swarm-0.1.1748636295.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
@@ -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
|
@@ -1,12 +1,68 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
import argparse
|
3
|
+
import subprocess
|
4
4
|
import sys
|
5
|
-
import
|
6
|
-
from swarm.core.swarm_api import main
|
5
|
+
from os import path, listdir, makedirs
|
7
6
|
|
8
|
-
def
|
9
|
-
|
7
|
+
def main():
|
8
|
+
parser = argparse.ArgumentParser(description="Swarm REST Launcher")
|
9
|
+
parser.add_argument("--blueprint", required=True, help="Comma-separated blueprint file paths or names for configuration purposes")
|
10
|
+
parser.add_argument("--port", type=int, default=8000, help="Port to run the REST server")
|
11
|
+
parser.add_argument("--config", default="~/.swarm/swarm_config.json", help="Configuration file path")
|
12
|
+
parser.add_argument("--daemon", action="store_true", help="Run in daemon mode and print process id")
|
13
|
+
args = parser.parse_args()
|
14
|
+
|
15
|
+
# Split blueprints by comma and strip whitespace
|
16
|
+
bp_list = [bp.strip() for bp in args.blueprint.split(",") if bp.strip()]
|
17
|
+
blueprint_paths = []
|
18
|
+
for bp_arg in bp_list:
|
19
|
+
resolved = None
|
20
|
+
if path.exists(bp_arg):
|
21
|
+
if path.isdir(bp_arg):
|
22
|
+
resolved = bp_arg
|
23
|
+
print(f"Using blueprint directory: {resolved}")
|
24
|
+
else:
|
25
|
+
resolved = bp_arg
|
26
|
+
print(f"Using blueprint file: {resolved}")
|
27
|
+
else:
|
28
|
+
managed_path = path.expanduser("~/.swarm/blueprints/" + bp_arg)
|
29
|
+
if path.isdir(managed_path):
|
30
|
+
matches = [f for f in listdir(managed_path) if f.startswith("blueprint_") and f.endswith(".py")]
|
31
|
+
if not matches:
|
32
|
+
print("Error: No blueprint file found in managed directory:", managed_path)
|
33
|
+
sys.exit(1)
|
34
|
+
resolved = path.join(managed_path, matches[0])
|
35
|
+
print(f"Using managed blueprint: {resolved}")
|
36
|
+
else:
|
37
|
+
print("Warning: Blueprint not found:", bp_arg, "- skipping.")
|
38
|
+
continue
|
39
|
+
if resolved:
|
40
|
+
blueprint_paths.append(resolved)
|
41
|
+
|
42
|
+
if not blueprint_paths:
|
43
|
+
print("Error: No valid blueprints found.")
|
44
|
+
sys.exit(1)
|
45
|
+
print("Blueprints to be configured:")
|
46
|
+
for bp in blueprint_paths:
|
47
|
+
print(" -", bp)
|
48
|
+
|
49
|
+
config_path = path.expanduser(args.config)
|
50
|
+
if not path.exists(config_path):
|
51
|
+
makedirs(path.dirname(config_path), exist_ok=True)
|
52
|
+
with open(config_path, 'w') as f:
|
53
|
+
f.write("{}")
|
54
|
+
print("Default config file created at:", config_path)
|
55
|
+
|
56
|
+
print("Launching Django server on port 0.0.0.0:{}".format(args.port))
|
57
|
+
try:
|
58
|
+
if args.daemon:
|
59
|
+
proc = subprocess.Popen(["python", "manage.py", "runserver", f"0.0.0.0:{args.port}"])
|
60
|
+
print("Running in daemon mode. Process ID:", proc.pid)
|
61
|
+
else:
|
62
|
+
subprocess.run(["python", "manage.py", "runserver", f"0.0.0.0:{args.port}"], check=True)
|
63
|
+
except subprocess.CalledProcessError as e:
|
64
|
+
print("Error launching Django server:", e)
|
65
|
+
sys.exit(1)
|
10
66
|
|
11
67
|
if __name__ == "__main__":
|
12
|
-
|
68
|
+
main()
|
@@ -1,12 +1,304 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
import sys
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
import argparse
|
3
|
+
import importlib.util
|
5
4
|
import os
|
6
|
-
|
5
|
+
import sys
|
6
|
+
import subprocess
|
7
|
+
import shutil
|
8
|
+
import json
|
9
|
+
import PyInstaller.__main__
|
7
10
|
|
8
|
-
def
|
9
|
-
|
11
|
+
def resolve_env_vars(data):
|
12
|
+
if isinstance(data, dict):
|
13
|
+
return {k: resolve_env_vars(v) for k, v in data.items()}
|
14
|
+
elif isinstance(data, list):
|
15
|
+
return [resolve_env_vars(item) for item in data]
|
16
|
+
elif isinstance(data, str):
|
17
|
+
return os.path.expandvars(data)
|
18
|
+
else:
|
19
|
+
return data
|
20
|
+
|
21
|
+
MANAGED_DIR = os.path.expanduser("~/.swarm/blueprints")
|
22
|
+
BIN_DIR = os.path.expanduser("~/.swarm/bin")
|
23
|
+
|
24
|
+
def ensure_managed_dir():
|
25
|
+
if not os.path.exists(MANAGED_DIR):
|
26
|
+
os.makedirs(MANAGED_DIR, exist_ok=True)
|
27
|
+
if not os.path.exists(BIN_DIR):
|
28
|
+
os.makedirs(BIN_DIR, exist_ok=True)
|
29
|
+
|
30
|
+
def add_blueprint(source_path, blueprint_name=None):
|
31
|
+
source_path = os.path.normpath(source_path)
|
32
|
+
if not os.path.exists(source_path):
|
33
|
+
print("Error: source file/directory does not exist:", source_path)
|
34
|
+
sys.exit(1)
|
35
|
+
if os.path.isdir(source_path):
|
36
|
+
if not blueprint_name:
|
37
|
+
blueprint_name = os.path.basename(os.path.normpath(source_path))
|
38
|
+
target_dir = os.path.join(MANAGED_DIR, blueprint_name)
|
39
|
+
if os.path.exists(target_dir):
|
40
|
+
shutil.rmtree(target_dir)
|
41
|
+
os.makedirs(target_dir, exist_ok=True)
|
42
|
+
for root, dirs, files in os.walk(source_path):
|
43
|
+
rel_path = os.path.relpath(root, source_path)
|
44
|
+
dest_root = os.path.join(target_dir, rel_path) if rel_path != '.' else target_dir
|
45
|
+
os.makedirs(dest_root, exist_ok=True)
|
46
|
+
for file in files:
|
47
|
+
shutil.copy2(os.path.join(root, file), os.path.join(dest_root, file))
|
48
|
+
print(f"Blueprint '{blueprint_name}' added successfully to {target_dir}.")
|
49
|
+
else:
|
50
|
+
blueprint_file = source_path
|
51
|
+
if not blueprint_name:
|
52
|
+
base = os.path.basename(blueprint_file)
|
53
|
+
if base.startswith("blueprint_") and base.endswith(".py"):
|
54
|
+
blueprint_name = base[len("blueprint_"):-3]
|
55
|
+
else:
|
56
|
+
blueprint_name = os.path.splitext(base)[0]
|
57
|
+
target_dir = os.path.join(MANAGED_DIR, blueprint_name)
|
58
|
+
os.makedirs(target_dir, exist_ok=True)
|
59
|
+
target_file = os.path.join(target_dir, f"blueprint_{blueprint_name}.py")
|
60
|
+
shutil.copy2(blueprint_file, target_file)
|
61
|
+
print(f"Blueprint '{blueprint_name}' added successfully to {target_dir}.")
|
62
|
+
|
63
|
+
def list_blueprints():
|
64
|
+
ensure_managed_dir()
|
65
|
+
entries = os.listdir(MANAGED_DIR)
|
66
|
+
blueprints = [d for d in entries if os.path.isdir(os.path.join(MANAGED_DIR, d))]
|
67
|
+
if blueprints:
|
68
|
+
print("Registered blueprints:")
|
69
|
+
for bp in blueprints:
|
70
|
+
print(" -", bp)
|
71
|
+
else:
|
72
|
+
print("No blueprints registered.")
|
73
|
+
|
74
|
+
def delete_blueprint(blueprint_name):
|
75
|
+
target_dir = os.path.join(MANAGED_DIR, blueprint_name)
|
76
|
+
if os.path.exists(target_dir) and os.path.isdir(target_dir):
|
77
|
+
shutil.rmtree(target_dir)
|
78
|
+
print(f"Blueprint '{blueprint_name}' deleted successfully.")
|
79
|
+
else:
|
80
|
+
print(f"Error: Blueprint '{blueprint_name}' does not exist.")
|
81
|
+
sys.exit(1)
|
82
|
+
|
83
|
+
def run_blueprint(blueprint_name):
|
84
|
+
target_dir = os.path.join(MANAGED_DIR, blueprint_name)
|
85
|
+
blueprint_file = os.path.join(target_dir, f"blueprint_{blueprint_name}.py")
|
86
|
+
if not os.path.exists(blueprint_file):
|
87
|
+
print(f"Error: Blueprint file not found for '{blueprint_name}'. Install it using 'swarm-cli add <path>'.")
|
88
|
+
sys.exit(1)
|
89
|
+
spec = importlib.util.spec_from_file_location("blueprint_module", blueprint_file)
|
90
|
+
if spec is None or spec.loader is None:
|
91
|
+
print("Error: Failed to load blueprint module from:", blueprint_file)
|
92
|
+
sys.exit(1)
|
93
|
+
blueprint = importlib.util.module_from_spec(spec)
|
94
|
+
loader = spec.loader
|
95
|
+
src_path = os.path.join(os.getcwd(), "src")
|
96
|
+
if src_path not in sys.path:
|
97
|
+
sys.path.insert(0, src_path)
|
98
|
+
loader.exec_module(blueprint)
|
99
|
+
if hasattr(blueprint, "main"):
|
100
|
+
blueprint.main()
|
101
|
+
else:
|
102
|
+
print("Error: The blueprint does not have a main() function.")
|
103
|
+
sys.exit(1)
|
104
|
+
|
105
|
+
def install_blueprint(blueprint_name):
|
106
|
+
target_dir = os.path.join(MANAGED_DIR, blueprint_name)
|
107
|
+
blueprint_file = os.path.join(target_dir, f"blueprint_{blueprint_name}.py")
|
108
|
+
if not os.path.exists(blueprint_file):
|
109
|
+
print(f"Error: Blueprint '{blueprint_name}' is not registered. Add it using 'swarm-cli add <path>'.")
|
110
|
+
sys.exit(1)
|
111
|
+
cli_name = blueprint_name # Use blueprint_name as default cli_name for simplicity
|
112
|
+
try:
|
113
|
+
PyInstaller.__main__.run([
|
114
|
+
blueprint_file,
|
115
|
+
"--onefile",
|
116
|
+
"--name", cli_name,
|
117
|
+
"--distpath", BIN_DIR,
|
118
|
+
"--workpath", os.path.join(target_dir, "build"),
|
119
|
+
"--specpath", target_dir
|
120
|
+
])
|
121
|
+
except KeyboardInterrupt:
|
122
|
+
print("Installation aborted by user request.")
|
123
|
+
sys.exit(1)
|
124
|
+
print(f"Blueprint '{blueprint_name}' installed as CLI utility '{cli_name}' at: {os.path.join(BIN_DIR, cli_name)}")
|
125
|
+
|
126
|
+
def uninstall_blueprint(blueprint_name, blueprint_only=False, wrapper_only=False):
|
127
|
+
target_dir = os.path.join(MANAGED_DIR, blueprint_name)
|
128
|
+
blueprint_file = os.path.join(target_dir, f"blueprint_{blueprint_name}.py")
|
129
|
+
cli_name = blueprint_name # Default to blueprint_name for uninstall
|
130
|
+
cli_path = os.path.join(BIN_DIR, cli_name)
|
131
|
+
removed = False
|
132
|
+
|
133
|
+
if not blueprint_only and not wrapper_only: # Remove both by default
|
134
|
+
if os.path.exists(target_dir):
|
135
|
+
shutil.rmtree(target_dir)
|
136
|
+
print(f"Blueprint '{blueprint_name}' removed from {MANAGED_DIR}.")
|
137
|
+
removed = True
|
138
|
+
if os.path.exists(cli_path):
|
139
|
+
os.remove(cli_path)
|
140
|
+
print(f"Wrapper '{cli_name}' removed from {BIN_DIR}.")
|
141
|
+
removed = True
|
142
|
+
elif blueprint_only:
|
143
|
+
if os.path.exists(target_dir):
|
144
|
+
shutil.rmtree(target_dir)
|
145
|
+
print(f"Blueprint '{blueprint_name}' removed from {MANAGED_DIR}.")
|
146
|
+
removed = True
|
147
|
+
elif wrapper_only:
|
148
|
+
if os.path.exists(cli_path):
|
149
|
+
os.remove(cli_path)
|
150
|
+
print(f"Wrapper '{cli_name}' removed from {BIN_DIR}.")
|
151
|
+
removed = True
|
152
|
+
|
153
|
+
if not removed:
|
154
|
+
print(f"Error: Nothing to uninstall for '{blueprint_name}' with specified options.")
|
155
|
+
sys.exit(1)
|
156
|
+
|
157
|
+
def main():
|
158
|
+
os.environ.pop("SWARM_BLUEPRINTS", None)
|
159
|
+
parser = argparse.ArgumentParser(
|
160
|
+
description="Swarm CLI Launcher\n\nSubcommands:\n"
|
161
|
+
" add : Add a blueprint to the managed directory.\n"
|
162
|
+
" list : List registered blueprints.\n"
|
163
|
+
" delete : Delete a registered blueprint.\n"
|
164
|
+
" run : Run a blueprint by name.\n"
|
165
|
+
" install : Install a blueprint as a CLI utility with PyInstaller.\n"
|
166
|
+
" uninstall : Uninstall a blueprint and/or its CLI wrapper.\n"
|
167
|
+
" migrate : Apply Django database migrations.\n"
|
168
|
+
" config : Manage swarm configuration (LLM and MCP servers).",
|
169
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
170
|
+
subparsers = parser.add_subparsers(dest="command", required=True, help="Available subcommands")
|
171
|
+
|
172
|
+
parser_add = subparsers.add_parser("add", help="Add a blueprint from a file or directory.")
|
173
|
+
parser_add.add_argument("source", help="Source blueprint file or directory.")
|
174
|
+
parser_add.add_argument("--name", help="Optional blueprint name. If not provided, inferred from filename.")
|
175
|
+
|
176
|
+
parser_list = subparsers.add_parser("list", help="List registered blueprints.")
|
177
|
+
|
178
|
+
parser_delete = subparsers.add_parser("delete", help="Delete a registered blueprint by name.")
|
179
|
+
parser_delete.add_argument("name", help="Blueprint name to delete.")
|
180
|
+
|
181
|
+
parser_run = subparsers.add_parser("run", help="Run a blueprint by name.")
|
182
|
+
parser_run.add_argument("name", help="Blueprint name to run.")
|
183
|
+
parser_run.add_argument("--config", default="~/.swarm/swarm_config.json", help="Path to configuration file.")
|
184
|
+
|
185
|
+
parser_install = subparsers.add_parser("install", help="Install a blueprint as a CLI utility with PyInstaller.")
|
186
|
+
parser_install.add_argument("name", help="Blueprint name to install as a CLI utility.")
|
187
|
+
|
188
|
+
parser_uninstall = subparsers.add_parser("uninstall", help="Uninstall a blueprint and/or its CLI wrapper.")
|
189
|
+
parser_uninstall.add_argument("name", help="Blueprint name to uninstall.")
|
190
|
+
parser_uninstall.add_argument("--blueprint-only", action="store_true", help="Remove only the blueprint directory.")
|
191
|
+
parser_uninstall.add_argument("--wrapper-only", action="store_true", help="Remove only the CLI wrapper.")
|
192
|
+
|
193
|
+
parser_migrate = subparsers.add_parser("migrate", help="Apply Django database migrations.")
|
194
|
+
|
195
|
+
parser_config = subparsers.add_parser("config", help="Manage swarm configuration (LLM and MCP servers).")
|
196
|
+
parser_config.add_argument("action", choices=["add", "list", "remove"], help="Action to perform on configuration")
|
197
|
+
parser_config.add_argument("--section", required=True, choices=["llm", "mcpServers"], help="Configuration section to manage")
|
198
|
+
parser_config.add_argument("--name", help="Name of the configuration entry (required for add and remove)")
|
199
|
+
parser_config.add_argument("--json", help="JSON string for configuration entry (required for add)")
|
200
|
+
parser_config.add_argument("--config", default="~/.swarm/swarm_config.json", help="Path to configuration file")
|
201
|
+
|
202
|
+
args = parser.parse_args()
|
203
|
+
ensure_managed_dir()
|
204
|
+
|
205
|
+
if args.command == "add":
|
206
|
+
add_blueprint(args.source, args.name)
|
207
|
+
elif args.command == "list":
|
208
|
+
list_blueprints()
|
209
|
+
elif args.command == "delete":
|
210
|
+
delete_blueprint(args.name)
|
211
|
+
elif args.command == "run":
|
212
|
+
config_path = os.path.expanduser(args.config)
|
213
|
+
if not os.path.exists(config_path):
|
214
|
+
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
215
|
+
default_config = {"llm": {}, "mcpServers": {}}
|
216
|
+
with open(config_path, 'w') as f:
|
217
|
+
json.dump(default_config, f, indent=4)
|
218
|
+
print("Default config file created at:", config_path)
|
219
|
+
run_blueprint(args.name)
|
220
|
+
elif args.command == "install":
|
221
|
+
install_blueprint(args.name)
|
222
|
+
elif args.command == "uninstall":
|
223
|
+
uninstall_blueprint(args.name, args.blueprint_only, args.wrapper_only)
|
224
|
+
elif args.command == "migrate":
|
225
|
+
try:
|
226
|
+
subprocess.run(["python", "manage.py", "migrate"], check=True)
|
227
|
+
print("Migrations applied successfully.")
|
228
|
+
except subprocess.CalledProcessError as e:
|
229
|
+
print("Error applying migrations:", e)
|
230
|
+
sys.exit(1)
|
231
|
+
elif args.command == "config":
|
232
|
+
config_path = os.path.expanduser(args.config)
|
233
|
+
if not os.path.exists(config_path):
|
234
|
+
default_conf = {"llm": {}, "mcpServers": {}}
|
235
|
+
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
236
|
+
with open(config_path, "w") as f:
|
237
|
+
json.dump(default_conf, f, indent=4)
|
238
|
+
print("Default config file created at:", config_path)
|
239
|
+
config = default_conf
|
240
|
+
else:
|
241
|
+
try:
|
242
|
+
with open(config_path, "r") as f:
|
243
|
+
config = json.load(f)
|
244
|
+
except json.JSONDecodeError:
|
245
|
+
print("Error: Invalid configuration file.")
|
246
|
+
sys.exit(1)
|
247
|
+
section = args.section
|
248
|
+
if args.action == "list":
|
249
|
+
entries = config.get(section, {})
|
250
|
+
if entries:
|
251
|
+
print(f"Entries in {section}:")
|
252
|
+
for key, value in entries.items():
|
253
|
+
print(f" - {key}: {json.dumps(value, indent=4)}")
|
254
|
+
else:
|
255
|
+
print(f"No entries found in {section}.")
|
256
|
+
elif args.action == "add":
|
257
|
+
if args.section == "mcpServers" and not args.name:
|
258
|
+
if not args.json:
|
259
|
+
print("Error: --json is required for adding an mcpServers block when --name is omitted.")
|
260
|
+
sys.exit(1)
|
261
|
+
try:
|
262
|
+
update_data = json.loads(args.json)
|
263
|
+
except json.JSONDecodeError:
|
264
|
+
print("Error: --json must be a valid JSON string.")
|
265
|
+
sys.exit(1)
|
266
|
+
if "mcpServers" not in update_data:
|
267
|
+
print("Error: JSON block must contain 'mcpServers' key for merging.")
|
268
|
+
sys.exit(1)
|
269
|
+
config.setdefault("mcpServers", {})
|
270
|
+
config["mcpServers"].update(update_data["mcpServers"])
|
271
|
+
with open(config_path, "w") as f:
|
272
|
+
json.dump(config, f, indent=4)
|
273
|
+
print("MCP servers updated in configuration.")
|
274
|
+
else:
|
275
|
+
if not args.name or not args.json:
|
276
|
+
print("Error: --name and --json are required for adding an entry.")
|
277
|
+
sys.exit(1)
|
278
|
+
try:
|
279
|
+
entry_data = json.loads(args.json)
|
280
|
+
except json.JSONDecodeError:
|
281
|
+
print("Error: --json must be a valid JSON string.")
|
282
|
+
sys.exit(1)
|
283
|
+
config.setdefault(section, {})[args.name] = entry_data
|
284
|
+
with open(config_path, "w") as f:
|
285
|
+
json.dump(config, f, indent=4)
|
286
|
+
print(f"Entry '{args.name}' added to {section} in configuration.")
|
287
|
+
elif args.action == "remove":
|
288
|
+
if not args.name:
|
289
|
+
print("Error: --name is required for removing an entry.")
|
290
|
+
sys.exit(1)
|
291
|
+
if args.name in config.get(section, {}):
|
292
|
+
del config[section][args.name]
|
293
|
+
with open(config_path, "w") as f:
|
294
|
+
json.dump(config, f, indent=4)
|
295
|
+
print(f"Entry '{args.name}' removed from {section} in configuration.")
|
296
|
+
else:
|
297
|
+
print(f"Error: Entry '{args.name}' not found in {section}.")
|
298
|
+
sys.exit(1)
|
299
|
+
else:
|
300
|
+
parser.print_help()
|
301
|
+
sys.exit(1)
|
10
302
|
|
11
303
|
if __name__ == "__main__":
|
12
|
-
|
304
|
+
main()
|