refactor(workflow): convert all plugins to class/struct + factory pattern

- Python: class extending NodeExecutor + factory.py (80+ plugins)
- TypeScript: class implements NodeExecutor + factory.ts (7 groups, 116 classes)
- Go: struct with methods + factory.go (36 plugins)
- Rust: struct impl NodeExecutor trait + factory.rs (54 plugins)
- Mojo: struct + factory.mojo (11 plugins)

All package.json files now include:
- files array listing source files
- metadata.class/struct field
- metadata.entrypoint field

This enables a unified plugin loading system across all languages
with no import side effects (Spring-style DI pattern).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-22 14:53:04 +00:00
parent 2562c2f55b
commit 7ce8b4ae8a
653 changed files with 13243 additions and 3034 deletions

View File

@@ -0,0 +1,7 @@
"""Factory for NotificationsAll plugin."""
from .notifications_all import NotificationsAll
def create():
return NotificationsAll()

View File

@@ -1,33 +1,45 @@
"""Workflow plugin: send notification to all channels."""
import os
import logging
from ...base import NodeExecutor
logger = logging.getLogger("metabuilder.notifications")
def run(runtime, inputs):
"""Send a notification to all configured channels (Slack and Discord).
class NotificationsAll(NodeExecutor):
"""Send a notification to all configured channels (Slack and Discord)."""
Inputs:
message: The message to send to all channels
node_type = "notifications.all"
category = "notifications"
description = "Send a notification to all configured channels (Slack and Discord)"
Returns:
dict: Contains success status for all channels
"""
message = inputs.get("message", "")
def execute(self, inputs, runtime=None):
"""Send a notification to all configured channels (Slack and Discord).
# Import sibling plugins
from . import notifications_slack, notifications_discord
Inputs:
message: The message to send to all channels
# Send to Slack
slack_result = notifications_slack.run(runtime, {"message": message})
Returns:
dict: Contains success status for all channels
"""
message = inputs.get("message", "")
# Send to Discord
discord_result = notifications_discord.run(runtime, {"message": message})
# Import sibling plugins
from ..notifications_slack.factory import create as create_slack
from ..notifications_discord.factory import create as create_discord
return {
"success": True,
"message": "Notifications sent to all channels",
"slack": slack_result,
"discord": discord_result
}
# Send to Slack
slack_plugin = create_slack()
slack_result = slack_plugin.execute({"message": message}, runtime)
# Send to Discord
discord_plugin = create_discord()
discord_result = discord_plugin.execute({"message": message}, runtime)
return {
"success": True,
"message": "Notifications sent to all channels",
"slack": slack_result,
"discord": discord_result
}

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/notifications_all",
"version": "1.0.0",
"description": "notifications_all plugin",
"description": "Send notifications to all configured channels (Slack and Discord)",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["notifications", "workflow", "plugin"],
"main": "notifications_all.py",
"files": ["notifications_all.py", "factory.py"],
"metadata": {
"plugin_type": "notifications.all",
"category": "notifications"
"category": "notifications",
"class": "NotificationsAll",
"entrypoint": "execute"
}
}

View File

@@ -0,0 +1,7 @@
"""Factory for NotificationsDiscord plugin."""
from .notifications_discord import NotificationsDiscord
def create():
return NotificationsDiscord()

View File

