diff --git a/prompt.yml b/prompt.yml index d3b2c5b..9005f01 100644 --- a/prompt.yml +++ b/prompt.yml @@ -29,4 +29,6 @@ model: openai/gpt-4o # Test Comment # Test Comment # Test Comment +# Test Comment +# Test Comment # Test Comment \ No newline at end of file diff --git a/src/autometabuilder/web/server.py b/src/autometabuilder/web/server.py index d7b2d66..b31a5c8 100644 --- a/src/autometabuilder/web/server.py +++ b/src/autometabuilder/web/server.py @@ -4,6 +4,7 @@ import secrets from fastapi import FastAPI, Request, Form, BackgroundTasks, Depends, HTTPException, status from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates +from fastapi.staticfiles import StaticFiles from fastapi.security import HTTPBasic, HTTPBasicCredentials from dotenv import load_dotenv, set_key import subprocess @@ -39,6 +40,11 @@ mock_running = False templates_dir = os.path.join(os.path.dirname(__file__), "templates") templates = Jinja2Templates(directory=templates_dir) +# Setup static files +static_dir = os.path.join(os.path.dirname(__file__), "static") +if os.path.exists(static_dir): + app.mount("/static", StaticFiles(directory=static_dir), name="static") + def run_bot_task(): global bot_process, mock_running if os.environ.get("MOCK_WEB_UI") == "true": diff --git a/src/autometabuilder/web/static/css/main.css b/src/autometabuilder/web/static/css/main.css new file mode 100644 index 0000000..9bdb2e5 --- /dev/null +++ b/src/autometabuilder/web/static/css/main.css @@ -0,0 +1,640 @@ +/* ========================================================================== + AutoMetabuilder - Main Stylesheet + ========================================================================== */ + +/* ========================================================================== + 1. CSS Custom Properties (Theme Variables) + ========================================================================== */ +:root { + /* Light mode (default) */ + --amb-bg-primary: #f8f9fa; + --amb-bg-secondary: #ffffff; + --amb-bg-tertiary: #e9ecef; + --amb-text-primary: #212529; + --amb-text-secondary: #495057; + --amb-text-muted: #6c757d; + --amb-border-color: #dee2e6; + --amb-card-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --amb-sidebar-bg: #212529; + --amb-sidebar-text: #adb5bd; + --amb-sidebar-text-active: #ffffff; + --amb-sidebar-hover: rgba(255, 255, 255, 0.1); + --amb-code-bg: #f1f3f5; + --amb-accent: #0d6efd; + --amb-accent-hover: #0b5ed7; + --amb-success: #198754; + --amb-warning: #ffc107; + --amb-danger: #dc3545; + --amb-sidebar-width: 260px; + --amb-transition: 0.2s ease; +} + +/* Dark mode */ +[data-theme="dark"] { + --amb-bg-primary: #1a1d21; + --amb-bg-secondary: #212529; + --amb-bg-tertiary: #2c3034; + --amb-text-primary: #f8f9fa; + --amb-text-secondary: #ced4da; + --amb-text-muted: #adb5bd; + --amb-border-color: #495057; + --amb-card-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.3); + --amb-sidebar-bg: #0d1117; + --amb-sidebar-text: #8b949e; + --amb-sidebar-text-active: #f0f6fc; + --amb-sidebar-hover: rgba(255, 255, 255, 0.05); + --amb-code-bg: #2d333b; + --amb-accent: #58a6ff; + --amb-accent-hover: #79b8ff; + --amb-success: #3fb950; + --amb-warning: #d29922; + --amb-danger: #f85149; +} + +/* ========================================================================== + 2. Base Styles + ========================================================================== */ +* { + box-sizing: border-box; +} + +html { + height: 100%; +} + +body { + margin: 0; + padding: 0; + min-height: 100%; + background-color: var(--amb-bg-primary); + color: var(--amb-text-primary); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + transition: background-color var(--amb-transition), color var(--amb-transition); +} + +/* ========================================================================== + 3. Layout + ========================================================================== */ +.amb-layout { + display: flex; + min-height: 100vh; +} + +/* Sidebar */ +.amb-sidebar { + width: var(--amb-sidebar-width); + background: var(--amb-sidebar-bg); + color: var(--amb-sidebar-text); + display: flex; + flex-direction: column; + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 1000; + transition: background-color var(--amb-transition); +} + +.amb-sidebar-header { + padding: 1.5rem 1.25rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.amb-sidebar-header h5 { + margin: 0; + color: var(--amb-sidebar-text-active); + font-weight: 600; + font-size: 1.1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.amb-sidebar-header .bi { + font-size: 1.25rem; +} + +/* Navigation */ +.amb-nav { + flex: 1; + padding: 1rem 0; + overflow-y: auto; +} + +.amb-nav-link { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1.25rem; + color: var(--amb-sidebar-text); + text-decoration: none; + font-size: 0.9rem; + transition: all var(--amb-transition); + border-left: 3px solid transparent; +} + +.amb-nav-link:hover { + background: var(--amb-sidebar-hover); + color: var(--amb-sidebar-text-active); +} + +.amb-nav-link.active { + background: var(--amb-sidebar-hover); + color: var(--amb-sidebar-text-active); + border-left-color: var(--amb-accent); +} + +.amb-nav-link .bi { + font-size: 1.1rem; + width: 1.25rem; + text-align: center; +} + +/* Sidebar Footer */ +.amb-sidebar-footer { + padding: 1rem 1.25rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.85rem; +} + +.amb-sidebar-footer .amb-user { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--amb-sidebar-text); +} + +.amb-theme-toggle { + background: transparent; + border: none; + color: var(--amb-sidebar-text); + cursor: pointer; + padding: 0.5rem; + border-radius: 0.375rem; + transition: all var(--amb-transition); + display: flex; + align-items: center; + justify-content: center; +} + +.amb-theme-toggle:hover { + background: var(--amb-sidebar-hover); + color: var(--amb-sidebar-text-active); +} + +.amb-theme-toggle .bi { + font-size: 1.1rem; +} + +/* Main Content */ +.amb-main { + flex: 1; + margin-left: var(--amb-sidebar-width); + padding: 2rem; + min-height: 100vh; +} + +/* Sections */ +.amb-section { + display: none; +} + +.amb-section.active { + display: block; +} + +.amb-section-header { + margin-bottom: 1.5rem; +} + +.amb-section-header h1 { + margin: 0 0 0.25rem 0; + font-size: 1.75rem; + font-weight: 600; +} + +.amb-section-header p { + margin: 0; + color: var(--amb-text-muted); +} + +/* ========================================================================== + 4. Cards + ========================================================================== */ +.amb-card { + background: var(--amb-bg-secondary); + border: 1px solid var(--amb-border-color); + border-radius: 0.75rem; + box-shadow: var(--amb-card-shadow); + margin-bottom: 1.5rem; + transition: background-color var(--amb-transition), border-color var(--amb-transition), box-shadow var(--amb-transition); +} + +.amb-card-header { + padding: 1rem 1.25rem; + border-bottom: 1px solid var(--amb-border-color); + font-weight: 600; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.amb-card-header h5 { + margin: 0; + font-size: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.amb-card-body { + padding: 1.25rem; +} + +.amb-card-footer { + padding: 1rem 1.25rem; + border-top: 1px solid var(--amb-border-color); + background: var(--amb-bg-tertiary); + border-radius: 0 0 0.75rem 0.75rem; +} + +/* ========================================================================== + 5. Forms + ========================================================================== */ +.amb-form-group { + margin-bottom: 1.25rem; +} + +.amb-form-label { + display: block; + font-weight: 500; + margin-bottom: 0.5rem; + font-size: 0.9rem; +} + +.amb-required { + color: var(--amb-danger); + margin-left: 0.25rem; +} + +/* Form control overrides for dark mode */ +[data-theme="dark"] .form-control, +[data-theme="dark"] .form-select { + background-color: var(--amb-bg-tertiary); + border-color: var(--amb-border-color); + color: var(--amb-text-primary); +} + +[data-theme="dark"] .form-control:focus, +[data-theme="dark"] .form-select:focus { + background-color: var(--amb-bg-tertiary); + border-color: var(--amb-accent); + color: var(--amb-text-primary); + box-shadow: 0 0 0 0.25rem rgba(88, 166, 255, 0.25); +} + +[data-theme="dark"] .form-control::placeholder { + color: var(--amb-text-muted); +} + +[data-theme="dark"] .form-control:disabled, +[data-theme="dark"] .form-select:disabled { + background-color: var(--amb-bg-primary); + opacity: 0.65; +} + +/* Validation states */ +.form-control.is-valid, +.form-select.is-valid { + border-color: var(--amb-success); +} + +.form-control.is-invalid, +.form-select.is-invalid { + border-color: var(--amb-danger); +} + +/* ========================================================================== + 6. Choices.js Overrides + ========================================================================== */ +.choices { + margin-bottom: 0; +} + +.choices__inner { + background-color: var(--amb-bg-secondary); + border-color: var(--amb-border-color); + border-radius: 0.375rem; + min-height: 38px; + padding: 0.375rem 0.75rem; + transition: border-color var(--amb-transition), box-shadow var(--amb-transition); +} + +.choices__inner:focus-within { + border-color: var(--amb-accent); + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); +} + +.choices__list--dropdown { + background-color: var(--amb-bg-secondary); + border-color: var(--amb-border-color); + border-radius: 0.375rem; + box-shadow: var(--amb-card-shadow); + z-index: 100; +} + +.choices__list--dropdown .choices__item--selectable.is-highlighted { + background-color: var(--amb-accent); + color: #fff; +} + +.choices__input { + background-color: transparent !important; + color: var(--amb-text-primary); +} + +.choices__placeholder { + color: var(--amb-text-muted); + opacity: 1; +} + +.choices[data-type*="select-one"] .choices__input { + background-color: var(--amb-bg-secondary) !important; +} + +/* Dark mode Choices.js */ +[data-theme="dark"] .choices__inner { + background-color: var(--amb-bg-tertiary); + border-color: var(--amb-border-color); +} + +[data-theme="dark"] .choices__inner:focus-within { + border-color: var(--amb-accent); + box-shadow: 0 0 0 0.25rem rgba(88, 166, 255, 0.25); +} + +[data-theme="dark"] .choices__list--dropdown { + background-color: var(--amb-bg-secondary); + border-color: var(--amb-border-color); +} + +[data-theme="dark"] .choices__list--dropdown .choices__item { + color: var(--amb-text-primary); +} + +[data-theme="dark"] .choices[data-type*="select-one"] .choices__input { + background-color: var(--amb-bg-tertiary) !important; + color: var(--amb-text-primary); +} + +/* ========================================================================== + 7. Tables + ========================================================================== */ +.amb-table { + background: var(--amb-bg-secondary); +} + +[data-theme="dark"] .table { + --bs-table-bg: var(--amb-bg-secondary); + --bs-table-color: var(--amb-text-primary); + --bs-table-border-color: var(--amb-border-color); + --bs-table-striped-bg: var(--amb-bg-tertiary); + --bs-table-hover-bg: var(--amb-bg-tertiary); +} + +/* ========================================================================== + 8. Logs Panel + ========================================================================== */ +.amb-logs { + background: var(--amb-code-bg); + border: 1px solid var(--amb-border-color); + border-radius: 0.5rem; + padding: 1rem; + font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Code", monospace; + font-size: 0.8rem; + line-height: 1.5; + max-height: 400px; + overflow-y: auto; + white-space: pre-wrap; + word-wrap: break-word; + transition: background-color var(--amb-transition), border-color var(--amb-transition); +} + +/* ========================================================================== + 9. Status Indicators + ========================================================================== */ +.amb-status { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + font-weight: 500; + font-size: 0.9rem; +} + +.amb-status-idle { + background: rgba(25, 135, 84, 0.1); + color: var(--amb-success); +} + +.amb-status-running { + background: rgba(255, 193, 7, 0.1); + color: var(--amb-warning); +} + +.amb-status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: currentColor; +} + +.amb-status-running .amb-status-dot { + animation: pulse 1.5s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } +} + +/* ========================================================================== + 10. Workflow Builder + ========================================================================== */ +.amb-workflow-task { + background: var(--amb-bg-secondary); + border: 1px solid var(--amb-border-color); + border-radius: 0.75rem; + margin-bottom: 1rem; + box-shadow: var(--amb-card-shadow); + transition: background-color var(--amb-transition), border-color var(--amb-transition); +} + +.amb-workflow-task-header { + background: var(--amb-accent); + color: #fff; + padding: 0.75rem 1rem; + border-radius: 0.75rem 0.75rem 0 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.amb-workflow-task-body { + padding: 1rem; +} + +.amb-workflow-step { + background: var(--amb-bg-tertiary); + border: 1px solid var(--amb-border-color); + border-radius: 0.5rem; + padding: 0.75rem; + margin-bottom: 0.75rem; + transition: background-color var(--amb-transition), border-color var(--amb-transition); +} + +.amb-workflow-step:last-child { + margin-bottom: 0; +} + +.amb-workflow-step-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; +} + +/* ========================================================================== + 11. Buttons + ========================================================================== */ +[data-theme="dark"] .btn-light { + background-color: var(--amb-bg-tertiary); + border-color: var(--amb-border-color); + color: var(--amb-text-primary); +} + +[data-theme="dark"] .btn-light:hover { + background-color: var(--amb-bg-secondary); + border-color: var(--amb-border-color); + color: var(--amb-text-primary); +} + +[data-theme="dark"] .btn-outline-primary { + color: var(--amb-accent); + border-color: var(--amb-accent); +} + +[data-theme="dark"] .btn-outline-primary:hover { + background-color: var(--amb-accent); + border-color: var(--amb-accent); + color: #fff; +} + +[data-theme="dark"] .btn-outline-secondary { + color: var(--amb-text-secondary); + border-color: var(--amb-border-color); +} + +[data-theme="dark"] .btn-outline-secondary:hover { + background-color: var(--amb-bg-tertiary); + border-color: var(--amb-border-color); + color: var(--amb-text-primary); +} + +/* ========================================================================== + 12. Badges + ========================================================================== */ +[data-theme="dark"] .badge.bg-light { + background-color: var(--amb-bg-tertiary) !important; + color: var(--amb-text-primary) !important; +} + +/* ========================================================================== + 13. List Groups + ========================================================================== */ +[data-theme="dark"] .list-group-item { + background-color: var(--amb-bg-secondary); + border-color: var(--amb-border-color); + color: var(--amb-text-primary); +} + +/* ========================================================================== + 14. Input Groups + ========================================================================== */ +[data-theme="dark"] .input-group-text { + background-color: var(--amb-bg-tertiary); + border-color: var(--amb-border-color); + color: var(--amb-text-primary); +} + +/* ========================================================================== + 15. Textarea (Code Editor) + ========================================================================== */ +.amb-code-editor { + font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Code", monospace; + font-size: 0.85rem; + line-height: 1.6; + background: var(--amb-code-bg); + border: 1px solid var(--amb-border-color); + border-radius: 0.5rem; + padding: 1rem; + resize: vertical; + transition: background-color var(--amb-transition), border-color var(--amb-transition); +} + +[data-theme="dark"] .amb-code-editor { + background: var(--amb-code-bg); + color: var(--amb-text-primary); +} + +/* ========================================================================== + 16. Utilities + ========================================================================== */ +.amb-scrollable { + max-height: 400px; + overflow-y: auto; +} + +.amb-gap-2 { + gap: 0.5rem; +} + +.amb-gap-3 { + gap: 1rem; +} + +/* ========================================================================== + 17. Responsive + ========================================================================== */ +@media (max-width: 991.98px) { + .amb-sidebar { + transform: translateX(-100%); + transition: transform var(--amb-transition); + } + + .amb-sidebar.show { + transform: translateX(0); + } + + .amb-main { + margin-left: 0; + } +} + +/* ========================================================================== + 18. Transitions + ========================================================================== */ +.amb-fade-in { + animation: fadeIn 0.3s ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} diff --git a/src/autometabuilder/web/static/js/main.js b/src/autometabuilder/web/static/js/main.js new file mode 100644 index 0000000..5eb713e --- /dev/null +++ b/src/autometabuilder/web/static/js/main.js @@ -0,0 +1,308 @@ +/** + * AutoMetabuilder - Main JavaScript + */ + +/* ========================================================================== + Theme Manager + ========================================================================== */ +const ThemeManager = { + STORAGE_KEY: 'amb-theme', + + init() { + const saved = localStorage.getItem(this.STORAGE_KEY); + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + const theme = saved || (prefersDark ? 'dark' : 'light'); + this.setTheme(theme); + + document.querySelectorAll('[data-theme-toggle]').forEach(btn => { + btn.addEventListener('click', () => this.toggle()); + }); + + window.matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', e => { + if (!localStorage.getItem(this.STORAGE_KEY)) { + this.setTheme(e.matches ? 'dark' : 'light'); + } + }); + }, + + setTheme(theme) { + document.documentElement.setAttribute('data-theme', theme); + localStorage.setItem(this.STORAGE_KEY, theme); + this.updateToggleIcon(theme); + }, + + toggle() { + const current = document.documentElement.getAttribute('data-theme'); + this.setTheme(current === 'dark' ? 'light' : 'dark'); + }, + + updateToggleIcon(theme) { + document.querySelectorAll('[data-theme-toggle] i').forEach(icon => { + icon.className = theme === 'dark' ? 'bi bi-moon-fill' : 'bi bi-sun-fill'; + }); + } +}; + +/* ========================================================================== + Navigation Manager + ========================================================================== */ +const NavigationManager = { + init() { + document.querySelectorAll('[data-section]').forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault(); + const target = link.dataset.section; + this.showSection(target); + }); + }); + + // Handle initial hash + const hash = window.location.hash.slice(1); + if (hash && document.querySelector(`#${hash}`)) { + this.showSection(hash); + } + + // Handle browser back/forward + window.addEventListener('popstate', () => { + const hash = window.location.hash.slice(1); + if (hash && document.querySelector(`#${hash}`)) { + this.showSection(hash, false); + } + }); + }, + + showSection(sectionId, updateHistory = true) { + // Hide all sections + document.querySelectorAll('.amb-section').forEach(s => s.classList.remove('active')); + + // Show target section + const targetSection = document.querySelector(`#${sectionId}`); + if (targetSection) { + targetSection.classList.add('active'); + } + + // Update nav active state + document.querySelectorAll('.amb-nav-link').forEach(n => n.classList.remove('active')); + const activeLink = document.querySelector(`[data-section="${sectionId}"]`); + if (activeLink) { + activeLink.classList.add('active'); + } + + // Update URL hash + if (updateHistory) { + history.pushState(null, '', `#${sectionId}`); + } + } +}; + +/* ========================================================================== + Choices Manager + ========================================================================== */ +const ChoicesManager = { + instances: [], + + init() { + this.initAll(); + }, + + initAll() { + document.querySelectorAll('[data-choices]').forEach(el => { + // Skip if already initialized + if (el.classList.contains('choices__input')) return; + + const optionsStr = el.dataset.choicesOptions || '{}'; + let options = {}; + try { + options = JSON.parse(optionsStr); + } catch (e) { + console.warn('Invalid choices options:', optionsStr); + } + + const instance = new Choices(el, { + searchEnabled: true, + shouldSort: false, + allowHTML: false, + removeItemButton: options.removeItemButton || false, + placeholder: true, + placeholderValue: el.dataset.placeholder || 'Select...', + searchPlaceholderValue: 'Type to search...', + noResultsText: 'No results found', + noChoicesText: 'No choices available', + ...options + }); + this.instances.push(instance); + }); + }, + + destroy() { + this.instances.forEach(instance => { + try { + instance.destroy(); + } catch (e) { + // Ignore errors from already destroyed instances + } + }); + this.instances = []; + }, + + refresh() { + this.destroy(); + this.initAll(); + } +}; + +/* ========================================================================== + Form Validator + ========================================================================== */ +const FormValidator = { + init() { + document.querySelectorAll('form[data-validate]').forEach(form => { + form.addEventListener('submit', event => { + if (!form.checkValidity()) { + event.preventDefault(); + event.stopPropagation(); + } + form.classList.add('was-validated'); + }); + }); + } +}; + +/* ========================================================================== + Status Poller + ========================================================================== */ +const StatusPoller = { + logsInterval: null, + statusInterval: null, + + init() { + this.refreshLogs(); + this.refreshStatus(); + this.logsInterval = setInterval(() => this.refreshLogs(), 2000); + this.statusInterval = setInterval(() => this.refreshStatus(), 2000); + }, + + async refreshLogs() { + try { + const response = await fetch('/api/logs'); + const data = await response.json(); + const logsPre = document.getElementById('logs'); + if (!logsPre) return; + + const wasAtBottom = logsPre.scrollHeight - logsPre.clientHeight <= logsPre.scrollTop + 1; + logsPre.textContent = data.logs; + if (wasAtBottom) { + logsPre.scrollTop = logsPre.scrollHeight; + } + } catch (error) { + console.error('Error fetching logs:', error); + } + }, + + async refreshStatus() { + try { + const response = await fetch('/api/status'); + const data = await response.json(); + + // Update status indicator + const statusIndicator = document.getElementById('status-indicator'); + if (statusIndicator) { + if (data.is_running) { + statusIndicator.className = 'amb-status amb-status-running'; + statusIndicator.innerHTML = ' Running'; + } else { + statusIndicator.className = 'amb-status amb-status-idle'; + statusIndicator.innerHTML = ' Idle'; + } + } + + // Update MVP badge + const mvpBadge = document.getElementById('mvp-badge'); + if (mvpBadge) { + if (data.mvp_reached) { + mvpBadge.className = 'badge bg-primary'; + mvpBadge.innerHTML = ' Reached'; + } else { + mvpBadge.className = 'badge bg-secondary'; + mvpBadge.innerHTML = ' In Progress'; + } + } + + // Update run button + const runBtn = document.getElementById('run-btn'); + if (runBtn) { + runBtn.disabled = data.is_running; + } + + // Update progress bar visibility + const progressBar = document.getElementById('status-progress'); + if (progressBar) { + progressBar.style.display = data.is_running ? 'block' : 'none'; + } + } catch (error) { + console.error('Error fetching status:', error); + } + } +}; + +/* ========================================================================== + Toast Notifications + ========================================================================== */ +const Toast = { + show(message, type = 'info') { + const container = document.getElementById('toast-container') || this.createContainer(); + const toast = document.createElement('div'); + toast.className = `toast align-items-center text-bg-${type} border-0 show`; + toast.setAttribute('role', 'alert'); + toast.innerHTML = ` +
Monitor system status and recent activity
{{ logs }}
- Status: - {% if is_running %} - Bot Running... - {% else %} - Idle - {% endif %} -
-MVP Milestone: - {% if mvp_reached %} - Reached ✓ - {% else %} - In Progress - {% endif %} -
+ +Configure the bot's task execution workflow
+