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 WebBuildPromptYaml plugin."""
from .web_build_prompt_yaml import WebBuildPromptYaml
def create():
return WebBuildPromptYaml()

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_build_prompt_yaml",
"version": "1.0.0",
"description": "web_build_prompt_yaml plugin",
"description": "Build prompt YAML from system and user content",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_build_prompt_yaml.py",
"files": ["web_build_prompt_yaml.py", "factory.py"],
"metadata": {
"plugin_type": "web.build_prompt_yaml",
"category": "web"
"category": "web",
"class": "WebBuildPromptYaml",
"entrypoint": "execute"
}
}

View File

@@ -1,22 +1,31 @@
"""Workflow plugin: build prompt YAML."""
from ...base import NodeExecutor
def run(_runtime, inputs):
class WebBuildPromptYaml(NodeExecutor):
"""Build prompt YAML from system and user content."""
system_content = inputs.get("system_content")
user_content = inputs.get("user_content")
model = inputs.get("model")
def indent_block(text):
if not text:
return ""
return "\n ".join(line.rstrip() for line in text.splitlines())
node_type = "web.build_prompt_yaml"
category = "web"
description = "Build prompt YAML from system and user content"
model_value = model or "openai/gpt-4o"
system_block = indent_block(system_content)
user_block = indent_block(user_content)
def execute(self, inputs, runtime=None):
"""Build prompt YAML from system and user content."""
system_content = inputs.get("system_content")
user_content = inputs.get("user_content")
model = inputs.get("model")
yaml_content = f"""messages:
def indent_block(text):
if not text:
return ""
return "\n ".join(line.rstrip() for line in text.splitlines())
model_value = model or "openai/gpt-4o"
system_block = indent_block(system_content)
user_block = indent_block(user_content)
yaml_content = f"""messages:
- role: system
content: >-
{system_block}
@@ -26,4 +35,4 @@ def run(_runtime, inputs):
model: {model_value}
"""
return {"result": yaml_content}
return {"result": yaml_content}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_create_flask_app",
"version": "1.0.0",
"description": "web_create_flask_app plugin",
"description": "Create a Flask application instance",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_create_flask_app.py",
"files": ["web_create_flask_app.py", "factory.py"],
"metadata": {
"plugin_type": "web.create_flask_app",
"category": "web"
"category": "web",
"class": "WebCreateFlaskApp",
"entrypoint": "execute"
}
}

View File

@@ -1,27 +1,38 @@
"""Workflow plugin: create Flask app."""
from flask import Flask
from ...base import NodeExecutor
def run(runtime, inputs):
"""Create a Flask application instance.
Inputs:
name: Application name (default: __name__)
config: Dictionary of Flask configuration options
class WebCreateFlaskApp(NodeExecutor):
"""Create a Flask application instance."""
Returns:
dict: Contains the Flask app in result
"""
name = inputs.get("name", "__main__")
config = inputs.get("config", {})
node_type = "web.create_flask_app"
category = "web"
description = "Create a Flask application instance"
app = Flask(name)
def execute(self, inputs, runtime=None):
"""Create a Flask application instance.
# Apply configuration
for key, value in config.items():
app.config[key] = value
Inputs:
name: Application name (default: __name__)
config: Dictionary of Flask configuration options
# Store app in runtime context for other plugins to use
runtime.context["flask_app"] = app
Returns:
dict: Contains the Flask app in result
"""
name = inputs.get("name", "__main__")
config = inputs.get("config", {})
return {"result": app, "message": "Flask app created"}
app = Flask(name)
# Apply configuration
for key, value in config.items():
app.config[key] = value
# Store app in runtime context for other plugins to use
if runtime is not None:
runtime.context["flask_app"] = app
return {"result": app, "message": "Flask app created"}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_get_env_vars",
"version": "1.0.0",
"description": "web_get_env_vars plugin",
"description": "Get environment variables from .env file",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_get_env_vars.py",
"files": ["web_get_env_vars.py", "factory.py"],
"metadata": {
"plugin_type": "web.get_env_vars",
"category": "web"
"category": "web",
"class": "WebGetEnvVars",
"entrypoint": "execute"
}
}

View File