@@ -1,8 +1,11 @@
"""Workflow plugin: send Discord notification."""
import os
import logging
import asyncio
from ...base import NodeExecutor
logger = logging.getLogger("metabuilder.notifications")
@@ -27,41 +30,48 @@ async def _send_discord_notification_async(message: str, token: str, intents, ch
raise
def run(runtime, inputs):
"""Send a notification to Discord.
class NotificationsDiscord(NodeExecutor):
"""Send a notification to Discord."""
Inputs:
message: The message to send
channel_id: Optional channel ID (defaults to DISCORD_CHANNEL_ID env var)
node_type = "notifications.discord"
category = "notifications"
description = "Send a notification to Discord"
Returns:
dict: Contains success status and any error message
"""
message = inputs.get("message", "")
channel_id = inputs.get("channel_id") or os.environ.get("DISCORD_CHANNEL_ID")
def execute(self, inputs, runtime=None):
"""Send a notification to Discord.
token = runtime.context.get("discord_token")
intents = runtime.context.get("discord_intents")
Inputs:
message: The message to send
channel_id: Optional channel ID (defaults to DISCORD_CHANNEL_ID env var)
if not token:
logger.warning("Discord notification skipped: Discord client not initialized.")
return {
"success": False,
"skipped": True,
"error": "Discord client not initialized"
}
Returns:
dict: Contains success status and any error message
"""
message = inputs.get("message", "")
channel_id = inputs.get("channel_id") or os.environ.get("DISCORD_CHANNEL_ID")
if not channel_id:
logger.warning("Discord notification skipped: DISCORD_CHANNEL_ID missing.")
return {
"success": False,
"skipped": True,
"error": "DISCORD_CHANNEL_ID missing"
}
token = runtime.context.get("discord_token") if runtime else None
intents = runtime.context.get("discord_intents") if runtime else None
try:
asyncio.run(_send_discord_notification_async(message, token, intents, channel_id))
return {"success": True, "message": "Discord notification sent"}
except Exception as e:
logger.error(f"Error running Discord notification: {e}")
return {"success": False, "error": str(e)}
if not token:
logger.warning("Discord notification skipped: Discord client not initialized.")
return {
"success": False,
"skipped": True,
"error": "Discord client not initialized"
}
if not channel_id:
logger.warning("Discord notification skipped: DISCORD_CHANNEL_ID missing.")
return {
"success": False,
"skipped": True,
"error": "DISCORD_CHANNEL_ID missing"
}
try:
asyncio.run(_send_discord_notification_async(message, token, intents, channel_id))
return {"success": True, "message": "Discord notification sent"}
except Exception as e:
logger.error(f"Error running Discord notification: {e}")
return {"success": False, "error": str(e)}

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/notifications_discord",
"version": "1.0.0",
"description": "notifications_discord plugin",
"description": "Send notifications to Discord channels",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["notifications", "workflow", "plugin"],
"main": "notifications_discord.py",
"files": ["notifications_discord.py", "factory.py"],
"metadata": {
"plugin_type": "notifications.discord",
"category": "notifications"
"category": "notifications",
"class": "NotificationsDiscord",
"entrypoint": "execute"
}
}

View File

@@ -0,0 +1,7 @@
"""Factory for NotificationsSlack plugin."""
from .notifications_slack import NotificationsSlack
def create():
return NotificationsSlack()

View File

@@ -1,46 +1,56 @@
"""Workflow plugin: send Slack notification."""
import os
import logging
from ...base import NodeExecutor
logger = logging.getLogger("metabuilder.notifications")
def run(runtime, inputs):
"""Send a notification to Slack.
class NotificationsSlack(NodeExecutor):
"""Send a notification to Slack."""
Inputs:
message: The message to send
channel: Optional channel (defaults to SLACK_CHANNEL env var)
node_type = "notifications.slack"
category = "notifications"
description = "Send a notification to Slack"
Returns:
dict: Contains success status and any error message
"""
message = inputs.get("message", "")
channel = inputs.get("channel") or os.environ.get("SLACK_CHANNEL")
def execute(self, inputs, runtime=None):
"""Send a notification to Slack.
client = runtime.context.get("slack_client")
Inputs:
message: The message to send
channel: Optional channel (defaults to SLACK_CHANNEL env var)
if not client:
logger.warning("Slack notification skipped: Slack client not initialized.")
return {
"success": False,
"skipped": True,
"error": "Slack client not initialized"
}
Returns:
dict: Contains success status and any error message
"""
message = inputs.get("message", "")
channel = inputs.get("channel") or os.environ.get("SLACK_CHANNEL")
if not channel:
logger.warning("Slack notification skipped: SLACK_CHANNEL missing.")
return {
"success": False,
"skipped": True,
"error": "SLACK_CHANNEL missing"
}
client = runtime.context.get("slack_client") if runtime else None
try:
from slack_sdk.errors import SlackApiError
client.chat_postMessage(channel=channel, text=message)
logger.info("Slack notification sent successfully.")
return {"success": True, "message": "Slack notification sent"}
except SlackApiError as e:
logger.error(f"Error sending Slack notification: {e}")
return {"success": False, "error": str(e)}
if not client:
logger.warning("Slack notification skipped: Slack client not initialized.")
return {
"success": False,
"skipped": True,
"error": "Slack client not initialized"
}
if not channel:
logger.warning("Slack notification skipped: SLACK_CHANNEL missing.")
return {
"success": False,
"skipped": True,
"error": "SLACK_CHANNEL missing"
}
try:
from slack_sdk.errors import SlackApiError
client.chat_postMessage(channel=channel, text=message)
logger.info("Slack notification sent successfully.")
return {"success": True, "message": "Slack notification sent"}
except SlackApiError as e:
logger.error(f"Error sending Slack notification: {e}")
return {"success": False, "error": str(e)}

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/notifications_slack",
"version": "1.0.0",
"description": "notifications_slack plugin",
"description": "Send notifications to Slack channels",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["notifications", "workflow", "plugin"],
"main": "notifications_slack.py",
"files": ["notifications_slack.py", "factory.py"],
"metadata": {
"plugin_type": "notifications.slack",
"category": "notifications"
"category": "notifications",
"class": "NotificationsSlack",
"entrypoint": "execute"
}
}