Create workflow plugins for run state management

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-10 21:18:49 +00:00
parent 7fc654802b
commit fa4f15b1d9
9 changed files with 197 additions and 7 deletions

View File

@@ -12,6 +12,9 @@
"backend.load_tool_registry": "autometabuilder.workflow.plugins.backend.backend_load_tool_registry.backend_load_tool_registry.run",
"backend.load_tools": "autometabuilder.workflow.plugins.backend.backend_load_tools.backend_load_tools.run",
"backend.parse_cli_args": "autometabuilder.workflow.plugins.backend.backend_parse_cli_args.backend_parse_cli_args.run",
"control.get_bot_status": "autometabuilder.workflow.plugins.control.control_get_bot_status.control_get_bot_status.run",
"control.reset_bot_state": "autometabuilder.workflow.plugins.control.control_reset_bot_state.control_reset_bot_state.run",
"control.start_bot": "autometabuilder.workflow.plugins.control.control_start_bot.control_start_bot.run",
"control.switch": "autometabuilder.workflow.plugins.control.control_switch.control_switch.run",
"convert.parse_json": "autometabuilder.workflow.plugins.convert.convert_parse_json.convert_parse_json.run",
"convert.to_boolean": "autometabuilder.workflow.plugins.convert.convert_to_boolean.convert_to_boolean.run",

View File

@@ -0,0 +1,22 @@
"""Workflow plugin: get current bot execution status."""
from autometabuilder.workflow.plugins.control.control_start_bot.control_start_bot import (
_bot_process,
_mock_running,
_current_run_config,
)
def run(_runtime, _inputs):
"""Get current bot execution status.
Returns:
Dictionary with:
- is_running: bool - Whether the bot is currently running
- config: dict - Current run configuration (empty if not running)
- process: object - Bot process object (or None if not running)
"""
return {
"is_running": _bot_process is not None or _mock_running,
"config": _current_run_config,
"process": _bot_process,
}

View File

@@ -0,0 +1,13 @@
{
"name": "@autometabuilder/control_get_bot_status",
"version": "1.0.0",
"description": "Get current bot execution status",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": ["control", "workflow", "plugin", "bot", "status"],
"main": "control_get_bot_status.py",
"metadata": {
"plugin_type": "control.get_bot_status",
"category": "control"
}
}

View File

@@ -0,0 +1,13 @@
"""Workflow plugin: reset bot execution state."""
from autometabuilder.workflow.plugins.control.control_start_bot.control_start_bot import _reset_run_state
def run(_runtime, _inputs):
"""Reset bot execution state.
Returns:
Dictionary with:
- reset: bool - Always True to indicate state was reset
"""
_reset_run_state()
return {"reset": True}

View File

@@ -0,0 +1,13 @@
{
"name": "@autometabuilder/control_reset_bot_state",
"version": "1.0.0",
"description": "Reset bot execution state",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": ["control", "workflow", "plugin", "bot", "reset"],
"main": "control_reset_bot_state.py",
"metadata": {
"plugin_type": "control.reset_bot_state",
"category": "control"
}
}

View File

