mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-27 15:24:56 +00:00
feat: Propagate workflow plugins to Go, Rust, C++, and Mojo
Multi-language workflow plugin system following Python's structure: - Each plugin in its own directory with implementation + package.json - Category-level package.json manifests listing all plugins - Consistent interface: run(runtime, inputs) -> outputs Languages added: - Go: math, string, logic, list, dict, var, convert (25+ plugins) - Rust: math, string, logic, list, convert, var (50+ functions) - C++: header-only math, string, logic, var, convert (30+ plugins) - Mojo: math, string, list with systems programming features Python structure fixed: - Reorganized flat files into plugin subdirectories - Added package.json metadata to all 120+ plugins - Added missing backend category (15 plugins) - Category manifests with plugin lists Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
1
workflow/plugins/python/backend/__init__.py
Normal file
1
workflow/plugins/python/backend/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Backend infrastructure and initialization plugins."""
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Workflow plugin: build tool map for function dispatch."""
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Build a map from tool names to their handlers.
|
||||
|
||||
This reads plugins from context and builds a dispatch map.
|
||||
"""
|
||||
plugins = runtime.context.get("plugins", {})
|
||||
|
||||
tool_map = {}
|
||||
for plugin_type, plugin_info in plugins.items():
|
||||
# Map plugin_type (e.g., "math.add") to handler info
|
||||
tool_map[plugin_type] = plugin_info
|
||||
|
||||
runtime.context["tool_map"] = tool_map
|
||||
|
||||
return {"success": True, "tool_count": len(tool_map)}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_build_tool_map",
|
||||
"version": "1.0.0",
|
||||
"description": "Build tool map for function dispatch",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "tools"],
|
||||
"main": "backend_build_tool_map.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.build_tool_map",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Workflow plugin: configure logging."""
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Configure logging for the workflow runtime.
|
||||
|
||||
Inputs:
|
||||
level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||
format: Log format string
|
||||
file: Optional file path for log output
|
||||
"""
|
||||
level_str = inputs.get("level", "INFO").upper()
|
||||
log_format = inputs.get("format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||
log_file = inputs.get("file")
|
||||
|
||||
level = getattr(logging, level_str, logging.INFO)
|
||||
|
||||
handlers = [logging.StreamHandler(sys.stdout)]
|
||||
if log_file:
|
||||
handlers.append(logging.FileHandler(log_file))
|
||||
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format=log_format,
|
||||
handlers=handlers
|
||||
)
|
||||
|
||||
logger = logging.getLogger("metabuilder")
|
||||
logger.setLevel(level)
|
||||
|
||||
runtime.context["logger"] = logger
|
||||
|
||||
return {"success": True, "level": level_str}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_configure_logging",
|
||||
"version": "1.0.0",
|
||||
"description": "Configure logging for workflow runtime",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "logging"],
|
||||
"main": "backend_configure_logging.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.configure_logging",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Workflow plugin: create Discord client."""
|
||||
import os
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Create a Discord webhook client and store in runtime context.
|
||||
|
||||
Inputs:
|
||||
webhook_url: Discord webhook URL (defaults to DISCORD_WEBHOOK_URL env var)
|
||||
"""
|
||||
webhook_url = inputs.get("webhook_url") or os.getenv("DISCORD_WEBHOOK_URL")
|
||||
|
||||
if not webhook_url:
|
||||
return {"success": False, "error": "No webhook URL provided"}
|
||||
|
||||
runtime.context["discord_webhook"] = webhook_url
|
||||
|
||||
return {"success": True}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_create_discord",
|
||||
"version": "1.0.0",
|
||||
"description": "Create Discord webhook client for notifications",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "discord"],
|
||||
"main": "backend_create_discord.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.create_discord",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Workflow plugin: create GitHub client."""
|
||||
import os
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Create a GitHub client and store in runtime context.
|
||||
|
||||
Inputs:
|
||||
token: GitHub token (defaults to GITHUB_TOKEN env var)
|
||||
"""
|
||||
try:
|
||||
from github import Github
|
||||
except ImportError:
|
||||
return {"success": False, "error": "PyGithub package not installed"}
|
||||
|
||||
token = inputs.get("token") or os.getenv("GITHUB_TOKEN")
|
||||
|
||||
if not token:
|
||||
return {"success": False, "error": "No token provided"}
|
||||
|
||||
client = Github(token)
|
||||
|
||||
runtime.context["github"] = client
|
||||
|
||||
return {"success": True}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_create_github",
|
||||
"version": "1.0.0",
|
||||
"description": "Create GitHub client for repository operations",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "github"],
|
||||
"main": "backend_create_github.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.create_github",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Workflow plugin: create OpenAI client."""
|
||||
import os
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Create an OpenAI client and store in runtime context.
|
||||
|
||||
Inputs:
|
||||
api_key: OpenAI API key (defaults to OPENAI_API_KEY env var)
|
||||
model: Model name (default: gpt-4)
|
||||
"""
|
||||
try:
|
||||
from openai import OpenAI
|
||||
except ImportError:
|
||||
return {"success": False, "error": "openai package not installed"}
|
||||
|
||||
api_key = inputs.get("api_key") or os.getenv("OPENAI_API_KEY")
|
||||
model = inputs.get("model", "gpt-4")
|
||||
|
||||
if not api_key:
|
||||
return {"success": False, "error": "No API key provided"}
|
||||
|
||||
client = OpenAI(api_key=api_key)
|
||||
|
||||
runtime.context["client"] = client
|
||||
runtime.context["model_name"] = model
|
||||
|
||||
return {"success": True, "model": model}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_create_openai",
|
||||
"version": "1.0.0",
|
||||
"description": "Create OpenAI client for AI operations",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "openai", "ai"],
|
||||
"main": "backend_create_openai.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.create_openai",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Workflow plugin: create Slack client."""
|
||||
import os
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Create a Slack client and store in runtime context.
|
||||
|
||||
Inputs:
|
||||
token: Slack bot token (defaults to SLACK_BOT_TOKEN env var)
|
||||
"""
|
||||
try:
|
||||
from slack_sdk import WebClient
|
||||
except ImportError:
|
||||
return {"success": False, "error": "slack_sdk package not installed"}
|
||||
|
||||
token = inputs.get("token") or os.getenv("SLACK_BOT_TOKEN")
|
||||
|
||||
if not token:
|
||||
return {"success": False, "error": "No token provided"}
|
||||
|
||||
client = WebClient(token=token)
|
||||
|
||||
runtime.context["slack"] = client
|
||||
|
||||
return {"success": True}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_create_slack",
|
||||
"version": "1.0.0",
|
||||
"description": "Create Slack client for messaging",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "slack"],
|
||||
"main": "backend_create_slack.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.create_slack",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Workflow plugin: load environment variables."""
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def run(_runtime, inputs):
|
||||
"""Load environment variables from .env file.
|
||||
|
||||
Inputs:
|
||||
path: Optional path to .env file (default: .env)
|
||||
override: Whether to override existing env vars (default: False)
|
||||
"""
|
||||
path = inputs.get("path", ".env")
|
||||
override = inputs.get("override", False)
|
||||
|
||||
if os.path.exists(path):
|
||||
load_dotenv(path, override=override)
|
||||
return {"success": True, "path": path}
|
||||
|
||||
return {"success": False, "error": f"File not found: {path}"}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_env",
|
||||
"version": "1.0.0",
|
||||
"description": "Load environment variables from .env file",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "env"],
|
||||
"main": "backend_load_env.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_env",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
"""Workflow plugin: load UI/CLI messages."""
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Load UI/CLI messages for localization.
|
||||
|
||||
Inputs:
|
||||
path: Path to messages file
|
||||
locale: Locale code (default: en)
|
||||
"""
|
||||
path = inputs.get("path", "config/messages")
|
||||
locale = inputs.get("locale", "en")
|
||||
|
||||
messages_file = os.path.join(path, f"{locale}.json")
|
||||
|
||||
if not os.path.exists(messages_file):
|
||||
messages_file = os.path.join(path, "en.json") # Fallback
|
||||
|
||||
if not os.path.exists(messages_file):
|
||||
return {"success": False, "error": "No messages file found"}
|
||||
|
||||
with open(messages_file) as f:
|
||||
messages = json.load(f)
|
||||
|
||||
runtime.context["msgs"] = messages
|
||||
|
||||
return {"success": True, "locale": locale, "message_count": len(messages)}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_messages",
|
||||
"version": "1.0.0",
|
||||
"description": "Load UI/CLI messages for localization",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "messages", "i18n"],
|
||||
"main": "backend_load_messages.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_messages",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Workflow plugin: load workflow metadata."""
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Load workflow metadata from package.json or config.
|
||||
|
||||
Inputs:
|
||||
path: Path to metadata file
|
||||
"""
|
||||
path = inputs.get("path", "package.json")
|
||||
|
||||
if not os.path.exists(path):
|
||||
return {"success": False, "error": f"File not found: {path}"}
|
||||
|
||||
with open(path) as f:
|
||||
metadata = json.load(f)
|
||||
|
||||
runtime.context["metadata"] = metadata
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"name": metadata.get("name"),
|
||||
"version": metadata.get("version")
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_metadata",
|
||||
"version": "1.0.0",
|
||||
"description": "Load workflow metadata from config",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "metadata"],
|
||||
"main": "backend_load_metadata.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_metadata",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
"""Workflow plugin: load workflow plugins."""
|
||||
import os
|
||||
import json
|
||||
import importlib.util
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Load workflow plugins from directory.
|
||||
|
||||
Inputs:
|
||||
path: Path to plugins directory
|
||||
"""
|
||||
path = inputs.get("path", "workflow/plugins/python")
|
||||
|
||||
if not os.path.exists(path):
|
||||
return {"success": False, "error": f"Path not found: {path}"}
|
||||
|
||||
plugins = {}
|
||||
categories = []
|
||||
|
||||
for category in os.listdir(path):
|
||||
category_path = os.path.join(path, category)
|
||||
if not os.path.isdir(category_path) or category.startswith("_"):
|
||||
continue
|
||||
|
||||
categories.append(category)
|
||||
|
||||
for plugin_name in os.listdir(category_path):
|
||||
plugin_path = os.path.join(category_path, plugin_name)
|
||||
if not os.path.isdir(plugin_path):
|
||||
continue
|
||||
|
||||
package_json = os.path.join(plugin_path, "package.json")
|
||||
if os.path.exists(package_json):
|
||||
with open(package_json) as f:
|
||||
metadata = json.load(f)
|
||||
plugin_type = metadata.get("metadata", {}).get("plugin_type")
|
||||
if plugin_type:
|
||||
plugins[plugin_type] = {
|
||||
"path": plugin_path,
|
||||
"metadata": metadata
|
||||
}
|
||||
|
||||
runtime.context["plugins"] = plugins
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"categories": categories,
|
||||
"plugin_count": len(plugins)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_plugins",
|
||||
"version": "1.0.0",
|
||||
"description": "Load workflow plugins from directory",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "loader"],
|
||||
"main": "backend_load_plugins.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_plugins",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Workflow plugin: load system prompt."""
|
||||
import os
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Load system prompt from file.
|
||||
|
||||
Inputs:
|
||||
path: Path to prompt file
|
||||
"""
|
||||
path = inputs.get("path", "config/system_prompt.txt")
|
||||
|
||||
if not os.path.exists(path):
|
||||
return {"success": False, "error": f"File not found: {path}"}
|
||||
|
||||
with open(path) as f:
|
||||
prompt = f.read()
|
||||
|
||||
runtime.context["system_prompt"] = prompt
|
||||
|
||||
return {"success": True, "length": len(prompt)}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_prompt",
|
||||
"version": "1.0.0",
|
||||
"description": "Load system prompt from file",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "prompt", "ai"],
|
||||
"main": "backend_load_prompt.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_prompt",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Workflow plugin: load tool policies."""
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Load tool policies for access control.
|
||||
|
||||
Inputs:
|
||||
path: Path to tool policies file
|
||||
"""
|
||||
path = inputs.get("path", "config/tool_policies.json")
|
||||
|
||||
if not os.path.exists(path):
|
||||
# Default to permissive if no policies file
|
||||
runtime.context["tool_policies"] = {}
|
||||
return {"success": True, "policy_count": 0}
|
||||
|
||||
with open(path) as f:
|
||||
policies = json.load(f)
|
||||
|
||||
runtime.context["tool_policies"] = policies
|
||||
|
||||
return {"success": True, "policy_count": len(policies)}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_tool_policies",
|
||||
"version": "1.0.0",
|
||||
"description": "Load tool policies for access control",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "tools", "policy"],
|
||||
"main": "backend_load_tool_policies.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_tool_policies",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
"""Workflow plugin: load tool registry."""
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Load tool registry defining available AI tools.
|
||||
|
||||
Inputs:
|
||||
path: Path to tool registry file
|
||||
"""
|
||||
path = inputs.get("path", "config/tool_registry.json")
|
||||
|
||||
if not os.path.exists(path):
|
||||
return {"success": False, "error": f"File not found: {path}"}
|
||||
|
||||
with open(path) as f:
|
||||
registry = json.load(f)
|
||||
|
||||
runtime.context["tool_registry"] = registry
|
||||
|
||||
return {"success": True, "tool_count": len(registry)}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_tool_registry",
|
||||
"version": "1.0.0",
|
||||
"description": "Load tool registry for AI function calling",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "tools", "registry"],
|
||||
"main": "backend_load_tool_registry.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_tool_registry",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Workflow plugin: load tools for AI function calling."""
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Load tool definitions for AI function calling.
|
||||
|
||||
Inputs:
|
||||
path: Path to tools definition file or directory
|
||||
"""
|
||||
path = inputs.get("path", "config/tools.json")
|
||||
|
||||
tools = []
|
||||
|
||||
if os.path.isfile(path):
|
||||
with open(path) as f:
|
||||
tools = json.load(f)
|
||||
elif os.path.isdir(path):
|
||||
for filename in os.listdir(path):
|
||||
if filename.endswith(".json"):
|
||||
with open(os.path.join(path, filename)) as f:
|
||||
tools.extend(json.load(f))
|
||||
|
||||
runtime.context["tools"] = tools
|
||||
|
||||
return {"success": True, "tool_count": len(tools)}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_load_tools",
|
||||
"version": "1.0.0",
|
||||
"description": "Load tool definitions for AI function calling",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "tools", "ai"],
|
||||
"main": "backend_load_tools.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.load_tools",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Workflow plugin: parse CLI arguments."""
|
||||
import argparse
|
||||
|
||||
|
||||
def run(runtime, inputs):
|
||||
"""Parse command line arguments.
|
||||
|
||||
Inputs:
|
||||
args: Optional list of arguments (defaults to sys.argv)
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description="MetaBuilder Workflow")
|
||||
|
||||
parser.add_argument("--config", "-c", default="config.json",
|
||||
help="Path to configuration file")
|
||||
parser.add_argument("--workflow", "-w",
|
||||
help="Path to workflow file")
|
||||
parser.add_argument("--verbose", "-v", action="store_true",
|
||||
help="Enable verbose output")
|
||||
parser.add_argument("--dry-run", action="store_true",
|
||||
help="Simulate workflow execution")
|
||||
|
||||
args_list = inputs.get("args")
|
||||
parsed = parser.parse_args(args_list)
|
||||
|
||||
runtime.context["cli_args"] = vars(parsed)
|
||||
|
||||
return {"success": True, "args": vars(parsed)}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/backend_parse_cli_args",
|
||||
"version": "1.0.0",
|
||||
"description": "Parse command line arguments",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "workflow", "plugin", "cli", "args"],
|
||||
"main": "backend_parse_cli_args.py",
|
||||
"metadata": {
|
||||
"plugin_type": "backend.parse_cli_args",
|
||||
"category": "backend"
|
||||
}
|
||||
}
|
||||
29
workflow/plugins/python/backend/package.json
Normal file
29
workflow/plugins/python/backend/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@metabuilder/workflow-plugins-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Backend infrastructure and initialization plugins",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["backend", "infrastructure", "workflow", "plugins"],
|
||||
"metadata": {
|
||||
"category": "backend",
|
||||
"plugin_count": 15
|
||||
},
|
||||
"plugins": [
|
||||
"backend_build_tool_map",
|
||||
"backend_configure_logging",
|
||||
"backend_create_discord",
|
||||
"backend_create_github",
|
||||
"backend_create_openai",
|
||||
"backend_create_slack",
|
||||
"backend_load_env",
|
||||
"backend_load_messages",
|
||||
"backend_load_metadata",
|
||||
"backend_load_plugins",
|
||||
"backend_load_prompt",
|
||||
"backend_load_tool_policies",
|
||||
"backend_load_tool_registry",
|
||||
"backend_load_tools",
|
||||
"backend_parse_cli_args"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/control_get_bot_status",
|
||||
"version": "1.0.0",
|
||||
"description": "control_get_bot_status plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["control", "workflow", "plugin"],
|
||||
"main": "control_get_bot_status.py",
|
||||
"metadata": {
|
||||
"plugin_type": "control.get_bot_status",
|
||||
"category": "control"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/control_reset_bot_state",
|
||||
"version": "1.0.0",
|
||||
"description": "control_reset_bot_state plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["control", "workflow", "plugin"],
|
||||
"main": "control_reset_bot_state.py",
|
||||
"metadata": {
|
||||
"plugin_type": "control.reset_bot_state",
|
||||
"category": "control"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/control_start_bot",
|
||||
"version": "1.0.0",
|
||||
"description": "control_start_bot plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["control", "workflow", "plugin"],
|
||||
"main": "control_start_bot.py",
|
||||
"metadata": {
|
||||
"plugin_type": "control.start_bot",
|
||||
"category": "control"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/control/control_switch/package.json
Normal file
13
workflow/plugins/python/control/control_switch/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/control_switch",
|
||||
"version": "1.0.0",
|
||||
"description": "control_switch plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["control", "workflow", "plugin"],
|
||||
"main": "control_switch.py",
|
||||
"metadata": {
|
||||
"plugin_type": "control.switch",
|
||||
"category": "control"
|
||||
}
|
||||
}
|
||||
18
workflow/plugins/python/control/package.json
Normal file
18
workflow/plugins/python/control/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@metabuilder/workflow-plugins-control",
|
||||
"version": "1.0.0",
|
||||
"description": "Control flow plugins",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["control", "flow", "workflow", "plugins"],
|
||||
"metadata": {
|
||||
"category": "control",
|
||||
"plugin_count": 4
|
||||
},
|
||||
"plugins": [
|
||||
"control_get_bot_status",
|
||||
"control_start_bot",
|
||||
"control_stop_bot",
|
||||
"control_switch"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/convert_parse_json",
|
||||
"version": "1.0.0",
|
||||
"description": "convert_parse_json plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "workflow", "plugin"],
|
||||
"main": "convert_parse_json.py",
|
||||
"metadata": {
|
||||
"plugin_type": "convert.parse_json",
|
||||
"category": "convert"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/convert_to_boolean",
|
||||
"version": "1.0.0",
|
||||
"description": "convert_to_boolean plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "workflow", "plugin"],
|
||||
"main": "convert_to_boolean.py",
|
||||
"metadata": {
|
||||
"plugin_type": "convert.to_boolean",
|
||||
"category": "convert"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/convert/convert_to_dict/package.json
Normal file
13
workflow/plugins/python/convert/convert_to_dict/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/convert_to_dict",
|
||||
"version": "1.0.0",
|
||||
"description": "convert_to_dict plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "workflow", "plugin"],
|
||||
"main": "convert_to_dict.py",
|
||||
"metadata": {
|
||||
"plugin_type": "convert.to_dict",
|
||||
"category": "convert"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/convert/convert_to_json/package.json
Normal file
13
workflow/plugins/python/convert/convert_to_json/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/convert_to_json",
|
||||
"version": "1.0.0",
|
||||
"description": "convert_to_json plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "workflow", "plugin"],
|
||||
"main": "convert_to_json.py",
|
||||
"metadata": {
|
||||
"plugin_type": "convert.to_json",
|
||||
"category": "convert"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/convert/convert_to_list/package.json
Normal file
13
workflow/plugins/python/convert/convert_to_list/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/convert_to_list",
|
||||
"version": "1.0.0",
|
||||
"description": "convert_to_list plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "workflow", "plugin"],
|
||||
"main": "convert_to_list.py",
|
||||
"metadata": {
|
||||
"plugin_type": "convert.to_list",
|
||||
"category": "convert"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/convert_to_number",
|
||||
"version": "1.0.0",
|
||||
"description": "convert_to_number plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "workflow", "plugin"],
|
||||
"main": "convert_to_number.py",
|
||||
"metadata": {
|
||||
"plugin_type": "convert.to_number",
|
||||
"category": "convert"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/convert_to_string",
|
||||
"version": "1.0.0",
|
||||
"description": "convert_to_string plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "workflow", "plugin"],
|
||||
"main": "convert_to_string.py",
|
||||
"metadata": {
|
||||
"plugin_type": "convert.to_string",
|
||||
"category": "convert"
|
||||
}
|
||||
}
|
||||
21
workflow/plugins/python/convert/package.json
Normal file
21
workflow/plugins/python/convert/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@metabuilder/workflow-plugins-convert",
|
||||
"version": "1.0.0",
|
||||
"description": "Type conversion plugins",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["convert", "type", "workflow", "plugins"],
|
||||
"metadata": {
|
||||
"category": "convert",
|
||||
"plugin_count": 7
|
||||
},
|
||||
"plugins": [
|
||||
"convert_parse_json",
|
||||
"convert_to_boolean",
|
||||
"convert_to_dict",
|
||||
"convert_to_json",
|
||||
"convert_to_list",
|
||||
"convert_to_number",
|
||||
"convert_to_string"
|
||||
]
|
||||
}
|
||||
13
workflow/plugins/python/core/core_ai_request/package.json
Normal file
13
workflow/plugins/python/core/core_ai_request/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/core_ai_request",
|
||||
"version": "1.0.0",
|
||||
"description": "core_ai_request plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "workflow", "plugin"],
|
||||
"main": "core_ai_request.py",
|
||||
"metadata": {
|
||||
"plugin_type": "core.ai_request",
|
||||
"category": "core"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/core_append_context_message",
|
||||
"version": "1.0.0",
|
||||
"description": "core_append_context_message plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "workflow", "plugin"],
|
||||
"main": "core_append_context_message.py",
|
||||
"metadata": {
|
||||
"plugin_type": "core.append_context_message",
|
||||
"category": "core"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/core_append_tool_results",
|
||||
"version": "1.0.0",
|
||||
"description": "core_append_tool_results plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "workflow", "plugin"],
|
||||
"main": "core_append_tool_results.py",
|
||||
"metadata": {
|
||||
"plugin_type": "core.append_tool_results",
|
||||
"category": "core"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/core_append_user_instruction",
|
||||
"version": "1.0.0",
|
||||
"description": "core_append_user_instruction plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "workflow", "plugin"],
|
||||
"main": "core_append_user_instruction.py",
|
||||
"metadata": {
|
||||
"plugin_type": "core.append_user_instruction",
|
||||
"category": "core"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/core/core_load_context/package.json
Normal file
13
workflow/plugins/python/core/core_load_context/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/core_load_context",
|
||||
"version": "1.0.0",
|
||||
"description": "core_load_context plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "workflow", "plugin"],
|
||||
"main": "core_load_context.py",
|
||||
"metadata": {
|
||||
"plugin_type": "core.load_context",
|
||||
"category": "core"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/core_run_tool_calls",
|
||||
"version": "1.0.0",
|
||||
"description": "core_run_tool_calls plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "workflow", "plugin"],
|
||||
"main": "core_run_tool_calls.py",
|
||||
"metadata": {
|
||||
"plugin_type": "core.run_tool_calls",
|
||||
"category": "core"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/core/core_seed_messages/package.json
Normal file
13
workflow/plugins/python/core/core_seed_messages/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/core_seed_messages",
|
||||
"version": "1.0.0",
|
||||
"description": "core_seed_messages plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "workflow", "plugin"],
|
||||
"main": "core_seed_messages.py",
|
||||
"metadata": {
|
||||
"plugin_type": "core.seed_messages",
|
||||
"category": "core"
|
||||
}
|
||||
}
|
||||
21
workflow/plugins/python/core/package.json
Normal file
21
workflow/plugins/python/core/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@metabuilder/workflow-plugins-core",
|
||||
"version": "1.0.0",
|
||||
"description": "Core AI/workflow operation plugins",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["core", "ai", "workflow", "plugins"],
|
||||
"metadata": {
|
||||
"category": "core",
|
||||
"plugin_count": 7
|
||||
},
|
||||
"plugins": [
|
||||
"core_ai_request",
|
||||
"core_append_context_message",
|
||||
"core_append_tool_results",
|
||||
"core_append_user_instruction",
|
||||
"core_load_context",
|
||||
"core_run_tool_calls",
|
||||
"core_seed_messages"
|
||||
]
|
||||
}
|
||||
13
workflow/plugins/python/dict/dict_get/package.json
Normal file
13
workflow/plugins/python/dict/dict_get/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/dict_get",
|
||||
"version": "1.0.0",
|
||||
"description": "dict_get plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["dict", "workflow", "plugin"],
|
||||
"main": "dict_get.py",
|
||||
"metadata": {
|
||||
"plugin_type": "dict.get",
|
||||
"category": "dict"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/dict/dict_items/package.json
Normal file
13
workflow/plugins/python/dict/dict_items/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/dict_items",
|
||||
"version": "1.0.0",
|
||||
"description": "dict_items plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["dict", "workflow", "plugin"],
|
||||
"main": "dict_items.py",
|
||||
"metadata": {
|
||||
"plugin_type": "dict.items",
|
||||
"category": "dict"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/dict/dict_keys/package.json
Normal file
13
workflow/plugins/python/dict/dict_keys/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/dict_keys",
|
||||
"version": "1.0.0",
|
||||
"description": "dict_keys plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["dict", "workflow", "plugin"],
|
||||
"main": "dict_keys.py",
|
||||
"metadata": {
|
||||
"plugin_type": "dict.keys",
|
||||
"category": "dict"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/dict/dict_merge/package.json
Normal file
13
workflow/plugins/python/dict/dict_merge/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/dict_merge",
|
||||
"version": "1.0.0",
|
||||
"description": "dict_merge plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["dict", "workflow", "plugin"],
|
||||
"main": "dict_merge.py",
|
||||
"metadata": {
|
||||
"plugin_type": "dict.merge",
|
||||
"category": "dict"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/dict/dict_set/package.json
Normal file
13
workflow/plugins/python/dict/dict_set/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/dict_set",
|
||||
"version": "1.0.0",
|
||||
"description": "dict_set plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["dict", "workflow", "plugin"],
|
||||
"main": "dict_set.py",
|
||||
"metadata": {
|
||||
"plugin_type": "dict.set",
|
||||
"category": "dict"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/dict/dict_values/package.json
Normal file
13
workflow/plugins/python/dict/dict_values/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/dict_values",
|
||||
"version": "1.0.0",
|
||||
"description": "dict_values plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["dict", "workflow", "plugin"],
|
||||
"main": "dict_values.py",
|
||||
"metadata": {
|
||||
"plugin_type": "dict.values",
|
||||
"category": "dict"
|
||||
}
|
||||
}
|
||||
20
workflow/plugins/python/dict/package.json
Normal file
20
workflow/plugins/python/dict/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@metabuilder/workflow-plugins-dict",
|
||||
"version": "1.0.0",
|
||||
"description": "Dictionary/object operation plugins",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["dict", "object", "workflow", "plugins"],
|
||||
"metadata": {
|
||||
"category": "dict",
|
||||
"plugin_count": 6
|
||||
},
|
||||
"plugins": [
|
||||
"dict_get",
|
||||
"dict_keys",
|
||||
"dict_merge",
|
||||
"dict_set",
|
||||
"dict_values",
|
||||
"dict_delete"
|
||||
]
|
||||
}
|
||||
13
workflow/plugins/python/list/list_concat/package.json
Normal file
13
workflow/plugins/python/list/list_concat/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/list_concat",
|
||||
"version": "1.0.0",
|
||||
"description": "list_concat plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "workflow", "plugin"],
|
||||
"main": "list_concat.py",
|
||||
"metadata": {
|
||||
"plugin_type": "list.concat",
|
||||
"category": "list"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/list/list_every/package.json
Normal file
13
workflow/plugins/python/list/list_every/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/list_every",
|
||||
"version": "1.0.0",
|
||||
"description": "list_every plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "workflow", "plugin"],
|
||||
"main": "list_every.py",
|
||||
"metadata": {
|
||||
"plugin_type": "list.every",
|
||||
"category": "list"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/list/list_find/package.json
Normal file
13
workflow/plugins/python/list/list_find/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/list_find",
|
||||
"version": "1.0.0",
|
||||
"description": "list_find plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "workflow", "plugin"],
|
||||
"main": "list_find.py",
|
||||
"metadata": {
|
||||
"plugin_type": "list.find",
|
||||
"category": "list"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/list/list_length/package.json
Normal file
13
workflow/plugins/python/list/list_length/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/list_length",
|
||||
"version": "1.0.0",
|
||||
"description": "list_length plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "workflow", "plugin"],
|
||||
"main": "list_length.py",
|
||||
"metadata": {
|
||||
"plugin_type": "list.length",
|
||||
"category": "list"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/list/list_slice/package.json
Normal file
13
workflow/plugins/python/list/list_slice/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/list_slice",
|
||||
"version": "1.0.0",
|
||||
"description": "list_slice plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "workflow", "plugin"],
|
||||
"main": "list_slice.py",
|
||||
"metadata": {
|
||||
"plugin_type": "list.slice",
|
||||
"category": "list"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/list/list_some/package.json
Normal file
13
workflow/plugins/python/list/list_some/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/list_some",
|
||||
"version": "1.0.0",
|
||||
"description": "list_some plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "workflow", "plugin"],
|
||||
"main": "list_some.py",
|
||||
"metadata": {
|
||||
"plugin_type": "list.some",
|
||||
"category": "list"
|
||||
}
|
||||
}
|
||||
13
workflow/plugins/python/list/list_sort/package.json
Normal file
13
workflow/plugins/python/list/list_sort/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@metabuilder/list_sort",
|
||||
"version": "1.0.0",
|
||||
"description": "list_sort plugin",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "workflow", "plugin"],
|
||||
"main": "list_sort.py",
|
||||
"metadata": {
|
||||
"plugin_type": "list.sort",
|
||||
"category": "list"
|
||||
}
|
||||
}
|
||||
21
workflow/plugins/python/list/package.json
Normal file
21
workflow/plugins/python/list/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@metabuilder/workflow-plugins-list",
|
||||
"version": "1.0.0",
|
||||
"description": "List/array operation plugins",
|
||||
"author": "MetaBuilder",
|
||||
"license": "MIT",
|
||||
"keywords": ["list", "array", "workflow", "plugins"],
|
||||
"metadata": {
|
||||
"category": "list",
|
||||
"plugin_count": 7
|
||||
},
|
||||
"plugins": [
|
||||
"list_concat",
|
||||
"list_find",
|
||||
"list_length",
|
||||
"list_reverse",
|
||||
"list_slice",
|
||||
"list_sort",
|
||||
"list_unique"
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user