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.
Files changed (307) hide show
  1. open_swarm-0.1.1748636295.dist-info/METADATA +257 -0
  2. open_swarm-0.1.1748636295.dist-info/RECORD +89 -0
  3. {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/WHEEL +2 -1
  4. open_swarm-0.1.1748636295.dist-info/entry_points.txt +3 -0
  5. open_swarm-0.1.1748636295.dist-info/top_level.txt +1 -0
  6. swarm/__init__.py +2 -0
  7. swarm/agent/agent.py +49 -0
  8. swarm/auth.py +48 -113
  9. swarm/consumers.py +0 -19
  10. swarm/core.py +411 -0
  11. swarm/extensions/blueprint/__init__.py +16 -30
  12. swarm/extensions/blueprint/agent_utils.py +45 -0
  13. swarm/extensions/blueprint/blueprint_base.py +562 -0
  14. swarm/extensions/blueprint/blueprint_discovery.py +112 -0
  15. swarm/extensions/blueprint/django_utils.py +79 -181
  16. swarm/extensions/blueprint/interactive_mode.py +72 -67
  17. swarm/extensions/blueprint/output_utils.py +82 -0
  18. swarm/{core → extensions/blueprint}/spinner.py +21 -30
  19. swarm/extensions/cli/cli_args.py +0 -6
  20. swarm/extensions/cli/commands/blueprint_management.py +9 -47
  21. swarm/extensions/cli/commands/config_management.py +6 -5
  22. swarm/extensions/cli/commands/edit_config.py +7 -16
  23. swarm/extensions/cli/commands/list_blueprints.py +1 -1
  24. swarm/extensions/cli/commands/validate_env.py +4 -11
  25. swarm/extensions/cli/commands/validate_envvars.py +6 -6
  26. swarm/extensions/cli/interactive_shell.py +2 -16
  27. swarm/extensions/config/config_loader.py +345 -107
  28. swarm/{core → extensions/config}/config_manager.py +38 -50
  29. swarm/{core → extensions/config}/server_config.py +0 -32
  30. swarm/extensions/launchers/build_launchers.py +14 -0
  31. swarm/{core → extensions/launchers}/build_swarm_wrapper.py +0 -0
  32. swarm/extensions/launchers/swarm_api.py +64 -8
  33. swarm/extensions/launchers/swarm_cli.py +300 -8
  34. swarm/extensions/mcp/__init__.py +1 -0
  35. swarm/extensions/mcp/cache_utils.py +32 -0
  36. swarm/extensions/mcp/mcp_client.py +233 -0
  37. swarm/extensions/mcp/mcp_tool_provider.py +135 -0
  38. swarm/extensions/mcp/mcp_utils.py +260 -0
  39. swarm/llm/chat_completion.py +166 -0
  40. swarm/serializers.py +5 -96
  41. swarm/settings.py +133 -85
  42. swarm/types.py +91 -0
  43. swarm/urls.py +74 -57
  44. swarm/utils/context_utils.py +4 -10
  45. swarm/utils/general_utils.py +0 -21
  46. swarm/utils/redact.py +36 -23
  47. swarm/views/api_views.py +39 -48
  48. swarm/views/chat_views.py +76 -236
  49. swarm/views/core_views.py +87 -80
  50. swarm/views/model_views.py +121 -64
  51. swarm/views/utils.py +439 -65
  52. swarm/views/web_views.py +2 -2
  53. open_swarm-0.1.1745275181.dist-info/METADATA +0 -874
  54. open_swarm-0.1.1745275181.dist-info/RECORD +0 -319
  55. open_swarm-0.1.1745275181.dist-info/entry_points.txt +0 -4
  56. swarm/blueprints/README.md +0 -68
  57. swarm/blueprints/blueprint_audit_status.json +0 -27
  58. swarm/blueprints/chatbot/README.md +0 -40
  59. swarm/blueprints/chatbot/blueprint_chatbot.py +0 -471
  60. swarm/blueprints/chatbot/metadata.json +0 -23
  61. swarm/blueprints/chatbot/templates/chatbot/chatbot.html +0 -33
  62. swarm/blueprints/chucks_angels/README.md +0 -11
  63. swarm/blueprints/chucks_angels/blueprint_chucks_angels.py +0 -7
  64. swarm/blueprints/chucks_angels/test_basic.py +0 -3
  65. swarm/blueprints/codey/CODEY.md +0 -15
  66. swarm/blueprints/codey/README.md +0 -115
  67. swarm/blueprints/codey/blueprint_codey.py +0 -1072
  68. swarm/blueprints/codey/codey_cli.py +0 -373
  69. swarm/blueprints/codey/instructions.md +0 -17
  70. swarm/blueprints/codey/metadata.json +0 -23
  71. swarm/blueprints/common/operation_box_utils.py +0 -83
  72. swarm/blueprints/digitalbutlers/README.md +0 -11
  73. swarm/blueprints/digitalbutlers/__init__.py +0 -1
  74. swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +0 -7
  75. swarm/blueprints/digitalbutlers/test_basic.py +0 -3
  76. swarm/blueprints/divine_code/README.md +0 -3
  77. swarm/blueprints/divine_code/__init__.py +0 -10
  78. swarm/blueprints/divine_code/apps.py +0 -11
  79. swarm/blueprints/divine_code/blueprint_divine_code.py +0 -270
  80. swarm/blueprints/django_chat/apps.py +0 -6
  81. swarm/blueprints/django_chat/blueprint_django_chat.py +0 -268
  82. swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +0 -37
  83. swarm/blueprints/django_chat/urls.py +0 -8
  84. swarm/blueprints/django_chat/views.py +0 -32
  85. swarm/blueprints/echocraft/blueprint_echocraft.py +0 -384
  86. swarm/blueprints/flock/README.md +0 -11
  87. swarm/blueprints/flock/__init__.py +0 -8
  88. swarm/blueprints/flock/blueprint_flock.py +0 -7
  89. swarm/blueprints/flock/test_basic.py +0 -3
  90. swarm/blueprints/geese/README.md +0 -10
  91. swarm/blueprints/geese/__init__.py +0 -8
  92. swarm/blueprints/geese/blueprint_geese.py +0 -384
  93. swarm/blueprints/geese/geese_cli.py +0 -102
  94. swarm/blueprints/jeeves/README.md +0 -41
  95. swarm/blueprints/jeeves/blueprint_jeeves.py +0 -722
  96. swarm/blueprints/jeeves/jeeves_cli.py +0 -55
  97. swarm/blueprints/jeeves/metadata.json +0 -24
  98. swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +0 -473
  99. swarm/blueprints/messenger/templates/messenger/messenger.html +0 -46
  100. swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +0 -423
  101. swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +0 -340
  102. swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +0 -265
  103. swarm/blueprints/omniplex/blueprint_omniplex.py +0 -298
  104. swarm/blueprints/poets/blueprint_poets.py +0 -546
  105. swarm/blueprints/poets/poets_cli.py +0 -23
  106. swarm/blueprints/rue_code/README.md +0 -8
  107. swarm/blueprints/rue_code/blueprint_rue_code.py +0 -448
  108. swarm/blueprints/rue_code/rue_code_cli.py +0 -43
  109. swarm/blueprints/stewie/apps.py +0 -12
  110. swarm/blueprints/stewie/blueprint_family_ties.py +0 -349
  111. swarm/blueprints/stewie/models.py +0 -19
  112. swarm/blueprints/stewie/serializers.py +0 -10
  113. swarm/blueprints/stewie/settings.py +0 -17
  114. swarm/blueprints/stewie/urls.py +0 -11
  115. swarm/blueprints/stewie/views.py +0 -26
  116. swarm/blueprints/suggestion/blueprint_suggestion.py +0 -222
  117. swarm/blueprints/whinge_surf/README.md +0 -22
  118. swarm/blueprints/whinge_surf/__init__.py +0 -1
  119. swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +0 -565
  120. swarm/blueprints/whinge_surf/whinge_surf_cli.py +0 -99
  121. swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
  122. swarm/blueprints/whiskeytango_foxtrot/apps.py +0 -11
  123. swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +0 -339
  124. swarm/blueprints/zeus/__init__.py +0 -2
  125. swarm/blueprints/zeus/apps.py +0 -4
  126. swarm/blueprints/zeus/blueprint_zeus.py +0 -270
  127. swarm/blueprints/zeus/zeus_cli.py +0 -13
  128. swarm/cli/async_input.py +0 -65
  129. swarm/cli/async_input_demo.py +0 -32
  130. swarm/core/agent_utils.py +0 -21
  131. swarm/core/blueprint_base.py +0 -769
  132. swarm/core/blueprint_discovery.py +0 -125
  133. swarm/core/blueprint_runner.py +0 -59
  134. swarm/core/blueprint_ux.py +0 -109
  135. swarm/core/build_launchers.py +0 -15
  136. swarm/core/cli/__init__.py +0 -1
  137. swarm/core/cli/commands/__init__.py +0 -1
  138. swarm/core/cli/commands/blueprint_management.py +0 -7
  139. swarm/core/cli/interactive_shell.py +0 -14
  140. swarm/core/cli/main.py +0 -50
  141. swarm/core/cli/utils/__init__.py +0 -1
  142. swarm/core/cli/utils/discover_commands.py +0 -18
  143. swarm/core/config_loader.py +0 -122
  144. swarm/core/output_utils.py +0 -193
  145. swarm/core/session_logger.py +0 -42
  146. swarm/core/slash_commands.py +0 -89
  147. swarm/core/swarm_api.py +0 -68
  148. swarm/core/swarm_cli.py +0 -216
  149. swarm/core/utils/__init__.py +0 -0
  150. swarm/extensions/blueprint/cli_handler.py +0 -197
  151. swarm/extensions/blueprint/runnable_blueprint.py +0 -42
  152. swarm/extensions/cli/utils/__init__.py +0 -1
  153. swarm/extensions/cli/utils/async_input.py +0 -46
  154. swarm/extensions/cli/utils/prompt_user.py +0 -3
  155. swarm/management/__init__.py +0 -0
  156. swarm/management/commands/__init__.py +0 -0
  157. swarm/management/commands/runserver.py +0 -58
  158. swarm/middleware.py +0 -65
  159. swarm/permissions.py +0 -38
  160. swarm/static/contrib/fonts/fontawesome-webfont.ttf +0 -7
  161. swarm/static/contrib/fonts/fontawesome-webfont.woff +0 -7
  162. swarm/static/contrib/fonts/fontawesome-webfont.woff2 +0 -7
  163. swarm/static/contrib/markedjs/marked.min.js +0 -6
  164. swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +0 -27
  165. swarm/static/contrib/tabler-icons/alert-triangle.svg +0 -21
  166. swarm/static/contrib/tabler-icons/archive.svg +0 -21
  167. swarm/static/contrib/tabler-icons/artboard.svg +0 -27
  168. swarm/static/contrib/tabler-icons/automatic-gearbox.svg +0 -23
  169. swarm/static/contrib/tabler-icons/box-multiple.svg +0 -19
  170. swarm/static/contrib/tabler-icons/carambola.svg +0 -19
  171. swarm/static/contrib/tabler-icons/copy.svg +0 -20
  172. swarm/static/contrib/tabler-icons/download.svg +0 -21
  173. swarm/static/contrib/tabler-icons/edit.svg +0 -21
  174. swarm/static/contrib/tabler-icons/filled/carambola.svg +0 -13
  175. swarm/static/contrib/tabler-icons/filled/paint.svg +0 -13
  176. swarm/static/contrib/tabler-icons/headset.svg +0 -22
  177. swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +0 -21
  178. swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +0 -21
  179. swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +0 -21
  180. swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +0 -21
  181. swarm/static/contrib/tabler-icons/message-chatbot.svg +0 -22
  182. swarm/static/contrib/tabler-icons/message-star.svg +0 -22
  183. swarm/static/contrib/tabler-icons/message-x.svg +0 -23
  184. swarm/static/contrib/tabler-icons/message.svg +0 -21
  185. swarm/static/contrib/tabler-icons/paperclip.svg +0 -18
  186. swarm/static/contrib/tabler-icons/playlist-add.svg +0 -22
  187. swarm/static/contrib/tabler-icons/robot.svg +0 -26
  188. swarm/static/contrib/tabler-icons/search.svg +0 -19
  189. swarm/static/contrib/tabler-icons/settings.svg +0 -20
  190. swarm/static/contrib/tabler-icons/thumb-down.svg +0 -19
  191. swarm/static/contrib/tabler-icons/thumb-up.svg +0 -19
  192. swarm/static/css/dropdown.css +0 -22
  193. swarm/static/htmx/htmx.min.js +0 -0
  194. swarm/static/js/dropdown.js +0 -23
  195. swarm/static/rest_mode/css/base.css +0 -470
  196. swarm/static/rest_mode/css/chat-history.css +0 -286
  197. swarm/static/rest_mode/css/chat.css +0 -251
  198. swarm/static/rest_mode/css/chatbot.css +0 -74
  199. swarm/static/rest_mode/css/chatgpt.css +0 -62
  200. swarm/static/rest_mode/css/colors/corporate.css +0 -74
  201. swarm/static/rest_mode/css/colors/pastel.css +0 -81
  202. swarm/static/rest_mode/css/colors/tropical.css +0 -82
  203. swarm/static/rest_mode/css/general.css +0 -142
  204. swarm/static/rest_mode/css/layout.css +0 -167
  205. swarm/static/rest_mode/css/layouts/messenger-layout.css +0 -17
  206. swarm/static/rest_mode/css/layouts/minimalist-layout.css +0 -57
  207. swarm/static/rest_mode/css/layouts/mobile-layout.css +0 -8
  208. swarm/static/rest_mode/css/messages.css +0 -84
  209. swarm/static/rest_mode/css/messenger.css +0 -135
  210. swarm/static/rest_mode/css/settings.css +0 -91
  211. swarm/static/rest_mode/css/simple.css +0 -44
  212. swarm/static/rest_mode/css/slack.css +0 -58
  213. swarm/static/rest_mode/css/style.css +0 -156
  214. swarm/static/rest_mode/css/theme.css +0 -30
  215. swarm/static/rest_mode/css/toast.css +0 -40
  216. swarm/static/rest_mode/js/auth.js +0 -9
  217. swarm/static/rest_mode/js/blueprint.js +0 -41
  218. swarm/static/rest_mode/js/blueprintUtils.js +0 -12
  219. swarm/static/rest_mode/js/chatLogic.js +0 -79
  220. swarm/static/rest_mode/js/debug.js +0 -63
  221. swarm/static/rest_mode/js/events.js +0 -98
  222. swarm/static/rest_mode/js/main.js +0 -19
  223. swarm/static/rest_mode/js/messages.js +0 -264
  224. swarm/static/rest_mode/js/messengerLogic.js +0 -355
  225. swarm/static/rest_mode/js/modules/apiService.js +0 -84
  226. swarm/static/rest_mode/js/modules/blueprintManager.js +0 -162
  227. swarm/static/rest_mode/js/modules/chatHistory.js +0 -110
  228. swarm/static/rest_mode/js/modules/debugLogger.js +0 -14
  229. swarm/static/rest_mode/js/modules/eventHandlers.js +0 -107
  230. swarm/static/rest_mode/js/modules/messageProcessor.js +0 -120
  231. swarm/static/rest_mode/js/modules/state.js +0 -7
  232. swarm/static/rest_mode/js/modules/userInteractions.js +0 -29
  233. swarm/static/rest_mode/js/modules/validation.js +0 -23
  234. swarm/static/rest_mode/js/rendering.js +0 -119
  235. swarm/static/rest_mode/js/settings.js +0 -130
  236. swarm/static/rest_mode/js/sidebar.js +0 -94
  237. swarm/static/rest_mode/js/simpleLogic.js +0 -37
  238. swarm/static/rest_mode/js/slackLogic.js +0 -66
  239. swarm/static/rest_mode/js/splash.js +0 -76
  240. swarm/static/rest_mode/js/theme.js +0 -111
  241. swarm/static/rest_mode/js/toast.js +0 -36
  242. swarm/static/rest_mode/js/ui.js +0 -265
  243. swarm/static/rest_mode/js/validation.js +0 -57
  244. swarm/static/rest_mode/svg/animated_spinner.svg +0 -12
  245. swarm/static/rest_mode/svg/arrow_down.svg +0 -5
  246. swarm/static/rest_mode/svg/arrow_left.svg +0 -5
  247. swarm/static/rest_mode/svg/arrow_right.svg +0 -5
  248. swarm/static/rest_mode/svg/arrow_up.svg +0 -5
  249. swarm/static/rest_mode/svg/attach.svg +0 -8
  250. swarm/static/rest_mode/svg/avatar.svg +0 -7
  251. swarm/static/rest_mode/svg/canvas.svg +0 -6
  252. swarm/static/rest_mode/svg/chat_history.svg +0 -4
  253. swarm/static/rest_mode/svg/close.svg +0 -5
  254. swarm/static/rest_mode/svg/copy.svg +0 -4
  255. swarm/static/rest_mode/svg/dark_mode.svg +0 -3
  256. swarm/static/rest_mode/svg/edit.svg +0 -5
  257. swarm/static/rest_mode/svg/layout.svg +0 -9
  258. swarm/static/rest_mode/svg/logo.svg +0 -29
  259. swarm/static/rest_mode/svg/logout.svg +0 -5
  260. swarm/static/rest_mode/svg/mobile.svg +0 -5
  261. swarm/static/rest_mode/svg/new_chat.svg +0 -4
  262. swarm/static/rest_mode/svg/not_visible.svg +0 -5
  263. swarm/static/rest_mode/svg/plus.svg +0 -7
  264. swarm/static/rest_mode/svg/run_code.svg +0 -6
  265. swarm/static/rest_mode/svg/save.svg +0 -4
  266. swarm/static/rest_mode/svg/search.svg +0 -6
  267. swarm/static/rest_mode/svg/settings.svg +0 -4
  268. swarm/static/rest_mode/svg/speaker.svg +0 -5
  269. swarm/static/rest_mode/svg/stop.svg +0 -6
  270. swarm/static/rest_mode/svg/thumbs_down.svg +0 -3
  271. swarm/static/rest_mode/svg/thumbs_up.svg +0 -3
  272. swarm/static/rest_mode/svg/toggle_off.svg +0 -6
  273. swarm/static/rest_mode/svg/toggle_on.svg +0 -6
  274. swarm/static/rest_mode/svg/trash.svg +0 -10
  275. swarm/static/rest_mode/svg/undo.svg +0 -3
  276. swarm/static/rest_mode/svg/visible.svg +0 -8
  277. swarm/static/rest_mode/svg/voice.svg +0 -10
  278. swarm/templates/account/login.html +0 -22
  279. swarm/templates/account/signup.html +0 -32
  280. swarm/templates/base.html +0 -30
  281. swarm/templates/chat.html +0 -43
  282. swarm/templates/index.html +0 -35
  283. swarm/templates/rest_mode/components/chat_sidebar.html +0 -55
  284. swarm/templates/rest_mode/components/header.html +0 -45
  285. swarm/templates/rest_mode/components/main_chat_pane.html +0 -41
  286. swarm/templates/rest_mode/components/settings_dialog.html +0 -97
  287. swarm/templates/rest_mode/components/splash_screen.html +0 -7
  288. swarm/templates/rest_mode/components/top_bar.html +0 -28
  289. swarm/templates/rest_mode/message_ui.html +0 -50
  290. swarm/templates/rest_mode/slackbot.html +0 -30
  291. swarm/templates/simple_blueprint_page.html +0 -24
  292. swarm/templates/websocket_partials/final_system_message.html +0 -3
  293. swarm/templates/websocket_partials/system_message.html +0 -4
  294. swarm/templates/websocket_partials/user_message.html +0 -5
  295. swarm/utils/ansi_box.py +0 -34
  296. swarm/utils/disable_tracing.py +0 -38
  297. swarm/utils/log_utils.py +0 -63
  298. swarm/utils/openai_patch.py +0 -33
  299. swarm/ux/ansi_box.py +0 -43
  300. swarm/ux/spinner.py +0 -53
  301. {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/licenses/LICENSE +0 -0
  302. /swarm/{core → extensions/blueprint}/blueprint_utils.py +0 -0
  303. /swarm/{core → extensions/blueprint}/common_utils.py +0 -0
  304. /swarm/{core → extensions/config}/setup_wizard.py +0 -0
  305. /swarm/{blueprints/rue_code → extensions/config/utils}/__init__.py +0 -0
  306. /swarm/{core → extensions/config}/utils/logger.py +0 -0
  307. /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.core.server_config import load_server_config, save_server_config
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.core.utils.logger import *
13
- from swarm.extensions.cli.utils.prompt_user import prompt_user
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
- print(f"Backup of configuration created at '{backup_path}'")
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
- print(f"Failed to create backup: {e}")
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
- print(f"Configuration file not found at {config_path}")
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
- print(f"Invalid JSON in configuration file {config_path}: {e}")
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
- print(f"Failed to resolve placeholders in configuration: {e}")
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
- print(f"Configuration saved to '{config_path}'")
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
- print(f"Failed to save configuration: {e}")
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
- print("Starting the process to add a new LLM.")
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
- print(f"User entered LLM name: {llm_name}")
120
+ display_message(f"User entered LLM name: {llm_name}", "info")
133
121
  if llm_name.lower() == 'done':
134
- print("Finished adding LLMs.")
122
+ display_message("Finished adding LLMs.", "info")
135
123
  break
136
124
  if not llm_name:
137
- print("LLM name cannot be empty.")
125
+ display_message("LLM name cannot be empty.", "error")
138
126
  continue
139
127
 
140
128
  if llm_name in config.get("llm", {}):
141
- print(f"LLM '{llm_name}' already exists.")
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
- print("Invalid temperature value. Using default 0.7.")
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
- print(f"LLM '{llm_name}' added.")
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
- print("LLM configuration process completed.")
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
- print(f"LLM '{llm_name}' does not exist.")
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
- print("Operation cancelled.")
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
- print(f"LLM '{llm_name}' has been removed.")
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
- print("Starting the process to add a new MCP server.")
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
- print(f"User entered MCP server name: {server_name}")
193
+ display_message(f"User entered MCP server name: {server_name}", "info")
206
194
  if server_name.lower() == 'done':
207
- print("Finished adding MCP servers.")
195
+ display_message("Finished adding MCP servers.", "info")
208
196
  break
209
197
  if not server_name:
210
- print("Server name cannot be empty.")
198
+ display_message("Server name cannot be empty.", "error")
211
199
  continue
212
200
 
213
201
  if server_name in config.get("mcpServers", {}):
214
- print(f"MCP server '{server_name}' already exists.")
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
- print("Invalid arguments format. Using an empty list.")
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
- print(f"MCP server '{server_name}' added.")
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
- print("MCP server configuration process completed.")
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
- print(f"MCP server '{server_name}' does not exist.")
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
- print("Operation cancelled.")
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
- print(f"MCP server '{server_name}' has been removed.")
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")
@@ -1,12 +1,68 @@
1
- """
2
- Swarm API entry point for installation via PyPI or local dev.
3
- """
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import subprocess
4
4
  import sys
5
- import os
6
- from swarm.core.swarm_api import main
5
+ from os import path, listdir, makedirs
7
6
 
8
- def main_entry():
9
- main()
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
- main_entry()
68
+ main()
@@ -1,12 +1,304 @@
1
- """
2
- Swarm CLI entry point for installation via PyPI or local dev.
3
- """
4
- import sys
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import importlib.util
5
4
  import os
6
- from swarm.extensions.cli.main import main
5
+ import sys
6
+ import subprocess
7
+ import shutil
8
+ import json
9
+ import PyInstaller.__main__
7
10
 
8
- def app():
9
- main()
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
- app()
304
+ main()