@@ -1,22 +1,32 @@
"""Workflow plugin: get environment variables."""
from pathlib import Path
from ...base import NodeExecutor
def run(_runtime, _inputs):
class WebGetEnvVars(NodeExecutor):
"""Get environment variables from .env file."""
env_path = Path(".env")
if not env_path.exists():
return {"result": {}}
result = {}
for raw in env_path.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line or line.startswith("#"):
continue
if "=" not in line:
continue
key, value = line.split("=", 1)
value = value.strip().strip("'\"")
result[key.strip()] = value
node_type = "web.get_env_vars"
category = "web"
description = "Get environment variables from .env file"
return {"result": result}
def execute(self, inputs, runtime=None):
"""Get environment variables from .env file."""
env_path = Path(".env")
if not env_path.exists():
return {"result": {}}
result = {}
for raw in env_path.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line or line.startswith("#"):
continue
if "=" not in line:
continue
key, value = line.split("=", 1)
value = value.strip().strip("'\"")
result[key.strip()] = value
return {"result": result}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_get_prompt_content",
"version": "1.0.0",
"description": "web_get_prompt_content plugin",
"description": "Get prompt content from prompt file",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_get_prompt_content.py",
"files": ["web_get_prompt_content.py", "factory.py"],
"metadata": {
"plugin_type": "web.get_prompt_content",
"category": "web"
"category": "web",
"class": "WebGetPromptContent",
"entrypoint": "execute"
}
}

View File

@@ -1,12 +1,22 @@
"""Workflow plugin: get prompt content."""
import os
from pathlib import Path
from ...base import NodeExecutor
def run(_runtime, _inputs):
class WebGetPromptContent(NodeExecutor):
"""Get prompt content from prompt file."""
path = Path(os.environ.get("PROMPT_PATH", "prompt.yml"))
if path.is_file():
content = path.read_text(encoding="utf-8")
return {"result": content}
return {"result": ""}
node_type = "web.get_prompt_content"
category = "web"
description = "Get prompt content from prompt file"
def execute(self, inputs, runtime=None):
"""Get prompt content from prompt file."""
path = Path(os.environ.get("PROMPT_PATH", "prompt.yml"))
if path.is_file():
content = path.read_text(encoding="utf-8")
return {"result": content}
return {"result": ""}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_get_recent_logs",
"version": "1.0.0",
"description": "web_get_recent_logs plugin",
"description": "Get recent log entries",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_get_recent_logs.py",
"files": ["web_get_recent_logs.py", "factory.py"],
"metadata": {
"plugin_type": "web.get_recent_logs",
"category": "web"
"category": "web",
"class": "WebGetRecentLogs",
"entrypoint": "execute"
}
}

View File

@@ -1,16 +1,26 @@
"""Workflow plugin: get recent logs."""
from pathlib import Path
from ...base import NodeExecutor
def run(_runtime, inputs):
class WebGetRecentLogs(NodeExecutor):
"""Get recent log entries."""
lines = inputs.get("lines", 50)
log_file = Path("metabuilder.log")
if not log_file.exists():
return {"result": ""}
node_type = "web.get_recent_logs"
category = "web"
description = "Get recent log entries"
with log_file.open("r", encoding="utf-8") as handle:
content = handle.readlines()
def execute(self, inputs, runtime=None):
"""Get recent log entries."""
lines = inputs.get("lines", 50)
log_file = Path("metabuilder.log")
return {"result": "".join(content[-lines:])}
if not log_file.exists():
return {"result": ""}
with log_file.open("r", encoding="utf-8") as handle:
content = handle.readlines()
return {"result": "".join(content[-lines:])}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_persist_env_vars",
"version": "1.0.0",
"description": "web_persist_env_vars plugin",
"description": "Persist environment variables to .env file",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_persist_env_vars.py",
"files": ["web_persist_env_vars.py", "factory.py"],
"metadata": {
"plugin_type": "web.persist_env_vars",
"category": "web"
"category": "web",
"class": "WebPersistEnvVars",
"entrypoint": "execute"
}
}

View File

@@ -1,15 +1,25 @@
"""Workflow plugin: persist environment variables."""
from pathlib import Path
from ...base import NodeExecutor
def run(_runtime, inputs):
class WebPersistEnvVars(NodeExecutor):
"""Persist environment variables to .env file."""
from dotenv import set_key
updates = inputs.get("updates", {})
env_path = Path(".env")
env_path.touch(exist_ok=True)
for key, value in updates.items():
set_key(env_path, key, value)
node_type = "web.persist_env_vars"
category = "web"
description = "Persist environment variables to .env file"
return {"result": "Environment variables persisted"}
def execute(self, inputs, runtime=None):
"""Persist environment variables to .env file."""
from dotenv import set_key
updates = inputs.get("updates", {})
env_path = Path(".env")
env_path.touch(exist_ok=True)
for key, value in updates.items():
set_key(env_path, key, value)
return {"result": "Environment variables persisted"}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_read_json",
"version": "1.0.0",
"description": "web_read_json plugin",
"description": "Read JSON file",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_read_json.py",
"files": ["web_read_json.py", "factory.py"],
"metadata": {
"plugin_type": "web.read_json",
"category": "web"
"category": "web",
"class": "WebReadJson",
"entrypoint": "execute"
}
}

