aaab
Version:
Advanced AI Agent Builder - A comprehensive framework for building AI agents with TypeScript support
1,579 lines (1,313 loc) • 47.4 kB
JavaScript
const { Command } = require('commander');
const chalk = require('chalk');
const path = require('path');
const fs = require('fs');
const parser = require('../lib/parser/parser');
const validator = require('../lib/validate/validator');
const linter = require('../lib/validate/linter');
const corrector = require('../lib/validate/corrector');
const orchestrator = require('../lib/core/orchestrator');
const logger = require('../lib/diagnostics/logger');
const console = require('../lib/utils/console');
const list = require('../lib/cli/list');
const explain = require('../lib/cli/explain');
const doctor = require('../lib/cli/doctor');
const program = new Command();
program
.name('aaab')
.description('Agent as a Backend - AI agent workflow executor')
.version('1.0.0');
program
.command('run')
.description('Execute an .agent file')
.argument('<file>', '.agent file to execute')
.option('--input <json>', 'Input data as JSON string', '{}')
.option('--stream', 'Stream provider output when supported')
.option('--temperature <num>', 'Sampling temperature', parseFloat)
.option('--top-p <num>', 'Top-p nucleus sampling', parseFloat)
.option('--top-k <num>', 'Top-k sampling (for some local models)', parseInt)
.option('--max-tokens <num>', 'Max tokens to generate', parseInt)
.option('--debug', 'Enable debug output')
.action(async (file, options) => {
try {
if (options.debug) {
logger.setLevel('debug');
}
logger.info(`Running agent file: ${file}`);
if (!fs.existsSync(file)) {
logger.error(`File not found: ${file}`);
process.exit(1);
}
const content = fs.readFileSync(file, 'utf8');
const ast = parser.parse(content);
// Validate the agent
try {
await validator.validate(ast);
} catch (error) {
logger.warn(`Validation warnings: ${error.message}`);
// Continue execution even with validation warnings
}
logger.debug(`Input string: "${options.input}"`);
const input = JSON.parse(options.input);
// Execution options forwarded to providers via context
const execOptions = {
stream: Boolean(options.stream),
temperature: options.temperature,
topP: options.topP,
topK: options.topK,
maxTokens: options.maxTokens,
};
if (execOptions.stream) {
execOptions.onStreamChunk = (chunk) => {
process.stdout.write(chunk);
};
}
const result = await orchestrator.execute(ast, input, execOptions);
logger.info(chalk.green('Execution completed successfully'));
if (result) {
// Use process.stdout directly to avoid conflicts with custom console wrapper
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
}
} catch (error) {
logger.error(`Execution failed: ${error.message}`);
if (options.debug) {
logger.debug(error.stack);
}
process.exit(1);
}
});
program
.command('validate')
.description('Validate an .agent file')
.argument('<file>', '.agent file to validate')
.action((file) => {
try {
logger.info(`Validating: ${file}`);
if (!fs.existsSync(file)) {
logger.error(`File not found: ${file}`);
process.exit(1);
}
const content = fs.readFileSync(file, 'utf8');
const ast = parser.parse(content);
const result = validator.validate(ast);
if (result.valid) {
logger.info(chalk.green('✓ Validation passed'));
} else {
logger.error('Validation failed:');
result.errors.forEach(error => logger.error(` ${error}`));
process.exit(1);
}
} catch (error) {
logger.error(`Validation error: ${error.message}`);
process.exit(1);
}
});
program
.command('lint')
.description('Lint an .agent file for best practices')
.argument('<file>', '.agent file to lint')
.action((file) => {
try {
logger.info(`Linting: ${file}`);
if (!fs.existsSync(file)) {
logger.error(`File not found: ${file}`);
process.exit(1);
}
const content = fs.readFileSync(file, 'utf8');
const ast = parser.parse(content);
const issues = linter.lint(ast);
if (issues.length === 0) {
logger.info(chalk.green('✓ No linting issues found'));
} else {
logger.warn(`Found ${issues.length} linting issue(s):`);
issues.forEach(issue => {
logger.warn(` ${issue.severity}: ${issue.message} (${issue.rule})`);
});
}
} catch (error) {
logger.error(`Linting error: ${error.message}`);
process.exit(1);
}
});
program
.command('fix')
.description('Auto-fix common issues in an .agent file')
.argument('<file>', '.agent file to fix')
.option('--output <file>', 'Output file (default: <input>.fixed.agent)')
.action((file, options) => {
try {
logger.info(`Fixing: ${file}`);
if (!fs.existsSync(file)) {
logger.error(`File not found: ${file}`);
process.exit(1);
}
const content = fs.readFileSync(file, 'utf8');
const ast = parser.parse(content);
const fixed = corrector.correct(ast);
const outputFile = options.output || file.replace(/\.agent$/, '.fixed.agent');
const fixedContent = corrector.serialize(fixed.ast);
fs.writeFileSync(outputFile, fixedContent);
logger.info(chalk.green(`✓ Fixed file written to: ${outputFile}`));
if (fixed.changes.length > 0) {
logger.info('Applied fixes:');
fixed.changes.forEach(change => logger.info(` ${change}`));
}
} catch (error) {
logger.error(`Fix error: ${error.message}`);
process.exit(1);
}
});
program
.command('new')
.description('Create a new .agent file from template')
.argument('<name>', 'Name for the new agent')
.option('--template <type>', 'Template type (basic, llm, http)', 'basic')
.action((name, options) => {
try {
const filename = `${name}.agent`;
if (fs.existsSync(filename)) {
logger.error(`File already exists: ${filename}`);
process.exit(1);
}
let template;
switch (options.template) {
case 'llm':
template = `@agent ${name} v1
trigger http POST /${name}
secret OPENAI=env:OPENAI_KEY
var name = input.name
step generate:
kind llm
model gpt-4o
provider openai
prompt """
Hello {name}, provide a helpful response.
"""
save result
output result
@end`;
break;
case 'http':
template = `@agent ${name} v1
trigger http POST /${name}
var url = input.url
step fetch:
kind http
action GET
url {url}
headers {"Content-Type":"application/json"}
save response
output response
@end`;
break;
default:
template = `@agent ${name} v1
trigger http POST /${name}
var message = input.message
step process:
kind llm
model gpt-4o
provider openai
prompt """
Process this message: {message}
"""
save result
output result
@end`;
}
fs.writeFileSync(filename, template);
logger.info(chalk.green(`✓ Created new agent: ${filename}`));
} catch (error) {
logger.error(`Create error: ${error.message}`);
process.exit(1);
}
});
program
.command('list')
.description('List all .agent files in current directory')
.option('--recursive', 'Search recursively')
.action((options) => {
try {
const files = list.findAgentFiles('.', options.recursive);
if (files.length === 0) {
logger.info('No .agent files found');
} else {
logger.info(`Found ${files.length} .agent file(s):`);
files.forEach(file => console.log(` ${file}`));
}
} catch (error) {
logger.error(`List error: ${error.message}`);
process.exit(1);
}
});
program
.command('explain')
.description('Explain an .agent file in human-readable format')
.argument('<file>', '.agent file to explain')
.action((file) => {
try {
if (!fs.existsSync(file)) {
logger.error(`File not found: ${file}`);
process.exit(1);
}
const content = fs.readFileSync(file, 'utf8');
const ast = parser.parse(content);
const explanation = explain.explainAgent(ast);
console.log(explanation);
} catch (error) {
logger.error(`Explain error: ${error.message}`);
process.exit(1);
}
});
program
.command('doctor')
.description('Check environment and dependencies')
.action(async () => {
try {
const results = await doctor.checkEnvironment();
doctor.printResults(results);
const hasErrors = results.some(result => !result.status);
if (hasErrors) {
process.exit(1);
}
} catch (error) {
logger.error(`Doctor error: ${error.message}`);
process.exit(1);
}
});
// Enhanced AI/ML Model Management Commands
program
.command('models')
.description('Manage AI/ML models')
.option('--list', 'List all available models')
.option('--type <type>', 'Filter by model type (LLM, Vision, ASR, TTS, etc.)')
.option('--provider <provider>', 'Filter by provider (openai, anthropic, ollama, etc.)')
.option('--capability <capability>', 'Filter by capability (text-generation, image-classification, etc.)')
.option('--search <query>', 'Search models by name or description')
.option('--recommend <task>', 'Get model recommendations for a specific task')
.action(async (options) => {
try {
const { ModelManager } = require('../dist/src/core/ModelManager');
const modelManager = ModelManager.getInstance();
if (options.list) {
const stats = modelManager.getModelStatistics();
console.header('AI/ML Model Statistics');
console.info(`Total Models: ${stats.total}`);
Object.entries(stats.byType).forEach(([type, count]) => {
console.info(`${type}: ${count} models`);
});
console.endSection();
} else if (options.recommend) {
const recommendations = modelManager.getRecommendedModels(options.recommend);
console.header(`Model Recommendations for: ${options.recommend}`);
recommendations.forEach((model, index) => {
console.info(`${index + 1}. ${model.name} (${model.provider})`);
console.info(` Type: ${model.type}`);
console.info(` Capabilities: ${model.capabilities.join(', ')}`);
console.info(` Size: ${Math.round((model.metadata.size || 0) / 1000000)}MB`);
});
console.endSection();
} else {
let models = Array.from(modelManager['models'].values());
if (options.type) {
models = models.filter(m => m.type === options.type);
}
if (options.provider) {
models = models.filter(m => m.provider === options.provider);
}
if (options.capability) {
models = models.filter(m => m.capabilities.includes(options.capability));
}
if (options.search) {
const query = options.search.toLowerCase();
models = models.filter(m =>
m.name.toLowerCase().includes(query) ||
m.metadata.description?.toLowerCase().includes(query)
);
}
console.header('Available AI/ML Models');
models.forEach(model => {
console.info(`${model.name} (${model.id})`);
console.info(` Provider: ${model.provider}`);
console.info(` Type: ${model.type}`);
console.info(` Capabilities: ${model.capabilities.join(', ')}`);
});
console.endSection();
}
} catch (error) {
console.error(`Models command error: ${error.message}`);
process.exit(1);
}
});
program
.command('fine-tune')
.description('Manage model fine-tuning')
.option('--create <modelId>', 'Create a new fine-tuning job')
.option('--list', 'List all fine-tuning jobs')
.option('--status <jobId>', 'Get status of a fine-tuning job')
.option('--cancel <jobId>', 'Cancel a fine-tuning job')
.option('--data <file>', 'Training data file (JSONL, CSV, JSON)')
.option('--hyperparameters <json>', 'Hyperparameters as JSON string')
.option('--provider <provider>', 'Fine-tuning provider (openai, huggingface)', 'openai')
.action(async (options) => {
try {
const FineTuneProvider = require('../lib/providers/finetune');
console.header('Model Fine-tuning Management');
const provider = new FineTuneProvider(options.provider);
// Initialize provider
const apiKey = process.env[`${options.provider.toUpperCase()}_API_KEY`];
if (!apiKey) {
console.error(`No API key found for ${options.provider}. Set ${options.provider.toUpperCase()}_API_KEY environment variable.`);
process.exit(1);
}
await provider.initialize({ apiKey });
if (options.create) {
console.info(`Creating fine-tuning job for model: ${options.create}`);
if (!options.data) {
console.error('Training data file is required. Use --data <file>');
process.exit(1);
}
const config = {
model: options.create,
trainingFile: options.data,
hyperparameters: options.hyperparameters ? JSON.parse(options.hyperparameters) : {}
};
const job = await provider.createFineTuneJob(config);
console.success('Fine-tuning job created successfully');
console.json(job, 'Job Details');
} else if (options.list) {
console.info('Listing fine-tuning jobs...');
const jobs = await provider.listFineTuneJobs();
if (jobs.length === 0) {
console.info('No fine-tuning jobs found');
} else {
console.table(['ID', 'Status', 'Model', 'Created'],
jobs.map(job => [
job.id,
job.status,
job.model,
job.createdAt ? new Date(job.createdAt).toLocaleDateString() : 'N/A'
])
);
}
} else if (options.status) {
console.info(`Getting status for job: ${options.status}`);
const status = await provider.getFineTuneStatus(options.status);
console.json(status, 'Job Status');
} else if (options.cancel) {
console.info(`Cancelling job: ${options.cancel}`);
const result = await provider.cancelFineTuneJob(options.cancel);
console.success('Job cancelled successfully');
console.json(result, 'Cancellation Result');
} else if (options.data) {
console.info(`Validating training data: ${options.data}`);
const format = path.extname(options.data).substring(1);
const validation = provider.validateTrainingData(options.data, format);
if (validation.valid) {
console.success('Training data is valid');
console.info(`Total lines: ${validation.totalLines}`);
console.info(`Valid lines: ${validation.validLines}`);
} else {
console.error('Training data validation failed');
validation.errors.forEach(error => console.error(` ${error}`));
process.exit(1);
}
} else {
console.info('Use --help to see available options');
}
console.endSection();
} catch (error) {
console.error(`Fine-tuning error: ${error.message}`);
process.exit(1);
}
});
program
.command('vision')
.description('Computer vision operations')
.option('--classify <image>', 'Classify image')
.option('--detect <image>', 'Detect objects in image')
.option('--ocr <image>', 'Extract text from image')
.option('--caption <image>', 'Generate image caption')
.option('--model <modelId>', 'Specify vision model to use')
.option('--provider <provider>', 'Vision provider (openai, huggingface)', 'openai')
.action(async (options) => {
try {
const visionProvider = require('../lib/providers/vision');
console.header('Computer Vision Operations');
if (!options.classify && !options.detect && !options.ocr && !options.caption) {
console.error('Please specify an operation: --classify, --detect, --ocr, or --caption');
process.exit(1);
}
const imagePath = options.classify || options.detect || options.ocr || options.caption;
const model = options.model || (options.provider === 'openai' ? 'gpt-4o-vision' : 'microsoft/DialoGPT-large');
// Check if image file exists
if (!fs.existsSync(imagePath)) {
console.error(`Image file not found: ${imagePath}`);
process.exit(1);
}
if (options.classify) {
console.info(`Classifying image: ${imagePath}`);
const result = await visionProvider.operations.classify(model, imagePath);
console.success('Classification completed');
console.json(result, 'Classification Result');
} else if (options.detect) {
console.info(`Detecting objects in: ${imagePath}`);
const result = await visionProvider.operations.analyze(model, imagePath);
console.success('Object detection completed');
console.json(result, 'Detection Result');
} else if (options.ocr) {
console.info(`Extracting text from: ${imagePath}`);
const result = await visionProvider.operations.analyze(model, imagePath);
console.success('OCR completed');
console.json(result, 'OCR Result');
} else if (options.caption) {
console.info(`Generating caption for: ${imagePath}`);
const result = await visionProvider.operations.caption(model, imagePath);
console.success('Caption generation completed');
console.json(result, 'Caption Result');
}
console.endSection();
} catch (error) {
console.error(`Vision error: ${error.message}`);
process.exit(1);
}
});
program
.command('audio')
.description('Audio processing operations')
.option('--stt <audio>', 'Speech to text conversion')
.option('--tts <text>', 'Text to speech conversion')
.option('--voice <voice>', 'Specify voice for TTS')
.option('--model <modelId>', 'Specify audio model to use')
.option('--output <file>', 'Output file for TTS')
.action(async (options) => {
try {
const audioProvider = require('../lib/providers/audio');
console.header('Audio Processing Operations');
if (!options.stt && !options.tts) {
console.error('Please specify an operation: --stt or --tts');
process.exit(1);
}
if (options.stt) {
console.info(`Converting speech to text: ${options.stt}`);
if (!fs.existsSync(options.stt)) {
console.error(`Audio file not found: ${options.stt}`);
process.exit(1);
}
const result = await audioProvider.operations.stt(options.stt, {
model: options.model || 'whisper-1'
});
console.success('Speech-to-text completed');
console.json(result, 'Transcription Result');
} else if (options.tts) {
console.info(`Converting text to speech: "${options.tts}"`);
const result = await audioProvider.operations.tts(options.tts, {
model: options.model || 'tts-1',
voice: options.voice || 'alloy',
outputPath: options.output
});
console.success('Text-to-speech completed');
console.json(result, 'TTS Result');
console.info(`Audio saved to: ${result.outputPath}`);
}
console.endSection();
} catch (error) {
console.error(`Audio error: ${error.message}`);
process.exit(1);
}
});
program
.command('ml')
.description('Machine learning operations')
.option('--train <config>', 'Train a new ML model')
.option('--predict <model> <data>', 'Make predictions with trained model')
.option('--evaluate <model> <data>', 'Evaluate model performance')
.option('--export <model> <format>', 'Export model to format (onnx, tensorflow, etc.)')
.action(async (options) => {
try {
console.header('Machine Learning Operations');
if (options.train) {
console.info(`Training ML model with config: ${options.train}`);
if (!fs.existsSync(options.train)) {
console.error(`Config file not found: ${options.train}`);
process.exit(1);
}
const config = JSON.parse(fs.readFileSync(options.train, 'utf8'));
console.info('Training configuration loaded');
console.json(config, 'Training Config');
// This would integrate with actual ML training frameworks
console.warn('ML training requires integration with frameworks like TensorFlow, PyTorch, or scikit-learn');
console.info('Consider using the fine-tuning command for model customization instead');
} else if (options.predict) {
const [model, data] = options.predict.split(' ');
console.info(`Making predictions with model: ${model}`);
if (!fs.existsSync(data)) {
console.error(`Data file not found: ${data}`);
process.exit(1);
}
console.warn('ML prediction requires integration with trained model files');
console.info('Consider using the LLM providers for text-based predictions');
} else if (options.evaluate) {
const [model, data] = options.evaluate.split(' ');
console.info(`Evaluating model: ${model}`);
if (!fs.existsSync(data)) {
console.error(`Data file not found: ${data}`);
process.exit(1);
}
console.warn('ML evaluation requires integration with trained model files');
} else if (options.export) {
const [model, format] = options.export.split(' ');
console.info(`Exporting model ${model} to ${format} format`);
console.warn('ML export requires integration with trained model files');
console.info('Supported formats: ONNX, TensorFlow SavedModel, PyTorch, scikit-learn pickle');
} else {
console.info('Use --help to see available options');
console.info('Note: Full ML operations require integration with ML frameworks');
console.info('For AI model operations, use: fine-tune, vision, audio commands');
}
console.endSection();
} catch (error) {
console.error(`ML error: ${error.message}`);
process.exit(1);
}
});
program
.command('vector-db')
.description('Vector database operations')
.option('--create <name>', 'Create new vector database')
.option('--list', 'List all vector databases')
.option('--add <db> <collection> <data>', 'Add documents to collection')
.option('--search <db> <collection> <query>', 'Search in collection')
.option('--delete <db> <collection>', 'Delete collection')
.option('--backend <backend>', 'Vector DB backend (pinecone, weaviate, qdrant)', 'pinecone')
.action(async (options) => {
try {
const VectorDBProvider = require('../lib/providers/vectordb');
console.header('Vector Database Operations');
if (!options.create && !options.list && !options.add && !options.search && !options.delete) {
console.error('Please specify an operation: --create, --list, --add, --search, or --delete');
process.exit(1);
}
const backend = options.backend;
const apiKey = process.env[`${backend.toUpperCase()}_API_KEY`];
if (!apiKey) {
console.error(`No API key found for ${backend}. Set ${backend.toUpperCase()}_API_KEY environment variable.`);
process.exit(1);
}
const vectorDB = new VectorDBProvider(backend);
try {
await vectorDB.initialize({ apiKey });
console.success(`Connected to ${backend} vector database`);
} catch (error) {
console.error(`Failed to connect to ${backend}: ${error.message}`);
process.exit(1);
}
if (options.create) {
console.info(`Creating vector database: ${options.create}`);
const result = await vectorDB.createCollection(options.create);
console.success('Vector database created successfully');
console.json(result, 'Database Info');
} else if (options.list) {
console.info('Listing vector databases...');
console.warn('Collection listing depends on the specific backend implementation');
console.info(`Using backend: ${backend}`);
} else if (options.add) {
const [db, collection, data] = options.add.split(' ');
console.info(`Adding documents to ${db}/${collection}`);
if (!fs.existsSync(data)) {
console.error(`Data file not found: ${data}`);
process.exit(1);
}
const documents = JSON.parse(fs.readFileSync(data, 'utf8'));
console.info(`Loaded ${documents.length} documents`);
// This would require embeddings to be generated first
console.warn('Document addition requires pre-generated embeddings');
console.info('Consider using OpenAI embeddings or similar service first');
} else if (options.search) {
const [db, collection, query] = options.search.split(' ');
console.info(`Searching in ${db}/${collection}: ${query}`);
// This would require query embeddings to be generated first
console.warn('Vector search requires query embeddings');
console.info('Consider using OpenAI embeddings or similar service first');
} else if (options.delete) {
const [db, collection] = options.delete.split(' ');
console.info(`Deleting collection: ${db}/${collection}`);
const result = await vectorDB.deleteCollection(collection);
console.success('Collection deleted successfully');
console.json(result, 'Deletion Result');
}
console.endSection();
} catch (error) {
console.error(`Vector DB error: ${error.message}`);
process.exit(1);
}
});
// Templates management commands
program
.command('templates')
.description('Manage agent templates')
.action(() => {
console.header('Available Templates', 'Pre-built agent workflows for common use cases');
listTemplates();
});
program
.command('template')
.description('Create new agent from template')
.argument('<template>', 'Template name (category/template)')
.argument('[output]', 'Output file name')
.option('--list', 'List available templates')
.action((template, output, options) => {
if (options.list) {
listTemplates();
return;
}
createFromTemplate(template, output);
});
program
.command('init')
.description('Initialize a new agent project')
.argument('[name]', 'Project name')
.option('--template <name>', 'Use a specific template')
.option('--provider <provider>', 'Default AI provider (openai, gemini, ollama)', 'openai')
.option('--model <model>', 'Default AI model', 'gpt-4o')
.option('--port <port>', 'Default server port', '5000')
.action((name, options) => {
initProject(name, options);
});
program
.command('workspace')
.description('Manage agent workspace')
.option('--list', 'List agents in workspace')
.option('--stats', 'Show workspace statistics')
.action((options) => {
if (options.list) {
listWorkspaceAgents();
} else if (options.stats) {
showWorkspaceStats();
} else {
showWorkspaceOverview();
}
});
program
.command('serve')
.description('Start HTTP server to expose agents as APIs')
.option('--port <port>', 'Port to listen on', '5000')
.option('--agents-dir <dir>', 'Directory containing .agent files', './agents')
.option('--watch', 'Watch for file changes and reload agents')
.option('--express', 'Use full Express server with auth and dynamic endpoints')
.option('--no-auth', 'Disable authentication (Express server only)')
.option('--api-key <key...>', 'API key(s) for server authentication (Express server)')
.option('--jwt-secret <secret>', 'JWT secret for token auth (Express server)')
.action(async (options) => {
const useExpress = Boolean(options.express);
const ServerClass = useExpress
? require('../lib/server/express-server')
: require('../lib/server/minimal-server');
const server = new ServerClass({
port: parseInt(options.port),
agentsDir: options.agentsDir,
enableAuth: useExpress ? options.auth !== false : undefined,
apiKeys: useExpress ? (options.apiKey || []) : undefined,
jwtSecret: useExpress ? (options.jwtSecret || process.env.AAAB_JWT_SECRET) : undefined,
});
try {
await server.start();
if (options.watch && server.loadAgents) {
const chokidar = require('chokidar');
const watcher = chokidar.watch(options.agentsDir + '/*.agent');
watcher.on('change', () => {
console.info('Agent files changed, reloading...');
server.loadAgents();
});
console.info('File watching enabled');
}
// Handle graceful shutdown
process.on('SIGINT', async () => {
console.info('Shutting down server...');
await server.stop();
process.exit(0);
});
} catch (error) {
console.error(`Server startup failed: ${error.message}`);
process.exit(1);
}
});
program
.command('deploy')
.description('Deploy AAAB application to various platforms')
.argument('<strategy>', 'Deployment strategy (replit, docker, serverless, kubernetes)')
.option('--port <port>', 'Port number', '5000')
.option('--app-name <name>', 'Application name', 'aaab-app')
.option('--image <image>', 'Docker image name', 'aaab:latest')
.option('--replicas <count>', 'Number of replicas', '2')
.action(async (strategy, options) => {
const AABBDeployer = require('../lib/deployment/deployer');
try {
const deployer = new AABBDeployer();
const result = await deployer.deploy(strategy, options);
console.success(`Deployment preparation complete!`);
console.json(result, 'Deployment Result');
} catch (error) {
console.error(`Deployment failed: ${error.message}`);
process.exit(1);
}
});
program.parse();
// Template management functions
function listTemplates() {
const templatesDir = path.join(__dirname, '../templates');
if (!fs.existsSync(templatesDir)) {
console.warn('Templates directory not found');
return;
}
const categories = fs.readdirSync(templatesDir, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
console.section('Template Categories');
categories.forEach(category => {
console.info(`📁 ${category.toUpperCase()}`);
const categoryPath = path.join(templatesDir, category);
const templates = fs.readdirSync(categoryPath)
.filter(file => file.endsWith('.agent'))
.map(file => file.replace('.agent', ''));
templates.forEach(template => {
console.info(` └─ ${template}`);
});
});
console.endSection();
console.info('Usage: aaab template <category>/<template> [output-file]');
}
function createFromTemplate(templateName, outputFile) {
const templatesDir = path.join(__dirname, '../templates');
let templatePath;
// Handle category/template format
if (templateName.includes('/')) {
templatePath = path.join(templatesDir, templateName + '.agent');
} else {
// Search for template in all categories
const categories = fs.readdirSync(templatesDir, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
for (const category of categories) {
const possiblePath = path.join(templatesDir, category, templateName + '.agent');
if (fs.existsSync(possiblePath)) {
templatePath = possiblePath;
break;
}
}
}
if (!templatePath || !fs.existsSync(templatePath)) {
console.error(`Template '${templateName}' not found`);
return;
}
const outputPath = outputFile || `${templateName.split('/').pop()}.agent`;
console.header('Creating Agent from Template', `Template: ${templateName}`);
console.info(`Source: ${templatePath}`);
console.info(`Output: ${outputPath}`);
const content = fs.readFileSync(templatePath, 'utf8');
fs.writeFileSync(outputPath, content);
console.success(`Agent created: ${outputPath}`);
console.info('Next steps:');
console.info('1. Edit the agent file to customize for your needs');
console.info('2. Configure required secrets in your environment');
console.info('3. Test with: aaab run ' + outputPath);
console.endSection();
}
function initProject(name, options) {
const projectName = name || 'my-agent-project';
const projectDir = path.join(process.cwd(), projectName);
console.header('Initializing Agent Project', `Project: ${projectName}`);
if (fs.existsSync(projectDir)) {
console.error(`Directory '${projectName}' already exists`);
return;
}
fs.mkdirSync(projectDir, { recursive: true });
// Create project structure
const dirs = ['agents', 'examples', 'tests', 'config'];
dirs.forEach(dir => {
fs.mkdirSync(path.join(projectDir, dir), { recursive: true });
});
// Create package.json
const packageJson = {
name: projectName,
version: '1.0.0',
description: 'AAAB Agent Project',
main: 'index.js',
scripts: {
'start': `aaab serve --port ${options.port || 5000}`,
'serve': `aaab serve --port ${options.port || 5000}`,
'dev': `aaab serve --port ${options.port || 5000} --watch`,
'run': 'aaab run',
'validate': 'aaab validate',
'lint': 'aaab lint',
'doctor': 'aaab doctor'
},
dependencies: {},
aaab: {
version: '1.0.0',
agentsDir: './agents',
examplesDir: './examples',
defaultProvider: options.provider || 'openai',
defaultModel: options.model || 'gpt-4o',
port: options.port || 5000
}
};
fs.writeFileSync(
path.join(projectDir, 'package.json'),
JSON.stringify(packageJson, null, 2)
);
// Create default agent files
createDefaultAgents(projectDir, options);
// Create configuration files
createConfigFiles(projectDir, options);
// Create README
const readme = createProjectReadme(projectName, options);
fs.writeFileSync(path.join(projectDir, 'README.md'), readme);
// Copy a starter template if specified
if (options.template) {
createFromTemplate(options.template, path.join(projectDir, 'agents', 'starter.agent'));
}
console.success(`Project created: ${projectDir}`);
console.info('Next steps:');
console.info(`1. cd ${projectName}`);
console.info('2. Set your API keys:');
console.info(` export ${options.provider?.toUpperCase() || 'OPENAI'}_API_KEY="your-api-key"`);
console.info('3. Start the server: npm start');
console.info('4. Test your agents: aaab run agents/chat.agent --input \'{"message":"Hello"}\'');
console.endSection();
}
function createDefaultAgents(projectDir, options) {
const provider = options.provider || 'openai';
const model = options.model || 'gpt-4o';
// Chat agent
const chatAgent = `@agent chat v1
description: "Simple chat agent for conversations"
trigger:
type: http
method: POST
path: /chat
secrets:
- name: ${provider.toUpperCase()}
type: env
value: ${provider.toUpperCase()}_API_KEY
vars:
message:
type: string
from: input
required: true
steps:
- id: respond
type: llm
provider: ${provider}
model: ${model}
prompt: |
You are a helpful AI assistant. Respond to the user's message in a friendly and helpful way.
User message: {message}
outputs:
response: content
outputs:
result: "{response}"
@end`;
// Global prompt agent
const globalPromptAgent = `@agent global-prompt v1
description: "Agent with global system prompt"
trigger:
type: http
method: POST
path: /global-prompt
secrets:
- name: ${provider.toUpperCase()}
type: env
value: ${provider.toUpperCase()}_API_KEY
vars:
message:
type: string
from: input
required: true
steps:
- id: process
type: llm
provider: ${provider}
model: ${model}
globalPrompt: |
You are an expert AI assistant with deep knowledge in technology, science, and business.
Always provide accurate, helpful, and well-structured responses.
Use markdown formatting when appropriate.
prompt: |
Please respond to: {message}
outputs:
response: content
outputs:
result: "{response}"
@end`;
// Knowledge base agent
const kbAgent = `@agent kb v1
description: "Knowledge base query agent"
trigger:
type: http
method: POST
path: /kb
secrets:
- name: ${provider.toUpperCase()}
type: env
value: ${provider.toUpperCase()}_API_KEY
vars:
query:
type: string
from: input
required: true
context:
type: string
from: input
default: ""
steps:
- id: search
type: llm
provider: ${provider}
model: ${model}
prompt: |
Based on the following context, answer the user's question:
Context: {context}
Question: {query}
If no context is provided, answer based on your general knowledge.
outputs:
answer: content
outputs:
result: "{answer}"
@end`;
// Settings agent
const settingsAgent = `@agent settings v1
description: "Agent configuration and settings"
trigger:
type: http
method: POST
path: /settings
vars:
action:
type: string
from: input
required: true
data:
type: object
from: input
default: {}
steps:
- id: process
type: http
action: GET
url: "https://httpbin.org/json"
headers:
Content-Type: "application/json"
outputs:
response: body
outputs:
result: "{response}"
@end`;
// Write agent files
fs.writeFileSync(path.join(projectDir, 'agents', 'chat.agent'), chatAgent);
fs.writeFileSync(path.join(projectDir, 'agents', 'global-prompt.agent'), globalPromptAgent);
fs.writeFileSync(path.join(projectDir, 'agents', 'kb.agent'), kbAgent);
fs.writeFileSync(path.join(projectDir, 'agents', 'settings.agent'), settingsAgent);
console.success('Created default agent files:');
console.info(' • agents/chat.agent - Simple chat agent');
console.info(' • agents/global-prompt.agent - Agent with system prompt');
console.info(' • agents/kb.agent - Knowledge base queries');
console.info(' • agents/settings.agent - Configuration agent');
}
function createConfigFiles(projectDir, options) {
// Create .env.example
const envExample = `# AAAB Configuration
# Copy this file to .env and fill in your values
# AI Provider API Keys
${options.provider?.toUpperCase() || 'OPENAI'}_API_KEY=your-api-key-here
GEMINI_API_KEY=your-gemini-key-here
OLLAMA_URL=http://localhost:11434
# Security
AAAB_ENCRYPTION_KEY=your-encryption-key-here
AAAB_JWT_SECRET=your-jwt-secret-here
# Server Configuration
PORT=${options.port || 5000}
NODE_ENV=development
# Optional: Cloud Secret Stores
# AWS_SECRETS_ACCESS_KEY=your-aws-key
# AWS_SECRETS_SECRET_KEY=your-aws-secret
# GCP_PROJECT_ID=your-gcp-project
`;
fs.writeFileSync(path.join(projectDir, '.env.example'), envExample);
// Create .gitignore
const gitignore = `# Dependencies
node_modules/
npm-debug.log*
# Environment variables
.env
.env.local
.env.production
# AAAB specific
.aaab-secrets
*.log
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
*.swo
# Build outputs
dist/
build/
`;
fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignore);
// Create aaab.config.js
const config = `module.exports = {
// Server configuration
port: ${options.port || 5000},
agentsDir: './agents',
// Default AI provider settings
defaultProvider: '${options.provider || 'openai'}',
defaultModel: '${options.model || 'gpt-4o'}',
// Security settings
enableAuth: true,
apiKeys: [
// Add your API keys here for server authentication
// 'your-api-key-1',
// 'your-api-key-2'
],
// CORS settings
cors: {
origin: ['http://localhost:3000', 'http://localhost:5000'],
credentials: true
},
// Rate limiting
rateLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
}
};
`;
fs.writeFileSync(path.join(projectDir, 'aaab.config.js'), config);
console.success('Created configuration files:');
console.info(' • .env.example - Environment variables template');
console.info(' • .gitignore - Git ignore rules');
console.info(' • aaab.config.js - AAAB configuration');
}
function createProjectReadme(projectName, options) {
const provider = options.provider || 'openai';
const model = options.model || 'gpt-4o';
const port = options.port || 5000;
return `# ${projectName}
AAAB Agent Project - AI-powered backend services
## 🚀 Quick Start
### 1. Install Dependencies
\`\`\`bash
npm install
\`\`\`
### 2. Configure Environment
\`\`\`bash
cp .env.example .env
# Edit .env with your API keys
\`\`\`
### 3. Set API Keys
\`\`\`bash
export ${provider.toUpperCase()}_API_KEY="your-api-key-here"
\`\`\`
### 4. Start the Server
\`\`\`bash
npm start
# or
aaab serve --port ${port}
\`\`\`
### 5. Test Your Agents
\`\`\`bash
# Test chat agent
curl -X POST http://localhost:${port}/api/agents/chat/execute \\
-H "Content-Type: application/json" \\
-d '{"message":"Hello, how are you?"}'
# Test with CLI
aaab run agents/chat.agent --input '{"message":"Hello"}'
\`\`\`
## 📁 Project Structure
\`\`\`
${projectName}/
├── agents/ # Your agent definitions
│ ├── chat.agent # Simple chat agent
│ ├── global-prompt.agent # Agent with system prompt
│ ├── kb.agent # Knowledge base queries
│ └── settings.agent # Configuration agent
├── examples/ # Example inputs and outputs
├── tests/ # Test cases
├── config/ # Configuration files
├── .env.example # Environment variables template
├── aaab.config.js # AAAB configuration
└── README.md # This file
\`\`\`
## 🤖 Available Agents
### Chat Agent (\`/api/agents/chat/execute\`)
Simple conversation agent using ${provider} ${model}.
**Input:**
\`\`\`json
{
"message": "Hello, how are you?"
}
\`\`\`
### Global Prompt Agent (\`/api/agents/global-prompt/execute\`)
Agent with predefined system prompt for consistent behavior.
### Knowledge Base Agent (\`/api/agents/kb/execute\`)
Query agent with context support.
**Input:**
\`\`\`json
{
"query": "What is AI?",
"context": "Additional context information..."
}
\`\`\`
### Settings Agent (\`/api/agents/settings/execute\`)
Configuration and settings management.
## 🔧 Configuration
### Environment Variables
- \`${provider.toUpperCase()}_API_KEY\` - Your ${provider} API key
- \`AAAB_ENCRYPTION_KEY\` - Encryption key for local secrets
- \`AAAB_JWT_SECRET\` - JWT secret for authentication
- \`PORT\` - Server port (default: ${port})
### AAAB Configuration (\`aaab.config.js\`)
Edit \`aaab.config.js\` to customize:
- Server settings
- Authentication
- CORS configuration
- Rate limiting
## 🛠️ Available Commands
\`\`\`bash
# Development
npm start # Start server
npm run dev # Start with file watching
npm run serve # Start server
# Agent Management
aaab run <file> # Execute agent
aaab validate <file> # Validate agent syntax
aaab lint <file> # Check best practices
aaab doctor # Diagnose issues
# Project Management
aaab templates # List available templates
aaab template <name> <output> # Create from template
\`\`\`
## 🔐 Security
- API key authentication for all endpoints
- Rate limiting to prevent abuse
- CORS protection
- Encrypted local secret storage
## 🚢 Deployment
### Docker
\`\`\`bash
aaab deploy docker
docker-compose up
\`\`\`
### Kubernetes
\`\`\`bash
aaab deploy kubernetes
kubectl apply -f k8s-manifest.yml
\`\`\`
### Serverless
\`\`\`bash
aaab deploy serverless
serverless deploy
\`\`\`
## 📚 Next Steps
1. **Customize Agents**: Edit the \`.agent\` files in the \`agents/\` directory
2. **Add New Agents**: Use \`aaab template <template-name> agents/my-agent.agent\`
3. **Configure Secrets**: Set up your API keys and encryption
4. **Deploy**: Choose your deployment strategy and go live!
## 🤝 Support
- Check \`aaab doctor\` for common issues
- Review agent validation with \`aaab validate\`
- See examples in the \`examples/\` directory
---
Built with ❤️ using AAAB (Agent as a Backend)
`;
}
function listWorkspaceAgents() {
console.header('Workspace Agents');
const agentFiles = [];
const searchDirs = ['.', 'agents', 'examples'];
searchDirs.forEach(dir => {
if (fs.existsSync(dir)) {
const files = fs.readdirSync(dir)
.filter(file => file.endsWith('.agent'))
.map(file => path.join(dir, file));
agentFiles.push(...files);
}
});
if (agentFiles.length === 0) {
console.warn('No .agent files found in workspace');
console.info('Create one with: aaab template <template-name> my-agent.agent');
return;
}
console.table(['File', 'Size', 'Modified'],
agentFiles.map(file => {
const stats = fs.statSync(file);
return [
file,
`${Math.round(stats.size / 1024)}KB`,
stats.mtime.toLocaleDateString()
];
})
);
console.endSection();
}
function showWorkspaceStats() {
console.header('Workspace Statistics');
const stats = {
'Agent Files': 0,
'Total Size': 0,
'Categories': new Set(),
'Last Modified': null
};
const searchDirs = ['.', 'agents', 'examples', 'templates'];
let latestTime = 0;
searchDirs.forEach(dir => {
if (fs.existsSync(dir)) {
const files = fs.readdirSync(dir, { recursive: true })
.filter(file => file.endsWith && file.endsWith('.agent'));
files.forEach(file => {
const fullPath = path.join(dir, file);
if (fs.existsSync(fullPath)) {
const fileStat = fs.statSync(fullPath);
stats['Agent Files']++;
stats['Total Size'] += fileStat.size;
if (fileStat.mtime.getTime() > latestTime) {
latestTime = fileStat.mtime.getTime();
stats['Last Modified'] = fileStat.mtime.toLocaleString();
}
// Determine category from path
const pathParts = file.split('/');
if (pathParts.length > 1) {
stats['Categories'].add(pathParts[0]);
}
}
});
}
});
stats['Total Size'] = `${Math.round(stats['Total Size'] / 1024)}KB`;
stats['Categories'] = stats['Categories'].size;
Object.entries(stats).forEach(([key, value]) => {
console.info(`${key}: ${value}`);
});
console.endSection();
}
function showWorkspaceOverview() {
console.header('Agent Workspace Overview');
console.info('🚀 Welcome to your AAAB workspace!');
console.info('');
console.info('Available commands:');
console.info('• aaab templates - Browse available templates');
console.info('• aaab workspace --list - List all agents');
console.info('• aaab workspace --stats - Show statistics');
console.info('• aaab init <name> - Create new project');
console.info('• aaab run <file> - Execute an agent');
console.endSection();
}