@@ -0,0 +1,93 @@
"""Workflow plugin: start bot execution in background thread."""
import os
import subprocess
import sys
import threading
import time
from autometabuilder.roadmap_utils import is_mvp_reached
# Global state for bot process
_bot_process = None
_mock_running = False
_current_run_config = {}
def _reset_run_state() -> None:
"""Reset the bot run state."""
global _bot_process, _current_run_config
_bot_process = None
_current_run_config = {}
def _run_bot_task(mode: str, iterations: int, yolo: bool, stop_at_mvp: bool) -> None:
"""Execute bot task in background thread."""
global _bot_process, _mock_running, _current_run_config
_current_run_config = {
"mode": mode,
"iterations": iterations,
"yolo": yolo,
"stop_at_mvp": stop_at_mvp,
}
if os.environ.get("MOCK_WEB_UI") == "true":
_mock_running = True
time.sleep(5)
_mock_running = False
_reset_run_state()
return
try:
cmd = [sys.executable, "-m", "autometabuilder.main"]
if yolo:
cmd.append("--yolo")
if mode == "once":
cmd.append("--once")
if mode == "iterations" and iterations > 1:
for _ in range(iterations):
if stop_at_mvp and is_mvp_reached():
break
_bot_process = subprocess.Popen(cmd + ["--once"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
_bot_process.wait()
else:
_bot_process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
_bot_process.wait()
finally:
_reset_run_state()
def run(_runtime, inputs):
"""Start bot execution in background thread.
Args:
inputs: Dictionary with keys:
- mode: str (default: "once") - Execution mode ("once", "iterations", etc.)
- iterations: int (default: 1) - Number of iterations for "iterations" mode
- yolo: bool (default: True) - Run in YOLO mode
- stop_at_mvp: bool (default: False) - Stop when MVP is reached
Returns:
Dictionary with:
- started: bool - Whether the bot was started successfully
- error: str (optional) - Error message if bot is already running
"""
global _bot_process, _mock_running
mode = inputs.get("mode", "once")
iterations = inputs.get("iterations", 1)
yolo = inputs.get("yolo", True)
stop_at_mvp = inputs.get("stop_at_mvp", False)
# Check if bot is already running
if _bot_process is not None or _mock_running:
return {"started": False, "error": "Bot already running"}
# Start bot in background thread
thread = threading.Thread(
target=_run_bot_task,
args=(mode, iterations, yolo, stop_at_mvp),
daemon=True
)
thread.start()
return {"started": True}

View File

@@ -0,0 +1,13 @@
{
"name": "@autometabuilder/control_start_bot",
"version": "1.0.0",
"description": "Start bot execution in a background thread",
"author": "AutoMetabuilder",
"license": "MIT",
"keywords": ["control", "workflow", "plugin", "bot"],
"main": "control_start_bot.py",
"metadata": {
"plugin_type": "control.start_bot",
"category": "control"
}
}

View File

@@ -2,12 +2,17 @@
import os
from flask import Blueprint, jsonify
from autometabuilder.loaders.metadata_loader import load_metadata
from autometabuilder.data.run_state import bot_process, current_run_config, mock_running
from autometabuilder.workflow.plugin_loader import load_plugin_callable
from autometabuilder.roadmap_utils import is_mvp_reached
def run(runtime, _inputs):
"""Create and return the context routes blueprint."""
# Load the control.get_bot_status plugin
get_bot_status_plugin = load_plugin_callable(
"autometabuilder.workflow.plugins.control.control_get_bot_status.control_get_bot_status.run"
)
context_bp = Blueprint("context", __name__)
def build_context():
@@ -28,6 +33,9 @@ def run(runtime, _inputs):
metadata = load_metadata()
packages = load_workflow_packages()
# Get bot status from plugin
bot_status = get_bot_status_plugin(runtime, {})
return {
"logs": get_recent_logs(),
"env_vars": get_env_vars(),
@@ -41,9 +49,9 @@ def run(runtime, _inputs):
"messages": get_ui_messages(lang),
"lang": lang,
"status": {
"is_running": bot_process is not None or mock_running,
"is_running": bot_status["is_running"],
"mvp_reached": is_mvp_reached(),
"config": current_run_config,
"config": bot_status["config"],
},
}

View File

@@ -1,10 +1,15 @@
"""Workflow plugin: run API routes blueprint."""
from flask import Blueprint, jsonify, request
from autometabuilder.data.run_state import start_bot
from autometabuilder.workflow.plugin_loader import load_plugin_callable
def run(runtime, _inputs):
"""Create and return the run routes blueprint."""
# Load the control.start_bot plugin
start_bot_plugin = load_plugin_callable(
"autometabuilder.workflow.plugins.control.control_start_bot.control_start_bot.run"
)
run_bp = Blueprint("run", __name__)
@run_bp.route("/api/run", methods=["POST"])
@@ -15,9 +20,16 @@ def run(runtime, _inputs):
yolo = payload.get("yolo", True)
stop_at_mvp = payload.get("stop_at_mvp", False)
started = start_bot(mode, iterations, yolo, stop_at_mvp)
if not started:
return jsonify({"error": "Bot already running"}), 400
# Call the control.start_bot plugin
result = start_bot_plugin(runtime, {
"mode": mode,
"iterations": iterations,
"yolo": yolo,
"stop_at_mvp": stop_at_mvp
})
if not result.get("started"):
return jsonify({"error": result.get("error", "Bot already running")}), 400
return jsonify({"status": "started"}), 200