open-swarm 0.1.1743070217__py3-none-any.whl → 0.1.1743362777__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.1743362777.dist-info/METADATA +217 -0
- open_swarm-0.1.1743362777.dist-info/RECORD +260 -0
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/WHEEL +1 -2
- open_swarm-0.1.1743362777.dist-info/entry_points.txt +2 -0
- swarm/__init__.py +0 -2
- swarm/auth.py +53 -49
- swarm/blueprints/README.md +67 -0
- swarm/blueprints/burnt_noodles/blueprint_burnt_noodles.py +412 -0
- swarm/blueprints/chatbot/blueprint_chatbot.py +98 -0
- swarm/blueprints/chatbot/templates/chatbot/chatbot.html +33 -0
- swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +183 -0
- swarm/blueprints/dilbot_universe/blueprint_dilbot_universe.py +285 -0
- swarm/blueprints/divine_code/__init__.py +0 -0
- swarm/blueprints/divine_code/apps.py +11 -0
- swarm/blueprints/divine_code/blueprint_divine_code.py +219 -0
- swarm/blueprints/django_chat/apps.py +6 -0
- swarm/blueprints/django_chat/blueprint_django_chat.py +84 -0
- swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +37 -0
- swarm/blueprints/django_chat/urls.py +8 -0
- swarm/blueprints/django_chat/views.py +32 -0
- swarm/blueprints/echocraft/blueprint_echocraft.py +44 -0
- swarm/blueprints/family_ties/apps.py +11 -0
- swarm/blueprints/family_ties/blueprint_family_ties.py +152 -0
- swarm/blueprints/family_ties/models.py +19 -0
- swarm/blueprints/family_ties/serializers.py +7 -0
- swarm/blueprints/family_ties/settings.py +16 -0
- swarm/blueprints/family_ties/urls.py +10 -0
- swarm/blueprints/family_ties/views.py +26 -0
- swarm/blueprints/flock/__init__.py +0 -0
- swarm/blueprints/gaggle/blueprint_gaggle.py +184 -0
- swarm/blueprints/gotchaman/blueprint_gotchaman.py +232 -0
- swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +133 -0
- swarm/blueprints/messenger/templates/messenger/messenger.html +46 -0
- swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +234 -0
- swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +248 -0
- swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +156 -0
- swarm/blueprints/omniplex/blueprint_omniplex.py +221 -0
- swarm/blueprints/rue_code/__init__.py +0 -0
- swarm/blueprints/rue_code/blueprint_rue_code.py +291 -0
- swarm/blueprints/suggestion/blueprint_suggestion.py +110 -0
- swarm/blueprints/unapologetic_press/blueprint_unapologetic_press.py +298 -0
- swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
- swarm/blueprints/whiskeytango_foxtrot/apps.py +11 -0
- swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +256 -0
- swarm/extensions/blueprint/__init__.py +30 -15
- swarm/extensions/blueprint/agent_utils.py +16 -40
- swarm/extensions/blueprint/blueprint_base.py +141 -543
- swarm/extensions/blueprint/blueprint_discovery.py +112 -98
- swarm/extensions/blueprint/cli_handler.py +185 -0
- swarm/extensions/blueprint/config_loader.py +122 -0
- swarm/extensions/blueprint/django_utils.py +181 -79
- swarm/extensions/blueprint/interactive_mode.py +1 -1
- swarm/extensions/config/config_loader.py +83 -200
- swarm/extensions/launchers/build_swarm_wrapper.py +0 -0
- swarm/extensions/launchers/swarm_cli.py +199 -287
- swarm/llm/chat_completion.py +26 -55
- swarm/management/__init__.py +0 -0
- swarm/management/commands/__init__.py +0 -0
- swarm/management/commands/runserver.py +58 -0
- swarm/permissions.py +38 -0
- swarm/serializers.py +96 -5
- swarm/settings.py +95 -110
- swarm/static/contrib/fonts/fontawesome-webfont.ttf +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff2 +7 -0
- swarm/static/contrib/markedjs/marked.min.js +6 -0
- swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +27 -0
- swarm/static/contrib/tabler-icons/alert-triangle.svg +21 -0
- swarm/static/contrib/tabler-icons/archive.svg +21 -0
- swarm/static/contrib/tabler-icons/artboard.svg +27 -0
- swarm/static/contrib/tabler-icons/automatic-gearbox.svg +23 -0
- swarm/static/contrib/tabler-icons/box-multiple.svg +19 -0
- swarm/static/contrib/tabler-icons/carambola.svg +19 -0
- swarm/static/contrib/tabler-icons/copy.svg +20 -0
- swarm/static/contrib/tabler-icons/download.svg +21 -0
- swarm/static/contrib/tabler-icons/edit.svg +21 -0
- swarm/static/contrib/tabler-icons/filled/carambola.svg +13 -0
- swarm/static/contrib/tabler-icons/filled/paint.svg +13 -0
- swarm/static/contrib/tabler-icons/headset.svg +22 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/message-chatbot.svg +22 -0
- swarm/static/contrib/tabler-icons/message-star.svg +22 -0
- swarm/static/contrib/tabler-icons/message-x.svg +23 -0
- swarm/static/contrib/tabler-icons/message.svg +21 -0
- swarm/static/contrib/tabler-icons/paperclip.svg +18 -0
- swarm/static/contrib/tabler-icons/playlist-add.svg +22 -0
- swarm/static/contrib/tabler-icons/robot.svg +26 -0
- swarm/static/contrib/tabler-icons/search.svg +19 -0
- swarm/static/contrib/tabler-icons/settings.svg +20 -0
- swarm/static/contrib/tabler-icons/thumb-down.svg +19 -0
- swarm/static/contrib/tabler-icons/thumb-up.svg +19 -0
- swarm/static/css/dropdown.css +22 -0
- swarm/static/htmx/htmx.min.js +0 -0
- swarm/static/js/dropdown.js +23 -0
- swarm/static/rest_mode/css/base.css +470 -0
- swarm/static/rest_mode/css/chat-history.css +286 -0
- swarm/static/rest_mode/css/chat.css +251 -0
- swarm/static/rest_mode/css/chatbot.css +74 -0
- swarm/static/rest_mode/css/chatgpt.css +62 -0
- swarm/static/rest_mode/css/colors/corporate.css +74 -0
- swarm/static/rest_mode/css/colors/pastel.css +81 -0
- swarm/static/rest_mode/css/colors/tropical.css +82 -0
- swarm/static/rest_mode/css/general.css +142 -0
- swarm/static/rest_mode/css/layout.css +167 -0
- swarm/static/rest_mode/css/layouts/messenger-layout.css +17 -0
- swarm/static/rest_mode/css/layouts/minimalist-layout.css +57 -0
- swarm/static/rest_mode/css/layouts/mobile-layout.css +8 -0
- swarm/static/rest_mode/css/messages.css +84 -0
- swarm/static/rest_mode/css/messenger.css +135 -0
- swarm/static/rest_mode/css/settings.css +91 -0
- swarm/static/rest_mode/css/simple.css +44 -0
- swarm/static/rest_mode/css/slack.css +58 -0
- swarm/static/rest_mode/css/style.css +156 -0
- swarm/static/rest_mode/css/theme.css +30 -0
- swarm/static/rest_mode/css/toast.css +40 -0
- swarm/static/rest_mode/js/auth.js +9 -0
- swarm/static/rest_mode/js/blueprint.js +41 -0
- swarm/static/rest_mode/js/blueprintUtils.js +12 -0
- swarm/static/rest_mode/js/chatLogic.js +79 -0
- swarm/static/rest_mode/js/debug.js +63 -0
- swarm/static/rest_mode/js/events.js +98 -0
- swarm/static/rest_mode/js/main.js +19 -0
- swarm/static/rest_mode/js/messages.js +264 -0
- swarm/static/rest_mode/js/messengerLogic.js +355 -0
- swarm/static/rest_mode/js/modules/apiService.js +84 -0
- swarm/static/rest_mode/js/modules/blueprintManager.js +162 -0
- swarm/static/rest_mode/js/modules/chatHistory.js +110 -0
- swarm/static/rest_mode/js/modules/debugLogger.js +14 -0
- swarm/static/rest_mode/js/modules/eventHandlers.js +107 -0
- swarm/static/rest_mode/js/modules/messageProcessor.js +120 -0
- swarm/static/rest_mode/js/modules/state.js +7 -0
- swarm/static/rest_mode/js/modules/userInteractions.js +29 -0
- swarm/static/rest_mode/js/modules/validation.js +23 -0
- swarm/static/rest_mode/js/rendering.js +119 -0
- swarm/static/rest_mode/js/settings.js +130 -0
- swarm/static/rest_mode/js/sidebar.js +94 -0
- swarm/static/rest_mode/js/simpleLogic.js +37 -0
- swarm/static/rest_mode/js/slackLogic.js +66 -0
- swarm/static/rest_mode/js/splash.js +76 -0
- swarm/static/rest_mode/js/theme.js +111 -0
- swarm/static/rest_mode/js/toast.js +36 -0
- swarm/static/rest_mode/js/ui.js +265 -0
- swarm/static/rest_mode/js/validation.js +57 -0
- swarm/static/rest_mode/svg/animated_spinner.svg +12 -0
- swarm/static/rest_mode/svg/arrow_down.svg +5 -0
- swarm/static/rest_mode/svg/arrow_left.svg +5 -0
- swarm/static/rest_mode/svg/arrow_right.svg +5 -0
- swarm/static/rest_mode/svg/arrow_up.svg +5 -0
- swarm/static/rest_mode/svg/attach.svg +8 -0
- swarm/static/rest_mode/svg/avatar.svg +7 -0
- swarm/static/rest_mode/svg/canvas.svg +6 -0
- swarm/static/rest_mode/svg/chat_history.svg +4 -0
- swarm/static/rest_mode/svg/close.svg +5 -0
- swarm/static/rest_mode/svg/copy.svg +4 -0
- swarm/static/rest_mode/svg/dark_mode.svg +3 -0
- swarm/static/rest_mode/svg/edit.svg +5 -0
- swarm/static/rest_mode/svg/layout.svg +9 -0
- swarm/static/rest_mode/svg/logo.svg +29 -0
- swarm/static/rest_mode/svg/logout.svg +5 -0
- swarm/static/rest_mode/svg/mobile.svg +5 -0
- swarm/static/rest_mode/svg/new_chat.svg +4 -0
- swarm/static/rest_mode/svg/not_visible.svg +5 -0
- swarm/static/rest_mode/svg/plus.svg +7 -0
- swarm/static/rest_mode/svg/run_code.svg +6 -0
- swarm/static/rest_mode/svg/save.svg +4 -0
- swarm/static/rest_mode/svg/search.svg +6 -0
- swarm/static/rest_mode/svg/settings.svg +4 -0
- swarm/static/rest_mode/svg/speaker.svg +5 -0
- swarm/static/rest_mode/svg/stop.svg +6 -0
- swarm/static/rest_mode/svg/thumbs_down.svg +3 -0
- swarm/static/rest_mode/svg/thumbs_up.svg +3 -0
- swarm/static/rest_mode/svg/toggle_off.svg +6 -0
- swarm/static/rest_mode/svg/toggle_on.svg +6 -0
- swarm/static/rest_mode/svg/trash.svg +10 -0
- swarm/static/rest_mode/svg/undo.svg +3 -0
- swarm/static/rest_mode/svg/visible.svg +8 -0
- swarm/static/rest_mode/svg/voice.svg +10 -0
- swarm/templates/account/login.html +22 -0
- swarm/templates/account/signup.html +32 -0
- swarm/templates/base.html +30 -0
- swarm/templates/chat.html +43 -0
- swarm/templates/index.html +35 -0
- swarm/templates/rest_mode/components/chat_sidebar.html +55 -0
- swarm/templates/rest_mode/components/header.html +45 -0
- swarm/templates/rest_mode/components/main_chat_pane.html +41 -0
- swarm/templates/rest_mode/components/settings_dialog.html +97 -0
- swarm/templates/rest_mode/components/splash_screen.html +7 -0
- swarm/templates/rest_mode/components/top_bar.html +28 -0
- swarm/templates/rest_mode/message_ui.html +50 -0
- swarm/templates/rest_mode/slackbot.html +30 -0
- swarm/templates/simple_blueprint_page.html +24 -0
- swarm/templates/websocket_partials/final_system_message.html +3 -0
- swarm/templates/websocket_partials/system_message.html +4 -0
- swarm/templates/websocket_partials/user_message.html +5 -0
- swarm/urls.py +57 -74
- swarm/utils/log_utils.py +63 -0
- swarm/views/api_views.py +48 -39
- swarm/views/chat_views.py +156 -70
- swarm/views/core_views.py +85 -90
- swarm/views/model_views.py +64 -121
- swarm/views/utils.py +65 -441
- open_swarm-0.1.1743070217.dist-info/METADATA +0 -258
- open_swarm-0.1.1743070217.dist-info/RECORD +0 -89
- open_swarm-0.1.1743070217.dist-info/entry_points.txt +0 -3
- open_swarm-0.1.1743070217.dist-info/top_level.txt +0 -1
- swarm/agent/agent.py +0 -49
- swarm/core.py +0 -326
- swarm/extensions/mcp/__init__.py +0 -1
- swarm/extensions/mcp/cache_utils.py +0 -36
- swarm/extensions/mcp/mcp_client.py +0 -341
- swarm/extensions/mcp/mcp_constants.py +0 -7
- swarm/extensions/mcp/mcp_tool_provider.py +0 -110
- swarm/types.py +0 -126
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/licenses/LICENSE +0 -0
@@ -1,304 +1,216 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
import argparse
|
3
|
-
import importlib.util
|
4
1
|
import os
|
5
|
-
import
|
2
|
+
import typer
|
3
|
+
import platformdirs
|
6
4
|
import subprocess
|
7
|
-
import
|
8
|
-
import
|
9
|
-
import
|
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
|
10
24
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
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)
|
20
29
|
|
21
|
-
MANAGED_DIR = os.path.expanduser("~/.swarm/blueprints")
|
22
|
-
BIN_DIR = os.path.expanduser("~/.swarm/bin")
|
23
30
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
31
|
+
# --- Typer App ---
|
32
|
+
app = typer.Typer(
|
33
|
+
help="Swarm CLI tool for managing blueprints.",
|
34
|
+
add_completion=True # Enable shell completion commands
|
35
|
+
)
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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}.")
|
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
|
62
45
|
|
63
|
-
|
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.")
|
46
|
+
# --- CLI Commands ---
|
73
47
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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)
|
82
65
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
blueprint
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
104
92
|
|
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
93
|
try:
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
except
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
+
|
125
111
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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)
|
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)
|
156
121
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
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":
|
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
|
225
172
|
try:
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
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():
|
241
199
|
try:
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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)
|
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
|
+
|
302
213
|
|
214
|
+
# --- Main Execution Guard ---
|
303
215
|
if __name__ == "__main__":
|
304
|
-
|
216
|
+
app()
|