mirror of
https://github.com/thecyberlearn/quantum-ai-v2.git
synced 2026-08-18 18:52:58 +00:00
- Add CyberSec Career Navigator with JotForm white-label integration - Implement direct access agent system alongside webhook agents - Add digital branding services page with SOSTAC+RACE methodology - Create dual integration patterns: webhook vs direct access flows - Clean up unused form API endpoints for streamlined architecture - Add career navigator management command and templates - Update marketplace with conditional logic for different agent types - Fix UI consistency issues (remove category labels, fix animations) - Update documentation to reflect new dual architecture 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
669 lines
25 KiB
JavaScript
669 lines
25 KiB
JavaScript
/**
|
|
* Agents Core - Dynamic Agent Execution System
|
|
* Handles form submission and N8N integration for any agent
|
|
*/
|
|
|
|
class AgentsCore extends WorkflowsCore {
|
|
constructor() {
|
|
super();
|
|
this.agentId = document.body.getAttribute('data-agent-id');
|
|
this.agentSlug = document.body.getAttribute('data-agent-slug');
|
|
this.webhookUrl = document.body.getAttribute('data-webhook-url');
|
|
this.price = parseFloat(document.body.getAttribute('data-agent-price') || '0');
|
|
this.sessionId = this.constructor.generateSessionId();
|
|
|
|
// Initialize on page load
|
|
this.initialize();
|
|
}
|
|
|
|
initialize() {
|
|
// Initialize form submission
|
|
const form = document.getElementById('agentForm');
|
|
if (form) {
|
|
form.addEventListener('submit', this.handleFormSubmission.bind(this));
|
|
}
|
|
|
|
// Initialize form validation
|
|
this.initializeDynamicFormValidation();
|
|
}
|
|
|
|
/**
|
|
* Handle form submission with agents API integration
|
|
*/
|
|
async handleFormSubmission(e) {
|
|
e.preventDefault();
|
|
|
|
if (!this.isFormValid()) {
|
|
this.constructor.showToast('Please fill in all required fields correctly', 'error');
|
|
return;
|
|
}
|
|
|
|
// Check authentication and balance
|
|
if (!this.constructor.checkAuthentication()) return;
|
|
if (!this.constructor.checkBalance(this.price)) return;
|
|
|
|
// Show processing status and disable submit button
|
|
this.constructor.showProcessing('Executing agent...');
|
|
|
|
const submitBtn = document.getElementById('generateBtn');
|
|
if (submitBtn) {
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = '⏳ Processing...';
|
|
}
|
|
|
|
try {
|
|
// Use agents API for execution
|
|
await this.executeViaAgentsAPI(e.target);
|
|
} catch (error) {
|
|
console.error('Form submission error:', error);
|
|
this.constructor.hideProcessing();
|
|
this.constructor.showToast('❌ Connection error. Please try again.', 'error');
|
|
this.resetSubmitButton();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute agent via the agents API
|
|
*/
|
|
async executeViaAgentsAPI(form) {
|
|
try {
|
|
const formData = new FormData(form);
|
|
|
|
// Check if form contains file uploads
|
|
const hasFiles = Array.from(formData.entries()).some(([key, value]) =>
|
|
value instanceof File && key !== 'csrfmiddlewaretoken'
|
|
);
|
|
|
|
if (hasFiles) {
|
|
// Handle file upload via multipart form data
|
|
await this.executeWithFileUpload(formData);
|
|
} else {
|
|
// Handle regular form data via JSON API
|
|
await this.executeWithJsonAPI(formData);
|
|
}
|
|
} catch (error) {
|
|
console.error('Agent execution error:', error);
|
|
this.constructor.hideProcessing();
|
|
this.constructor.showToast(`❌ ${error.message}`, 'error');
|
|
this.resetSubmitButton();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute agent with file upload using multipart form data
|
|
*/
|
|
async executeWithFileUpload(formData) {
|
|
// Get CSRF token
|
|
const csrfToken = formData.get('csrfmiddlewaretoken');
|
|
|
|
// Prepare multipart form data for direct webhook call (similar to workflows)
|
|
const webhookFormData = new FormData();
|
|
|
|
// Add files and regular form fields
|
|
for (let [key, value] of formData.entries()) {
|
|
if (key !== 'csrfmiddlewaretoken') {
|
|
if (value instanceof File) {
|
|
webhookFormData.append('file', value);
|
|
} else {
|
|
// Map form fields to webhook expected format
|
|
if (key === 'analysis_type') {
|
|
webhookFormData.append('analysisType', value);
|
|
} else {
|
|
webhookFormData.append(key, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call webhook directly for file uploads (similar to workflows approach)
|
|
const response = await fetch(this.webhookUrl, {
|
|
method: 'POST',
|
|
body: webhookFormData,
|
|
signal: AbortSignal.timeout(120000) // 2 minute timeout for file processing
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`File processing failed: ${response.status}`);
|
|
}
|
|
|
|
// Parse response
|
|
const data = await response.json();
|
|
|
|
// Deduct wallet balance manually since we bypassed the API
|
|
await this.deductBalanceForFileUpload();
|
|
|
|
// Process successful execution
|
|
this.constructor.hideProcessing();
|
|
|
|
// Display results
|
|
this.displayFileProcessingResults(data);
|
|
|
|
this.constructor.showToast('✅ File processed successfully!', 'success');
|
|
}
|
|
|
|
/**
|
|
* Execute agent with JSON API (for non-file uploads)
|
|
*/
|
|
async executeWithJsonAPI(formData) {
|
|
// Extract all form data dynamically
|
|
const inputData = {};
|
|
for (let [key, value] of formData.entries()) {
|
|
if (key !== 'csrfmiddlewaretoken') {
|
|
inputData[key] = value;
|
|
}
|
|
}
|
|
|
|
// Get CSRF token
|
|
const csrfToken = formData.get('csrfmiddlewaretoken');
|
|
|
|
// Call agents API
|
|
const response = await fetch('/agents/api/execute/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': csrfToken
|
|
},
|
|
body: JSON.stringify({
|
|
agent_slug: this.agentSlug,
|
|
input_data: inputData
|
|
}),
|
|
signal: AbortSignal.timeout(90000) // 90 second timeout
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
throw new Error(errorData.error || `API error: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
// Process successful execution
|
|
this.constructor.hideProcessing();
|
|
|
|
// Update wallet balance if fee was charged
|
|
if (data.fee_charged) {
|
|
const currentBalance = parseFloat(document.body.getAttribute('data-user-balance') || '0');
|
|
const newBalance = currentBalance - parseFloat(data.fee_charged);
|
|
|
|
// Update the wallet balance display
|
|
this.constructor.updateWalletBalance(newBalance);
|
|
|
|
// Update the data attribute for future calculations
|
|
document.body.setAttribute('data-user-balance', newBalance.toString());
|
|
}
|
|
|
|
// Display results
|
|
this.displayExecutionResults(data);
|
|
|
|
this.constructor.showToast('✅ Agent executed successfully!', 'success');
|
|
}
|
|
|
|
/**
|
|
* Deduct wallet balance for file upload (manual deduction)
|
|
*/
|
|
async deductBalanceForFileUpload() {
|
|
try {
|
|
// Call the wallet deduction API
|
|
const response = await fetch('/wallet/api/deduct/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
|
},
|
|
body: JSON.stringify({
|
|
amount: this.price,
|
|
description: `${this.agentSlug.replace('-', ' ')} execution`,
|
|
agent_slug: this.agentSlug
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
if (data.new_balance !== undefined) {
|
|
// Update wallet balance display
|
|
this.constructor.updateWalletBalance(data.new_balance);
|
|
document.body.setAttribute('data-user-balance', data.new_balance.toString());
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Wallet deduction error:', error);
|
|
// Continue execution even if wallet update fails
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display results from file processing
|
|
*/
|
|
displayFileProcessingResults(data) {
|
|
const resultsContainer = document.getElementById('resultsContainer');
|
|
const resultsContent = document.getElementById('resultsContent');
|
|
|
|
if (!resultsContainer || !resultsContent) return;
|
|
|
|
let content = '';
|
|
|
|
// Handle different response formats from file processing
|
|
if (data && typeof data === 'object') {
|
|
if (data.sections && Array.isArray(data.sections)) {
|
|
// Multi-section response (array format)
|
|
content = data.sections.map(section => {
|
|
const heading = section.heading || 'Section';
|
|
const sectionContent = section.content || '';
|
|
return `## ${heading}\n\n${sectionContent}`;
|
|
}).join('\n\n');
|
|
} else if (data.sections && typeof data.sections === 'object') {
|
|
// Multi-section response (object format)
|
|
content = Object.entries(data.sections).map(([section, text]) => {
|
|
return `## ${section.replace('_', ' ').toUpperCase()}\n\n${text}`;
|
|
}).join('\n\n');
|
|
} else if (data.output || data.result || data.summary) {
|
|
content = data.output || data.result || data.summary;
|
|
} else if (data.error) {
|
|
content = `Error: ${data.error}`;
|
|
} else {
|
|
content = JSON.stringify(data, null, 2);
|
|
}
|
|
} else if (typeof data === 'string') {
|
|
content = data;
|
|
} else {
|
|
content = 'File processed successfully!';
|
|
}
|
|
|
|
// Clear and populate results securely
|
|
resultsContent.textContent = '';
|
|
this.renderSecureContent(resultsContent, content);
|
|
|
|
// Show results container
|
|
resultsContainer.style.display = 'block';
|
|
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
|
|
this.resetSubmitButton();
|
|
}
|
|
|
|
/**
|
|
* Display results from agent execution
|
|
*/
|
|
displayExecutionResults(executionData) {
|
|
const resultsContainer = document.getElementById('resultsContainer');
|
|
const resultsContent = document.getElementById('resultsContent');
|
|
|
|
if (!resultsContainer || !resultsContent) return;
|
|
|
|
let content = '';
|
|
|
|
// Handle different response formats
|
|
if (executionData.output_data && typeof executionData.output_data === 'object') {
|
|
// Handle N8N response formats
|
|
const output = executionData.output_data;
|
|
content = output.output || output.text || output.content || output.result || output.message || JSON.stringify(output, null, 2);
|
|
} else if (executionData.output_data && typeof executionData.output_data === 'string') {
|
|
content = executionData.output_data;
|
|
} else {
|
|
content = `Agent executed successfully!\n\nExecution ID: ${executionData.id}\nStatus: ${executionData.status}\nFee Charged: ${executionData.fee_charged} AED`;
|
|
}
|
|
|
|
// Clear and populate results securely
|
|
resultsContent.textContent = '';
|
|
this.renderSecureContent(resultsContent, content);
|
|
|
|
// Show results container
|
|
resultsContainer.style.display = 'block';
|
|
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
|
|
this.resetSubmitButton();
|
|
}
|
|
|
|
/**
|
|
* Secure content rendering without innerHTML to prevent XSS
|
|
*/
|
|
renderSecureContent(container, content) {
|
|
// Sanitize and validate content
|
|
if (!content || typeof content !== 'string') {
|
|
container.textContent = 'No content available';
|
|
return;
|
|
}
|
|
|
|
// Create wrapper div
|
|
const wrapper = document.createElement('div');
|
|
wrapper.className = 'results-content';
|
|
|
|
// Split content into lines and process safely
|
|
const lines = content.split('\n');
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim();
|
|
|
|
if (!line) {
|
|
// Add line break for empty lines
|
|
if (i > 0) wrapper.appendChild(document.createElement('br'));
|
|
continue;
|
|
}
|
|
|
|
let element;
|
|
|
|
// Handle headers (but escape content)
|
|
if (line.startsWith('### ')) {
|
|
element = document.createElement('h3');
|
|
element.textContent = line.substring(4);
|
|
} else if (line.startsWith('## ')) {
|
|
element = document.createElement('h2');
|
|
element.textContent = line.substring(3);
|
|
} else if (line.startsWith('# ')) {
|
|
element = document.createElement('h1');
|
|
element.textContent = line.substring(2);
|
|
} else {
|
|
// Handle regular text with basic formatting
|
|
element = document.createElement('span');
|
|
this.formatTextSecurely(element, line);
|
|
}
|
|
|
|
wrapper.appendChild(element);
|
|
|
|
// Add line break if not the last line
|
|
if (i < lines.length - 1) {
|
|
wrapper.appendChild(document.createElement('br'));
|
|
}
|
|
}
|
|
|
|
container.appendChild(wrapper);
|
|
}
|
|
|
|
/**
|
|
* Format text with basic styling while preventing XSS
|
|
*/
|
|
formatTextSecurely(element, text) {
|
|
// Simple approach: handle bold and italic formatting securely
|
|
const parts = [];
|
|
let currentText = text;
|
|
|
|
// Process **bold** text
|
|
currentText = currentText.replace(/\*\*(.*?)\*\*/g, (match, content) => {
|
|
const placeholder = `__BOLD_${parts.length}__`;
|
|
parts.push({type: 'bold', content: content});
|
|
return placeholder;
|
|
});
|
|
|
|
// Process *italic* text
|
|
currentText = currentText.replace(/\*(.*?)\*/g, (match, content) => {
|
|
const placeholder = `__ITALIC_${parts.length}__`;
|
|
parts.push({type: 'italic', content: content});
|
|
return placeholder;
|
|
});
|
|
|
|
// Split by placeholders and create DOM elements
|
|
const segments = currentText.split(/(__(?:BOLD|ITALIC)_\d+__)/);
|
|
|
|
segments.forEach(segment => {
|
|
if (segment.startsWith('__BOLD_')) {
|
|
const index = parseInt(segment.match(/\d+/)[0]);
|
|
const strong = document.createElement('strong');
|
|
strong.textContent = parts[index].content;
|
|
element.appendChild(strong);
|
|
} else if (segment.startsWith('__ITALIC_')) {
|
|
const index = parseInt(segment.match(/\d+/)[0]);
|
|
const em = document.createElement('em');
|
|
em.textContent = parts[index].content;
|
|
element.appendChild(em);
|
|
} else if (segment) {
|
|
element.appendChild(document.createTextNode(segment));
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize dynamic form validation based on form schema
|
|
*/
|
|
initializeDynamicFormValidation() {
|
|
const fields = document.querySelectorAll('#agentForm [name]');
|
|
|
|
fields.forEach(field => {
|
|
const fieldName = field.getAttribute('name');
|
|
if (fieldName && fieldName !== 'csrfmiddlewaretoken') {
|
|
field.addEventListener('blur', () => this.validateField(fieldName));
|
|
field.addEventListener('input', () => this.constructor.clearFieldError(fieldName));
|
|
}
|
|
});
|
|
}
|
|
|
|
validateField(fieldName) {
|
|
const field = document.getElementById(fieldName);
|
|
if (!field) return true;
|
|
|
|
const value = field.type === 'checkbox' ? field.checked : field.value.trim();
|
|
const required = field.hasAttribute('required');
|
|
|
|
// Basic required field validation
|
|
if (required && (!value || value === '')) {
|
|
this.constructor.showFieldError(fieldName, `${fieldName.replace('_', ' ')} is required`);
|
|
return false;
|
|
}
|
|
|
|
// Specific validation based on field type
|
|
if (field.type === 'textarea' && value && value.length < 10) {
|
|
this.constructor.showFieldError(fieldName, 'Please provide more detailed information (at least 10 characters)');
|
|
return false;
|
|
}
|
|
|
|
if (field.type === 'url' && value && !this.isValidURL(value)) {
|
|
this.constructor.showFieldError(fieldName, 'Please enter a valid URL');
|
|
return false;
|
|
}
|
|
|
|
this.constructor.clearFieldError(fieldName);
|
|
return true;
|
|
}
|
|
|
|
isValidURL(string) {
|
|
try {
|
|
new URL(string);
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
isFormValid() {
|
|
const fields = document.querySelectorAll('#agentForm [name]');
|
|
let isValid = true;
|
|
|
|
fields.forEach(field => {
|
|
const fieldName = field.getAttribute('name');
|
|
if (fieldName && fieldName !== 'csrfmiddlewaretoken') {
|
|
if (!this.validateField(fieldName)) {
|
|
isValid = false;
|
|
}
|
|
}
|
|
});
|
|
|
|
return isValid;
|
|
}
|
|
|
|
/**
|
|
* Handle JotForm agent execution (CyberSec Career Navigator)
|
|
*/
|
|
async handleJotFormAgent() {
|
|
// Check authentication and balance
|
|
if (!this.constructor.checkAuthentication()) return;
|
|
if (!this.constructor.checkBalance(this.price)) return;
|
|
|
|
const submitBtn = document.getElementById('generateBtn');
|
|
if (submitBtn) {
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = '⏳ Processing Payment...';
|
|
}
|
|
|
|
try {
|
|
// Create execution record and charge wallet via form API
|
|
const response = await fetch('/agents/api/form/access/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
|
},
|
|
body: JSON.stringify({
|
|
agent_slug: this.agentSlug
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
const result = await response.json();
|
|
|
|
// Update wallet balance
|
|
this.constructor.updateWalletBalance(result.new_balance);
|
|
|
|
// Show white-label interface
|
|
this.showWhiteLabelInterface(result.interface_url);
|
|
|
|
this.constructor.showToast('✅ Payment processed! Access granted to Quantum AI Career Navigator', 'success');
|
|
|
|
} else {
|
|
const error = await response.json();
|
|
throw new Error(error.error || 'Failed to process payment');
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Form agent error:', error);
|
|
this.constructor.showToast(`❌ ${error.message}`, 'error');
|
|
this.resetSubmitButton();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show white-label interface in results container
|
|
*/
|
|
showWhiteLabelInterface(interfaceUrl) {
|
|
const resultsContainer = document.getElementById('resultsContainer');
|
|
const resultsContent = document.getElementById('resultsContent');
|
|
|
|
if (resultsContainer && resultsContent) {
|
|
// Update header
|
|
const widgetTitle = resultsContainer.querySelector('.widget-title');
|
|
if (widgetTitle) {
|
|
widgetTitle.innerHTML = '<span class="widget-icon">🎓</span>Quantum AI Career Navigator';
|
|
}
|
|
|
|
// Create white-label interface
|
|
resultsContent.innerHTML = `
|
|
<div class="career-nav-container" style="text-align: center; margin-bottom: 20px;">
|
|
<h3 style="color: #0369a1; margin-bottom: 10px;">🎓 Quantum AI Career Navigator</h3>
|
|
<p style="color: #6b7280; margin-bottom: 20px;">Meet Jessica, your personal AI cybersecurity career advisor. Share your goals and get expert guidance tailored to your journey.</p>
|
|
</div>
|
|
<div class="career-interface-container" style="width: 100%; min-height: 600px; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.1);">
|
|
<iframe
|
|
src="${interfaceUrl}"
|
|
style="width: 100%; min-height: 600px; border: none; background: white;"
|
|
frameborder="0"
|
|
scrolling="auto"
|
|
title="Quantum AI Career Navigator - Your Personal Career Advisor">
|
|
</iframe>
|
|
</div>
|
|
<div class="career-footer" style="margin-top: 20px; padding: 15px; background: #f8fafc; border-radius: 8px; text-align: center;">
|
|
<p style="color: #6b7280; font-size: 14px; margin: 0;">
|
|
💡 <strong>Pro Tip:</strong> Be specific about your experience level and career goals for the most personalized advice from your AI advisor!
|
|
</p>
|
|
</div>
|
|
`;
|
|
|
|
// Hide action buttons since this is an interactive interface
|
|
const actionButtons = resultsContainer.querySelector('.results-actions');
|
|
if (actionButtons) {
|
|
actionButtons.style.display = 'none';
|
|
}
|
|
|
|
// Show results container
|
|
resultsContainer.style.display = 'block';
|
|
|
|
// Scroll to results
|
|
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
|
|
// Reset submit button
|
|
this.resetSubmitButton();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset submit button to original state
|
|
*/
|
|
resetSubmitButton() {
|
|
const submitBtn = document.getElementById('generateBtn');
|
|
if (submitBtn) {
|
|
submitBtn.disabled = false;
|
|
const agentSlug = document.body.getAttribute('data-agent-slug') || 'agent';
|
|
const agentName = agentSlug.replace('-', ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
submitBtn.textContent = `🚀 Execute ${agentName} (${this.price} AED)`;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Result action functions (global for button onclick handlers)
|
|
function copyResults() {
|
|
const content = document.getElementById('resultsContent');
|
|
if (content) {
|
|
const text = content.textContent || '';
|
|
WorkflowsCore.copyToClipboard(text, 'Results copied to clipboard!');
|
|
}
|
|
}
|
|
|
|
function downloadResults() {
|
|
const content = document.getElementById('resultsContent');
|
|
if (content) {
|
|
const text = content.textContent || '';
|
|
const agentSlug = document.body.getAttribute('data-agent-slug') || 'agent';
|
|
WorkflowsCore.downloadAsFile(text, `${agentSlug}-results.txt`, 'Results downloaded!');
|
|
}
|
|
}
|
|
|
|
function resetForm() {
|
|
const form = document.getElementById('agentForm');
|
|
if (form) {
|
|
// Reset form but preserve CSRF token
|
|
const csrfToken = form.querySelector('[name="csrfmiddlewaretoken"]').value;
|
|
form.reset();
|
|
form.querySelector('[name="csrfmiddlewaretoken"]').value = csrfToken;
|
|
}
|
|
|
|
const resultsContainer = document.getElementById('resultsContainer');
|
|
const processingStatus = document.getElementById('processingStatus');
|
|
|
|
if (resultsContainer) resultsContainer.style.display = 'none';
|
|
if (processingStatus) processingStatus.style.display = 'none';
|
|
|
|
// Clear validation errors
|
|
const fields = document.querySelectorAll('#agentForm [name]');
|
|
fields.forEach(field => {
|
|
const fieldName = field.getAttribute('name');
|
|
if (fieldName && fieldName !== 'csrfmiddlewaretoken') {
|
|
WorkflowsCore.clearFieldError(fieldName);
|
|
}
|
|
});
|
|
|
|
// Reset file upload UI components
|
|
const fileUploadContainers = document.querySelectorAll('.file-upload-container');
|
|
fileUploadContainers.forEach(container => {
|
|
const input = container.querySelector('.form-file-input');
|
|
const label = container.querySelector('.file-upload-label');
|
|
const selectedDiv = container.querySelector('.file-selected');
|
|
|
|
if (input) {
|
|
input.value = '';
|
|
}
|
|
if (label) {
|
|
label.style.display = 'block';
|
|
}
|
|
if (selectedDiv) {
|
|
selectedDiv.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// Scroll back to form
|
|
const formSection = document.getElementById('agentForm');
|
|
if (formSection) {
|
|
formSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
}
|
|
|
|
// Initialize Agents Core when DOM is ready
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize processor (data attributes set by template)
|
|
window.agentsCore = new AgentsCore();
|
|
}); |