open-swarm 0.1.1745275181__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.1745275181.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.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/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.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
swarm/core/output_utils.py
DELETED
@@ -1,193 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Output utilities for Swarm blueprints.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import json
|
6
|
-
import logging
|
7
|
-
import os
|
8
|
-
import sys
|
9
|
-
from typing import List, Dict, Any
|
10
|
-
import re
|
11
|
-
|
12
|
-
# Optional import for markdown rendering
|
13
|
-
try:
|
14
|
-
from rich.markdown import Markdown
|
15
|
-
from rich.console import Console
|
16
|
-
from rich.panel import Panel
|
17
|
-
from rich.text import Text
|
18
|
-
from rich.rule import Rule
|
19
|
-
RICH_AVAILABLE = True
|
20
|
-
except ImportError:
|
21
|
-
RICH_AVAILABLE = False
|
22
|
-
|
23
|
-
logger = logging.getLogger(__name__)
|
24
|
-
|
25
|
-
def render_markdown(content: str) -> None:
|
26
|
-
"""Render markdown content using rich, if available."""
|
27
|
-
# --- DEBUG PRINT ---
|
28
|
-
print(f"\n[DEBUG render_markdown called with rich={RICH_AVAILABLE}]", flush=True)
|
29
|
-
if not RICH_AVAILABLE:
|
30
|
-
print(content, flush=True) # Fallback print with flush
|
31
|
-
return
|
32
|
-
console = Console()
|
33
|
-
md = Markdown(content)
|
34
|
-
console.print(md) # Rich handles flushing
|
35
|
-
|
36
|
-
def ansi_box(title: str, content: str, color: str = "94", emoji: str = "🔎", border: str = "─", width: int = 70) -> str:
|
37
|
-
"""Return a string or Panel with ANSI box formatting for search/analysis results using Rich if available."""
|
38
|
-
if RICH_AVAILABLE:
|
39
|
-
console = Console()
|
40
|
-
# Rich supports color names or hex, map color code to name
|
41
|
-
color_map = {
|
42
|
-
"94": "bright_blue",
|
43
|
-
"96": "bright_cyan",
|
44
|
-
"92": "bright_green",
|
45
|
-
"93": "bright_yellow",
|
46
|
-
"91": "bright_red",
|
47
|
-
"95": "bright_magenta",
|
48
|
-
"90": "grey82",
|
49
|
-
}
|
50
|
-
style = color_map.get(color, "bright_blue")
|
51
|
-
panel = Panel(
|
52
|
-
content,
|
53
|
-
title=f"{emoji} {title} {emoji}",
|
54
|
-
border_style=style,
|
55
|
-
width=width
|
56
|
-
)
|
57
|
-
# Return the rendered panel as a string for testability
|
58
|
-
with console.capture() as capture:
|
59
|
-
console.print(panel)
|
60
|
-
return capture.get()
|
61
|
-
# Fallback: legacy manual ANSI box
|
62
|
-
top = f"\033[{color}m{emoji} {border * (width - 4)} {emoji}\033[0m"
|
63
|
-
mid_title = f"\033[{color}m│ {title.center(width - 6)} │\033[0m"
|
64
|
-
lines = content.splitlines()
|
65
|
-
boxed = [top, mid_title, top]
|
66
|
-
for line in lines:
|
67
|
-
boxed.append(f"\033[{color}m│\033[0m {line.ljust(width - 6)} \033[{color}m│\033[0m")
|
68
|
-
boxed.append(top)
|
69
|
-
return "\n".join(boxed)
|
70
|
-
|
71
|
-
def print_search_box(title: str, content: str, color: str = "94", emoji: str = "🔎"):
|
72
|
-
print(ansi_box(title, content, color=color, emoji=emoji))
|
73
|
-
|
74
|
-
def pretty_print_response(messages: List[Dict[str, Any]], use_markdown: bool = False, spinner=None, agent_name: str = None) -> None:
|
75
|
-
"""Format and print messages, optionally rendering assistant content as markdown, and always prefixing agent responses with the agent's name."""
|
76
|
-
# --- DEBUG PRINT ---
|
77
|
-
print(f"\n[DEBUG pretty_print_response called with {len(messages)} messages, use_markdown={use_markdown}, agent_name={agent_name}]", flush=True)
|
78
|
-
|
79
|
-
if spinner:
|
80
|
-
spinner.stop()
|
81
|
-
sys.stdout.write("\r\033[K") # Clear spinner line
|
82
|
-
sys.stdout.flush()
|
83
|
-
|
84
|
-
if not messages:
|
85
|
-
logger.debug("No messages to print in pretty_print_response.")
|
86
|
-
return
|
87
|
-
|
88
|
-
for i, msg in enumerate(messages):
|
89
|
-
# --- DEBUG PRINT ---
|
90
|
-
print(f"\n[DEBUG Processing message {i}: type={type(msg)}]", flush=True)
|
91
|
-
if not isinstance(msg, dict):
|
92
|
-
print(f"[DEBUG Skipping non-dict message {i}]", flush=True)
|
93
|
-
continue
|
94
|
-
|
95
|
-
role = msg.get("role")
|
96
|
-
sender = msg.get("sender", role if role else "Unknown")
|
97
|
-
msg_content = msg.get("content")
|
98
|
-
tool_calls = msg.get("tool_calls")
|
99
|
-
# --- DEBUG PRINT ---
|
100
|
-
print(f"[DEBUG Message {i}: role={role}, sender={sender}, has_content={bool(msg_content)}, has_tools={bool(tool_calls)}]", flush=True)
|
101
|
-
|
102
|
-
if role == "assistant":
|
103
|
-
# Use agent_name if provided, else sender, else 'assistant'
|
104
|
-
display_name = agent_name or sender or "assistant"
|
105
|
-
# Magenta for agent output
|
106
|
-
print(f"\033[95m[{display_name}]\033[0m: ", end="", flush=True)
|
107
|
-
if msg_content:
|
108
|
-
# --- DEBUG PRINT ---
|
109
|
-
print(f"\n[DEBUG Assistant content found, printing/rendering... Rich={RICH_AVAILABLE}, Markdown={use_markdown}]", flush=True)
|
110
|
-
# --- CODE FENCE HIGHLIGHTING ---
|
111
|
-
if RICH_AVAILABLE and '```' in msg_content:
|
112
|
-
import re
|
113
|
-
code_fence_pattern = r"```([\w\d]*)\n([\s\S]*?)```"
|
114
|
-
matches = re.findall(code_fence_pattern, msg_content)
|
115
|
-
if matches:
|
116
|
-
from rich.syntax import Syntax
|
117
|
-
from rich.console import Console
|
118
|
-
console = Console()
|
119
|
-
for lang, code in matches:
|
120
|
-
syntax = Syntax(code, lang or "python", theme="monokai", line_numbers=False)
|
121
|
-
console.print(syntax)
|
122
|
-
# Optionally print any non-code parts
|
123
|
-
non_code = re.split(code_fence_pattern, msg_content)
|
124
|
-
for i, part in enumerate(non_code):
|
125
|
-
if i % 3 == 0 and part.strip():
|
126
|
-
print(part.strip(), flush=True)
|
127
|
-
else:
|
128
|
-
print(msg_content, flush=True)
|
129
|
-
elif use_markdown and RICH_AVAILABLE:
|
130
|
-
render_markdown(msg_content)
|
131
|
-
else:
|
132
|
-
print(msg_content, flush=True)
|
133
|
-
elif not tool_calls:
|
134
|
-
print(flush=True)
|
135
|
-
|
136
|
-
if tool_calls and isinstance(tool_calls, list):
|
137
|
-
print(" \033[92mTool Calls:\033[0m", flush=True)
|
138
|
-
for tc in tool_calls:
|
139
|
-
if not isinstance(tc, dict): continue
|
140
|
-
func = tc.get("function", {})
|
141
|
-
tool_name = func.get("name", "Unnamed Tool")
|
142
|
-
args_str = func.get("arguments", "{}")
|
143
|
-
try: args_obj = json.loads(args_str); args_pretty = ", ".join(f"{k}={v!r}" for k, v in args_obj.items())
|
144
|
-
except json.JSONDecodeError: args_pretty = args_str
|
145
|
-
print(f" \033[95m{tool_name}\033[0m({args_pretty})", flush=True)
|
146
|
-
|
147
|
-
elif role == "tool":
|
148
|
-
tool_name = msg.get("tool_name", msg.get("name", "tool"))
|
149
|
-
tool_id = msg.get("tool_call_id", "N/A")
|
150
|
-
try: content_obj = json.loads(msg_content); pretty_content = json.dumps(content_obj, indent=2)
|
151
|
-
except (json.JSONDecodeError, TypeError): pretty_content = msg_content
|
152
|
-
print(f" \033[93m[{tool_name} Result ID: {tool_id}]\033[0m:\n {pretty_content.replace(chr(10), chr(10) + ' ')}", flush=True)
|
153
|
-
else:
|
154
|
-
# --- DEBUG PRINT ---
|
155
|
-
print(f"[DEBUG Skipping message {i} with role '{role}']", flush=True)
|
156
|
-
|
157
|
-
def print_terminal_command_result(cmd: str, result: dict, max_lines: int = 10):
|
158
|
-
"""
|
159
|
-
Render a terminal command result in the CLI with a shell prompt emoji, header, and Rich box.
|
160
|
-
- Header: 🐚 Ran terminal command
|
161
|
-
- Top line: colored, [basename(pwd)] > [cmd]
|
162
|
-
- Output: Rich Panel, max 10 lines, tailing if longer, show hint for toggle
|
163
|
-
"""
|
164
|
-
if not RICH_AVAILABLE:
|
165
|
-
# Fallback to simple print
|
166
|
-
print(f"🐚 Ran terminal command\n[{os.path.basename(result['cwd'])}] > {cmd}")
|
167
|
-
lines = result['output'].splitlines()
|
168
|
-
if len(lines) > max_lines:
|
169
|
-
lines = lines[-max_lines:]
|
170
|
-
print("[Output truncated. Showing last 10 lines.]")
|
171
|
-
print("\n".join(lines))
|
172
|
-
return
|
173
|
-
|
174
|
-
console = Console()
|
175
|
-
cwd_base = os.path.basename(result['cwd'])
|
176
|
-
header = Text(f"🐚 Ran terminal command", style="bold yellow")
|
177
|
-
subheader = Rule(f"[{cwd_base}] > {cmd}", style="bright_black")
|
178
|
-
lines = result['output'].splitlines()
|
179
|
-
truncated = False
|
180
|
-
if len(lines) > max_lines:
|
181
|
-
lines = lines[-max_lines:]
|
182
|
-
truncated = True
|
183
|
-
output_body = "\n".join(lines)
|
184
|
-
panel = Panel(
|
185
|
-
output_body,
|
186
|
-
title="Output",
|
187
|
-
border_style="cyan",
|
188
|
-
subtitle="[Output truncated. Showing last 10 lines. Press [t] to expand.]" if truncated else "",
|
189
|
-
width=80
|
190
|
-
)
|
191
|
-
console.print(header)
|
192
|
-
console.print(subheader)
|
193
|
-
console.print(panel)
|
swarm/core/session_logger.py
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import datetime
|
3
|
-
from typing import Optional, List, Dict
|
4
|
-
|
5
|
-
class SessionLogger:
|
6
|
-
def __init__(self, blueprint_name: str, log_dir: Optional[str] = None):
|
7
|
-
if log_dir is None:
|
8
|
-
base_dir = os.path.dirname(__file__)
|
9
|
-
log_dir = os.path.join(base_dir, f"../blueprints/{blueprint_name}/session_logs")
|
10
|
-
os.makedirs(log_dir, exist_ok=True)
|
11
|
-
self.log_dir = log_dir
|
12
|
-
self.session_time = datetime.datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
|
13
|
-
self.log_path = os.path.join(log_dir, f"session_{self.session_time}.md")
|
14
|
-
self._open_log()
|
15
|
-
|
16
|
-
def _open_log(self):
|
17
|
-
self.log_file = open(self.log_path, "w")
|
18
|
-
self.log_file.write(f"# Session Log\n\nStarted: {self.session_time}\n\n")
|
19
|
-
self.log_file.flush()
|
20
|
-
|
21
|
-
def log_instructions(self, global_instructions: Optional[str], project_instructions: Optional[str]):
|
22
|
-
self.log_file.write("## Instructions\n")
|
23
|
-
if global_instructions:
|
24
|
-
self.log_file.write("### Global Instructions\n" + global_instructions + "\n\n")
|
25
|
-
if project_instructions:
|
26
|
-
self.log_file.write("### Project Instructions\n" + project_instructions + "\n\n")
|
27
|
-
self.log_file.write("## Messages\n")
|
28
|
-
self.log_file.flush()
|
29
|
-
|
30
|
-
def log_message(self, role: str, content: str, agent_name: str = None):
|
31
|
-
# Log agent name if provided, else fallback to role
|
32
|
-
display_name = agent_name or role
|
33
|
-
self.log_file.write(f"- **{display_name}**: {content}\n")
|
34
|
-
self.log_file.flush()
|
35
|
-
|
36
|
-
def log_tool_call(self, tool_name: str, result: str):
|
37
|
-
self.log_file.write(f"- **assistant (tool:{tool_name})**: {result}\n")
|
38
|
-
self.log_file.flush()
|
39
|
-
|
40
|
-
def close(self):
|
41
|
-
self.log_file.write(f"\nEnded: {datetime.datetime.now().strftime('%Y-%m-%dT%H-%M-%S')}\n")
|
42
|
-
self.log_file.close()
|
swarm/core/slash_commands.py
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
# Minimal slash_commands.py to restore compatibility
|
2
|
-
|
3
|
-
class SlashCommandRegistry:
|
4
|
-
def __init__(self):
|
5
|
-
self.commands = {}
|
6
|
-
def register(self, command, func=None):
|
7
|
-
if func is None:
|
8
|
-
def decorator(f):
|
9
|
-
self.commands[command] = f
|
10
|
-
return f
|
11
|
-
return decorator
|
12
|
-
self.commands[command] = func
|
13
|
-
return func
|
14
|
-
def get(self, command):
|
15
|
-
return self.commands.get(command)
|
16
|
-
|
17
|
-
slash_registry = SlashCommandRegistry()
|
18
|
-
# Built-in '/help' slash command
|
19
|
-
@slash_registry.register('/help')
|
20
|
-
def _help_command(blueprint=None, args=None):
|
21
|
-
"""List available slash commands."""
|
22
|
-
cmds = sorted(slash_registry.commands.keys())
|
23
|
-
return "Available slash commands:\n" + "\n".join(cmds)
|
24
|
-
|
25
|
-
# Built-in '/compact' slash command
|
26
|
-
@slash_registry.register('/compact')
|
27
|
-
def _compact_command(blueprint=None, args=None):
|
28
|
-
"""Placeholder for compacting conversation context."""
|
29
|
-
return "[slash command] compact summary not implemented yet."
|
30
|
-
|
31
|
-
# Built-in '/model' slash command
|
32
|
-
@slash_registry.register('/model')
|
33
|
-
def _model_command(blueprint=None, args=None):
|
34
|
-
"""
|
35
|
-
/model Show current default and overrides
|
36
|
-
/model <profile> Set session default LLM profile
|
37
|
-
/model <agent> <profile> Override model profile for specific agent
|
38
|
-
"""
|
39
|
-
# Sanity: blueprint must be provided
|
40
|
-
if blueprint is None:
|
41
|
-
return "No blueprint context available."
|
42
|
-
profiles = list(blueprint.config.get('llm', {}).keys())
|
43
|
-
# No args: list current settings
|
44
|
-
if not args:
|
45
|
-
lines = [f"Session default: {blueprint._session_model_profile}"]
|
46
|
-
for agent, prof in blueprint._agent_model_overrides.items():
|
47
|
-
lines.append(f"Agent {agent}: {prof}")
|
48
|
-
lines.append("Available profiles: " + ", ".join(profiles))
|
49
|
-
return lines
|
50
|
-
parts = args.split()
|
51
|
-
# Set session default
|
52
|
-
if len(parts) == 1:
|
53
|
-
prof = parts[0]
|
54
|
-
if prof not in profiles:
|
55
|
-
return f"Unknown profile '{prof}'. Available: {', '.join(profiles)}"
|
56
|
-
blueprint._session_model_profile = prof
|
57
|
-
return f"Session default LLM profile set to '{prof}'"
|
58
|
-
# Override specific agent
|
59
|
-
if len(parts) == 2:
|
60
|
-
agent_name, prof = parts
|
61
|
-
if prof not in profiles:
|
62
|
-
return f"Unknown profile '{prof}'. Available: {', '.join(profiles)}"
|
63
|
-
blueprint._agent_model_overrides[agent_name] = prof
|
64
|
-
return f"Model for agent '{agent_name}' overridden to '{prof}'"
|
65
|
-
return "Usage: /model [agent_name] <profile_name>"
|
66
|
-
|
67
|
-
# Built-in '/approval' slash command
|
68
|
-
@slash_registry.register('/approval')
|
69
|
-
def _approval_command(blueprint=None, args=None):
|
70
|
-
"""Toggle or display auto-approval mode."""
|
71
|
-
return "[slash command] approval mode not implemented yet."
|
72
|
-
|
73
|
-
# Built-in '/history' slash command
|
74
|
-
@slash_registry.register('/history')
|
75
|
-
def _history_command(blueprint=None, args=None):
|
76
|
-
"""Display session history of commands and files."""
|
77
|
-
return "[slash command] history not implemented yet."
|
78
|
-
|
79
|
-
# Built-in '/clear' slash command
|
80
|
-
@slash_registry.register('/clear')
|
81
|
-
def _clear_command(blueprint=None, args=None):
|
82
|
-
"""Clear the screen and current context."""
|
83
|
-
return "[slash command] context cleared."
|
84
|
-
|
85
|
-
# Built-in '/clearhistory' slash command
|
86
|
-
@slash_registry.register('/clearhistory')
|
87
|
-
def _clearhistory_command(blueprint=None, args=None):
|
88
|
-
"""Clear the command history."""
|
89
|
-
return "[slash command] command history cleared."
|
swarm/core/swarm_api.py
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
import argparse
|
3
|
-
import subprocess
|
4
|
-
import sys
|
5
|
-
from os import path, listdir, makedirs
|
6
|
-
|
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)
|
66
|
-
|
67
|
-
if __name__ == "__main__":
|
68
|
-
main()
|
swarm/core/swarm_cli.py
DELETED
@@ -1,216 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import typer
|
3
|
-
import platformdirs
|
4
|
-
import subprocess
|
5
|
-
import sys
|
6
|
-
from pathlib import Path
|
7
|
-
import importlib.resources as pkg_resources
|
8
|
-
import swarm # Import the main package to access its resources
|
9
|
-
|
10
|
-
# --- Configuration ---
|
11
|
-
APP_NAME = "swarm"
|
12
|
-
APP_AUTHOR = "swarm-authors" # Replace if needed
|
13
|
-
|
14
|
-
# Use platformdirs for user-specific data, config locations
|
15
|
-
USER_DATA_DIR = Path(os.getenv("SWARM_USER_DATA_DIR", platformdirs.user_data_dir(APP_NAME, APP_AUTHOR)))
|
16
|
-
USER_CONFIG_DIR = Path(os.getenv("SWARM_USER_CONFIG_DIR", platformdirs.user_config_dir(APP_NAME, APP_AUTHOR)))
|
17
|
-
|
18
|
-
# *** CORRECTED: Define user bin dir as a subdir of user data dir ***
|
19
|
-
USER_BIN_DIR = Path(os.getenv("SWARM_USER_BIN_DIR", USER_DATA_DIR / "bin"))
|
20
|
-
|
21
|
-
# Derived paths
|
22
|
-
BLUEPRINTS_DIR = USER_DATA_DIR / "blueprints"
|
23
|
-
INSTALLED_BIN_DIR = USER_BIN_DIR # Keep using this variable name for clarity
|
24
|
-
|
25
|
-
# Ensure directories exist
|
26
|
-
BLUEPRINTS_DIR.mkdir(parents=True, exist_ok=True)
|
27
|
-
INSTALLED_BIN_DIR.mkdir(parents=True, exist_ok=True) # Ensure the user bin dir is created
|
28
|
-
USER_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
29
|
-
|
30
|
-
|
31
|
-
# --- Typer App ---
|
32
|
-
app = typer.Typer(
|
33
|
-
help="Swarm CLI tool for managing blueprints.",
|
34
|
-
add_completion=True # Enable shell completion commands
|
35
|
-
)
|
36
|
-
|
37
|
-
# --- Helper Functions ---
|
38
|
-
def find_entry_point(blueprint_dir: Path) -> str | None:
|
39
|
-
"""Placeholder: Finds the main Python script in a blueprint directory."""
|
40
|
-
# Improve this logic: Look for a specific file, check pyproject.toml, etc.
|
41
|
-
for item in blueprint_dir.glob("*.py"):
|
42
|
-
if item.is_file() and not item.name.startswith("_"):
|
43
|
-
return item.name
|
44
|
-
return None
|
45
|
-
|
46
|
-
# --- CLI Commands ---
|
47
|
-
|
48
|
-
@app.command()
|
49
|
-
def install(
|
50
|
-
blueprint_name: str = typer.Argument(..., help="Name of the blueprint directory to install."),
|
51
|
-
# Add options for specifying source dir if needed later
|
52
|
-
):
|
53
|
-
"""
|
54
|
-
Install a blueprint by creating a standalone executable using PyInstaller.
|
55
|
-
"""
|
56
|
-
# Decide where to look for the source blueprint: User dir first, then bundled?
|
57
|
-
# For now, let's assume it must exist in the user dir for installation
|
58
|
-
# TODO: Enhance this to allow installing bundled blueprints directly?
|
59
|
-
source_dir = BLUEPRINTS_DIR / blueprint_name
|
60
|
-
if not source_dir.is_dir():
|
61
|
-
# Could also check bundled blueprints here if desired
|
62
|
-
typer.echo(f"Error: Blueprint source directory not found in user directory: {source_dir}", err=True)
|
63
|
-
typer.echo("Currently, only blueprints placed in the user directory can be installed.", err=True)
|
64
|
-
raise typer.Exit(code=1)
|
65
|
-
|
66
|
-
entry_point = find_entry_point(source_dir)
|
67
|
-
if not entry_point:
|
68
|
-
typer.echo(f"Error: Could not find entry point script in {source_dir}", err=True)
|
69
|
-
raise typer.Exit(code=1)
|
70
|
-
|
71
|
-
entry_point_path = source_dir / entry_point
|
72
|
-
output_bin = INSTALLED_BIN_DIR / blueprint_name
|
73
|
-
dist_path = USER_DATA_DIR / "dist" # PyInstaller dist path within user data
|
74
|
-
build_path = USER_DATA_DIR / "build" # PyInstaller build path within user data
|
75
|
-
|
76
|
-
typer.echo(f"Installing blueprint '{blueprint_name}'...")
|
77
|
-
typer.echo(f" Source: {source_dir}")
|
78
|
-
typer.echo(f" Entry Point: {entry_point}")
|
79
|
-
typer.echo(f" Output Executable: {output_bin}")
|
80
|
-
|
81
|
-
pyinstaller_cmd = [
|
82
|
-
"pyinstaller",
|
83
|
-
"--onefile", # Create a single executable file
|
84
|
-
"--name", str(output_bin.name), # Name of the output executable
|
85
|
-
"--distpath", str(INSTALLED_BIN_DIR), # Output directory for the executable
|
86
|
-
"--workpath", str(build_path), # Directory for temporary build files
|
87
|
-
"--specpath", str(USER_DATA_DIR), # Directory for the .spec file
|
88
|
-
str(entry_point_path), # The main script to bundle
|
89
|
-
]
|
90
|
-
|
91
|
-
typer.echo(f"Running PyInstaller: {' '.join(map(str, pyinstaller_cmd))}") # Use map(str,...) for Path objects
|
92
|
-
|
93
|
-
try:
|
94
|
-
# Use subprocess.run for better control and error handling
|
95
|
-
result = subprocess.run(pyinstaller_cmd, check=True, capture_output=True, text=True)
|
96
|
-
typer.echo("PyInstaller output:")
|
97
|
-
typer.echo(result.stdout)
|
98
|
-
typer.echo(f"Successfully installed '{blueprint_name}' to {output_bin}")
|
99
|
-
except FileNotFoundError:
|
100
|
-
typer.echo("Error: PyInstaller command not found. Is PyInstaller installed?", err=True)
|
101
|
-
raise typer.Exit(code=1)
|
102
|
-
except subprocess.CalledProcessError as e:
|
103
|
-
typer.echo(f"Error during PyInstaller execution (Return Code: {e.returncode}):", err=True)
|
104
|
-
typer.echo(e.stderr, err=True)
|
105
|
-
typer.echo("Check the output above for details.", err=True)
|
106
|
-
raise typer.Exit(code=1)
|
107
|
-
except Exception as e:
|
108
|
-
typer.echo(f"An unexpected error occurred: {e}", err=True)
|
109
|
-
raise typer.Exit(code=1)
|
110
|
-
|
111
|
-
|
112
|
-
@app.command()
|
113
|
-
def launch(blueprint_name: str = typer.Argument(..., help="Name of the installed blueprint executable to launch.")):
|
114
|
-
"""
|
115
|
-
Launch a previously installed blueprint executable.
|
116
|
-
"""
|
117
|
-
executable_path = INSTALLED_BIN_DIR / blueprint_name
|
118
|
-
if not executable_path.is_file() or not os.access(executable_path, os.X_OK):
|
119
|
-
typer.echo(f"Error: Blueprint executable not found or not executable: {executable_path}", err=True)
|
120
|
-
raise typer.Exit(code=1)
|
121
|
-
|
122
|
-
typer.echo(f"Launching '{blueprint_name}' from {executable_path}...")
|
123
|
-
try:
|
124
|
-
# Execute the blueprint directly
|
125
|
-
# Using subprocess.run is generally safer and more flexible than os.execv
|
126
|
-
result = subprocess.run([str(executable_path)], capture_output=True, text=True, check=False) # check=False to handle non-zero exits gracefully
|
127
|
-
typer.echo(f"--- {blueprint_name} Output ---")
|
128
|
-
typer.echo(result.stdout)
|
129
|
-
if result.stderr:
|
130
|
-
typer.echo("--- Errors/Warnings ---", err=True)
|
131
|
-
typer.echo(result.stderr, err=True)
|
132
|
-
typer.echo(f"--- '{blueprint_name}' finished (Return Code: {result.returncode}) ---")
|
133
|
-
|
134
|
-
except Exception as e:
|
135
|
-
typer.echo(f"Error launching blueprint: {e}", err=True)
|
136
|
-
raise typer.Exit(code=1)
|
137
|
-
|
138
|
-
|
139
|
-
@app.command(name="list")
|
140
|
-
def list_blueprints(
|
141
|
-
installed: bool = typer.Option(False, "--installed", "-i", help="List only installed blueprint executables."),
|
142
|
-
available: bool = typer.Option(False, "--available", "-a", help="List only available blueprints (source dirs).")
|
143
|
-
):
|
144
|
-
"""
|
145
|
-
Lists available blueprints (bundled and user-provided) and/or installed executables.
|
146
|
-
"""
|
147
|
-
list_installed = not available or installed
|
148
|
-
list_available = not installed or available
|
149
|
-
|
150
|
-
# --- List Installed Blueprints ---
|
151
|
-
if list_installed:
|
152
|
-
typer.echo(f"--- Installed Blueprints (in {INSTALLED_BIN_DIR}) ---")
|
153
|
-
found_installed = False
|
154
|
-
if INSTALLED_BIN_DIR.exists():
|
155
|
-
try:
|
156
|
-
for item in INSTALLED_BIN_DIR.iterdir():
|
157
|
-
# Basic check: is it a file and executable? Refine as needed.
|
158
|
-
if item.is_file() and os.access(item, os.X_OK):
|
159
|
-
typer.echo(f"- {item.name}")
|
160
|
-
found_installed = True
|
161
|
-
except OSError as e:
|
162
|
-
typer.echo(f"(Warning: Could not read installed directory: {e})", err=True)
|
163
|
-
if not found_installed:
|
164
|
-
typer.echo("(No installed blueprint executables found)")
|
165
|
-
typer.echo("") # Add spacing
|
166
|
-
|
167
|
-
# --- List Available Blueprints (Bundled and User) ---
|
168
|
-
if list_available:
|
169
|
-
# --- Bundled ---
|
170
|
-
typer.echo("--- Bundled Blueprints (installed with package) ---")
|
171
|
-
bundled_found = False
|
172
|
-
try:
|
173
|
-
# Use importlib.resources to access the 'blueprints' directory within the installed 'swarm' package
|
174
|
-
bundled_blueprints_path = pkg_resources.files(swarm) / 'blueprints'
|
175
|
-
|
176
|
-
if bundled_blueprints_path.is_dir(): # Check if the directory exists in the package
|
177
|
-
for item in bundled_blueprints_path.iterdir():
|
178
|
-
# Check if it's a directory containing an entry point (adapt check as needed)
|
179
|
-
if item.is_dir() and not item.name.startswith("__"): # Skip __pycache__ etc.
|
180
|
-
entry_point = find_entry_point(item) # Use helper, might need refinement
|
181
|
-
if entry_point:
|
182
|
-
typer.echo(f"- {item.name} (entry: {entry_point})")
|
183
|
-
bundled_found = True
|
184
|
-
except ModuleNotFoundError:
|
185
|
-
typer.echo("(Could not find bundled blueprints - package structure issue?)", err=True)
|
186
|
-
except FileNotFoundError: # Can happen if package data wasn't included correctly
|
187
|
-
typer.echo("(Could not find bundled blueprints path - package data missing?)", err=True)
|
188
|
-
except Exception as e:
|
189
|
-
typer.echo(f"(Error accessing bundled blueprints: {e})", err=True)
|
190
|
-
|
191
|
-
if not bundled_found:
|
192
|
-
typer.echo("(No bundled blueprints found or accessible)")
|
193
|
-
typer.echo("") # Add spacing
|
194
|
-
|
195
|
-
# --- User ---
|
196
|
-
typer.echo(f"--- User Blueprints (in {BLUEPRINTS_DIR}) ---")
|
197
|
-
user_found = False
|
198
|
-
if BLUEPRINTS_DIR.exists() and BLUEPRINTS_DIR.is_dir():
|
199
|
-
try:
|
200
|
-
for item in BLUEPRINTS_DIR.iterdir():
|
201
|
-
if item.is_dir():
|
202
|
-
entry_point = find_entry_point(item) # Use helper
|
203
|
-
if entry_point:
|
204
|
-
typer.echo(f"- {item.name} (entry: {entry_point})")
|
205
|
-
user_found = True
|
206
|
-
except OSError as e:
|
207
|
-
typer.echo(f"(Warning: Could not read user blueprints directory: {e})", err=True)
|
208
|
-
|
209
|
-
if not user_found:
|
210
|
-
typer.echo("(No user blueprints found)")
|
211
|
-
typer.echo("") # Add spacing
|
212
|
-
|
213
|
-
|
214
|
-
# --- Main Execution Guard ---
|
215
|
-
if __name__ == "__main__":
|
216
|
-
app()
|
swarm/core/utils/__init__.py
DELETED
File without changes
|