UNPKG

c9ai

Version:

Universal AI assistant with vibe-based workflows, hybrid cloud+local AI, and comprehensive tool integration

1,404 lines (1,204 loc) 97.7 kB
"use strict"; /** * JIT (Just-In-Time) App Executor * Dynamically creates, executes, and cleans up single-purpose programs */ const fs = require("node:fs"); const path = require("node:path"); const { exec } = require("node:child_process"); const { promisify } = require("node:util"); const os = require("node:os"); const crypto = require("node:crypto"); const execAsync = promisify(exec); class JITExecutor { constructor() { this.tempDir = path.join(os.tmpdir(), 'c9ai-jit'); this.ensureTempDir(); } ensureTempDir() { if (!fs.existsSync(this.tempDir)) { fs.mkdirSync(this.tempDir, { recursive: true }); } } /** * Get user-defined functions as context assignments for calculator context */ getUserFunctions() { try { const { globalRegistry } = require('./function-registry'); const contextCode = globalRegistry.getContextAssignments(); if (contextCode.trim()) { console.log(`📚 Loading ${globalRegistry.getFunctionNames().length} user-defined functions`); return contextCode; } } catch (error) { console.log(`Failed to load user functions: ${error.message}`); } return ''; } /** * Generate unique filename for JIT app */ generateFilename(extension) { const timestamp = Date.now(); const random = crypto.randomBytes(4).toString('hex'); return `c9ai_jit_${timestamp}_${random}.${extension}`; } /** * Execute a JIT calculator app with VM-based evaluation */ async executeCalculator(expression) { const filename = this.generateFilename('js'); const filepath = path.join(this.tempDir, filename); // Get user-defined functions const userFunctionCode = this.getUserFunctions(); // Generate Node.js calculator app with VM const code = ` // JIT Advanced Calculator App - Generated by C9AI const vm = require('vm'); try { // Create safe execution context const context = { // Core Math functions Math: Math, sqrt: Math.sqrt, pow: Math.pow, sin: Math.sin, cos: Math.cos, tan: Math.tan, log: Math.log, log10: Math.log10, log2: Math.log2, exp: Math.exp, PI: Math.PI, E: Math.E, // Additional math functions abs: Math.abs, round: Math.round, floor: Math.floor, ceil: Math.ceil, max: Math.max, min: Math.min, random: Math.random, // Trigonometric asin: Math.asin, acos: Math.acos, atan: Math.atan, atan2: Math.atan2, sinh: Math.sinh, cosh: Math.cosh, tanh: Math.tanh, // Constants pi: Math.PI, e: Math.E, // User-defined functions ${userFunctionCode} // Result storage result: undefined, // Statistics functions sum: (...args) => args.reduce((a, b) => a + b, 0), mean: (...args) => args.reduce((a, b) => a + b, 0) / args.length, median: (...args) => { const sorted = args.sort((a, b) => a - b); const mid = Math.floor(sorted.length / 2); return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]; }, std: (...args) => { const avg = args.reduce((a, b) => a + b, 0) / args.length; const variance = args.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / args.length; return Math.sqrt(variance); }, // Financial functions pv: (rate, nper, pmt, fv = 0) => { // Present Value calculation if (rate === 0) return -(pmt * nper + fv); return -(pmt * (1 - Math.pow(1 + rate, -nper)) / rate + fv * Math.pow(1 + rate, -nper)); }, fv: (rate, nper, pmt, pv = 0) => { // Future Value calculation if (rate === 0) return -(pv + pmt * nper); return -(pv * Math.pow(1 + rate, nper) + pmt * (Math.pow(1 + rate, nper) - 1) / rate); }, npv: (rate, ...values) => { // Net Present Value - Excel standard: NPV(rate, value1, value2, ...) // Also accepts: NPV(rate, [value1, value2, ...]) let cashFlows; if (values.length === 1 && Array.isArray(values[0])) { // Called as npv(rate, [array]) cashFlows = values[0]; } else { // Called as npv(rate, val1, val2, val3, ...) cashFlows = values; } if (!Array.isArray(cashFlows) || cashFlows.length === 0) { throw new Error('NPV requires cash flow values'); } return cashFlows.reduce((acc, value, index) => acc + value / Math.pow(1 + rate, index), 0); }, pmt: (rate, nper, pv, fv = 0, type = 0) => { // Payment calculation for loans/annuities // rate: interest rate per period // nper: number of periods // pv: present value (loan amount, negative for money borrowed) // fv: future value (default 0) // type: 0 = payment at end of period, 1 = at beginning if (rate === 0) return -(pv + fv) / nper; const pvif = Math.pow(1 + rate, nper); return -((pv * pvif + fv) * rate) / ((pvif - 1) * (1 + rate * type)); }, irr: (values, guess = 0.1) => { // Internal Rate of Return (Newton-Raphson approximation) let rate = guess; for (let i = 0; i < 100; i++) { const npvRate = values.reduce((acc, value, index) => acc + value / Math.pow(1 + rate, index), 0); const derivRate = values.reduce((acc, value, index) => acc - index * value / Math.pow(1 + rate, index + 1), 0); const newRate = rate - npvRate / derivRate; if (Math.abs(newRate - rate) < 0.0001) return newRate; rate = newRate; } return rate; }, // Growth calculations cagr: (beginning, ending, periods) => Math.pow(ending / beginning, 1 / periods) - 1, compound: (principal, rate, periods) => principal * Math.pow(1 + rate, periods) }; // Add case-insensitive aliases for all functions const functionNames = Object.keys(context).filter(key => typeof context[key] === 'function'); functionNames.forEach(name => { // Add uppercase version const upperName = name.toUpperCase(); if (!context[upperName]) { context[upperName] = context[name]; } // Add title case version for common functions const titleName = name.charAt(0).toUpperCase() + name.slice(1); if (!context[titleName]) { context[titleName] = context[name]; } }); // Make context safe for execution vm.createContext(context); // Use the original expression let expression = ${JSON.stringify(expression)}; // Normalize statement separators - replace periods followed by whitespace/word with semicolons expression = expression.replace(/\\.\\s+/g, '; '); // Clean up trailing periods expression = expression.replace(/\\.\\s*$/, ''); // Handle variable assignments and return statements if (expression.includes('return ')) { // Handle explicit return statement: "x=3; y=4; return x+y" expression = expression.replace(/return\\s+/, 'result = '); } else if (expression.includes('=') && !expression.includes('result =')) { // Parse statements: "x=3; y=4; x+y" const parts = expression.split(/;+/).map(s => s.trim()).filter(s => s); const lastPart = parts[parts.length - 1]; if (!lastPart.includes('=')) { // Last part is the result expression const assignments = parts.slice(0, -1).filter(p => p.includes('=')); const resultExpr = lastPart; expression = assignments.join('; ') + (assignments.length ? '; ' : '') + 'result = ' + resultExpr; } else { // All parts are assignments, use last assigned variable as result const matches = expression.match(/([a-zA-Z_]\\w*)\\s*=/g); if (matches) { const lastVar = matches[matches.length - 1].replace(/\\s*=$/, ''); expression += '; result = ' + lastVar; } } } else if (!expression.includes('result =')) { // Simple expression without assignments expression = 'result = ' + expression; } // Execute in safe VM context vm.runInContext(expression, context, { timeout: 10000, // 10 second timeout displayErrors: true }); if (context.result === undefined || context.result === null) { throw new Error('Expression did not produce a result'); } if (typeof context.result === 'number' && !isFinite(context.result)) { throw new Error('Result is not a finite number'); } // Extract variables from context (excluding built-in functions and 'result') const builtins = new Set([ 'Math', 'sqrt', 'pow', 'sin', 'cos', 'tan', 'log', 'log10', 'log2', 'exp', 'PI', 'E', 'abs', 'round', 'floor', 'ceil', 'max', 'min', 'random', 'asin', 'acos', 'atan', 'atan2', 'sinh', 'cosh', 'tanh', 'pi', 'e', 'result', 'sum', 'mean', 'median', 'std', 'pv', 'fv', 'npv', 'irr', 'cagr', 'compound' ]); const variables = {}; for (const [key, value] of Object.entries(context)) { // Exclude built-ins, 'result' variable, and variables that have the same value as result (to avoid duplication) if (!builtins.has(key) && key !== 'result' && typeof value === 'number' && value !== context.result) { variables[key] = value; } } const result = { success: true, expression: ${JSON.stringify(expression)}, result: context.result, formatted: typeof context.result === 'number' ? context.result.toLocaleString(undefined, { maximumFractionDigits: 10 }) : String(context.result) }; // Add variables if any were defined if (Object.keys(variables).length > 0) { result.variables = variables; } console.log(JSON.stringify(result)); } catch (error) { console.log(JSON.stringify({ success: false, expression: ${JSON.stringify(expression)}, error: error.message })); } `; try { // Write JIT app fs.writeFileSync(filepath, code); // Execute JIT app const { stdout, stderr } = await execAsync(`node "${filepath}"`, { timeout: 5000, cwd: this.tempDir }); // Parse result const result = JSON.parse(stdout.trim()); // Cleanup this.cleanup(filepath); return result; } catch (error) { this.cleanup(filepath); return { success: false, expression: expression, error: `Execution failed: ${error.message}` }; } } /** * Execute a JIT data analyzer app */ async executeDataAnalyzer(filename, analysis_type = 'summary') { if (!fs.existsSync(filename)) { return { success: false, error: 'File not found' }; } const jitFilename = this.generateFilename('py'); const jitPath = path.join(this.tempDir, jitFilename); // Generate Python data analyzer app const code = ` # JIT Data Analyzer App - Generated by C9AI import json import csv import sys from pathlib import Path def analyze_csv(filepath, analysis_type): try: data = [] with open(filepath, 'r', encoding='utf-8') as f: reader = csv.DictReader(f) data = list(reader) if not data: return {"success": False, "error": "No data found in CSV"} result = { "success": True, "file": filepath, "analysis_type": analysis_type, "rows": len(data), "columns": list(data[0].keys()) if data else [], "sample": data[:3] if data else [] } # Basic statistics for numeric columns numeric_stats = {} for col in result["columns"]: try: values = [float(row[col]) for row in data if row[col].strip()] if values: numeric_stats[col] = { "count": len(values), "min": min(values), "max": max(values), "avg": sum(values) / len(values) } except (ValueError, AttributeError): pass # Skip non-numeric columns result["numeric_stats"] = numeric_stats return result except Exception as e: return {"success": False, "error": str(e)} if __name__ == "__main__": filepath = ${JSON.stringify(filename)} analysis_type = ${JSON.stringify(analysis_type)} result = analyze_csv(filepath, analysis_type) print(json.dumps(result, indent=2)) `; try { // Write JIT app fs.writeFileSync(jitPath, code); // Execute JIT app const { stdout, stderr } = await execAsync(`python3 "${jitPath}"`, { timeout: 30000, cwd: this.tempDir }); // Parse result const result = JSON.parse(stdout.trim()); // Cleanup this.cleanup(jitPath); return result; } catch (error) { this.cleanup(jitPath); return { success: false, error: `Python execution failed: ${error.message}` }; } } /** * Execute a JIT file processor app */ async executeFileProcessor(operation, inputFile, outputFile = null, options = {}) { const jitFilename = this.generateFilename('js'); const jitPath = path.join(this.tempDir, jitFilename); // Generate Node.js file processor const code = ` // JIT File Processor App - Generated by C9AI const fs = require('fs'); const path = require('path'); const operation = ${JSON.stringify(operation)}; const inputFile = ${JSON.stringify(inputFile)}; const outputFile = ${JSON.stringify(outputFile)}; const options = ${JSON.stringify(options)}; async function processFile() { try { if (!fs.existsSync(inputFile)) { throw new Error('Input file not found'); } const content = fs.readFileSync(inputFile, 'utf8'); let result = content; switch (operation) { case 'count-lines': const lines = content.split('\\n').length; result = { lines: lines, characters: content.length, words: content.split(/\\s+/).length }; break; case 'uppercase': result = content.toUpperCase(); break; case 'lowercase': result = content.toLowerCase(); break; case 'extract-emails': const emailRegex = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/g; result = content.match(emailRegex) || []; break; case 'word-frequency': const words = content.toLowerCase().match(/\\b\\w+\\b/g) || []; const frequency = {}; words.forEach(word => frequency[word] = (frequency[word] || 0) + 1); const sorted = Object.entries(frequency).sort((a, b) => b[1] - a[1]).slice(0, 20); result = Object.fromEntries(sorted); break; default: throw new Error('Unknown operation: ' + operation); } if (outputFile && typeof result === 'string') { fs.writeFileSync(outputFile, result); console.log(JSON.stringify({ success: true, operation: operation, input: inputFile, output: outputFile, message: 'File processed successfully' })); } else { console.log(JSON.stringify({ success: true, operation: operation, input: inputFile, result: result })); } } catch (error) { console.log(JSON.stringify({ success: false, operation: operation, input: inputFile, error: error.message })); } } processFile(); `; try { // Write JIT app fs.writeFileSync(jitPath, code); // Execute JIT app const { stdout, stderr } = await execAsync(`node "${jitPath}"`, { timeout: 15000, cwd: this.tempDir }); // Parse result const result = JSON.parse(stdout.trim()); // Cleanup this.cleanup(jitPath); return result; } catch (error) { this.cleanup(jitPath); return { success: false, operation: operation, error: `Execution failed: ${error.message}` }; } } /** * Execute a JIT quiz generator app */ async executeQuizGenerator(topic = 'frontend', difficulty = 'intermediate', questions = 10) { const filename = this.generateFilename('js'); const filepath = path.join(this.tempDir, filename); const code = ` // JIT Quiz Generator App - Generated by C9AI const fs = require('fs'); const path = require('path'); const quizData = { frontend: { beginner: [ { question: "What does HTML stand for?", options: ["Hypertext Markup Language", "High Tech Modern Language", "Home Tool Markup Language", "Hyperlink and Text Markup Language"], correct: 0, explanation: "HTML stands for Hypertext Markup Language, the standard markup language for creating web pages." }, { question: "Which CSS property is used to change the text color?", options: ["font-color", "text-color", "color", "foreground-color"], correct: 2, explanation: "The 'color' property in CSS is used to set the text color of an element." }, { question: "What does CSS stand for?", options: ["Computer Style Sheets", "Cascading Style Sheets", "Creative Style Sheets", "Colorful Style Sheets"], correct: 1, explanation: "CSS stands for Cascading Style Sheets, used for styling HTML documents." }, { question: "Which HTML tag is used to create a hyperlink?", options: ["<link>", "<a>", "<href>", "<url>"], correct: 1, explanation: "The <a> tag with the href attribute is used to create hyperlinks in HTML." }, { question: "What is the correct way to write a JavaScript array?", options: ["var colors = 'red', 'green', 'blue'", "var colors = (1:'red', 2:'green', 3:'blue')", "var colors = ['red', 'green', 'blue']", "var colors = 1 = ('red'), 2 = ('green'), 3 = ('blue')"], correct: 2, explanation: "JavaScript arrays are created using square brackets with elements separated by commas." } ], intermediate: [ { question: "What is the virtual DOM in React?", options: ["A copy of the real DOM stored in memory", "A new HTML specification", "A browser API", "A CSS framework"], correct: 0, explanation: "The virtual DOM is React's representation of the real DOM kept in memory and synced with the real DOM through reconciliation." }, { question: "Which CSS methodology follows the Block, Element, Modifier naming convention?", options: ["SMACSS", "BEM", "OOCSS", "ITCSS"], correct: 1, explanation: "BEM (Block, Element, Modifier) is a CSS naming methodology that helps create reusable components." }, { question: "What is the purpose of webpack in modern frontend development?", options: ["Database management", "Module bundling and build automation", "Server-side rendering", "API testing"], correct: 1, explanation: "Webpack is a module bundler that processes and bundles JavaScript files and other assets for web applications." }, { question: "In JavaScript, what does 'hoisting' refer to?", options: ["Moving variables to the top of their scope during compilation", "Lifting heavy objects", "Increasing performance", "Creating new functions"], correct: 0, explanation: "Hoisting is JavaScript's behavior of moving variable and function declarations to the top of their containing scope during compilation." }, { question: "What is the difference between '==' and '===' in JavaScript?", options: ["No difference", "=== checks type and value, == only checks value", "== is faster than ===", "=== is deprecated"], correct: 1, explanation: "=== (strict equality) checks both type and value, while == (loose equality) performs type coercion before comparison." }, { question: "Which React hook is used for managing component state?", options: ["useEffect", "useState", "useContext", "useReducer"], correct: 1, explanation: "useState is the React hook used for managing local component state in functional components." }, { question: "What is the purpose of CSS Grid?", options: ["Creating animations", "Two-dimensional layout system", "Color management", "Font loading"], correct: 1, explanation: "CSS Grid is a powerful two-dimensional layout system for creating complex web layouts with rows and columns." }, { question: "In modern JavaScript, what does 'destructuring' allow you to do?", options: ["Delete objects", "Extract values from arrays or objects into variables", "Create new data types", "Optimize performance"], correct: 1, explanation: "Destructuring allows you to extract values from arrays or properties from objects into distinct variables using a concise syntax." } ], advanced: [ { question: "What is the purpose of React.memo()?", options: ["Memoizing expensive calculations", "Preventing unnecessary re-renders of functional components", "Creating memory leaks", "Storing component state"], correct: 1, explanation: "React.memo() is a higher-order component that prevents unnecessary re-renders by memoizing the component based on props comparison." }, { question: "What is the difference between Server-Side Rendering (SSR) and Static Site Generation (SSG)?", options: ["No difference", "SSR renders at request time, SSG renders at build time", "SSG is always faster", "SSR only works with databases"], correct: 1, explanation: "SSR renders pages on each request on the server, while SSG pre-renders pages at build time, creating static HTML files." }, { question: "In JavaScript, what is a closure?", options: ["A way to close browser windows", "A function that has access to variables in its outer scope even after the outer function returns", "A CSS property", "A React component"], correct: 1, explanation: "A closure is a function that retains access to variables from its outer (enclosing) scope even after the outer function has finished executing." }, { question: "What is the purpose of Web Workers in frontend development?", options: ["Styling web pages", "Running JavaScript in background threads", "Database operations", "Creating animations"], correct: 1, explanation: "Web Workers allow you to run JavaScript code in background threads, preventing blocking of the main UI thread for heavy computations." }, { question: "What is tree shaking in the context of module bundlers?", options: ["Removing unused code from bundles", "Organizing file structures", "Creating component trees", "Performance testing"], correct: 0, explanation: "Tree shaking is the process of eliminating dead code (unused exports) from JavaScript bundles to reduce bundle size." } ] } }; try { const topic = ${JSON.stringify(topic)}; const difficulty = ${JSON.stringify(difficulty)}; const questionCount = ${JSON.stringify(questions)}; if (!quizData[topic]) { throw new Error(\`Topic '\${topic}' not found. Available topics: \${Object.keys(quizData).join(', ')}\`); } if (!quizData[topic][difficulty]) { throw new Error(\`Difficulty '\${difficulty}' not found for topic '\${topic}'. Available: \${Object.keys(quizData[topic]).join(', ')}\`); } const availableQuestions = quizData[topic][difficulty]; const selectedQuestions = availableQuestions.slice(0, Math.min(questionCount, availableQuestions.length)); // Generate HTML quiz application const htmlContent = \`<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Frontend Stack Quiz - \${topic.toUpperCase()} (\${difficulty})</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .quiz-container { max-width: 800px; margin: 0 auto; background: white; border-radius: 15px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); overflow: hidden; } .quiz-header { background: linear-gradient(45deg, #FF6B6B, #4ECDC4); color: white; padding: 30px; text-align: center; } .quiz-header h1 { font-size: 2.5em; margin-bottom: 10px; } .quiz-header p { font-size: 1.2em; opacity: 0.9; } .quiz-content { padding: 40px; } .question-container { display: none; animation: fadeIn 0.5s ease-in; } .question-container.active { display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .question-number { color: #667eea; font-size: 0.9em; font-weight: bold; margin-bottom: 10px; } .question { font-size: 1.3em; color: #333; margin-bottom: 30px; line-height: 1.5; } .options { list-style: none; } .option { margin-bottom: 15px; } .option input[type="radio"] { display: none; } .option label { display: block; padding: 20px; background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 10px; cursor: pointer; transition: all 0.3s ease; font-size: 1.1em; } .option label:hover { background: #e3f2fd; border-color: #2196f3; } .option input[type="radio"]:checked + label { background: #4CAF50; color: white; border-color: #4CAF50; } .option.correct label { background: #4CAF50 !important; color: white; border-color: #4CAF50; } .option.incorrect label { background: #f44336 !important; color: white; border-color: #f44336; } .explanation { background: #fff3cd; border: 1px solid #ffeeba; border-radius: 8px; padding: 15px; margin-top: 20px; display: none; } .explanation.show { display: block; animation: fadeIn 0.5s ease-in; } .navigation { display: flex; justify-content: space-between; align-items: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #e9ecef; } .btn { padding: 15px 30px; border: none; border-radius: 8px; font-size: 1.1em; cursor: pointer; transition: all 0.3s ease; } .btn-primary { background: #667eea; color: white; } .btn-primary:hover { background: #5a67d8; } .btn-secondary { background: #6c757d; color: white; } .btn-secondary:hover { background: #545b62; } .btn:disabled { background: #ccc; cursor: not-allowed; } .progress-bar { width: 100%; height: 6px; background: #e9ecef; border-radius: 3px; margin-bottom: 20px; } .progress { height: 100%; background: linear-gradient(90deg, #4ECDC4, #44A08D); border-radius: 3px; transition: width 0.3s ease; } .quiz-results { display: none; text-align: center; padding: 40px; } .score { font-size: 3em; font-weight: bold; color: #4CAF50; margin-bottom: 20px; } .score.poor { color: #f44336; } .score.average { color: #ff9800; } .score.good { color: #4CAF50; } .score-message { font-size: 1.3em; color: #666; margin-bottom: 30px; } .restart-btn { background: linear-gradient(45deg, #FF6B6B, #4ECDC4); color: white; padding: 15px 40px; border: none; border-radius: 25px; font-size: 1.2em; cursor: pointer; transition: transform 0.3s ease; } .restart-btn:hover { transform: translateY(-2px); } </style> </head> <body> <div class="quiz-container"> <div class="quiz-header"> <h1>🚀 Frontend Stack Quiz</h1> <p>\${topic.toUpperCase()} - \${difficulty.toUpperCase()} Level</p> </div> <div class="quiz-content"> <div class="progress-bar"> <div class="progress" style="width: 0%"></div> </div> \${selectedQuestions.map((q, index) => \` <div class="question-container \${index === 0 ? 'active' : ''}" data-question="\${index}"> <div class="question-number">Question \${index + 1} of \${selectedQuestions.length}</div> <div class="question">\${q.question}</div> <ul class="options"> \${q.options.map((option, optIndex) => \` <li class="option"> <input type="radio" id="q\${index}_opt\${optIndex}" name="question\${index}" value="\${optIndex}"> <label for="q\${index}_opt\${optIndex}">\${option}</label> </li> \`).join('')} </ul> <div class="explanation"> <strong>Explanation:</strong> \${q.explanation} </div> <div class="navigation"> <button class="btn btn-secondary" id="prevBtn\${index}" \${index === 0 ? 'style="visibility: hidden"' : ''}>Previous</button> <button class="btn btn-primary" id="nextBtn\${index}">\${index === selectedQuestions.length - 1 ? 'Finish Quiz' : 'Next Question'}</button> </div> </div> \`).join('')} <div class="quiz-results" id="quizResults"> <div class="score" id="finalScore">0%</div> <div class="score-message" id="scoreMessage">Loading...</div> <button class="restart-btn" id="restartBtn">Take Quiz Again</button> </div> </div> </div> <script> const quizData = \${JSON.stringify(selectedQuestions)}; let currentQuestion = 0; let userAnswers = []; let score = 0; console.log('Quiz initialized with', quizData.length, 'questions'); console.log('First question container:', document.querySelector('[data-question="0"]')); function updateProgress() { const progress = ((currentQuestion + 1) / quizData.length) * 100; document.querySelector('.progress').style.width = progress + '%'; } function nextQuestion() { console.log('nextQuestion clicked, currentQuestion:', currentQuestion); const selectedOption = document.querySelector('input[name="question' + currentQuestion + '"]:checked'); console.log('selectedOption found:', selectedOption); if (!selectedOption) { alert('Please select an answer before proceeding.'); return; } const selectedValue = parseInt(selectedOption.value); userAnswers[currentQuestion] = selectedValue; console.log('Stored answer:', selectedValue, 'for question', currentQuestion); // Show correct/incorrect styling and explanation const currentContainer = document.querySelector('[data-question="' + currentQuestion + '"]'); const options = currentContainer.querySelectorAll('.option'); const correctIndex = quizData[currentQuestion].correct; options.forEach((option, index) => { const input = option.querySelector('input'); if (index === correctIndex) { option.classList.add('correct'); } else if (input.checked && index !== correctIndex) { option.classList.add('incorrect'); } input.disabled = true; }); currentContainer.querySelector('.explanation').classList.add('show'); setTimeout(() => { if (currentQuestion < quizData.length - 1) { currentContainer.classList.remove('active'); currentQuestion++; document.querySelector('[data-question="' + currentQuestion + '"]').classList.add('active'); updateProgress(); } else { showResults(); } }, 2000); } function prevQuestion() { if (currentQuestion > 0) { document.querySelector('[data-question="' + currentQuestion + '"]').classList.remove('active'); currentQuestion--; document.querySelector('[data-question="' + currentQuestion + '"]').classList.add('active'); updateProgress(); } } function showResults() { score = userAnswers.reduce((total, answer, index) => { return total + (answer === quizData[index].correct ? 1 : 0); }, 0); const percentage = Math.round((score / quizData.length) * 100); document.querySelector('.quiz-content .question-container.active').style.display = 'none'; document.getElementById('quizResults').style.display = 'block'; document.getElementById('finalScore').textContent = percentage + '%'; const scoreElement = document.getElementById('finalScore'); const messageElement = document.getElementById('scoreMessage'); if (percentage >= 80) { scoreElement.className = 'score good'; messageElement.textContent = 'Excellent! You got ' + score + ' out of ' + quizData.length + ' questions correct. You have a strong understanding of frontend development!'; } else if (percentage >= 60) { scoreElement.className = 'score average'; messageElement.textContent = 'Good job! You got ' + score + ' out of ' + quizData.length + ' questions correct. Keep learning to improve your skills!'; } else { scoreElement.className = 'score poor'; messageElement.textContent = 'You got ' + score + ' out of ' + quizData.length + ' questions correct. Don\'t worry, keep practicing and you\'ll improve!'; } } // Initialize quiz when DOM is loaded document.addEventListener('DOMContentLoaded', function() { console.log('DOM loaded, initializing quiz...'); updateProgress(); // Add event listeners to all Next buttons document.querySelectorAll('[id^="nextBtn"]').forEach(function(btn) { btn.addEventListener('click', function() { console.log('Next button clicked via event listener'); nextQuestion(); }); }); // Add event listeners to all Previous buttons document.querySelectorAll('[id^="prevBtn"]').forEach(function(btn) { btn.addEventListener('click', function() { console.log('Previous button clicked via event listener'); prevQuestion(); }); }); // Add event listener to restart button const restartBtn = document.getElementById('restartBtn'); if (restartBtn) { restartBtn.addEventListener('click', function() { console.log('Restart button clicked via event listener'); restartQuiz(); }); } console.log('Event listeners added:'); console.log('- Next buttons:', document.querySelectorAll('[id^="nextBtn"]').length); console.log('- Previous buttons:', document.querySelectorAll('[id^="prevBtn"]').length); console.log('- Restart button:', restartBtn ? 1 : 0); }); function restartQuiz() { currentQuestion = 0; userAnswers = []; score = 0; // Reset all questions document.querySelectorAll('.question-container').forEach((container, index) => { container.style.display = index === 0 ? 'block' : 'none'; container.classList.toggle('active', index === 0); // Reset options container.querySelectorAll('.option').forEach(option => { option.classList.remove('correct', 'incorrect'); const input = option.querySelector('input'); input.checked = false; input.disabled = false; }); // Hide explanations container.querySelector('.explanation').classList.remove('show'); }); document.getElementById('quizResults').style.display = 'none'; updateProgress(); } // Initialize updateProgress(); </script> </body> </html>\`; // Save the HTML file to public directory const htmlFilename = 'frontend_quiz_' + Date.now() + '.html'; const publicDir = '/Users/hebbarp/todo-management/c9ai/public'; const htmlPath = path.join(publicDir, htmlFilename); // Ensure public directory exists if (!fs.existsSync(publicDir)) { fs.mkdirSync(publicDir, { recursive: true }); } fs.writeFileSync(htmlPath, htmlContent); console.log(JSON.stringify({ success: true, topic: topic, difficulty: difficulty, questions: selectedQuestions.length, quiz_file: htmlFilename, quiz_url: 'http://127.0.0.1:8787/' + htmlFilename, message: 'Quiz generated successfully! ' + selectedQuestions.length + ' questions on ' + topic + ' (' + difficulty + ' level)' })); } catch (error) { console.log(JSON.stringify({ success: false, error: error.message })); } `; try { // Write JIT app fs.writeFileSync(filepath, code); // Execute JIT app const { stdout, stderr } = await execAsync(`node "${filepath}"`, { timeout: 15000, cwd: this.tempDir }); // Parse result const result = JSON.parse(stdout.trim()); // Cleanup this.cleanup(filepath); return result; } catch (error) { this.cleanup(filepath); return { success: false, error: `Quiz generation failed: ${error.message}` }; } } /** * Read document file in various formats */ async readDocumentFile(filePath) { // Resolve relative paths const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath); // Check if file exists if (!fs.existsSync(absolutePath)) { throw new Error(`File not found: ${absolutePath}`); } const ext = path.extname(absolutePath).toLowerCase(); switch (ext) { case '.txt': case '.md': return fs.readFileSync(absolutePath, 'utf8'); case '.html': case '.htm': // Strip HTML tags for basic text extraction const htmlContent = fs.readFileSync(absolutePath, 'utf8'); return htmlContent.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); case '.json': const jsonContent = fs.readFileSync(absolutePath, 'utf8'); const parsed = JSON.parse(jsonContent); return typeof parsed === 'string' ? parsed : JSON.stringify(parsed, null, 2); case '.pdf': // For now, return error with instructions throw new Error('PDF support requires additional dependencies. Please convert to .txt or paste content directly.'); case '.docx': // For now, return error with instructions throw new Error('DOCX support requires additional dependencies. Please convert to .txt or paste content directly.'); default: // Try to read as plain text try { return fs.readFileSync(absolutePath, 'utf8'); } catch (error) { throw new Error(`Unsupported file type: ${ext}. Supported: .txt, .md, .html, .json`); } } } /** * Execute RFQ Analysis JIT app */ async executeRFQAnalysis(rfqText, hourlyRate = 150, marginTarget = 20, filePath = null) { // If filePath provided, read the file first if (filePath) { try { rfqText = await this.readDocumentFile(filePath); } catch (error) { return { success: false, error: `Failed to read file "${filePath}": ${error.message}`, file_path: filePath }; } } const filename = this.generateFilename('js'); const filepath = path.join(this.tempDir, filename); // Generate RFQ Analysis JIT app using string concatenation to avoid template issues const code = ` // JIT RFQ Analysis App - Generated by C9AI try { const rfqText = ${JSON.stringify(rfqText)}; const hourlyRate = ${hourlyRate}; const marginTarget = ${marginTarget}; // RFQ Analysis Engine function analyzeRFQ(text) { const analysis = { requirements: [], complexity_score: 0, estimated_hours: 0, phases: [], risks: [] }; // Extract requirements using keyword analysis const requirementKeywords = [ 'must', 'should', 'require', 'need', 'implement', 'develop', 'create', 'build', 'integrate', 'support', 'provide', 'ensure', 'deliver' ]; const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 10); sentences.forEach(sentence => { const lowerSentence = sentence.toLowerCase(); // Check if sentence contains requirement keywords const hasRequirement = requirementKeywords.some(keyword => lowerSentence.includes(keyword) ); if (hasRequirement) { const complexity = getComplexity(lowerSentence); analysis.requirements.push({ text: sentence.trim(), priority: getPriority(lowerSentence), complexity: complexity, effort_hours: estimateHours(complexity) }); } }); // Calculate overall complexity score (1-10) analysis.complexity_score = Math.min(10, Math.max(1, Math.round(analysis.requirements.reduce((sum, r) => sum + r.complexity, 0) / Math.max(1, analysis.requirements.length)) )); // Estimate total hours with buffer const baseHours = analysis.requirements.reduce((total, req) => total + req.effort_hours, 0); const bufferMultiplier = analysis.complexity_score > 7 ? 1.4 : 1.25; analysis.estimated_hours = Math.round(baseHours * bufferMultiplier); // Generate project phases analysis.phases = [ { name: 'Planning & Analysis', weeks: Math.ceil(analysis.estimated_hours * 0.15 / 40), description: 'Requirements analysis and technical design' }, { name: 'Core Development', weeks: Math.ceil(analysis.estimated_hours * 0.5 / 40), description: 'Main feature development and implementation' }, { name: 'Integration & Testing', weeks: Math.ceil(analysis.estimated_hours * 0.25 / 40), description: 'System integration and quality assurance' }, { name: 'Deployment & Handover', weeks: Math.ceil(analysis.estimated_hours * 0.1 / 40), description: 'Production deployment and documentation' } ]; // Identify risks based on complexity and content if (analysis.complexity_score >= 8) { analysis.risks.push('High technical complexity may impact timeline'); } if (text.toLowerCase().includes('integration')) { analysis.risks.push('Third-party integrations may cause dependencies'); } if (text.toLowerCase().includes('real-time')) { analysis.risks.push('Real-time requirements need performance validation'); } if (analysis.requirements.length > 15) { analysis.risks.push('Large scope may require scope prioritization'); } return analysis; } function getPriority(sentence) { if (sentence.includes('must') || sentence.includes('critical') || sentence.includes('required')) { return 'HIGH'; } else if (sentence.includes('should') || sentence.includes('important')) { return 'MEDIUM'; } return 'LOW'; } function getComplexity(sentence) { const complexityKeywords = { 'integration': 4, 'api': 3, 'database': 3, 'authentication': 4, 'real-time': 5, 'machine learning': 5, 'ai': 5, 'security': 4, 'scalable': 4, 'microservices': 5, 'mobile': 2, 'web': 2, 'dashboard': 2, 'reporting': 2, 'automation': 3, 'workflow': 3 }; let complexity = 1; Object.entries(complexityKeywords).forEach(([keyword, score]) => { if (sentence.includes(keyword)) { complexity = Math.max(complexity, score); } }); return complexity; } function estimateHours(complexity) { const baseHours = { 1: 8, 2: 16, 3: 32, 4: 64, 5: 120 }; return baseHours[complexity] || 16; } // Run the analysis const result = analyzeRFQ(rfqText); // Calculate financial projections const totalCost = result.estimated_hours * hourlyRate; const marginAmount = (totalCost * marginTarget) / 100; const proposalAmount = totalCost + marginAmount; const timeline = result.phases.reduce((total, phase) => total + phase.weeks, 0); // Generate bid recommendation let bidDecision = 'GO'; let confidence = 85; if (result.complexity_score >= 9 || result.risks.length >= 4) { bidDecision = 'CAUTION'; confidence = 60; } if (result.estimated_hours < 20 || proposalAmount < 5000) { bidDecision = 'NO-GO'; confidence = 90; } console.log(JSON.stringify({ success: true, analysis_type: 'RFQ_ANALYSIS', requirements_found: result.requirements.length, complexity_score: result.complexity_score, estimated_hours: result.estimated_hours, timeline_weeks: timeline, total_cost: totalCost, proposal_amount: proposalAmount, margin_amount: marginAmount, bid_decision: bidDecision, confidence: confidence + '%', requirements: result.requirements.slice(0, 10), // Limit for display phases: result.phases, risks: result.risks, message: 'RFQ Analysis: ' + result.requirements.length + ' requirements, ' + result.estimated_hours + 'h effort, $' + Math.round(proposalAmount/1000) + 'K proposal (' + bidDecision + ')' })); } catch (error) { console.log(JSON.stringify({ success: false, error: error.message })); } `; try