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
JavaScript
"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