/* ============================================================================= AUTO TASK JAVASCRIPT - Intelligent Self-Executing Task Interface Premium VIP Mode Functionality ============================================================================= */ // ============================================================================= // STATE MANAGEMENT // ============================================================================= const AutoTaskState = { currentFilter: 'all', tasks: [], compiledPlan: null, pendingDecisions: [], pendingApprovals: [], refreshInterval: null, wsConnection: null }; // ============================================================================= // INITIALIZATION // ============================================================================= document.addEventListener('DOMContentLoaded', function() { initAutoTask(); }); function initAutoTask() { // Initialize WebSocket for real-time updates initWebSocket(); // Start auto-refresh startAutoRefresh(); // Setup event listeners setupEventListeners(); // Load initial stats updateStats(); // Setup keyboard shortcuts setupKeyboardShortcuts(); console.log('AutoTask initialized'); } // ============================================================================= // WEBSOCKET CONNECTION // ============================================================================= function initWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws/autotask`; try { AutoTaskState.wsConnection = new WebSocket(wsUrl); AutoTaskState.wsConnection.onopen = function() { console.log('AutoTask WebSocket connected'); }; AutoTaskState.wsConnection.onmessage = function(event) { handleWebSocketMessage(JSON.parse(event.data)); }; AutoTaskState.wsConnection.onclose = function() { console.log('AutoTask WebSocket disconnected, reconnecting...'); setTimeout(initWebSocket, 5000); }; AutoTaskState.wsConnection.onerror = function(error) { console.error('AutoTask WebSocket error:', error); }; } catch (e) { console.warn('WebSocket not available, using polling'); } } function handleWebSocketMessage(data) { switch (data.type) { case 'task_update': updateTaskInList(data.task); break; case 'step_progress': updateStepProgress(data.taskId, data.step, data.progress); break; case 'decision_required': showDecisionNotification(data.decision); break; case 'approval_required': showApprovalNotification(data.approval); break; case 'task_completed': onTaskCompleted(data.task); break; case 'task_failed': onTaskFailed(data.task, data.error); break; case 'stats_update': updateStatsFromData(data.stats); break; } } // ============================================================================= // EVENT LISTENERS // ============================================================================= function setupEventListeners() { // Intent form submission const intentForm = document.getElementById('intent-form'); if (intentForm) { intentForm.addEventListener('htmx:afterSwap', function(event) { if (event.detail.target.id === 'compilation-result') { onCompilationComplete(event); } }); } // Task list updates const taskList = document.getElementById('task-list'); if (taskList) { taskList.addEventListener('htmx:afterSwap', function() { updateStats(); highlightPendingItems(); }); } // Expand log entries on details open document.addEventListener('toggle', function(event) { if (event.target.classList.contains('execution-log') && event.target.open) { const taskId = event.target.closest('.autotask-item')?.dataset.taskId; if (taskId) { loadExecutionLogs(taskId); } } }, true); } function setupKeyboardShortcuts() { document.addEventListener('keydown', function(e) { // Alt + N: Focus on intent input if (e.altKey && e.key === 'n') { e.preventDefault(); document.getElementById('intent-input')?.focus(); } // Alt + R: Refresh tasks if (e.altKey && e.key === 'r') { e.preventDefault(); refreshTasks(); } // Escape: Close any open modal if (e.key === 'Escape') { closeAllModals(); } // Alt + 1-4: Switch filters if (e.altKey && e.key >= '1' && e.key <= '4') { e.preventDefault(); const filters = ['all', 'running', 'approval', 'decision']; const index = parseInt(e.key) - 1; const tabs = document.querySelectorAll('.filter-tab'); if (tabs[index]) { tabs[index].click(); } } }); } // ============================================================================= // AUTO REFRESH // ============================================================================= function startAutoRefresh() { // Refresh every 5 seconds AutoTaskState.refreshInterval = setInterval(function() { if (!document.hidden) { updateStats(); } }, 5000); } function stopAutoRefresh() { if (AutoTaskState.refreshInterval) { clearInterval(AutoTaskState.refreshInterval); AutoTaskState.refreshInterval = null; } } // ============================================================================= // STATS MANAGEMENT // ============================================================================= function updateStats() { fetch('/api/autotask/stats') .then(response => response.json()) .then(stats => { updateStatsFromData(stats); }) .catch(error => { console.error('Failed to fetch stats:', error); }); } function updateStatsFromData(stats) { // Header stats document.getElementById('stat-running').textContent = stats.running || 0; document.getElementById('stat-pending').textContent = stats.pending || 0; document.getElementById('stat-completed').textContent = stats.completed || 0; document.getElementById('stat-approval').textContent = stats.pending_approval || 0; // Filter counts document.getElementById('count-all').textContent = stats.total || 0; document.getElementById('count-running').textContent = stats.running || 0; document.getElementById('count-approval').textContent = stats.pending_approval || 0; document.getElementById('count-decision').textContent = stats.pending_decision || 0; // Highlight if approvals needed const approvalStat = document.querySelector('.stat-item.highlight'); if (approvalStat && stats.pending_approval > 0) { approvalStat.classList.add('attention'); } else if (approvalStat) { approvalStat.classList.remove('attention'); } } // ============================================================================= // TASK FILTERING // ============================================================================= function filterTasks(filter, button) { AutoTaskState.currentFilter = filter; // Update active tab document.querySelectorAll('.filter-tab').forEach(tab => { tab.classList.remove('active'); }); button.classList.add('active'); // Trigger HTMX request htmx.ajax('GET', `/api/autotask/list?filter=${filter}`, { target: '#task-list', swap: 'innerHTML' }); } function refreshTasks() { const filter = AutoTaskState.currentFilter; htmx.ajax('GET', `/api/autotask/list?filter=${filter}`, { target: '#task-list', swap: 'innerHTML' }); updateStats(); } // ============================================================================= // COMPILATION HANDLING // ============================================================================= function onCompilationComplete(event) { const result = event.detail.target.querySelector('.compiled-plan'); if (result) { // Scroll to result result.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Store compiled plan const planId = result.dataset?.planId; if (planId) { AutoTaskState.compiledPlan = planId; } // Syntax highlight the code highlightBasicCode(); } } function highlightBasicCode() { const codeBlocks = document.querySelectorAll('.code-preview code'); codeBlocks.forEach(block => { // Basic syntax highlighting for BASIC keywords let html = block.innerHTML; // Keywords const keywords = [ 'PLAN_START', 'PLAN_END', 'STEP', 'SET', 'GET', 'IF', 'THEN', 'ELSE', 'END IF', 'FOR EACH', 'NEXT', 'WHILE', 'WEND', 'TALK', 'HEAR', 'LLM', 'CREATE_TASK', 'RUN_PYTHON', 'RUN_JAVASCRIPT', 'RUN_BASH', 'USE_MCP', 'POST', 'GET', 'PUT', 'PATCH', 'DELETE HTTP', 'REQUIRE_APPROVAL', 'SIMULATE_IMPACT', 'AUDIT_LOG', 'SEND_MAIL', 'SAVE', 'UPDATE', 'INSERT', 'DELETE', 'FIND' ]; keywords.forEach(keyword => { const regex = new RegExp(`\\b${keyword}\\b`, 'g'); html = html.replace(regex, `${keyword}`); }); // Comments html = html.replace(/(\'[^\n]*)/g, '$1'); // Strings html = html.replace(/("[^"]*")/g, '$1'); // Numbers html = html.replace(/\b(\d+)\b/g, '$1'); block.innerHTML = html; }); } function copyGeneratedCode() { const code = document.querySelector('.code-preview code')?.textContent; if (code) { navigator.clipboard.writeText(code).then(() => { showToast('Code copied to clipboard', 'success'); }).catch(() => { showToast('Failed to copy code', 'error'); }); } } function discardPlan() { if (confirm('Are you sure you want to discard this plan?')) { document.getElementById('compilation-result').innerHTML = ''; AutoTaskState.compiledPlan = null; document.getElementById('intent-input').value = ''; document.getElementById('intent-input').focus(); } } function editPlan() { // TODO: Implement plan editor showToast('Plan editor coming soon!', 'info'); } // ============================================================================= // PLAN EXECUTION // ============================================================================= function simulatePlan(planId) { showSimulationModal(); fetch(`/api/autotask/simulate/${planId}`, { method: 'POST' }) .then(response => response.json()) .then(result => { renderSimulationResult(result); }) .catch(error => { document.getElementById('simulation-content').innerHTML = `
`; }); } function executePlan(planId) { const executionMode = document.querySelector('[name="execution_mode"]')?.value || 'semi-automatic'; const priority = document.querySelector('[name="priority"]')?.value || 'medium'; if (!confirm('Are you sure you want to execute this plan?')) { return; } fetch('/api/autotask/execute', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ plan_id: planId, execution_mode: executionMode, priority: priority }) }) .then(response => response.json()) .then(result => { if (result.success) { showToast('Task execution started!', 'success'); document.getElementById('compilation-result').innerHTML = ''; document.getElementById('intent-input').value = ''; refreshTasks(); } else { showToast(`Failed to start execution: ${result.error}`, 'error'); } }) .catch(error => { showToast(`Failed to execute plan: ${error.message}`, 'error'); }); } // ============================================================================= // TASK ACTIONS // ============================================================================= function viewTaskDetails(taskId) { window.location.href = `/suite/tasks/detail/${taskId}`; } function simulateTask(taskId) { showSimulationModal(); fetch(`/api/autotask/${taskId}/simulate`, { method: 'POST' }) .then(response => response.json()) .then(result => { result.task_id = taskId; renderSimulationResult(result); }) .catch(error => { document.getElementById('simulation-content').innerHTML = ` `; }); } function pauseTask(taskId) { fetch(`/api/autotask/${taskId}/pause`, { method: 'POST' }) .then(response => response.json()) .then(result => { if (result.success) { showToast('Task paused', 'success'); refreshTasks(); } else { showToast(`Failed to pause task: ${result.error}`, 'error'); } }); } function resumeTask(taskId) { fetch(`/api/autotask/${taskId}/resume`, { method: 'POST' }) .then(response => response.json()) .then(result => { if (result.success) { showToast('Task resumed', 'success'); refreshTasks(); } else { showToast(`Failed to resume task: ${result.error}`, 'error'); } }); } function cancelTask(taskId) { if (!confirm('Are you sure you want to cancel this task? This may not be reversible.')) { return; } fetch(`/api/autotask/${taskId}/cancel`, { method: 'POST' }) .then(response => response.json()) .then(result => { if (result.success) { showToast('Task cancelled', 'success'); refreshTasks(); } else { showToast(`Failed to cancel task: ${result.error}`, 'error'); } }); } function updateTaskInList(task) { const taskElement = document.querySelector(`[data-task-id="${task.id}"]`); if (taskElement) { // Update status badge const statusBadge = taskElement.querySelector('.task-status-badge'); if (statusBadge) { statusBadge.className = `task-status-badge status-${task.status}`; statusBadge.textContent = task.status.replace(/-/g, ' '); } // Update progress const progressFill = taskElement.querySelector('.progress-fill'); const progressText = taskElement.querySelector('.progress-text'); if (progressFill && progressText) { progressFill.style.width = `${task.progress}%`; progressText.textContent = `${task.current_step}/${task.total_steps} steps (${Math.round(task.progress)}%)`; } // Update data attribute taskElement.dataset.status = task.status; } } function updateStepProgress(taskId, step, progress) { const taskElement = document.querySelector(`[data-task-id="${taskId}"]`); if (taskElement) { const currentStep = taskElement.querySelector('.current-step'); if (currentStep) { currentStep.querySelector('.step-name').textContent = `Step ${step.order}: ${step.name}`; currentStep.querySelector('.step-status').textContent = `${Math.round(progress)}%`; } } } // ============================================================================= // DECISIONS // ============================================================================= function viewDecisions(taskId) { showDecisionModal(); fetch(`/api/autotask/${taskId}/decisions`) .then(response => response.json()) .then(decisions => { renderDecisions(taskId, decisions); }) .catch(error => { document.getElementById('decision-content').innerHTML = ` `; }); } function renderDecisions(taskId, decisions) { const container = document.getElementById('decision-content'); if (!decisions || decisions.length === 0) { container.innerHTML = 'No pending decisions.
'; return; } let html = '${decision.description}
No pending approvals.
'; return; } let html = '${approval.description}
${approval.impact_summary}