feat: Add Python plugins from AutoMetabuilder + restructure workflow folder

Restructure workflow/ for multi-language plugin support:
- Rename src/ to core/ (engine code: DAG executor, registry, types)
- Create executor/{cpp,python,ts}/ for language-specific runtimes
- Consolidate plugins to plugins/{ts,python}/ by language then category

Add 80+ Python plugins from AutoMetabuilder in 14 categories:
- control: bot control, switch logic, state management
- convert: type conversions (json, boolean, dict, list, number, string)
- core: AI requests, context management, tool calls
- dict: dictionary operations (get, set, keys, values, merge)
- list: list operations (concat, find, sort, slice, filter)
- logic: boolean logic (and, or, xor, equals, comparisons)
- math: arithmetic operations (add, subtract, multiply, power, etc.)
- string: string manipulation (concat, split, replace, format)
- notifications: Slack, Discord integrations
- test: assertion helpers and test suite runner
- tools: file operations, git, docker, testing utilities
- utils: filtering, mapping, reducing, condition branching
- var: variable store operations (get, set, delete, exists)
- web: Flask server, environment variables, JSON handling

Add language executor runtimes:
- TypeScript: direct import execution (default, fast startup)
- Python: child process with JSON stdin/stdout communication
- C++: placeholder for native FFI bindings (Phase 3)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 16:08:08 +00:00
parent c760bd7cd0
commit 5ac579a2ed
171 changed files with 2188 additions and 1565 deletions

View File

@@ -0,0 +1,44 @@
"""Web workflow plugins: Flask server, API endpoints, file I/O, translations.
These plugins provide workflow-based access to web data operations, enabling
declarative workflows to interact with web-related functionality.
Available Plugins:
Environment Management:
- web_get_env_vars - Load environment variables
- web_persist_env_vars - Save environment variables
File I/O:
- web_read_json - Read JSON files
- web_get_recent_logs - Get recent log entries
- web_load_messages - Load translation messages
Translation Management:
- web_list_translations - List available translations
- web_load_translation - Load a translation
- web_create_translation - Create new translation
- web_update_translation - Update translation
- web_delete_translation - Delete translation
- web_get_ui_messages - Get UI messages with fallback
Navigation & Metadata:
- web_get_navigation_items - Get navigation menu items
Prompt Management:
- web_get_prompt_content - Read prompt content
- web_write_prompt - Write prompt content
- web_build_prompt_yaml - Build YAML prompt
Workflow Operations:
- web_get_workflow_content - Read workflow JSON
- web_write_workflow - Write workflow JSON
- web_load_workflow_packages - Load workflow packages
- web_summarize_workflow_packages - Summarize packages
Flask Server Setup:
- web_create_flask_app - Create Flask application
- web_register_blueprint - Register Flask blueprints
- web_start_server - Start Flask server
- web_build_context - Build API context
"""

View File

@@ -0,0 +1,29 @@
"""Workflow plugin: build prompt YAML."""
def run(_runtime, inputs):
"""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())
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}
- role: user
content: >-
{user_block}
model: {model_value}
"""
return {"result": yaml_content}

View File

@@ -0,0 +1,27 @@
"""Workflow plugin: create Flask app."""
from flask import Flask
def run(runtime, inputs):
"""Create a Flask application instance.
Inputs:
name: Application name (default: __name__)
config: Dictionary of Flask configuration options
Returns:
dict: Contains the Flask app in result
"""
name = inputs.get("name", "__main__")
config = inputs.get("config", {})
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
runtime.context["flask_app"] = app
return {"result": app, "message": "Flask app created"}

View File

@@ -0,0 +1,22 @@
"""Workflow plugin: get environment variables."""
from pathlib import Path
def run(_runtime, _inputs):
"""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,12 @@
"""Workflow plugin: get prompt content."""
import os
from pathlib import Path
def run(_runtime, _inputs):
"""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,16 @@
"""Workflow plugin: get recent logs."""
from pathlib import Path
def run(_runtime, inputs):
"""Get recent log entries."""
lines = inputs.get("lines", 50)
log_file = Path("metabuilder.log")
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,15 @@
"""Workflow plugin: persist environment variables."""
from pathlib import Path
def run(_runtime, inputs):
"""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,21 @@
"""Workflow plugin: read JSON file."""
import json
from pathlib import Path
def run(_runtime, inputs):
"""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": {}}
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,26 @@
"""Workflow plugin: start Flask server."""
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)
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."}
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,11 @@
"""Workflow plugin: write prompt."""
import os
from pathlib import Path
def run(_runtime, inputs):
"""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"}