View File

@@ -1,21 +1,31 @@
"""Workflow plugin: read JSON file."""
import json
from pathlib import Path
from ...base import NodeExecutor
def run(_runtime, inputs):
class WebReadJson(NodeExecutor):
"""Read JSON file."""
path = inputs.get("path")
if not path:
return {"error": "path is required"}
path_obj = Path(path)
if not path_obj.exists():
return {"result": {}}
node_type = "web.read_json"
category = "web"
description = "Read JSON file"
try:
json_data = json.loads(path_obj.read_text(encoding="utf-8"))
except json.JSONDecodeError:
return {"result": {}}
def execute(self, inputs, runtime=None):
"""Read JSON file."""
path = inputs.get("path")
if not path:
return {"error": "path is required"}
return {"result": json_data}
path_obj = Path(path)
if not path_obj.exists():
return {"result": {}}
try:
json_data = json.loads(path_obj.read_text(encoding="utf-8"))
except json.JSONDecodeError:
return {"result": {}}
return {"result": json_data}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_start_server",
"version": "1.0.0",
"description": "web_start_server plugin",
"description": "Start the Flask web server",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_start_server.py",
"files": ["web_start_server.py", "factory.py"],
"metadata": {
"plugin_type": "web.start_server",
"category": "web"
"category": "web",
"class": "WebStartServer",
"entrypoint": "execute"
}
}

View File

@@ -1,26 +1,38 @@
"""Workflow plugin: start Flask server."""
from ...base import NodeExecutor
def run(runtime, inputs):
"""Start the Flask web server.
Inputs:
host: Host address (default: 0.0.0.0)
port: Port number (default: 8000)
debug: Enable debug mode (default: False)
class WebStartServer(NodeExecutor):
"""Start the Flask web server."""
Returns:
dict: Success indicator (note: this blocks until server stops)
"""
app = runtime.context.get("flask_app")
if not app:
return {"error": "Flask app not found in context. Run web.create_flask_app first."}
node_type = "web.start_server"
category = "web"
description = "Start the Flask web server"
host = inputs.get("host", "0.0.0.0")
port = inputs.get("port", 8000)
debug = inputs.get("debug", False)
def execute(self, inputs, runtime=None):
"""Start the Flask web server.
# This will block until the server is stopped
app.run(host=host, port=port, debug=debug)
Inputs:
host: Host address (default: 0.0.0.0)
port: Port number (default: 8000)
debug: Enable debug mode (default: False)
return {"result": "Server stopped"}
Returns:
dict: Success indicator (note: this blocks until server stops)
"""
if runtime is None:
return {"error": "Runtime context required"}
app = runtime.context.get("flask_app")
if not app:
return {"error": "Flask app not found in context. Run web.create_flask_app first."}
host = inputs.get("host", "0.0.0.0")
port = inputs.get("port", 8000)
debug = inputs.get("debug", False)
# This will block until the server is stopped
app.run(host=host, port=port, debug=debug)
return {"result": "Server stopped"}

View File

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

View File

@@ -1,13 +1,16 @@
{
"name": "@metabuilder/web_write_prompt",
"version": "1.0.0",
"description": "web_write_prompt plugin",
"description": "Write prompt content to file",
"author": "MetaBuilder",
"license": "MIT",
"keywords": ["web", "workflow", "plugin"],
"main": "web_write_prompt.py",
"files": ["web_write_prompt.py", "factory.py"],
"metadata": {
"plugin_type": "web.write_prompt",
"category": "web"
"category": "web",
"class": "WebWritePrompt",
"entrypoint": "execute"
}
}

View File

@@ -1,11 +1,21 @@
"""Workflow plugin: write prompt."""
import os
from pathlib import Path
from ...base import NodeExecutor
def run(_runtime, inputs):
class WebWritePrompt(NodeExecutor):
"""Write prompt content to file."""
content = inputs.get("content", "")
path = Path(os.environ.get("PROMPT_PATH", "prompt.yml"))
path.write_text(content or "", encoding="utf-8")
return {"result": "Prompt written successfully"}
node_type = "web.write_prompt"
category = "web"
description = "Write prompt content to file"
def execute(self, inputs, runtime=None):
"""Write prompt content to file."""
content = inputs.get("content", "")
path = Path(os.environ.get("PROMPT_PATH", "prompt.yml"))
path.write_text(content or "", encoding="utf-8")
return {"result": "Prompt written successfully"}