@ggakila/agentx-framework 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -0
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/dist/agent/Agent.d.ts +110 -0
- package/dist/agent/Agent.d.ts.map +1 -0
- package/dist/agent/Agent.js +291 -0
- package/dist/agent/Agent.js.map +1 -0
- package/dist/agent/index.d.ts +5 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +11 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/cli/CLI.d.ts +74 -0
- package/dist/cli/CLI.d.ts.map +1 -0
- package/dist/cli/CLI.js +255 -0
- package/dist/cli/CLI.js.map +1 -0
- package/dist/cli/InteractiveSetup.d.ts +104 -0
- package/dist/cli/InteractiveSetup.d.ts.map +1 -0
- package/dist/cli/InteractiveSetup.js +2225 -0
- package/dist/cli/InteractiveSetup.js.map +1 -0
- package/dist/cli/bin.d.ts +7 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +35 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/ProjectCommands.d.ts +23 -0
- package/dist/cli/commands/ProjectCommands.d.ts.map +1 -0
- package/dist/cli/commands/ProjectCommands.js +504 -0
- package/dist/cli/commands/ProjectCommands.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +21 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/credential/CredentialManager.d.ts +112 -0
- package/dist/credential/CredentialManager.d.ts.map +1 -0
- package/dist/credential/CredentialManager.js +343 -0
- package/dist/credential/CredentialManager.js.map +1 -0
- package/dist/credential/OAuth2Manager.d.ts +206 -0
- package/dist/credential/OAuth2Manager.d.ts.map +1 -0
- package/dist/credential/OAuth2Manager.js +463 -0
- package/dist/credential/OAuth2Manager.js.map +1 -0
- package/dist/credential/index.d.ts +6 -0
- package/dist/credential/index.d.ts.map +1 -0
- package/dist/credential/index.js +16 -0
- package/dist/credential/index.js.map +1 -0
- package/dist/error/ErrorHandler.d.ts +74 -0
- package/dist/error/ErrorHandler.d.ts.map +1 -0
- package/dist/error/ErrorHandler.js +279 -0
- package/dist/error/ErrorHandler.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/DatabaseTool.d.ts +149 -0
- package/dist/integrations/DatabaseTool.d.ts.map +1 -0
- package/dist/integrations/DatabaseTool.js +900 -0
- package/dist/integrations/DatabaseTool.js.map +1 -0
- package/dist/integrations/EmailTool.d.ts +142 -0
- package/dist/integrations/EmailTool.d.ts.map +1 -0
- package/dist/integrations/EmailTool.js +259 -0
- package/dist/integrations/EmailTool.js.map +1 -0
- package/dist/integrations/FileSystemTool.d.ts +153 -0
- package/dist/integrations/FileSystemTool.d.ts.map +1 -0
- package/dist/integrations/FileSystemTool.js +835 -0
- package/dist/integrations/FileSystemTool.js.map +1 -0
- package/dist/integrations/GoogleWorkspaceTool.d.ts +125 -0
- package/dist/integrations/GoogleWorkspaceTool.d.ts.map +1 -0
- package/dist/integrations/GoogleWorkspaceTool.js +765 -0
- package/dist/integrations/GoogleWorkspaceTool.js.map +1 -0
- package/dist/integrations/HttpTool.d.ts +55 -0
- package/dist/integrations/HttpTool.d.ts.map +1 -0
- package/dist/integrations/HttpTool.js +209 -0
- package/dist/integrations/HttpTool.js.map +1 -0
- package/dist/integrations/MessagingTool.d.ts +136 -0
- package/dist/integrations/MessagingTool.d.ts.map +1 -0
- package/dist/integrations/MessagingTool.js +503 -0
- package/dist/integrations/MessagingTool.js.map +1 -0
- package/dist/integrations/SchedulerTool.d.ts +147 -0
- package/dist/integrations/SchedulerTool.d.ts.map +1 -0
- package/dist/integrations/SchedulerTool.js +471 -0
- package/dist/integrations/SchedulerTool.js.map +1 -0
- package/dist/integrations/WebhookTool.d.ts +97 -0
- package/dist/integrations/WebhookTool.d.ts.map +1 -0
- package/dist/integrations/WebhookTool.js +351 -0
- package/dist/integrations/WebhookTool.js.map +1 -0
- package/dist/integrations/index.d.ts +13 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +60 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/llm/LLMProvider.d.ts +83 -0
- package/dist/llm/LLMProvider.d.ts.map +1 -0
- package/dist/llm/LLMProvider.js +370 -0
- package/dist/llm/LLMProvider.js.map +1 -0
- package/dist/llm/index.d.ts +5 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +14 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/payment/PaymentProvider.d.ts +157 -0
- package/dist/payment/PaymentProvider.d.ts.map +1 -0
- package/dist/payment/PaymentProvider.js +525 -0
- package/dist/payment/PaymentProvider.js.map +1 -0
- package/dist/payment/index.d.ts +5 -0
- package/dist/payment/index.d.ts.map +1 -0
- package/dist/payment/index.js +16 -0
- package/dist/payment/index.js.map +1 -0
- package/dist/plugin/PluginManager.d.ts +156 -0
- package/dist/plugin/PluginManager.d.ts.map +1 -0
- package/dist/plugin/PluginManager.js +288 -0
- package/dist/plugin/PluginManager.js.map +1 -0
- package/dist/plugin/index.d.ts +5 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +10 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/runtime/AgentXRuntime.d.ts +90 -0
- package/dist/runtime/AgentXRuntime.d.ts.map +1 -0
- package/dist/runtime/AgentXRuntime.js +469 -0
- package/dist/runtime/AgentXRuntime.js.map +1 -0
- package/dist/security/SecurityManager.d.ts +245 -0
- package/dist/security/SecurityManager.d.ts.map +1 -0
- package/dist/security/SecurityManager.js +512 -0
- package/dist/security/SecurityManager.js.map +1 -0
- package/dist/security/index.d.ts +5 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +14 -0
- package/dist/security/index.js.map +1 -0
- package/dist/tool/ToolRegistry.d.ts +58 -0
- package/dist/tool/ToolRegistry.d.ts.map +1 -0
- package/dist/tool/ToolRegistry.js +173 -0
- package/dist/tool/ToolRegistry.js.map +1 -0
- package/dist/tool/ToolValidator.d.ts +41 -0
- package/dist/tool/ToolValidator.d.ts.map +1 -0
- package/dist/tool/ToolValidator.js +158 -0
- package/dist/tool/ToolValidator.js.map +1 -0
- package/dist/tool/index.d.ts +6 -0
- package/dist/tool/index.d.ts.map +1 -0
- package/dist/tool/index.js +11 -0
- package/dist/tool/index.js.map +1 -0
- package/dist/transport/BaseTransport.d.ts +66 -0
- package/dist/transport/BaseTransport.d.ts.map +1 -0
- package/dist/transport/BaseTransport.js +103 -0
- package/dist/transport/BaseTransport.js.map +1 -0
- package/dist/transport/HttpTransport.d.ts +41 -0
- package/dist/transport/HttpTransport.d.ts.map +1 -0
- package/dist/transport/HttpTransport.js +160 -0
- package/dist/transport/HttpTransport.js.map +1 -0
- package/dist/transport/LocalTransport.d.ts +40 -0
- package/dist/transport/LocalTransport.d.ts.map +1 -0
- package/dist/transport/LocalTransport.js +157 -0
- package/dist/transport/LocalTransport.js.map +1 -0
- package/dist/transport/QueueTransport.d.ts +63 -0
- package/dist/transport/QueueTransport.d.ts.map +1 -0
- package/dist/transport/QueueTransport.js +194 -0
- package/dist/transport/QueueTransport.js.map +1 -0
- package/dist/transport/StdioTransport.d.ts +51 -0
- package/dist/transport/StdioTransport.d.ts.map +1 -0
- package/dist/transport/StdioTransport.js +216 -0
- package/dist/transport/StdioTransport.js.map +1 -0
- package/dist/transport/TransportFactory.d.ts +35 -0
- package/dist/transport/TransportFactory.d.ts.map +1 -0
- package/dist/transport/TransportFactory.js +100 -0
- package/dist/transport/TransportFactory.js.map +1 -0
- package/dist/transport/index.d.ts +10 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +19 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/types/agent.d.ts +66 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +3 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/config.d.ts +60 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/credential.d.ts +38 -0
- package/dist/types/credential.d.ts.map +1 -0
- package/dist/types/credential.js +3 -0
- package/dist/types/credential.js.map +1 -0
- package/dist/types/error.d.ts +136 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +223 -0
- package/dist/types/error.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +27 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/llm.d.ts +43 -0
- package/dist/types/llm.d.ts.map +1 -0
- package/dist/types/llm.js +3 -0
- package/dist/types/llm.js.map +1 -0
- package/dist/types/payment.d.ts +129 -0
- package/dist/types/payment.d.ts.map +1 -0
- package/dist/types/payment.js +6 -0
- package/dist/types/payment.js.map +1 -0
- package/dist/types/runtime.d.ts +31 -0
- package/dist/types/runtime.d.ts.map +1 -0
- package/dist/types/runtime.js +3 -0
- package/dist/types/runtime.js.map +1 -0
- package/dist/types/tool.d.ts +72 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +3 -0
- package/dist/types/tool.js.map +1 -0
- package/dist/types/transport.d.ts +53 -0
- package/dist/types/transport.d.ts.map +1 -0
- package/dist/types/transport.js +3 -0
- package/dist/types/transport.js.map +1 -0
- package/dist/types/workflow.d.ts +72 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +6 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/utils/factory.d.ts +14 -0
- package/dist/utils/factory.d.ts.map +1 -0
- package/dist/utils/factory.js +146 -0
- package/dist/utils/factory.js.map +1 -0
- package/dist/workflow/StateManager.d.ts +93 -0
- package/dist/workflow/StateManager.d.ts.map +1 -0
- package/dist/workflow/StateManager.js +223 -0
- package/dist/workflow/StateManager.js.map +1 -0
- package/dist/workflow/WorkflowDefinition.d.ts +49 -0
- package/dist/workflow/WorkflowDefinition.d.ts.map +1 -0
- package/dist/workflow/WorkflowDefinition.js +264 -0
- package/dist/workflow/WorkflowDefinition.js.map +1 -0
- package/dist/workflow/WorkflowExecutor.d.ts +42 -0
- package/dist/workflow/WorkflowExecutor.d.ts.map +1 -0
- package/dist/workflow/WorkflowExecutor.js +372 -0
- package/dist/workflow/WorkflowExecutor.js.map +1 -0
- package/dist/workflow/index.d.ts +7 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +17 -0
- package/dist/workflow/index.js.map +1 -0
- package/package.json +122 -0
|
@@ -0,0 +1,2225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Interactive Setup for AgentX
|
|
4
|
+
* Provides a Next.js-like interactive CLI experience for project setup
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.ProjectGenerator = exports.SetupWizard = exports.InteractivePrompt = exports.AGENT_PERSONALITIES = exports.SECURITY_OPTIONS = exports.DEPLOYMENT_OPTIONS = exports.FEATURES = exports.INTEGRATIONS = exports.LLM_PROVIDERS = void 0;
|
|
41
|
+
const readline = __importStar(require("readline"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
// Available LLM providers
|
|
45
|
+
exports.LLM_PROVIDERS = [
|
|
46
|
+
{ value: 'openai', label: 'OpenAI', description: 'GPT-4, GPT-3.5 (requires OPENAI_API_KEY)' },
|
|
47
|
+
{ value: 'anthropic', label: 'Anthropic', description: 'Claude 3 (requires ANTHROPIC_API_KEY)' },
|
|
48
|
+
{ value: 'local', label: 'Local Model', description: 'Ollama, LM Studio (no API key needed)' },
|
|
49
|
+
];
|
|
50
|
+
// Available integrations
|
|
51
|
+
exports.INTEGRATIONS = [
|
|
52
|
+
{ value: 'http', label: 'HTTP/REST', description: 'Make HTTP requests to APIs', selected: true },
|
|
53
|
+
{ value: 'webhook', label: 'Webhooks', description: 'Receive and send webhooks' },
|
|
54
|
+
{ value: 'scheduler', label: 'Scheduler', description: 'Cron jobs and scheduled tasks' },
|
|
55
|
+
{ value: 'email', label: 'Email', description: 'Send emails via SMTP' },
|
|
56
|
+
{ value: 'slack', label: 'Slack', description: 'Slack messaging integration' },
|
|
57
|
+
{ value: 'whatsapp', label: 'WhatsApp', description: 'WhatsApp Business API' },
|
|
58
|
+
{ value: 'telegram', label: 'Telegram', description: 'Telegram Bot API' },
|
|
59
|
+
{ value: 'google-docs', label: 'Google Docs', description: 'Google Docs API' },
|
|
60
|
+
{ value: 'google-sheets', label: 'Google Sheets', description: 'Google Sheets API' },
|
|
61
|
+
{ value: 'google-drive', label: 'Google Drive', description: 'Google Drive API' },
|
|
62
|
+
{ value: 'sql', label: 'SQL Database', description: 'PostgreSQL, MySQL, SQLite' },
|
|
63
|
+
{ value: 'mongodb', label: 'MongoDB', description: 'MongoDB connector' },
|
|
64
|
+
{ value: 'redis', label: 'Redis', description: 'Redis cache/store' },
|
|
65
|
+
{ value: 's3', label: 'Amazon S3', description: 'S3 file storage' },
|
|
66
|
+
{ value: 'filesystem', label: 'File System', description: 'Local file operations' },
|
|
67
|
+
];
|
|
68
|
+
// Available features
|
|
69
|
+
exports.FEATURES = [
|
|
70
|
+
{ value: 'workflows', label: 'Workflows', description: 'DAG-based workflow execution', selected: true },
|
|
71
|
+
{ value: 'agents', label: 'AI Agents', description: 'Autonomous AI agents with tool use', selected: true },
|
|
72
|
+
{ value: 'credentials', label: 'Credential Manager', description: 'Encrypted credential storage' },
|
|
73
|
+
{ value: 'security', label: 'Security', description: 'RBAC, rate limiting, audit logs' },
|
|
74
|
+
{ value: 'plugins', label: 'Plugin System', description: 'Extensible plugin architecture' },
|
|
75
|
+
{ value: 'payments', label: 'Payments', description: 'Stripe, PayPal, M-Pesa integration' },
|
|
76
|
+
];
|
|
77
|
+
// Deployment options
|
|
78
|
+
exports.DEPLOYMENT_OPTIONS = [
|
|
79
|
+
{ value: 'docker', label: 'Docker', description: 'Containerized deployment with Dockerfile' },
|
|
80
|
+
{ value: 'docker-compose', label: 'Docker Compose', description: 'Multi-service setup with databases' },
|
|
81
|
+
{ value: 'kubernetes', label: 'Kubernetes', description: 'K8s manifests for production deployment' },
|
|
82
|
+
{ value: 'railway', label: 'Railway', description: 'One-click Railway deployment' },
|
|
83
|
+
{ value: 'vercel', label: 'Vercel', description: 'Serverless deployment on Vercel' },
|
|
84
|
+
{ value: 'aws-lambda', label: 'AWS Lambda', description: 'Serverless functions on AWS' },
|
|
85
|
+
{ value: 'systemd', label: 'Systemd Service', description: 'Linux service configuration' },
|
|
86
|
+
];
|
|
87
|
+
// Security enhancements
|
|
88
|
+
exports.SECURITY_OPTIONS = [
|
|
89
|
+
{ value: 'https-only', label: 'HTTPS Only', description: 'Force HTTPS and security headers', selected: true },
|
|
90
|
+
{ value: 'api-keys', label: 'API Key Authentication', description: 'Secure API access with keys' },
|
|
91
|
+
{ value: 'jwt', label: 'JWT Authentication', description: 'JSON Web Token authentication' },
|
|
92
|
+
{ value: 'rate-limiting', label: 'Rate Limiting', description: 'Prevent API abuse and DoS attacks' },
|
|
93
|
+
{ value: 'input-validation', label: 'Input Validation', description: 'Strict input sanitization' },
|
|
94
|
+
{ value: 'audit-logging', label: 'Audit Logging', description: 'Security event logging' },
|
|
95
|
+
{ value: 'secrets-manager', label: 'Secrets Manager', description: 'External secrets management' },
|
|
96
|
+
];
|
|
97
|
+
// Agent personality presets
|
|
98
|
+
exports.AGENT_PERSONALITIES = [
|
|
99
|
+
{ value: 'helpful', label: 'Helpful Assistant', description: 'Friendly and supportive' },
|
|
100
|
+
{ value: 'professional', label: 'Professional', description: 'Formal and business-focused' },
|
|
101
|
+
{ value: 'concise', label: 'Concise', description: 'Brief and to-the-point' },
|
|
102
|
+
{ value: 'creative', label: 'Creative', description: 'Imaginative and exploratory' },
|
|
103
|
+
{ value: 'custom', label: 'Custom', description: 'Define your own personality' },
|
|
104
|
+
];
|
|
105
|
+
/**
|
|
106
|
+
* Interactive prompt utilities
|
|
107
|
+
*/
|
|
108
|
+
class InteractivePrompt {
|
|
109
|
+
rl;
|
|
110
|
+
constructor() {
|
|
111
|
+
this.rl = readline.createInterface({
|
|
112
|
+
input: process.stdin,
|
|
113
|
+
output: process.stdout,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async text(question, defaultValue) {
|
|
117
|
+
const prompt = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
this.rl.question(prompt, (answer) => {
|
|
120
|
+
resolve(answer.trim() || defaultValue || '');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async confirm(question, defaultValue = true) {
|
|
125
|
+
const hint = defaultValue ? '(Y/n)' : '(y/N)';
|
|
126
|
+
return new Promise((resolve) => {
|
|
127
|
+
this.rl.question(`${question} ${hint}: `, (answer) => {
|
|
128
|
+
const normalized = answer.trim().toLowerCase();
|
|
129
|
+
if (normalized === '')
|
|
130
|
+
resolve(defaultValue);
|
|
131
|
+
else
|
|
132
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async select(question, choices) {
|
|
137
|
+
console.log(`\n${question}`);
|
|
138
|
+
choices.forEach((choice, index) => {
|
|
139
|
+
const desc = choice.description ? ` - ${choice.description}` : '';
|
|
140
|
+
console.log(` ${index + 1}. ${choice.label}${desc}`);
|
|
141
|
+
});
|
|
142
|
+
return new Promise((resolve) => {
|
|
143
|
+
const ask = () => {
|
|
144
|
+
this.rl.question('\nEnter number: ', (answer) => {
|
|
145
|
+
const index = parseInt(answer.trim(), 10) - 1;
|
|
146
|
+
if (index >= 0 && index < choices.length) {
|
|
147
|
+
resolve(choices[index].value);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.log('Invalid selection. Please try again.');
|
|
151
|
+
ask();
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
ask();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async multiSelect(question, choices) {
|
|
159
|
+
console.log(`\n${question}`);
|
|
160
|
+
console.log('(Enter numbers separated by commas, or "all" for all, "none" to skip)\n');
|
|
161
|
+
choices.forEach((choice, index) => {
|
|
162
|
+
const selected = choice.selected ? '✓' : ' ';
|
|
163
|
+
const desc = choice.description ? ` - ${choice.description}` : '';
|
|
164
|
+
console.log(` [${selected}] ${index + 1}. ${choice.label}${desc}`);
|
|
165
|
+
});
|
|
166
|
+
return new Promise((resolve) => {
|
|
167
|
+
const ask = () => {
|
|
168
|
+
this.rl.question('\nEnter numbers (e.g., 1,2,3) or "all"/"none": ', (answer) => {
|
|
169
|
+
const normalized = answer.trim().toLowerCase();
|
|
170
|
+
if (normalized === 'all') {
|
|
171
|
+
resolve(choices.map(c => c.value));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (normalized === '' || normalized === 'none') {
|
|
175
|
+
resolve(choices.filter(c => c.selected).map(c => c.value));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const indices = normalized.split(',').map(s => parseInt(s.trim(), 10) - 1);
|
|
179
|
+
const valid = indices.every(i => i >= 0 && i < choices.length);
|
|
180
|
+
if (valid) {
|
|
181
|
+
resolve(indices.map(i => choices[i].value));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log('Invalid selection. Please try again.');
|
|
185
|
+
ask();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
ask();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
close() {
|
|
193
|
+
this.rl.close();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.InteractivePrompt = InteractivePrompt;
|
|
197
|
+
/**
|
|
198
|
+
* Setup Wizard - orchestrates the interactive project setup
|
|
199
|
+
*/
|
|
200
|
+
class SetupWizard {
|
|
201
|
+
prompt;
|
|
202
|
+
constructor() {
|
|
203
|
+
this.prompt = new InteractivePrompt();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Run the interactive setup wizard
|
|
207
|
+
*/
|
|
208
|
+
async run(projectName) {
|
|
209
|
+
this.printBanner();
|
|
210
|
+
// Project name
|
|
211
|
+
const name = projectName || await this.prompt.text('What is your project named?', 'my-agentx-app');
|
|
212
|
+
// LLM Providers
|
|
213
|
+
console.log('\n📦 LLM Providers');
|
|
214
|
+
const llmProviders = await this.prompt.multiSelect('Which LLM providers would you like to use?', exports.LLM_PROVIDERS);
|
|
215
|
+
// Integrations
|
|
216
|
+
console.log('\n🔌 Integrations');
|
|
217
|
+
const integrations = await this.prompt.multiSelect('Which integrations would you like to include?', exports.INTEGRATIONS);
|
|
218
|
+
// Features
|
|
219
|
+
console.log('\n⚡ Features');
|
|
220
|
+
const features = await this.prompt.multiSelect('Which features would you like to enable?', exports.FEATURES);
|
|
221
|
+
// Agent personality
|
|
222
|
+
console.log('\n🤖 Agent Configuration');
|
|
223
|
+
let agentPersonality = await this.prompt.select('Choose a default agent personality:', exports.AGENT_PERSONALITIES);
|
|
224
|
+
if (agentPersonality === 'custom') {
|
|
225
|
+
agentPersonality = await this.prompt.text('Describe your agent personality', 'You are a helpful AI assistant.');
|
|
226
|
+
}
|
|
227
|
+
// TypeScript
|
|
228
|
+
const typescript = await this.prompt.confirm('Use TypeScript?', true);
|
|
229
|
+
// Deployment options
|
|
230
|
+
console.log('\n🚀 Deployment');
|
|
231
|
+
const deployment = await this.prompt.multiSelect('How would you like to deploy your application?', exports.DEPLOYMENT_OPTIONS);
|
|
232
|
+
// Security options
|
|
233
|
+
console.log('\n🔒 Security');
|
|
234
|
+
const security = await this.prompt.multiSelect('Which security features would you like to enable?', exports.SECURITY_OPTIONS);
|
|
235
|
+
// Git
|
|
236
|
+
const git = await this.prompt.confirm('Initialize a git repository?', true);
|
|
237
|
+
this.prompt.close();
|
|
238
|
+
return {
|
|
239
|
+
projectName: name,
|
|
240
|
+
llmProviders,
|
|
241
|
+
integrations,
|
|
242
|
+
features,
|
|
243
|
+
agentPersonality,
|
|
244
|
+
typescript,
|
|
245
|
+
git,
|
|
246
|
+
deployment,
|
|
247
|
+
security,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
printBanner() {
|
|
251
|
+
console.log(`
|
|
252
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
253
|
+
║ ║
|
|
254
|
+
║ █████╗ ██████╗ ███████╗███╗ ██╗████████╗██╗ ██╗ ║
|
|
255
|
+
║ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝╚██╗██╔╝ ║
|
|
256
|
+
║ ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ╚███╔╝ ║
|
|
257
|
+
║ ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██╔██╗ ║
|
|
258
|
+
║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ██╔╝ ██╗ ║
|
|
259
|
+
║ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ║
|
|
260
|
+
║ ║
|
|
261
|
+
║ Production-Grade AI Agent Framework ║
|
|
262
|
+
║ ║
|
|
263
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
264
|
+
`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.SetupWizard = SetupWizard;
|
|
268
|
+
/**
|
|
269
|
+
* Project Generator - creates project files based on setup options
|
|
270
|
+
*/
|
|
271
|
+
class ProjectGenerator {
|
|
272
|
+
/**
|
|
273
|
+
* Generate project from setup options
|
|
274
|
+
*/
|
|
275
|
+
async generate(options, targetDir) {
|
|
276
|
+
const projectDir = path.join(targetDir, options.projectName);
|
|
277
|
+
// Create directories
|
|
278
|
+
this.createDirectories(projectDir, options);
|
|
279
|
+
// Generate files
|
|
280
|
+
await this.generatePackageJson(projectDir, options);
|
|
281
|
+
await this.generateTsConfig(projectDir, options);
|
|
282
|
+
await this.generateConfig(projectDir, options);
|
|
283
|
+
await this.generateEnvFile(projectDir, options);
|
|
284
|
+
await this.generateEntryPoint(projectDir, options);
|
|
285
|
+
await this.generateExampleAgent(projectDir, options);
|
|
286
|
+
// Generate payment examples if payments feature is selected
|
|
287
|
+
if (options.features.includes('payments')) {
|
|
288
|
+
await this.generatePaymentExamples(projectDir, options);
|
|
289
|
+
}
|
|
290
|
+
// Generate deployment files
|
|
291
|
+
await this.generateDeploymentFiles(projectDir, options);
|
|
292
|
+
// Generate security files
|
|
293
|
+
await this.generateSecurityFiles(projectDir, options);
|
|
294
|
+
if (options.git) {
|
|
295
|
+
await this.generateGitIgnore(projectDir);
|
|
296
|
+
}
|
|
297
|
+
console.log(`\n✅ Project created at ${projectDir}`);
|
|
298
|
+
this.printNextSteps(options);
|
|
299
|
+
}
|
|
300
|
+
createDirectories(projectDir, options) {
|
|
301
|
+
const dirs = [
|
|
302
|
+
projectDir,
|
|
303
|
+
path.join(projectDir, 'src'),
|
|
304
|
+
path.join(projectDir, 'src', 'agents'),
|
|
305
|
+
];
|
|
306
|
+
if (options.features.includes('workflows')) {
|
|
307
|
+
dirs.push(path.join(projectDir, 'src', 'workflows'));
|
|
308
|
+
}
|
|
309
|
+
if (options.integrations.length > 0) {
|
|
310
|
+
dirs.push(path.join(projectDir, 'src', 'tools'));
|
|
311
|
+
}
|
|
312
|
+
for (const dir of dirs) {
|
|
313
|
+
if (!fs.existsSync(dir)) {
|
|
314
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async generatePackageJson(projectDir, options) {
|
|
319
|
+
const dependencies = {
|
|
320
|
+
agentx: '^0.1.0',
|
|
321
|
+
};
|
|
322
|
+
const devDependencies = {
|
|
323
|
+
jest: '^29.0.0',
|
|
324
|
+
};
|
|
325
|
+
// Add TypeScript dependencies
|
|
326
|
+
if (options.typescript) {
|
|
327
|
+
devDependencies['@types/node'] = '^20.0.0';
|
|
328
|
+
devDependencies['typescript'] = '^5.0.0';
|
|
329
|
+
devDependencies['ts-node'] = '^10.0.0';
|
|
330
|
+
devDependencies['@types/jest'] = '^29.0.0';
|
|
331
|
+
}
|
|
332
|
+
// Add security dependencies
|
|
333
|
+
if (options.security.includes('input-validation')) {
|
|
334
|
+
dependencies['joi'] = '^17.11.0';
|
|
335
|
+
if (options.typescript) {
|
|
336
|
+
devDependencies['@types/joi'] = '^17.2.0';
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (options.security.includes('jwt')) {
|
|
340
|
+
dependencies['jsonwebtoken'] = '^9.0.2';
|
|
341
|
+
if (options.typescript) {
|
|
342
|
+
devDependencies['@types/jsonwebtoken'] = '^9.0.5';
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (options.security.includes('rate-limiting')) {
|
|
346
|
+
dependencies['express-rate-limit'] = '^7.1.5';
|
|
347
|
+
}
|
|
348
|
+
// Add deployment dependencies
|
|
349
|
+
if (options.deployment.includes('aws-lambda')) {
|
|
350
|
+
dependencies['aws-lambda'] = '^1.0.7';
|
|
351
|
+
if (options.typescript) {
|
|
352
|
+
devDependencies['@types/aws-lambda'] = '^8.10.130';
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Add integration-specific dependencies
|
|
356
|
+
if (options.integrations.includes('email')) {
|
|
357
|
+
dependencies['nodemailer'] = '^6.9.7';
|
|
358
|
+
if (options.typescript) {
|
|
359
|
+
devDependencies['@types/nodemailer'] = '^6.4.14';
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const scripts = {
|
|
363
|
+
test: 'jest',
|
|
364
|
+
};
|
|
365
|
+
if (options.typescript) {
|
|
366
|
+
scripts.build = 'tsc';
|
|
367
|
+
scripts.start = 'node dist/index.js';
|
|
368
|
+
scripts.dev = 'ts-node src/index.ts';
|
|
369
|
+
scripts['type-check'] = 'tsc --noEmit';
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
scripts.start = 'node src/index.js';
|
|
373
|
+
scripts.dev = 'nodemon src/index.js';
|
|
374
|
+
devDependencies.nodemon = '^3.0.2';
|
|
375
|
+
}
|
|
376
|
+
// Add deployment scripts
|
|
377
|
+
if (options.deployment.includes('docker')) {
|
|
378
|
+
scripts['docker:build'] = `docker build -t ${options.projectName} .`;
|
|
379
|
+
scripts['docker:run'] = `docker run -p 3000:3000 ${options.projectName}`;
|
|
380
|
+
}
|
|
381
|
+
if (options.deployment.includes('docker-compose')) {
|
|
382
|
+
scripts['compose:up'] = 'docker-compose up -d';
|
|
383
|
+
scripts['compose:down'] = 'docker-compose down';
|
|
384
|
+
scripts['compose:logs'] = 'docker-compose logs -f';
|
|
385
|
+
}
|
|
386
|
+
if (options.deployment.includes('kubernetes')) {
|
|
387
|
+
scripts['k8s:deploy'] = 'kubectl apply -f k8s/';
|
|
388
|
+
scripts['k8s:delete'] = 'kubectl delete -f k8s/';
|
|
389
|
+
}
|
|
390
|
+
// Add security scripts
|
|
391
|
+
if (options.security.length > 0) {
|
|
392
|
+
scripts['security:audit'] = 'npm audit';
|
|
393
|
+
scripts['security:fix'] = 'npm audit fix';
|
|
394
|
+
}
|
|
395
|
+
const pkg = {
|
|
396
|
+
name: options.projectName,
|
|
397
|
+
version: '1.0.0',
|
|
398
|
+
description: 'AgentX AI Agent Project',
|
|
399
|
+
main: options.typescript ? 'dist/index.js' : 'src/index.js',
|
|
400
|
+
scripts,
|
|
401
|
+
dependencies,
|
|
402
|
+
devDependencies,
|
|
403
|
+
engines: {
|
|
404
|
+
node: '>=18.0.0',
|
|
405
|
+
},
|
|
406
|
+
keywords: [
|
|
407
|
+
'agentx',
|
|
408
|
+
'ai',
|
|
409
|
+
'agent',
|
|
410
|
+
'automation',
|
|
411
|
+
...options.integrations,
|
|
412
|
+
...options.features,
|
|
413
|
+
],
|
|
414
|
+
};
|
|
415
|
+
fs.writeFileSync(path.join(projectDir, 'package.json'), JSON.stringify(pkg, null, 2));
|
|
416
|
+
}
|
|
417
|
+
async generateTsConfig(projectDir, options) {
|
|
418
|
+
if (!options.typescript)
|
|
419
|
+
return;
|
|
420
|
+
const tsconfig = {
|
|
421
|
+
compilerOptions: {
|
|
422
|
+
target: 'ES2020',
|
|
423
|
+
module: 'commonjs',
|
|
424
|
+
lib: ['ES2020'],
|
|
425
|
+
outDir: './dist',
|
|
426
|
+
rootDir: './src',
|
|
427
|
+
strict: true,
|
|
428
|
+
esModuleInterop: true,
|
|
429
|
+
skipLibCheck: true,
|
|
430
|
+
forceConsistentCasingInFileNames: true,
|
|
431
|
+
declaration: true,
|
|
432
|
+
},
|
|
433
|
+
include: ['src/**/*'],
|
|
434
|
+
exclude: ['node_modules', 'dist'],
|
|
435
|
+
};
|
|
436
|
+
fs.writeFileSync(path.join(projectDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
|
|
437
|
+
}
|
|
438
|
+
async generateConfig(projectDir, options) {
|
|
439
|
+
const ext = options.typescript ? 'ts' : 'js';
|
|
440
|
+
const exportKeyword = options.typescript ? 'export default' : 'module.exports =';
|
|
441
|
+
const llmConfig = this.generateLLMConfig(options);
|
|
442
|
+
const integrationsConfig = this.generateIntegrationsConfig(options);
|
|
443
|
+
const content = `/**
|
|
444
|
+
* AgentX Configuration
|
|
445
|
+
* Generated by create-agentx
|
|
446
|
+
*/
|
|
447
|
+
${exportKeyword} {
|
|
448
|
+
runtime: {
|
|
449
|
+
name: '${options.projectName}',
|
|
450
|
+
version: '1.0.0',
|
|
451
|
+
},
|
|
452
|
+
llm: {
|
|
453
|
+
defaultProvider: '${options.llmProviders[0] || 'openai'}',
|
|
454
|
+
providers: {
|
|
455
|
+
${llmConfig}
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
integrations: {
|
|
459
|
+
${integrationsConfig}
|
|
460
|
+
},
|
|
461
|
+
features: {
|
|
462
|
+
workflows: ${options.features.includes('workflows')},
|
|
463
|
+
agents: ${options.features.includes('agents')},
|
|
464
|
+
credentials: ${options.features.includes('credentials')},
|
|
465
|
+
security: ${options.features.includes('security')},
|
|
466
|
+
plugins: ${options.features.includes('plugins')},
|
|
467
|
+
payments: ${options.features.includes('payments')},
|
|
468
|
+
},
|
|
469
|
+
agent: {
|
|
470
|
+
defaultPersonality: '${this.escapeString(options.agentPersonality)}',
|
|
471
|
+
},
|
|
472
|
+
};
|
|
473
|
+
`;
|
|
474
|
+
fs.writeFileSync(path.join(projectDir, 'src', `config.${ext}`), content);
|
|
475
|
+
}
|
|
476
|
+
generateLLMConfig(options) {
|
|
477
|
+
const configs = [];
|
|
478
|
+
if (options.llmProviders.includes('openai')) {
|
|
479
|
+
configs.push(` openai: {
|
|
480
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
481
|
+
model: 'gpt-4',
|
|
482
|
+
}`);
|
|
483
|
+
}
|
|
484
|
+
if (options.llmProviders.includes('anthropic')) {
|
|
485
|
+
configs.push(` anthropic: {
|
|
486
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
487
|
+
model: 'claude-3-opus-20240229',
|
|
488
|
+
}`);
|
|
489
|
+
}
|
|
490
|
+
if (options.llmProviders.includes('local')) {
|
|
491
|
+
configs.push(` local: {
|
|
492
|
+
baseUrl: process.env.LOCAL_LLM_URL || 'http://localhost:11434',
|
|
493
|
+
model: 'llama2',
|
|
494
|
+
}`);
|
|
495
|
+
}
|
|
496
|
+
return configs.join(',\n');
|
|
497
|
+
}
|
|
498
|
+
generateIntegrationsConfig(options) {
|
|
499
|
+
const configs = [];
|
|
500
|
+
for (const integration of options.integrations) {
|
|
501
|
+
configs.push(` '${integration}': { enabled: true }`);
|
|
502
|
+
}
|
|
503
|
+
return configs.join(',\n');
|
|
504
|
+
}
|
|
505
|
+
async generateEnvFile(projectDir, options) {
|
|
506
|
+
const lines = ['# AgentX Environment Variables', '# Generated based on your selections', ''];
|
|
507
|
+
// Core application settings
|
|
508
|
+
lines.push('# Application Configuration');
|
|
509
|
+
lines.push(`NODE_ENV=development`);
|
|
510
|
+
lines.push(`PORT=3000`);
|
|
511
|
+
lines.push('');
|
|
512
|
+
// LLM Provider configurations (only selected ones)
|
|
513
|
+
if (options.llmProviders.includes('openai')) {
|
|
514
|
+
lines.push('# OpenAI Configuration');
|
|
515
|
+
lines.push('OPENAI_API_KEY=your-openai-api-key-here');
|
|
516
|
+
lines.push('# Optional: OPENAI_ORG_ID=your-org-id');
|
|
517
|
+
lines.push('');
|
|
518
|
+
}
|
|
519
|
+
if (options.llmProviders.includes('anthropic')) {
|
|
520
|
+
lines.push('# Anthropic Configuration');
|
|
521
|
+
lines.push('ANTHROPIC_API_KEY=your-anthropic-api-key-here');
|
|
522
|
+
lines.push('');
|
|
523
|
+
}
|
|
524
|
+
if (options.llmProviders.includes('local')) {
|
|
525
|
+
lines.push('# Local LLM Configuration');
|
|
526
|
+
lines.push('LOCAL_LLM_URL=http://localhost:11434');
|
|
527
|
+
lines.push('# For Ollama: LOCAL_LLM_MODEL=llama2');
|
|
528
|
+
lines.push('');
|
|
529
|
+
}
|
|
530
|
+
// Integration-specific env vars (only selected ones)
|
|
531
|
+
if (options.integrations.includes('email')) {
|
|
532
|
+
lines.push('# Email Configuration (SMTP)');
|
|
533
|
+
lines.push('SMTP_HOST=smtp.gmail.com');
|
|
534
|
+
lines.push('SMTP_PORT=587');
|
|
535
|
+
lines.push('SMTP_SECURE=false');
|
|
536
|
+
lines.push('SMTP_USER=your-email@gmail.com');
|
|
537
|
+
lines.push('SMTP_PASS=your-app-password');
|
|
538
|
+
lines.push('');
|
|
539
|
+
}
|
|
540
|
+
if (options.integrations.includes('slack')) {
|
|
541
|
+
lines.push('# Slack Configuration');
|
|
542
|
+
lines.push('SLACK_BOT_TOKEN=xoxb-your-bot-token');
|
|
543
|
+
lines.push('SLACK_SIGNING_SECRET=your-signing-secret');
|
|
544
|
+
lines.push('');
|
|
545
|
+
}
|
|
546
|
+
if (options.integrations.includes('whatsapp')) {
|
|
547
|
+
lines.push('# WhatsApp Business API');
|
|
548
|
+
lines.push('WHATSAPP_TOKEN=your-whatsapp-token');
|
|
549
|
+
lines.push('WHATSAPP_PHONE_ID=your-phone-number-id');
|
|
550
|
+
lines.push('WHATSAPP_VERIFY_TOKEN=your-verify-token');
|
|
551
|
+
lines.push('');
|
|
552
|
+
}
|
|
553
|
+
if (options.integrations.includes('telegram')) {
|
|
554
|
+
lines.push('# Telegram Bot Configuration');
|
|
555
|
+
lines.push('TELEGRAM_BOT_TOKEN=your-bot-token');
|
|
556
|
+
lines.push('');
|
|
557
|
+
}
|
|
558
|
+
if (options.integrations.includes('google-docs') ||
|
|
559
|
+
options.integrations.includes('google-sheets') ||
|
|
560
|
+
options.integrations.includes('google-drive')) {
|
|
561
|
+
lines.push('# Google Workspace Configuration');
|
|
562
|
+
lines.push('GOOGLE_CLIENT_ID=your-client-id');
|
|
563
|
+
lines.push('GOOGLE_CLIENT_SECRET=your-client-secret');
|
|
564
|
+
lines.push('GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google/callback');
|
|
565
|
+
lines.push('# Service Account (alternative to OAuth)');
|
|
566
|
+
lines.push('# GOOGLE_SERVICE_ACCOUNT_KEY=path/to/service-account.json');
|
|
567
|
+
lines.push('');
|
|
568
|
+
}
|
|
569
|
+
if (options.integrations.includes('sql')) {
|
|
570
|
+
lines.push('# Database Configuration');
|
|
571
|
+
lines.push('DATABASE_URL=postgresql://user:password@localhost:5432/agentx_db');
|
|
572
|
+
lines.push('# Alternative formats:');
|
|
573
|
+
lines.push('# DATABASE_URL=mysql://user:password@localhost:3306/agentx_db');
|
|
574
|
+
lines.push('# DATABASE_URL=sqlite:./data/agentx.db');
|
|
575
|
+
lines.push('');
|
|
576
|
+
}
|
|
577
|
+
if (options.integrations.includes('mongodb')) {
|
|
578
|
+
lines.push('# MongoDB Configuration');
|
|
579
|
+
lines.push('MONGODB_URI=mongodb://localhost:27017/agentx');
|
|
580
|
+
lines.push('# For MongoDB Atlas:');
|
|
581
|
+
lines.push('# MONGODB_URI=mongodb+srv://user:password@cluster.mongodb.net/agentx');
|
|
582
|
+
lines.push('');
|
|
583
|
+
}
|
|
584
|
+
if (options.integrations.includes('redis')) {
|
|
585
|
+
lines.push('# Redis Configuration');
|
|
586
|
+
lines.push('REDIS_URL=redis://localhost:6379');
|
|
587
|
+
lines.push('# For Redis Cloud:');
|
|
588
|
+
lines.push('# REDIS_URL=redis://user:password@host:port');
|
|
589
|
+
lines.push('');
|
|
590
|
+
}
|
|
591
|
+
if (options.integrations.includes('s3')) {
|
|
592
|
+
lines.push('# AWS S3 Configuration');
|
|
593
|
+
lines.push('AWS_ACCESS_KEY_ID=your-access-key-id');
|
|
594
|
+
lines.push('AWS_SECRET_ACCESS_KEY=your-secret-access-key');
|
|
595
|
+
lines.push('AWS_REGION=us-east-1');
|
|
596
|
+
lines.push('S3_BUCKET_NAME=your-bucket-name');
|
|
597
|
+
lines.push('');
|
|
598
|
+
}
|
|
599
|
+
// Payment integrations (only if selected)
|
|
600
|
+
if (options.features.includes('payments')) {
|
|
601
|
+
lines.push('# Payment Configuration');
|
|
602
|
+
lines.push('# Stripe');
|
|
603
|
+
lines.push('STRIPE_SECRET_KEY=sk_test_your-stripe-secret-key');
|
|
604
|
+
lines.push('STRIPE_PUBLISHABLE_KEY=pk_test_your-stripe-publishable-key');
|
|
605
|
+
lines.push('STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret');
|
|
606
|
+
lines.push('');
|
|
607
|
+
lines.push('# PayPal Configuration');
|
|
608
|
+
lines.push('PAYPAL_CLIENT_ID=your-paypal-client-id');
|
|
609
|
+
lines.push('PAYPAL_CLIENT_SECRET=your-paypal-client-secret');
|
|
610
|
+
lines.push('PAYPAL_MODE=sandbox # or live');
|
|
611
|
+
lines.push('');
|
|
612
|
+
lines.push('# M-Pesa Configuration (Safaricom)');
|
|
613
|
+
lines.push('MPESA_CONSUMER_KEY=your-mpesa-consumer-key');
|
|
614
|
+
lines.push('MPESA_CONSUMER_SECRET=your-mpesa-consumer-secret');
|
|
615
|
+
lines.push('MPESA_SHORTCODE=174379 # Your business shortcode');
|
|
616
|
+
lines.push('MPESA_PASSKEY=your-mpesa-passkey');
|
|
617
|
+
lines.push('MPESA_ENVIRONMENT=sandbox # or production');
|
|
618
|
+
lines.push('MPESA_CALLBACK_URL=https://yourdomain.com/api/mpesa/callback');
|
|
619
|
+
lines.push('MPESA_WEBHOOK_URL=https://yourdomain.com/api/mpesa/webhook');
|
|
620
|
+
lines.push('');
|
|
621
|
+
}
|
|
622
|
+
// Security configurations (only if selected)
|
|
623
|
+
if (options.security.includes('jwt')) {
|
|
624
|
+
lines.push('# JWT Configuration');
|
|
625
|
+
lines.push('JWT_SECRET=your-super-secret-jwt-key-change-this-in-production');
|
|
626
|
+
lines.push('JWT_EXPIRES_IN=24h');
|
|
627
|
+
lines.push('');
|
|
628
|
+
}
|
|
629
|
+
if (options.security.includes('api-keys')) {
|
|
630
|
+
lines.push('# API Key Configuration');
|
|
631
|
+
lines.push('API_KEY_HEADER=X-API-Key');
|
|
632
|
+
lines.push('MASTER_API_KEY=your-master-api-key-change-this');
|
|
633
|
+
lines.push('');
|
|
634
|
+
}
|
|
635
|
+
if (options.security.includes('rate-limiting')) {
|
|
636
|
+
lines.push('# Rate Limiting Configuration');
|
|
637
|
+
lines.push('RATE_LIMIT_WINDOW_MS=900000 # 15 minutes');
|
|
638
|
+
lines.push('RATE_LIMIT_MAX_REQUESTS=100');
|
|
639
|
+
lines.push('');
|
|
640
|
+
}
|
|
641
|
+
if (options.security.includes('secrets-manager')) {
|
|
642
|
+
lines.push('# External Secrets Manager');
|
|
643
|
+
lines.push('# AWS Secrets Manager');
|
|
644
|
+
lines.push('# AWS_SECRETS_REGION=us-east-1');
|
|
645
|
+
lines.push('# HashiCorp Vault');
|
|
646
|
+
lines.push('# VAULT_ADDR=https://vault.example.com');
|
|
647
|
+
lines.push('# VAULT_TOKEN=your-vault-token');
|
|
648
|
+
lines.push('');
|
|
649
|
+
}
|
|
650
|
+
// Credential manager (only if selected)
|
|
651
|
+
if (options.features.includes('credentials')) {
|
|
652
|
+
lines.push('# Credential Manager');
|
|
653
|
+
lines.push('CREDENTIAL_ENCRYPTION_KEY=your-32-character-encryption-key-here');
|
|
654
|
+
lines.push('');
|
|
655
|
+
}
|
|
656
|
+
// Deployment-specific variables
|
|
657
|
+
if (options.deployment.includes('docker') || options.deployment.includes('docker-compose')) {
|
|
658
|
+
lines.push('# Docker Configuration');
|
|
659
|
+
lines.push('DOCKER_REGISTRY=your-registry.com');
|
|
660
|
+
lines.push('IMAGE_TAG=latest');
|
|
661
|
+
lines.push('');
|
|
662
|
+
}
|
|
663
|
+
if (options.deployment.includes('kubernetes')) {
|
|
664
|
+
lines.push('# Kubernetes Configuration');
|
|
665
|
+
lines.push('K8S_NAMESPACE=agentx');
|
|
666
|
+
lines.push('K8S_SERVICE_ACCOUNT=agentx-service');
|
|
667
|
+
lines.push('');
|
|
668
|
+
}
|
|
669
|
+
// Monitoring and logging (recommended for production)
|
|
670
|
+
if (options.security.includes('audit-logging') || options.deployment.length > 0) {
|
|
671
|
+
lines.push('# Monitoring & Logging');
|
|
672
|
+
lines.push('LOG_LEVEL=info # debug, info, warn, error');
|
|
673
|
+
lines.push('# SENTRY_DSN=your-sentry-dsn-for-error-tracking');
|
|
674
|
+
lines.push('# DATADOG_API_KEY=your-datadog-api-key');
|
|
675
|
+
lines.push('');
|
|
676
|
+
}
|
|
677
|
+
fs.writeFileSync(path.join(projectDir, '.env.example'), lines.join('\n'));
|
|
678
|
+
// Create actual .env with placeholder values
|
|
679
|
+
const envContent = lines
|
|
680
|
+
.map(line => {
|
|
681
|
+
if (line.includes('=') && !line.startsWith('#')) {
|
|
682
|
+
// Keep the structure but make values obviously placeholder
|
|
683
|
+
return line.replace(/=.*/, '=CHANGE_ME');
|
|
684
|
+
}
|
|
685
|
+
return line;
|
|
686
|
+
})
|
|
687
|
+
.join('\n');
|
|
688
|
+
fs.writeFileSync(path.join(projectDir, '.env'), envContent);
|
|
689
|
+
}
|
|
690
|
+
async generateEntryPoint(projectDir, options) {
|
|
691
|
+
const ext = options.typescript ? 'ts' : 'js';
|
|
692
|
+
const importStyle = options.typescript
|
|
693
|
+
? "import { AgentXRuntime } from 'agentx';\nimport config from './config';"
|
|
694
|
+
: "const { AgentXRuntime } = require('agentx');\nconst config = require('./config');";
|
|
695
|
+
const content = `/**
|
|
696
|
+
* AgentX Application Entry Point
|
|
697
|
+
* Generated by create-agentx
|
|
698
|
+
*/
|
|
699
|
+
${importStyle}
|
|
700
|
+
|
|
701
|
+
async function main() {
|
|
702
|
+
console.log('🚀 Starting ${options.projectName}...');
|
|
703
|
+
|
|
704
|
+
const runtime = new AgentXRuntime(config);
|
|
705
|
+
await runtime.start();
|
|
706
|
+
|
|
707
|
+
console.log('✅ AgentX runtime started successfully');
|
|
708
|
+
console.log('📦 Loaded providers:', ${options.typescript ? 'config.llm.providers' : 'Object.keys(config.llm.providers)'});
|
|
709
|
+
|
|
710
|
+
// Graceful shutdown
|
|
711
|
+
process.on('SIGINT', async () => {
|
|
712
|
+
console.log('\\n🛑 Shutting down...');
|
|
713
|
+
await runtime.stop();
|
|
714
|
+
process.exit(0);
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
main().catch((error) => {
|
|
719
|
+
console.error('❌ Failed to start:', error);
|
|
720
|
+
process.exit(1);
|
|
721
|
+
});
|
|
722
|
+
`;
|
|
723
|
+
fs.writeFileSync(path.join(projectDir, 'src', `index.${ext}`), content);
|
|
724
|
+
}
|
|
725
|
+
async generateExampleAgent(projectDir, options) {
|
|
726
|
+
if (!options.features.includes('agents'))
|
|
727
|
+
return;
|
|
728
|
+
const ext = options.typescript ? 'ts' : 'js';
|
|
729
|
+
const exportStyle = options.typescript ? 'export const' : 'module.exports.myAgent =';
|
|
730
|
+
const typeAnnotation = options.typescript ? ': AgentConfig' : '';
|
|
731
|
+
const importLine = options.typescript
|
|
732
|
+
? "import { AgentConfig } from 'agentx';\n\n"
|
|
733
|
+
: '';
|
|
734
|
+
const personality = this.getPersonalityPrompt(options.agentPersonality);
|
|
735
|
+
const content = `/**
|
|
736
|
+
* Example AI Agent
|
|
737
|
+
* Generated by create-agentx
|
|
738
|
+
*/
|
|
739
|
+
${importLine}${exportStyle} myAgent${typeAnnotation} = {
|
|
740
|
+
name: 'my-agent',
|
|
741
|
+
description: 'Your custom AI agent',
|
|
742
|
+
llmProvider: '${options.llmProviders[0] || 'openai'}',
|
|
743
|
+
model: '${this.getDefaultModel(options.llmProviders[0])}',
|
|
744
|
+
systemPrompt: \`${personality}\`,
|
|
745
|
+
allowedTools: [${options.integrations.map(i => `'${i}'`).join(', ')}],
|
|
746
|
+
maxIterations: 10,
|
|
747
|
+
temperature: 0.7,
|
|
748
|
+
};
|
|
749
|
+
`;
|
|
750
|
+
fs.writeFileSync(path.join(projectDir, 'src', 'agents', `myAgent.${ext}`), content);
|
|
751
|
+
}
|
|
752
|
+
async generateGitIgnore(projectDir) {
|
|
753
|
+
const content = `# Dependencies
|
|
754
|
+
node_modules/
|
|
755
|
+
|
|
756
|
+
# Build output
|
|
757
|
+
dist/
|
|
758
|
+
|
|
759
|
+
# Environment
|
|
760
|
+
.env
|
|
761
|
+
.env.local
|
|
762
|
+
.env.production
|
|
763
|
+
|
|
764
|
+
# IDE
|
|
765
|
+
.idea/
|
|
766
|
+
.vscode/
|
|
767
|
+
*.swp
|
|
768
|
+
*.swo
|
|
769
|
+
|
|
770
|
+
# OS
|
|
771
|
+
.DS_Store
|
|
772
|
+
Thumbs.db
|
|
773
|
+
|
|
774
|
+
# Logs
|
|
775
|
+
*.log
|
|
776
|
+
npm-debug.log*
|
|
777
|
+
logs/
|
|
778
|
+
|
|
779
|
+
# Test coverage
|
|
780
|
+
coverage/
|
|
781
|
+
|
|
782
|
+
# Runtime data
|
|
783
|
+
pids/
|
|
784
|
+
*.pid
|
|
785
|
+
*.seed
|
|
786
|
+
*.pid.lock
|
|
787
|
+
|
|
788
|
+
# Docker
|
|
789
|
+
.dockerignore
|
|
790
|
+
|
|
791
|
+
# Secrets
|
|
792
|
+
secrets/
|
|
793
|
+
*.pem
|
|
794
|
+
*.key
|
|
795
|
+
service-account.json
|
|
796
|
+
`;
|
|
797
|
+
fs.writeFileSync(path.join(projectDir, '.gitignore'), content);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Generate deployment files based on selected options
|
|
801
|
+
*/
|
|
802
|
+
async generateDeploymentFiles(projectDir, options) {
|
|
803
|
+
if (options.deployment.includes('docker')) {
|
|
804
|
+
await this.generateDockerfile(projectDir, options);
|
|
805
|
+
await this.generateDockerIgnore(projectDir);
|
|
806
|
+
}
|
|
807
|
+
if (options.deployment.includes('docker-compose')) {
|
|
808
|
+
await this.generateDockerCompose(projectDir, options);
|
|
809
|
+
}
|
|
810
|
+
if (options.deployment.includes('kubernetes')) {
|
|
811
|
+
await this.generateKubernetesManifests(projectDir, options);
|
|
812
|
+
}
|
|
813
|
+
if (options.deployment.includes('railway')) {
|
|
814
|
+
await this.generateRailwayConfig(projectDir, options);
|
|
815
|
+
}
|
|
816
|
+
if (options.deployment.includes('vercel')) {
|
|
817
|
+
await this.generateVercelConfig(projectDir, options);
|
|
818
|
+
}
|
|
819
|
+
if (options.deployment.includes('aws-lambda')) {
|
|
820
|
+
await this.generateLambdaConfig(projectDir, options);
|
|
821
|
+
}
|
|
822
|
+
if (options.deployment.includes('systemd')) {
|
|
823
|
+
await this.generateSystemdService(projectDir, options);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
async generateDockerfile(projectDir, options) {
|
|
827
|
+
const content = `# Multi-stage Docker build for ${options.projectName}
|
|
828
|
+
FROM node:18-alpine AS builder
|
|
829
|
+
|
|
830
|
+
WORKDIR /app
|
|
831
|
+
|
|
832
|
+
# Copy package files
|
|
833
|
+
COPY package*.json ./
|
|
834
|
+
${options.typescript ? 'COPY tsconfig.json ./' : ''}
|
|
835
|
+
|
|
836
|
+
# Install dependencies
|
|
837
|
+
RUN npm ci --only=production && npm cache clean --force
|
|
838
|
+
|
|
839
|
+
# Copy source code
|
|
840
|
+
COPY src/ ./src/
|
|
841
|
+
|
|
842
|
+
# Build application
|
|
843
|
+
${options.typescript ? 'RUN npm run build' : ''}
|
|
844
|
+
|
|
845
|
+
# Production stage
|
|
846
|
+
FROM node:18-alpine AS production
|
|
847
|
+
|
|
848
|
+
# Create non-root user
|
|
849
|
+
RUN addgroup -g 1001 -S agentx && \\
|
|
850
|
+
adduser -S agentx -u 1001
|
|
851
|
+
|
|
852
|
+
WORKDIR /app
|
|
853
|
+
|
|
854
|
+
# Copy built application
|
|
855
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
856
|
+
${options.typescript ?
|
|
857
|
+
'COPY --from=builder /app/dist ./dist' :
|
|
858
|
+
'COPY --from=builder /app/src ./src'}
|
|
859
|
+
COPY --from=builder /app/package*.json ./
|
|
860
|
+
|
|
861
|
+
# Set ownership
|
|
862
|
+
RUN chown -R agentx:agentx /app
|
|
863
|
+
USER agentx
|
|
864
|
+
|
|
865
|
+
# Health check
|
|
866
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
867
|
+
CMD node -e "require('http').get('http://localhost:3000/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"
|
|
868
|
+
|
|
869
|
+
# Expose port
|
|
870
|
+
EXPOSE 3000
|
|
871
|
+
|
|
872
|
+
# Start application
|
|
873
|
+
CMD ["npm", "start"]
|
|
874
|
+
`;
|
|
875
|
+
fs.writeFileSync(path.join(projectDir, 'Dockerfile'), content);
|
|
876
|
+
}
|
|
877
|
+
async generateDockerIgnore(projectDir) {
|
|
878
|
+
const content = `node_modules
|
|
879
|
+
npm-debug.log
|
|
880
|
+
.env
|
|
881
|
+
.env.local
|
|
882
|
+
.git
|
|
883
|
+
.gitignore
|
|
884
|
+
README.md
|
|
885
|
+
Dockerfile
|
|
886
|
+
.dockerignore
|
|
887
|
+
coverage
|
|
888
|
+
.nyc_output
|
|
889
|
+
`;
|
|
890
|
+
fs.writeFileSync(path.join(projectDir, '.dockerignore'), content);
|
|
891
|
+
}
|
|
892
|
+
async generateDockerCompose(projectDir, options) {
|
|
893
|
+
const services = [];
|
|
894
|
+
// Main application service
|
|
895
|
+
services.push(` ${options.projectName}:
|
|
896
|
+
build: .
|
|
897
|
+
ports:
|
|
898
|
+
- "3000:3000"
|
|
899
|
+
environment:
|
|
900
|
+
- NODE_ENV=production
|
|
901
|
+
env_file:
|
|
902
|
+
- .env
|
|
903
|
+
depends_on:${options.integrations.includes('sql') ? '\n - postgres' : ''}${options.integrations.includes('redis') ? '\n - redis' : ''}${options.integrations.includes('mongodb') ? '\n - mongodb' : ''}
|
|
904
|
+
restart: unless-stopped
|
|
905
|
+
networks:
|
|
906
|
+
- agentx-network`);
|
|
907
|
+
// Add database services based on integrations
|
|
908
|
+
if (options.integrations.includes('sql')) {
|
|
909
|
+
services.push(` postgres:
|
|
910
|
+
image: postgres:15-alpine
|
|
911
|
+
environment:
|
|
912
|
+
POSTGRES_DB: agentx
|
|
913
|
+
POSTGRES_USER: agentx
|
|
914
|
+
POSTGRES_PASSWORD: agentx_password
|
|
915
|
+
volumes:
|
|
916
|
+
- postgres_data:/var/lib/postgresql/data
|
|
917
|
+
ports:
|
|
918
|
+
- "5432:5432"
|
|
919
|
+
restart: unless-stopped
|
|
920
|
+
networks:
|
|
921
|
+
- agentx-network`);
|
|
922
|
+
}
|
|
923
|
+
if (options.integrations.includes('redis')) {
|
|
924
|
+
services.push(` redis:
|
|
925
|
+
image: redis:7-alpine
|
|
926
|
+
ports:
|
|
927
|
+
- "6379:6379"
|
|
928
|
+
volumes:
|
|
929
|
+
- redis_data:/data
|
|
930
|
+
restart: unless-stopped
|
|
931
|
+
networks:
|
|
932
|
+
- agentx-network`);
|
|
933
|
+
}
|
|
934
|
+
if (options.integrations.includes('mongodb')) {
|
|
935
|
+
services.push(` mongodb:
|
|
936
|
+
image: mongo:6
|
|
937
|
+
environment:
|
|
938
|
+
MONGO_INITDB_ROOT_USERNAME: agentx
|
|
939
|
+
MONGO_INITDB_ROOT_PASSWORD: agentx_password
|
|
940
|
+
volumes:
|
|
941
|
+
- mongodb_data:/data/db
|
|
942
|
+
ports:
|
|
943
|
+
- "27017:27017"
|
|
944
|
+
restart: unless-stopped
|
|
945
|
+
networks:
|
|
946
|
+
- agentx-network`);
|
|
947
|
+
}
|
|
948
|
+
// Add monitoring if security features are enabled
|
|
949
|
+
if (options.security.includes('audit-logging')) {
|
|
950
|
+
services.push(` # Uncomment for monitoring
|
|
951
|
+
# grafana:
|
|
952
|
+
# image: grafana/grafana:latest
|
|
953
|
+
# ports:
|
|
954
|
+
# - "3001:3000"
|
|
955
|
+
# environment:
|
|
956
|
+
# - GF_SECURITY_ADMIN_PASSWORD=admin
|
|
957
|
+
# networks:
|
|
958
|
+
# - agentx-network`);
|
|
959
|
+
}
|
|
960
|
+
const volumes = [];
|
|
961
|
+
if (options.integrations.includes('sql'))
|
|
962
|
+
volumes.push(' postgres_data:');
|
|
963
|
+
if (options.integrations.includes('redis'))
|
|
964
|
+
volumes.push(' redis_data:');
|
|
965
|
+
if (options.integrations.includes('mongodb'))
|
|
966
|
+
volumes.push(' mongodb_data:');
|
|
967
|
+
const content = `version: '3.8'
|
|
968
|
+
|
|
969
|
+
services:
|
|
970
|
+
${services.join('\n\n')}
|
|
971
|
+
|
|
972
|
+
${volumes.length > 0 ? `volumes:\n${volumes.join('\n')}\n` : ''}
|
|
973
|
+
networks:
|
|
974
|
+
agentx-network:
|
|
975
|
+
driver: bridge
|
|
976
|
+
`;
|
|
977
|
+
fs.writeFileSync(path.join(projectDir, 'docker-compose.yml'), content);
|
|
978
|
+
}
|
|
979
|
+
async generateKubernetesManifests(projectDir, options) {
|
|
980
|
+
const k8sDir = path.join(projectDir, 'k8s');
|
|
981
|
+
if (!fs.existsSync(k8sDir)) {
|
|
982
|
+
fs.mkdirSync(k8sDir, { recursive: true });
|
|
983
|
+
}
|
|
984
|
+
// Namespace
|
|
985
|
+
const namespace = `apiVersion: v1
|
|
986
|
+
kind: Namespace
|
|
987
|
+
metadata:
|
|
988
|
+
name: agentx
|
|
989
|
+
`;
|
|
990
|
+
// Deployment
|
|
991
|
+
const deployment = `apiVersion: apps/v1
|
|
992
|
+
kind: Deployment
|
|
993
|
+
metadata:
|
|
994
|
+
name: ${options.projectName}
|
|
995
|
+
namespace: agentx
|
|
996
|
+
spec:
|
|
997
|
+
replicas: 3
|
|
998
|
+
selector:
|
|
999
|
+
matchLabels:
|
|
1000
|
+
app: ${options.projectName}
|
|
1001
|
+
template:
|
|
1002
|
+
metadata:
|
|
1003
|
+
labels:
|
|
1004
|
+
app: ${options.projectName}
|
|
1005
|
+
spec:
|
|
1006
|
+
containers:
|
|
1007
|
+
- name: ${options.projectName}
|
|
1008
|
+
image: ${options.projectName}:latest
|
|
1009
|
+
ports:
|
|
1010
|
+
- containerPort: 3000
|
|
1011
|
+
env:
|
|
1012
|
+
- name: NODE_ENV
|
|
1013
|
+
value: "production"
|
|
1014
|
+
envFrom:
|
|
1015
|
+
- secretRef:
|
|
1016
|
+
name: ${options.projectName}-secrets
|
|
1017
|
+
resources:
|
|
1018
|
+
requests:
|
|
1019
|
+
memory: "256Mi"
|
|
1020
|
+
cpu: "250m"
|
|
1021
|
+
limits:
|
|
1022
|
+
memory: "512Mi"
|
|
1023
|
+
cpu: "500m"
|
|
1024
|
+
livenessProbe:
|
|
1025
|
+
httpGet:
|
|
1026
|
+
path: /health
|
|
1027
|
+
port: 3000
|
|
1028
|
+
initialDelaySeconds: 30
|
|
1029
|
+
periodSeconds: 10
|
|
1030
|
+
readinessProbe:
|
|
1031
|
+
httpGet:
|
|
1032
|
+
path: /ready
|
|
1033
|
+
port: 3000
|
|
1034
|
+
initialDelaySeconds: 5
|
|
1035
|
+
periodSeconds: 5
|
|
1036
|
+
`;
|
|
1037
|
+
// Service
|
|
1038
|
+
const service = `apiVersion: v1
|
|
1039
|
+
kind: Service
|
|
1040
|
+
metadata:
|
|
1041
|
+
name: ${options.projectName}-service
|
|
1042
|
+
namespace: agentx
|
|
1043
|
+
spec:
|
|
1044
|
+
selector:
|
|
1045
|
+
app: ${options.projectName}
|
|
1046
|
+
ports:
|
|
1047
|
+
- protocol: TCP
|
|
1048
|
+
port: 80
|
|
1049
|
+
targetPort: 3000
|
|
1050
|
+
type: ClusterIP
|
|
1051
|
+
`;
|
|
1052
|
+
// Ingress
|
|
1053
|
+
const ingress = `apiVersion: networking.k8s.io/v1
|
|
1054
|
+
kind: Ingress
|
|
1055
|
+
metadata:
|
|
1056
|
+
name: ${options.projectName}-ingress
|
|
1057
|
+
namespace: agentx
|
|
1058
|
+
annotations:
|
|
1059
|
+
kubernetes.io/ingress.class: nginx
|
|
1060
|
+
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
1061
|
+
spec:
|
|
1062
|
+
tls:
|
|
1063
|
+
- hosts:
|
|
1064
|
+
- ${options.projectName}.yourdomain.com
|
|
1065
|
+
secretName: ${options.projectName}-tls
|
|
1066
|
+
rules:
|
|
1067
|
+
- host: ${options.projectName}.yourdomain.com
|
|
1068
|
+
http:
|
|
1069
|
+
paths:
|
|
1070
|
+
- path: /
|
|
1071
|
+
pathType: Prefix
|
|
1072
|
+
backend:
|
|
1073
|
+
service:
|
|
1074
|
+
name: ${options.projectName}-service
|
|
1075
|
+
port:
|
|
1076
|
+
number: 80
|
|
1077
|
+
`;
|
|
1078
|
+
fs.writeFileSync(path.join(k8sDir, '01-namespace.yaml'), namespace);
|
|
1079
|
+
fs.writeFileSync(path.join(k8sDir, '02-deployment.yaml'), deployment);
|
|
1080
|
+
fs.writeFileSync(path.join(k8sDir, '03-service.yaml'), service);
|
|
1081
|
+
fs.writeFileSync(path.join(k8sDir, '04-ingress.yaml'), ingress);
|
|
1082
|
+
}
|
|
1083
|
+
async generateRailwayConfig(projectDir, options) {
|
|
1084
|
+
const content = `{
|
|
1085
|
+
"build": {
|
|
1086
|
+
"builder": "NIXPACKS"
|
|
1087
|
+
},
|
|
1088
|
+
"deploy": {
|
|
1089
|
+
"startCommand": "npm start",
|
|
1090
|
+
"healthcheckPath": "/health",
|
|
1091
|
+
"healthcheckTimeout": 300,
|
|
1092
|
+
"restartPolicyType": "ON_FAILURE",
|
|
1093
|
+
"restartPolicyMaxRetries": 10
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
`;
|
|
1097
|
+
fs.writeFileSync(path.join(projectDir, 'railway.json'), content);
|
|
1098
|
+
}
|
|
1099
|
+
async generateVercelConfig(projectDir, options) {
|
|
1100
|
+
const content = `{
|
|
1101
|
+
"version": 2,
|
|
1102
|
+
"builds": [
|
|
1103
|
+
{
|
|
1104
|
+
"src": "${options.typescript ? 'dist/index.js' : 'src/index.js'}",
|
|
1105
|
+
"use": "@vercel/node"
|
|
1106
|
+
}
|
|
1107
|
+
],
|
|
1108
|
+
"routes": [
|
|
1109
|
+
{
|
|
1110
|
+
"src": "/(.*)",
|
|
1111
|
+
"dest": "${options.typescript ? 'dist/index.js' : 'src/index.js'}"
|
|
1112
|
+
}
|
|
1113
|
+
],
|
|
1114
|
+
"env": {
|
|
1115
|
+
"NODE_ENV": "production"
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
`;
|
|
1119
|
+
fs.writeFileSync(path.join(projectDir, 'vercel.json'), content);
|
|
1120
|
+
}
|
|
1121
|
+
async generateLambdaConfig(projectDir, options) {
|
|
1122
|
+
const content = `{
|
|
1123
|
+
"name": "${options.projectName}",
|
|
1124
|
+
"description": "AgentX application deployed on AWS Lambda",
|
|
1125
|
+
"runtime": "nodejs18.x",
|
|
1126
|
+
"handler": "${options.typescript ? 'dist/lambda.handler' : 'src/lambda.handler'}",
|
|
1127
|
+
"timeout": 30,
|
|
1128
|
+
"memorySize": 512,
|
|
1129
|
+
"environment": {
|
|
1130
|
+
"NODE_ENV": "production"
|
|
1131
|
+
},
|
|
1132
|
+
"layers": [],
|
|
1133
|
+
"events": [
|
|
1134
|
+
{
|
|
1135
|
+
"http": {
|
|
1136
|
+
"path": "/{proxy+}",
|
|
1137
|
+
"method": "ANY",
|
|
1138
|
+
"cors": true
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
]
|
|
1142
|
+
}
|
|
1143
|
+
`;
|
|
1144
|
+
fs.writeFileSync(path.join(projectDir, 'serverless.yml'), content);
|
|
1145
|
+
// Create Lambda handler
|
|
1146
|
+
const ext = options.typescript ? 'ts' : 'js';
|
|
1147
|
+
const lambdaHandler = `/**
|
|
1148
|
+
* AWS Lambda Handler
|
|
1149
|
+
*/
|
|
1150
|
+
${options.typescript ? "import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';" : ""}
|
|
1151
|
+
|
|
1152
|
+
export const handler = async (event${options.typescript ? ': APIGatewayProxyEvent' : ''}, context${options.typescript ? ': Context' : ''})${options.typescript ? ': Promise<APIGatewayProxyResult>' : ''} => {
|
|
1153
|
+
// Initialize your AgentX runtime here
|
|
1154
|
+
// This is a basic example - adapt based on your needs
|
|
1155
|
+
|
|
1156
|
+
return {
|
|
1157
|
+
statusCode: 200,
|
|
1158
|
+
headers: {
|
|
1159
|
+
'Content-Type': 'application/json',
|
|
1160
|
+
'Access-Control-Allow-Origin': '*',
|
|
1161
|
+
},
|
|
1162
|
+
body: JSON.stringify({
|
|
1163
|
+
message: 'AgentX Lambda function is running',
|
|
1164
|
+
event: event.path,
|
|
1165
|
+
}),
|
|
1166
|
+
};
|
|
1167
|
+
};
|
|
1168
|
+
`;
|
|
1169
|
+
fs.writeFileSync(path.join(projectDir, 'src', `lambda.${ext}`), lambdaHandler);
|
|
1170
|
+
}
|
|
1171
|
+
async generateSystemdService(projectDir, options) {
|
|
1172
|
+
const content = `[Unit]
|
|
1173
|
+
Description=${options.projectName} - AgentX Application
|
|
1174
|
+
After=network.target
|
|
1175
|
+
|
|
1176
|
+
[Service]
|
|
1177
|
+
Type=simple
|
|
1178
|
+
User=agentx
|
|
1179
|
+
WorkingDirectory=/opt/${options.projectName}
|
|
1180
|
+
ExecStart=/usr/bin/npm start
|
|
1181
|
+
Restart=on-failure
|
|
1182
|
+
RestartSec=10
|
|
1183
|
+
StandardOutput=syslog
|
|
1184
|
+
StandardError=syslog
|
|
1185
|
+
SyslogIdentifier=${options.projectName}
|
|
1186
|
+
Environment=NODE_ENV=production
|
|
1187
|
+
EnvironmentFile=/opt/${options.projectName}/.env
|
|
1188
|
+
|
|
1189
|
+
[Install]
|
|
1190
|
+
WantedBy=multi-user.target
|
|
1191
|
+
`;
|
|
1192
|
+
fs.writeFileSync(path.join(projectDir, `${options.projectName}.service`), content);
|
|
1193
|
+
// Create installation script
|
|
1194
|
+
const installScript = `#!/bin/bash
|
|
1195
|
+
# Installation script for ${options.projectName}
|
|
1196
|
+
|
|
1197
|
+
set -e
|
|
1198
|
+
|
|
1199
|
+
echo "Installing ${options.projectName} as systemd service..."
|
|
1200
|
+
|
|
1201
|
+
# Create user
|
|
1202
|
+
sudo useradd -r -s /bin/false agentx || true
|
|
1203
|
+
|
|
1204
|
+
# Create directory
|
|
1205
|
+
sudo mkdir -p /opt/${options.projectName}
|
|
1206
|
+
sudo cp -r . /opt/${options.projectName}/
|
|
1207
|
+
sudo chown -R agentx:agentx /opt/${options.projectName}
|
|
1208
|
+
|
|
1209
|
+
# Install service
|
|
1210
|
+
sudo cp ${options.projectName}.service /etc/systemd/system/
|
|
1211
|
+
sudo systemctl daemon-reload
|
|
1212
|
+
sudo systemctl enable ${options.projectName}
|
|
1213
|
+
|
|
1214
|
+
echo "Installation complete!"
|
|
1215
|
+
echo "Start the service with: sudo systemctl start ${options.projectName}"
|
|
1216
|
+
echo "Check status with: sudo systemctl status ${options.projectName}"
|
|
1217
|
+
`;
|
|
1218
|
+
fs.writeFileSync(path.join(projectDir, 'install.sh'), installScript);
|
|
1219
|
+
// Make install script executable (on Unix systems)
|
|
1220
|
+
try {
|
|
1221
|
+
fs.chmodSync(path.join(projectDir, 'install.sh'), 0o755);
|
|
1222
|
+
}
|
|
1223
|
+
catch (error) {
|
|
1224
|
+
// Ignore on Windows
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Generate security-related files
|
|
1229
|
+
*/
|
|
1230
|
+
async generateSecurityFiles(projectDir, options) {
|
|
1231
|
+
if (options.security.includes('https-only')) {
|
|
1232
|
+
await this.generateSecurityHeaders(projectDir);
|
|
1233
|
+
}
|
|
1234
|
+
if (options.security.includes('input-validation')) {
|
|
1235
|
+
await this.generateValidationSchemas(projectDir, options);
|
|
1236
|
+
}
|
|
1237
|
+
if (options.security.includes('audit-logging')) {
|
|
1238
|
+
await this.generateAuditConfig(projectDir);
|
|
1239
|
+
}
|
|
1240
|
+
// Generate security documentation
|
|
1241
|
+
await this.generateSecurityDocs(projectDir, options);
|
|
1242
|
+
}
|
|
1243
|
+
async generateSecurityHeaders(projectDir) {
|
|
1244
|
+
const ext = 'ts'; // Always generate TypeScript for middleware
|
|
1245
|
+
const content = `/**
|
|
1246
|
+
* Security Headers Middleware
|
|
1247
|
+
* Implements security best practices
|
|
1248
|
+
*/
|
|
1249
|
+
import { Request, Response, NextFunction } from 'express';
|
|
1250
|
+
|
|
1251
|
+
export function securityHeaders(req: Request, res: Response, next: NextFunction): void {
|
|
1252
|
+
// Force HTTPS
|
|
1253
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
1254
|
+
|
|
1255
|
+
// Prevent clickjacking
|
|
1256
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
1257
|
+
|
|
1258
|
+
// Prevent MIME type sniffing
|
|
1259
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
1260
|
+
|
|
1261
|
+
// XSS Protection
|
|
1262
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
1263
|
+
|
|
1264
|
+
// Content Security Policy
|
|
1265
|
+
res.setHeader('Content-Security-Policy',
|
|
1266
|
+
"default-src 'self'; " +
|
|
1267
|
+
"script-src 'self' 'unsafe-inline'; " +
|
|
1268
|
+
"style-src 'self' 'unsafe-inline'; " +
|
|
1269
|
+
"img-src 'self' data: https:; " +
|
|
1270
|
+
"connect-src 'self'; " +
|
|
1271
|
+
"font-src 'self'; " +
|
|
1272
|
+
"object-src 'none'; " +
|
|
1273
|
+
"media-src 'self'; " +
|
|
1274
|
+
"frame-src 'none';"
|
|
1275
|
+
);
|
|
1276
|
+
|
|
1277
|
+
// Referrer Policy
|
|
1278
|
+
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
1279
|
+
|
|
1280
|
+
// Permissions Policy
|
|
1281
|
+
res.setHeader('Permissions-Policy',
|
|
1282
|
+
'camera=(), microphone=(), geolocation=(), payment=()'
|
|
1283
|
+
);
|
|
1284
|
+
|
|
1285
|
+
next();
|
|
1286
|
+
}
|
|
1287
|
+
`;
|
|
1288
|
+
const middlewareDir = path.join(projectDir, 'src', 'middleware');
|
|
1289
|
+
if (!fs.existsSync(middlewareDir)) {
|
|
1290
|
+
fs.mkdirSync(middlewareDir, { recursive: true });
|
|
1291
|
+
}
|
|
1292
|
+
fs.writeFileSync(path.join(middlewareDir, `security.${ext}`), content);
|
|
1293
|
+
}
|
|
1294
|
+
async generateValidationSchemas(projectDir, options) {
|
|
1295
|
+
const ext = options.typescript ? 'ts' : 'js';
|
|
1296
|
+
const content = `/**
|
|
1297
|
+
* Input Validation Schemas
|
|
1298
|
+
* Define validation rules for all inputs
|
|
1299
|
+
*/
|
|
1300
|
+
${options.typescript ? "import Joi from 'joi';" : "const Joi = require('joi');"}
|
|
1301
|
+
|
|
1302
|
+
// Common validation patterns
|
|
1303
|
+
const patterns = {
|
|
1304
|
+
email: Joi.string().email().required(),
|
|
1305
|
+
password: Joi.string().min(8).pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]/).required(),
|
|
1306
|
+
apiKey: Joi.string().alphanum().length(32).required(),
|
|
1307
|
+
uuid: Joi.string().uuid().required(),
|
|
1308
|
+
};
|
|
1309
|
+
|
|
1310
|
+
// Agent configuration validation
|
|
1311
|
+
export const agentConfigSchema = Joi.object({
|
|
1312
|
+
name: Joi.string().alphanum().min(3).max(50).required(),
|
|
1313
|
+
description: Joi.string().max(500),
|
|
1314
|
+
llmProvider: Joi.string().valid('openai', 'anthropic', 'local').required(),
|
|
1315
|
+
model: Joi.string().required(),
|
|
1316
|
+
systemPrompt: Joi.string().max(2000).required(),
|
|
1317
|
+
temperature: Joi.number().min(0).max(2).default(0.7),
|
|
1318
|
+
maxIterations: Joi.number().integer().min(1).max(50).default(10),
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
// Workflow validation
|
|
1322
|
+
export const workflowSchema = Joi.object({
|
|
1323
|
+
id: Joi.string().alphanum().required(),
|
|
1324
|
+
name: Joi.string().min(3).max(100).required(),
|
|
1325
|
+
version: Joi.string().pattern(/^\\d+\\.\\d+\\.\\d+$/).required(),
|
|
1326
|
+
steps: Joi.array().items(Joi.object({
|
|
1327
|
+
id: Joi.string().required(),
|
|
1328
|
+
name: Joi.string().required(),
|
|
1329
|
+
type: Joi.string().valid('tool', 'condition', 'loop').required(),
|
|
1330
|
+
config: Joi.object().required(),
|
|
1331
|
+
})).min(1).required(),
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
// API request validation
|
|
1335
|
+
export const apiRequestSchema = Joi.object({
|
|
1336
|
+
method: Joi.string().valid('GET', 'POST', 'PUT', 'DELETE', 'PATCH').required(),
|
|
1337
|
+
url: Joi.string().uri().required(),
|
|
1338
|
+
headers: Joi.object().pattern(Joi.string(), Joi.string()),
|
|
1339
|
+
body: Joi.any(),
|
|
1340
|
+
timeout: Joi.number().integer().min(1000).max(30000).default(10000),
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
${options.typescript ? 'export default { patterns, agentConfigSchema, workflowSchema, apiRequestSchema };' : 'module.exports = { patterns, agentConfigSchema, workflowSchema, apiRequestSchema };'}
|
|
1344
|
+
`;
|
|
1345
|
+
const schemasDir = path.join(projectDir, 'src', 'schemas');
|
|
1346
|
+
if (!fs.existsSync(schemasDir)) {
|
|
1347
|
+
fs.mkdirSync(schemasDir, { recursive: true });
|
|
1348
|
+
}
|
|
1349
|
+
fs.writeFileSync(path.join(schemasDir, `validation.${ext}`), content);
|
|
1350
|
+
}
|
|
1351
|
+
async generateAuditConfig(projectDir) {
|
|
1352
|
+
const content = `/**
|
|
1353
|
+
* Audit Logging Configuration
|
|
1354
|
+
* Tracks security-relevant events
|
|
1355
|
+
*/
|
|
1356
|
+
|
|
1357
|
+
export interface AuditEvent {
|
|
1358
|
+
timestamp: string;
|
|
1359
|
+
userId?: string;
|
|
1360
|
+
action: string;
|
|
1361
|
+
resource: string;
|
|
1362
|
+
outcome: 'success' | 'failure';
|
|
1363
|
+
details?: Record<string, any>;
|
|
1364
|
+
ipAddress?: string;
|
|
1365
|
+
userAgent?: string;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
export const auditEvents = {
|
|
1369
|
+
// Authentication events
|
|
1370
|
+
LOGIN_SUCCESS: 'auth.login.success',
|
|
1371
|
+
LOGIN_FAILURE: 'auth.login.failure',
|
|
1372
|
+
LOGOUT: 'auth.logout',
|
|
1373
|
+
|
|
1374
|
+
// API access events
|
|
1375
|
+
API_ACCESS: 'api.access',
|
|
1376
|
+
API_KEY_CREATED: 'api.key.created',
|
|
1377
|
+
API_KEY_REVOKED: 'api.key.revoked',
|
|
1378
|
+
|
|
1379
|
+
// Agent events
|
|
1380
|
+
AGENT_CREATED: 'agent.created',
|
|
1381
|
+
AGENT_MODIFIED: 'agent.modified',
|
|
1382
|
+
AGENT_DELETED: 'agent.deleted',
|
|
1383
|
+
AGENT_EXECUTED: 'agent.executed',
|
|
1384
|
+
|
|
1385
|
+
// Workflow events
|
|
1386
|
+
WORKFLOW_CREATED: 'workflow.created',
|
|
1387
|
+
WORKFLOW_EXECUTED: 'workflow.executed',
|
|
1388
|
+
WORKFLOW_FAILED: 'workflow.failed',
|
|
1389
|
+
|
|
1390
|
+
// Security events
|
|
1391
|
+
RATE_LIMIT_EXCEEDED: 'security.rate_limit.exceeded',
|
|
1392
|
+
INVALID_INPUT: 'security.input.invalid',
|
|
1393
|
+
UNAUTHORIZED_ACCESS: 'security.access.unauthorized',
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
export class AuditLogger {
|
|
1397
|
+
static log(event: AuditEvent): void {
|
|
1398
|
+
// In production, send to your logging service
|
|
1399
|
+
console.log('[AUDIT]', JSON.stringify(event));
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
static logAuth(action: string, userId?: string, outcome: 'success' | 'failure' = 'success'): void {
|
|
1403
|
+
this.log({
|
|
1404
|
+
timestamp: new Date().toISOString(),
|
|
1405
|
+
userId,
|
|
1406
|
+
action,
|
|
1407
|
+
resource: 'authentication',
|
|
1408
|
+
outcome,
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
static logApiAccess(endpoint: string, userId?: string, outcome: 'success' | 'failure' = 'success'): void {
|
|
1413
|
+
this.log({
|
|
1414
|
+
timestamp: new Date().toISOString(),
|
|
1415
|
+
userId,
|
|
1416
|
+
action: auditEvents.API_ACCESS,
|
|
1417
|
+
resource: endpoint,
|
|
1418
|
+
outcome,
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
`;
|
|
1423
|
+
const securityDir = path.join(projectDir, 'src', 'security');
|
|
1424
|
+
if (!fs.existsSync(securityDir)) {
|
|
1425
|
+
fs.mkdirSync(securityDir, { recursive: true });
|
|
1426
|
+
}
|
|
1427
|
+
fs.writeFileSync(path.join(securityDir, 'audit.ts'), content);
|
|
1428
|
+
}
|
|
1429
|
+
async generateSecurityDocs(projectDir, options) {
|
|
1430
|
+
const content = `# Security Guide for ${options.projectName}
|
|
1431
|
+
|
|
1432
|
+
## Overview
|
|
1433
|
+
|
|
1434
|
+
This document outlines the security measures implemented in your AgentX application.
|
|
1435
|
+
|
|
1436
|
+
## Implemented Security Features
|
|
1437
|
+
|
|
1438
|
+
${options.security.map(feature => {
|
|
1439
|
+
switch (feature) {
|
|
1440
|
+
case 'https-only':
|
|
1441
|
+
return '### HTTPS Only\n- Strict Transport Security headers\n- Secure cookie settings\n- Content Security Policy';
|
|
1442
|
+
case 'api-keys':
|
|
1443
|
+
return '### API Key Authentication\n- Secure API key generation\n- Key rotation capabilities\n- Rate limiting per key';
|
|
1444
|
+
case 'jwt':
|
|
1445
|
+
return '### JWT Authentication\n- Secure token generation\n- Configurable expiration\n- Refresh token support';
|
|
1446
|
+
case 'rate-limiting':
|
|
1447
|
+
return '### Rate Limiting\n- Per-IP rate limiting\n- Per-user rate limiting\n- Configurable limits';
|
|
1448
|
+
case 'input-validation':
|
|
1449
|
+
return '### Input Validation\n- Schema-based validation\n- SQL injection prevention\n- XSS protection';
|
|
1450
|
+
case 'audit-logging':
|
|
1451
|
+
return '### Audit Logging\n- Security event tracking\n- User action logging\n- Compliance reporting';
|
|
1452
|
+
case 'secrets-manager':
|
|
1453
|
+
return '### Secrets Management\n- External secrets integration\n- Encrypted storage\n- Access control';
|
|
1454
|
+
default:
|
|
1455
|
+
return `### ${feature}\n- Feature implemented`;
|
|
1456
|
+
}
|
|
1457
|
+
}).join('\n\n')}
|
|
1458
|
+
|
|
1459
|
+
## Security Checklist
|
|
1460
|
+
|
|
1461
|
+
### Before Production
|
|
1462
|
+
|
|
1463
|
+
- [ ] Change all default passwords and API keys
|
|
1464
|
+
- [ ] Enable HTTPS with valid SSL certificates
|
|
1465
|
+
- [ ] Configure firewall rules
|
|
1466
|
+
- [ ] Set up monitoring and alerting
|
|
1467
|
+
- [ ] Review and test backup procedures
|
|
1468
|
+
- [ ] Conduct security audit
|
|
1469
|
+
- [ ] Update dependencies to latest versions
|
|
1470
|
+
|
|
1471
|
+
### Environment Variables
|
|
1472
|
+
|
|
1473
|
+
Ensure these environment variables are properly configured:
|
|
1474
|
+
|
|
1475
|
+
${options.security.includes('jwt') ? '- `JWT_SECRET`: Use a strong, random secret key' : ''}
|
|
1476
|
+
${options.security.includes('api-keys') ? '- `MASTER_API_KEY`: Change from default value' : ''}
|
|
1477
|
+
${options.features.includes('credentials') ? '- `CREDENTIAL_ENCRYPTION_KEY`: 32-character encryption key' : ''}
|
|
1478
|
+
|
|
1479
|
+
### Regular Maintenance
|
|
1480
|
+
|
|
1481
|
+
- [ ] Regular security updates
|
|
1482
|
+
- [ ] Log monitoring and analysis
|
|
1483
|
+
- [ ] Access review and cleanup
|
|
1484
|
+
- [ ] Backup testing
|
|
1485
|
+
- [ ] Incident response testing
|
|
1486
|
+
|
|
1487
|
+
## Deployment Security
|
|
1488
|
+
|
|
1489
|
+
${options.deployment.includes('docker') ? `
|
|
1490
|
+
### Docker Security
|
|
1491
|
+
- Use non-root user in containers
|
|
1492
|
+
- Scan images for vulnerabilities
|
|
1493
|
+
- Use minimal base images
|
|
1494
|
+
- Keep containers updated
|
|
1495
|
+
` : ''}
|
|
1496
|
+
|
|
1497
|
+
${options.deployment.includes('kubernetes') ? `
|
|
1498
|
+
### Kubernetes Security
|
|
1499
|
+
- Use network policies
|
|
1500
|
+
- Enable RBAC
|
|
1501
|
+
- Scan container images
|
|
1502
|
+
- Use secrets for sensitive data
|
|
1503
|
+
- Regular cluster updates
|
|
1504
|
+
` : ''}
|
|
1505
|
+
|
|
1506
|
+
## Incident Response
|
|
1507
|
+
|
|
1508
|
+
1. **Detection**: Monitor logs and alerts
|
|
1509
|
+
2. **Assessment**: Determine scope and impact
|
|
1510
|
+
3. **Containment**: Isolate affected systems
|
|
1511
|
+
4. **Recovery**: Restore normal operations
|
|
1512
|
+
5. **Lessons Learned**: Update procedures
|
|
1513
|
+
|
|
1514
|
+
## Contact
|
|
1515
|
+
|
|
1516
|
+
For security issues, contact: security@yourcompany.com
|
|
1517
|
+
`;
|
|
1518
|
+
fs.writeFileSync(path.join(projectDir, 'SECURITY.md'), content);
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Generate payment integration examples
|
|
1522
|
+
*/
|
|
1523
|
+
async generatePaymentExamples(projectDir, options) {
|
|
1524
|
+
const paymentsDir = path.join(projectDir, 'src', 'payments');
|
|
1525
|
+
if (!fs.existsSync(paymentsDir)) {
|
|
1526
|
+
fs.mkdirSync(paymentsDir, { recursive: true });
|
|
1527
|
+
}
|
|
1528
|
+
const ext = options.typescript ? 'ts' : 'js';
|
|
1529
|
+
// Generate payment service
|
|
1530
|
+
await this.generatePaymentService(paymentsDir, ext, options.typescript);
|
|
1531
|
+
// Generate M-Pesa specific example
|
|
1532
|
+
await this.generateMPesaExample(paymentsDir, ext, options.typescript);
|
|
1533
|
+
// Generate payment webhook handler
|
|
1534
|
+
await this.generatePaymentWebhooks(paymentsDir, ext, options.typescript);
|
|
1535
|
+
// Generate payment documentation
|
|
1536
|
+
await this.generatePaymentDocs(projectDir);
|
|
1537
|
+
}
|
|
1538
|
+
async generatePaymentService(paymentsDir, ext, typescript) {
|
|
1539
|
+
const imports = typescript
|
|
1540
|
+
? `import { PaymentProviderRegistry, createPaymentProvider, PaymentRequest, PaymentResult } from 'agentx';
|
|
1541
|
+
import { Logger } from 'agentx';`
|
|
1542
|
+
: `const { PaymentProviderRegistry, createPaymentProvider } = require('agentx');`;
|
|
1543
|
+
const content = `/**
|
|
1544
|
+
* Payment Service
|
|
1545
|
+
* Handles all payment operations
|
|
1546
|
+
*/
|
|
1547
|
+
${imports}
|
|
1548
|
+
|
|
1549
|
+
export class PaymentService {
|
|
1550
|
+
private registry${typescript ? ': PaymentProviderRegistry' : ''};
|
|
1551
|
+
private logger${typescript ? ': Logger' : ''};
|
|
1552
|
+
|
|
1553
|
+
constructor(logger${typescript ? ': Logger' : ''}) {
|
|
1554
|
+
this.logger = logger;
|
|
1555
|
+
this.registry = new PaymentProviderRegistry(logger);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* Initialize payment providers
|
|
1560
|
+
*/
|
|
1561
|
+
async initialize()${typescript ? ': Promise<void>' : ''} {
|
|
1562
|
+
// Initialize Stripe
|
|
1563
|
+
if (process.env.STRIPE_SECRET_KEY) {
|
|
1564
|
+
const stripe = createPaymentProvider('stripe', this.logger);
|
|
1565
|
+
await this.registry.register(stripe, {
|
|
1566
|
+
name: 'stripe',
|
|
1567
|
+
credentials: {
|
|
1568
|
+
apiKey: process.env.STRIPE_SECRET_KEY,
|
|
1569
|
+
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
|
|
1570
|
+
},
|
|
1571
|
+
environment: process.env.NODE_ENV === 'production' ? 'production' : 'test',
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
// Initialize PayPal
|
|
1576
|
+
if (process.env.PAYPAL_CLIENT_ID) {
|
|
1577
|
+
const paypal = createPaymentProvider('paypal', this.logger);
|
|
1578
|
+
await this.registry.register(paypal, {
|
|
1579
|
+
name: 'paypal',
|
|
1580
|
+
credentials: {
|
|
1581
|
+
clientId: process.env.PAYPAL_CLIENT_ID,
|
|
1582
|
+
clientSecret: process.env.PAYPAL_CLIENT_SECRET,
|
|
1583
|
+
},
|
|
1584
|
+
environment: process.env.PAYPAL_MODE || 'sandbox',
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// Initialize M-Pesa
|
|
1589
|
+
if (process.env.MPESA_CONSUMER_KEY) {
|
|
1590
|
+
const mpesa = createPaymentProvider('mpesa', this.logger);
|
|
1591
|
+
await this.registry.register(mpesa, {
|
|
1592
|
+
name: 'mpesa',
|
|
1593
|
+
credentials: {
|
|
1594
|
+
consumerKey: process.env.MPESA_CONSUMER_KEY,
|
|
1595
|
+
consumerSecret: process.env.MPESA_CONSUMER_SECRET,
|
|
1596
|
+
shortcode: process.env.MPESA_SHORTCODE,
|
|
1597
|
+
passkey: process.env.MPESA_PASSKEY,
|
|
1598
|
+
},
|
|
1599
|
+
environment: process.env.MPESA_ENVIRONMENT || 'sandbox',
|
|
1600
|
+
callbackUrl: process.env.MPESA_CALLBACK_URL,
|
|
1601
|
+
webhookUrl: process.env.MPESA_WEBHOOK_URL,
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
this.logger.info('Payment service initialized');
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* Process payment with specified provider
|
|
1610
|
+
*/
|
|
1611
|
+
async processPayment(
|
|
1612
|
+
providerName${typescript ? ': string' : ''},
|
|
1613
|
+
request${typescript ? ': PaymentRequest' : ''}
|
|
1614
|
+
)${typescript ? ': Promise<PaymentResult>' : ''} {
|
|
1615
|
+
const provider = this.registry.get(providerName);
|
|
1616
|
+
if (!provider) {
|
|
1617
|
+
throw new Error(\`Payment provider not found: \${providerName}\`);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
return await provider.processPayment(request);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* Get available payment providers
|
|
1625
|
+
*/
|
|
1626
|
+
getAvailableProviders()${typescript ? ': string[]' : ''} {
|
|
1627
|
+
return this.registry.list();
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* Get payment status
|
|
1632
|
+
*/
|
|
1633
|
+
async getPaymentStatus(
|
|
1634
|
+
providerName${typescript ? ': string' : ''},
|
|
1635
|
+
paymentId${typescript ? ': string' : ''}
|
|
1636
|
+
)${typescript ? ': Promise<string>' : ''} {
|
|
1637
|
+
const provider = this.registry.get(providerName);
|
|
1638
|
+
if (!provider) {
|
|
1639
|
+
throw new Error(\`Payment provider not found: \${providerName}\`);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
return await provider.getPaymentStatus(paymentId);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
`;
|
|
1646
|
+
fs.writeFileSync(path.join(paymentsDir, `PaymentService.${ext}`), content);
|
|
1647
|
+
}
|
|
1648
|
+
async generateMPesaExample(paymentsDir, ext, typescript) {
|
|
1649
|
+
const imports = typescript
|
|
1650
|
+
? `import { PaymentService } from './PaymentService';
|
|
1651
|
+
import { Logger } from 'agentx';`
|
|
1652
|
+
: `const { PaymentService } = require('./PaymentService');`;
|
|
1653
|
+
const content = `/**
|
|
1654
|
+
* M-Pesa Payment Example
|
|
1655
|
+
* Complete working example for M-Pesa STK Push
|
|
1656
|
+
*/
|
|
1657
|
+
${imports}
|
|
1658
|
+
|
|
1659
|
+
export class MPesaPaymentExample {
|
|
1660
|
+
private paymentService${typescript ? ': PaymentService' : ''};
|
|
1661
|
+
private logger${typescript ? ': Logger' : ''};
|
|
1662
|
+
|
|
1663
|
+
constructor(logger${typescript ? ': Logger' : ''}) {
|
|
1664
|
+
this.logger = logger;
|
|
1665
|
+
this.paymentService = new PaymentService(logger);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/**
|
|
1669
|
+
* Initialize M-Pesa payment service
|
|
1670
|
+
*/
|
|
1671
|
+
async initialize()${typescript ? ': Promise<void>' : ''} {
|
|
1672
|
+
await this.paymentService.initialize();
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Process M-Pesa STK Push payment
|
|
1677
|
+
* @param phoneNumber - Customer phone number (254XXXXXXXXX format)
|
|
1678
|
+
* @param amount - Amount in KES
|
|
1679
|
+
* @param accountReference - Account reference (optional)
|
|
1680
|
+
* @param description - Transaction description
|
|
1681
|
+
*/
|
|
1682
|
+
async processMPesaPayment(
|
|
1683
|
+
phoneNumber${typescript ? ': string' : ''},
|
|
1684
|
+
amount${typescript ? ': number' : ''},
|
|
1685
|
+
accountReference${typescript ? ': string' : ''} = 'Payment',
|
|
1686
|
+
description${typescript ? ': string' : ''} = 'Payment for services'
|
|
1687
|
+
)${typescript ? ': Promise<any>' : ''} {
|
|
1688
|
+
try {
|
|
1689
|
+
// Validate phone number format
|
|
1690
|
+
const formattedPhone = this.formatPhoneNumber(phoneNumber);
|
|
1691
|
+
|
|
1692
|
+
const paymentRequest = {
|
|
1693
|
+
amount,
|
|
1694
|
+
currency: 'KES',
|
|
1695
|
+
paymentMethod: {
|
|
1696
|
+
type: 'mobile_money',
|
|
1697
|
+
provider: 'mpesa',
|
|
1698
|
+
details: {
|
|
1699
|
+
phoneNumber: formattedPhone,
|
|
1700
|
+
accountReference,
|
|
1701
|
+
transactionDesc: description,
|
|
1702
|
+
},
|
|
1703
|
+
},
|
|
1704
|
+
metadata: {
|
|
1705
|
+
phoneNumber: formattedPhone,
|
|
1706
|
+
accountReference,
|
|
1707
|
+
},
|
|
1708
|
+
description,
|
|
1709
|
+
callbackUrl: process.env.MPESA_CALLBACK_URL,
|
|
1710
|
+
};
|
|
1711
|
+
|
|
1712
|
+
const result = await this.paymentService.processPayment('mpesa', paymentRequest);
|
|
1713
|
+
|
|
1714
|
+
this.logger.info('M-Pesa payment initiated', {
|
|
1715
|
+
paymentId: result.paymentId,
|
|
1716
|
+
phoneNumber: formattedPhone,
|
|
1717
|
+
amount,
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
return {
|
|
1721
|
+
success: result.success,
|
|
1722
|
+
paymentId: result.paymentId,
|
|
1723
|
+
message: result.success
|
|
1724
|
+
? 'Payment request sent. Please check your phone for M-Pesa prompt.'
|
|
1725
|
+
: 'Payment failed. Please try again.',
|
|
1726
|
+
error: result.error,
|
|
1727
|
+
};
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
this.logger.error('M-Pesa payment error', error);
|
|
1730
|
+
throw error;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
/**
|
|
1735
|
+
* Check M-Pesa payment status
|
|
1736
|
+
*/
|
|
1737
|
+
async checkPaymentStatus(paymentId${typescript ? ': string' : ''})${typescript ? ': Promise<string>' : ''} {
|
|
1738
|
+
try {
|
|
1739
|
+
const status = await this.paymentService.getPaymentStatus('mpesa', paymentId);
|
|
1740
|
+
this.logger.info('M-Pesa payment status checked', { paymentId, status });
|
|
1741
|
+
return status;
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
this.logger.error('M-Pesa status check error', error);
|
|
1744
|
+
throw error;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* Format phone number to M-Pesa format (254XXXXXXXXX)
|
|
1750
|
+
*/
|
|
1751
|
+
private formatPhoneNumber(phone${typescript ? ': string' : ''})${typescript ? ': string' : ''} {
|
|
1752
|
+
// Remove all non-digits
|
|
1753
|
+
let formatted = phone.replace(/\\D/g, '');
|
|
1754
|
+
|
|
1755
|
+
// Handle different formats
|
|
1756
|
+
if (formatted.startsWith('0')) {
|
|
1757
|
+
// Convert 07XXXXXXXX to 2547XXXXXXXX
|
|
1758
|
+
formatted = '254' + formatted.slice(1);
|
|
1759
|
+
} else if (formatted.startsWith('7')) {
|
|
1760
|
+
// Convert 7XXXXXXXX to 2547XXXXXXXX
|
|
1761
|
+
formatted = '254' + formatted;
|
|
1762
|
+
} else if (!formatted.startsWith('254')) {
|
|
1763
|
+
// Assume it needs 254 prefix
|
|
1764
|
+
formatted = '254' + formatted;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
// Validate length (should be 12 digits: 254XXXXXXXXX)
|
|
1768
|
+
if (formatted.length !== 12) {
|
|
1769
|
+
throw new Error('Invalid phone number format. Expected format: 254XXXXXXXXX');
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
return formatted;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* Example usage function
|
|
1777
|
+
*/
|
|
1778
|
+
static async example()${typescript ? ': Promise<void>' : ''} {
|
|
1779
|
+
const logger${typescript ? ': Logger' : ''} = {
|
|
1780
|
+
debug: (msg${typescript ? ': string' : ''}) => console.log(\`[DEBUG] \${msg}\`),
|
|
1781
|
+
info: (msg${typescript ? ': string' : ''}) => console.log(\`[INFO] \${msg}\`),
|
|
1782
|
+
warn: (msg${typescript ? ': string' : ''}) => console.warn(\`[WARN] \${msg}\`),
|
|
1783
|
+
error: (msg${typescript ? ': string' : ''}) => console.error(\`[ERROR] \${msg}\`),
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
const mpesa = new MPesaPaymentExample(logger);
|
|
1787
|
+
await mpesa.initialize();
|
|
1788
|
+
|
|
1789
|
+
// Example: Process payment
|
|
1790
|
+
try {
|
|
1791
|
+
const result = await mpesa.processMPesaPayment(
|
|
1792
|
+
'0712345678', // Phone number
|
|
1793
|
+
100, // Amount in KES
|
|
1794
|
+
'ORDER123', // Account reference
|
|
1795
|
+
'Payment for Order #123'
|
|
1796
|
+
);
|
|
1797
|
+
|
|
1798
|
+
console.log('Payment Result:', result);
|
|
1799
|
+
|
|
1800
|
+
// Check status after a delay
|
|
1801
|
+
if (result.success && result.paymentId) {
|
|
1802
|
+
setTimeout(async () => {
|
|
1803
|
+
const status = await mpesa.checkPaymentStatus(result.paymentId);
|
|
1804
|
+
console.log('Payment Status:', status);
|
|
1805
|
+
}, 5000);
|
|
1806
|
+
}
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
console.error('Payment failed:', error);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// Uncomment to run example
|
|
1814
|
+
// MPesaPaymentExample.example();
|
|
1815
|
+
`;
|
|
1816
|
+
fs.writeFileSync(path.join(paymentsDir, `MPesaExample.${ext}`), content);
|
|
1817
|
+
}
|
|
1818
|
+
async generatePaymentWebhooks(paymentsDir, ext, typescript) {
|
|
1819
|
+
const imports = typescript
|
|
1820
|
+
? `import { Request, Response } from 'express';
|
|
1821
|
+
import { PaymentProviderRegistry } from 'agentx';`
|
|
1822
|
+
: `// const express = require('express');`;
|
|
1823
|
+
const content = `/**
|
|
1824
|
+
* Payment Webhook Handlers
|
|
1825
|
+
* Handle webhooks from payment providers
|
|
1826
|
+
*/
|
|
1827
|
+
${imports}
|
|
1828
|
+
|
|
1829
|
+
export class PaymentWebhookHandler {
|
|
1830
|
+
private registry${typescript ? ': PaymentProviderRegistry' : ''};
|
|
1831
|
+
|
|
1832
|
+
constructor(registry${typescript ? ': PaymentProviderRegistry' : ''}) {
|
|
1833
|
+
this.registry = registry;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* Handle M-Pesa callback webhook
|
|
1838
|
+
*/
|
|
1839
|
+
async handleMPesaCallback(req${typescript ? ': Request' : ''}, res${typescript ? ': Response' : ''})${typescript ? ': Promise<void>' : ''} {
|
|
1840
|
+
try {
|
|
1841
|
+
const payload = req.body;
|
|
1842
|
+
console.log('M-Pesa Callback received:', JSON.stringify(payload, null, 2));
|
|
1843
|
+
|
|
1844
|
+
const mpesa = this.registry.get('mpesa');
|
|
1845
|
+
if (!mpesa) {
|
|
1846
|
+
throw new Error('M-Pesa provider not initialized');
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// Validate and process webhook
|
|
1850
|
+
const isValid = await mpesa.validateWebhook(payload, '');
|
|
1851
|
+
if (!isValid) {
|
|
1852
|
+
return res.status(400).json({ error: 'Invalid webhook' });
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
const result = await mpesa.processWebhook(payload);
|
|
1856
|
+
|
|
1857
|
+
if (result.processed) {
|
|
1858
|
+
// Handle successful payment
|
|
1859
|
+
const callback = payload.Body?.stkCallback;
|
|
1860
|
+
if (callback?.ResultCode === 0) {
|
|
1861
|
+
console.log('Payment successful:', {
|
|
1862
|
+
paymentId: callback.CheckoutRequestID,
|
|
1863
|
+
mpesaReceiptNumber: callback.CallbackMetadata?.Item?.find((item${typescript ? ': any' : ''}) => item.Name === 'MpesaReceiptNumber')?.Value,
|
|
1864
|
+
amount: callback.CallbackMetadata?.Item?.find((item${typescript ? ': any' : ''}) => item.Name === 'Amount')?.Value,
|
|
1865
|
+
phoneNumber: callback.CallbackMetadata?.Item?.find((item${typescript ? ': any' : ''}) => item.Name === 'PhoneNumber')?.Value,
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
// TODO: Update your database with payment success
|
|
1869
|
+
// await updatePaymentStatus(callback.CheckoutRequestID, 'completed');
|
|
1870
|
+
} else {
|
|
1871
|
+
console.log('Payment failed:', {
|
|
1872
|
+
paymentId: callback.CheckoutRequestID,
|
|
1873
|
+
errorCode: callback.ResultCode,
|
|
1874
|
+
errorMessage: callback.ResultDesc,
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1877
|
+
// TODO: Update your database with payment failure
|
|
1878
|
+
// await updatePaymentStatus(callback.CheckoutRequestID, 'failed');
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
res.status(200).json({ message: 'Webhook processed successfully' });
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
console.error('M-Pesa webhook error:', error);
|
|
1885
|
+
res.status(500).json({ error: 'Webhook processing failed' });
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* Handle Stripe webhook
|
|
1891
|
+
*/
|
|
1892
|
+
async handleStripeWebhook(req${typescript ? ': Request' : ''}, res${typescript ? ': Response' : ''})${typescript ? ': Promise<void>' : ''} {
|
|
1893
|
+
try {
|
|
1894
|
+
const signature = req.headers['stripe-signature']${typescript ? ' as string' : ''};
|
|
1895
|
+
const payload = req.body;
|
|
1896
|
+
|
|
1897
|
+
const stripe = this.registry.get('stripe');
|
|
1898
|
+
if (!stripe) {
|
|
1899
|
+
throw new Error('Stripe provider not initialized');
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
const isValid = await stripe.validateWebhook(payload, signature);
|
|
1903
|
+
if (!isValid) {
|
|
1904
|
+
return res.status(400).json({ error: 'Invalid webhook signature' });
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
const result = await stripe.processWebhook(payload);
|
|
1908
|
+
console.log('Stripe webhook processed:', result);
|
|
1909
|
+
|
|
1910
|
+
res.status(200).json({ received: true });
|
|
1911
|
+
} catch (error) {
|
|
1912
|
+
console.error('Stripe webhook error:', error);
|
|
1913
|
+
res.status(500).json({ error: 'Webhook processing failed' });
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* Handle PayPal webhook
|
|
1919
|
+
*/
|
|
1920
|
+
async handlePayPalWebhook(req${typescript ? ': Request' : ''}, res${typescript ? ': Response' : ''})${typescript ? ': Promise<void>' : ''} {
|
|
1921
|
+
try {
|
|
1922
|
+
const payload = req.body;
|
|
1923
|
+
|
|
1924
|
+
const paypal = this.registry.get('paypal');
|
|
1925
|
+
if (!paypal) {
|
|
1926
|
+
throw new Error('PayPal provider not initialized');
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
const result = await paypal.processWebhook(payload);
|
|
1930
|
+
console.log('PayPal webhook processed:', result);
|
|
1931
|
+
|
|
1932
|
+
res.status(200).json({ received: true });
|
|
1933
|
+
} catch (error) {
|
|
1934
|
+
console.error('PayPal webhook error:', error);
|
|
1935
|
+
res.status(500).json({ error: 'Webhook processing failed' });
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
/**
|
|
1941
|
+
* Express.js route setup example
|
|
1942
|
+
*/
|
|
1943
|
+
export function setupPaymentRoutes(app${typescript ? ': any' : ''}, webhookHandler${typescript ? ': PaymentWebhookHandler' : ''})${typescript ? ': void' : ''} {
|
|
1944
|
+
// M-Pesa callback endpoint
|
|
1945
|
+
app.post('/api/payments/mpesa/callback', (req${typescript ? ': Request' : ''}, res${typescript ? ': Response' : ''}) => {
|
|
1946
|
+
webhookHandler.handleMPesaCallback(req, res);
|
|
1947
|
+
});
|
|
1948
|
+
|
|
1949
|
+
// Stripe webhook endpoint
|
|
1950
|
+
app.post('/api/payments/stripe/webhook', (req${typescript ? ': Request' : ''}, res${typescript ? ': Response' : ''}) => {
|
|
1951
|
+
webhookHandler.handleStripeWebhook(req, res);
|
|
1952
|
+
});
|
|
1953
|
+
|
|
1954
|
+
// PayPal webhook endpoint
|
|
1955
|
+
app.post('/api/payments/paypal/webhook', (req${typescript ? ': Request' : ''}, res${typescript ? ': Response' : ''}) => {
|
|
1956
|
+
webhookHandler.handlePayPalWebhook(req, res);
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1959
|
+
`;
|
|
1960
|
+
fs.writeFileSync(path.join(paymentsDir, `WebhookHandler.${ext}`), content);
|
|
1961
|
+
}
|
|
1962
|
+
async generatePaymentDocs(projectDir) {
|
|
1963
|
+
const content = `# Payment Integration Guide
|
|
1964
|
+
|
|
1965
|
+
## Overview
|
|
1966
|
+
|
|
1967
|
+
This project includes complete payment integration with Stripe, PayPal, and M-Pesa (Safaricom). All providers are production-ready and only require API credentials to be configured.
|
|
1968
|
+
|
|
1969
|
+
## Supported Payment Providers
|
|
1970
|
+
|
|
1971
|
+
### 1. M-Pesa (Safaricom)
|
|
1972
|
+
- **Type**: Mobile Money
|
|
1973
|
+
- **Currency**: KES (Kenyan Shillings)
|
|
1974
|
+
- **Features**: STK Push, Payment Status Checking, Webhooks
|
|
1975
|
+
- **Use Case**: Kenya mobile payments
|
|
1976
|
+
|
|
1977
|
+
### 2. Stripe
|
|
1978
|
+
- **Type**: Credit/Debit Cards, Bank Transfers
|
|
1979
|
+
- **Currencies**: USD, EUR, GBP, CAD, AUD, JPY
|
|
1980
|
+
- **Features**: Full payment lifecycle, refunds, webhooks
|
|
1981
|
+
- **Use Case**: Global card payments
|
|
1982
|
+
|
|
1983
|
+
### 3. PayPal
|
|
1984
|
+
- **Type**: Digital Wallet, Cards
|
|
1985
|
+
- **Currencies**: USD, EUR, GBP, CAD, AUD
|
|
1986
|
+
- **Features**: PayPal wallet, card processing, refunds
|
|
1987
|
+
- **Use Case**: Global digital payments
|
|
1988
|
+
|
|
1989
|
+
## Quick Start
|
|
1990
|
+
|
|
1991
|
+
### 1. Configure Environment Variables
|
|
1992
|
+
|
|
1993
|
+
Copy the values from \`.env.example\` to \`.env\` and fill in your credentials:
|
|
1994
|
+
|
|
1995
|
+
\`\`\`bash
|
|
1996
|
+
# M-Pesa Configuration
|
|
1997
|
+
MPESA_CONSUMER_KEY=your-consumer-key
|
|
1998
|
+
MPESA_CONSUMER_SECRET=your-consumer-secret
|
|
1999
|
+
MPESA_SHORTCODE=174379
|
|
2000
|
+
MPESA_PASSKEY=your-passkey
|
|
2001
|
+
MPESA_ENVIRONMENT=sandbox
|
|
2002
|
+
MPESA_CALLBACK_URL=https://yourdomain.com/api/payments/mpesa/callback
|
|
2003
|
+
\`\`\`
|
|
2004
|
+
|
|
2005
|
+
### 2. Initialize Payment Service
|
|
2006
|
+
|
|
2007
|
+
\`\`\`typescript
|
|
2008
|
+
import { PaymentService } from './src/payments/PaymentService';
|
|
2009
|
+
|
|
2010
|
+
const paymentService = new PaymentService(logger);
|
|
2011
|
+
await paymentService.initialize();
|
|
2012
|
+
\`\`\`
|
|
2013
|
+
|
|
2014
|
+
### 3. Process M-Pesa Payment
|
|
2015
|
+
|
|
2016
|
+
\`\`\`typescript
|
|
2017
|
+
import { MPesaPaymentExample } from './src/payments/MPesaExample';
|
|
2018
|
+
|
|
2019
|
+
const mpesa = new MPesaPaymentExample(logger);
|
|
2020
|
+
await mpesa.initialize();
|
|
2021
|
+
|
|
2022
|
+
const result = await mpesa.processMPesaPayment(
|
|
2023
|
+
'0712345678', // Phone number
|
|
2024
|
+
100, // Amount in KES
|
|
2025
|
+
'ORDER123', // Account reference
|
|
2026
|
+
'Payment for services' // Description
|
|
2027
|
+
);
|
|
2028
|
+
|
|
2029
|
+
console.log('Payment initiated:', result);
|
|
2030
|
+
\`\`\`
|
|
2031
|
+
|
|
2032
|
+
## M-Pesa Integration Details
|
|
2033
|
+
|
|
2034
|
+
### Getting M-Pesa Credentials
|
|
2035
|
+
|
|
2036
|
+
1. **Register on Safaricom Developer Portal**
|
|
2037
|
+
- Visit: https://developer.safaricom.co.ke/
|
|
2038
|
+
- Create an account and verify your email
|
|
2039
|
+
|
|
2040
|
+
2. **Create an App**
|
|
2041
|
+
- Go to "My Apps" and create a new app
|
|
2042
|
+
- Select "Lipa Na M-Pesa Online" product
|
|
2043
|
+
- Get your Consumer Key and Consumer Secret
|
|
2044
|
+
|
|
2045
|
+
3. **Get Shortcode and Passkey**
|
|
2046
|
+
- For sandbox: Use test shortcode 174379
|
|
2047
|
+
- For production: Apply for your business shortcode
|
|
2048
|
+
- Passkey is provided when you get your shortcode
|
|
2049
|
+
|
|
2050
|
+
### Phone Number Format
|
|
2051
|
+
|
|
2052
|
+
M-Pesa requires phone numbers in international format:
|
|
2053
|
+
- ✅ Correct: \`254712345678\`
|
|
2054
|
+
- ❌ Wrong: \`0712345678\`, \`+254712345678\`, \`712345678\`
|
|
2055
|
+
|
|
2056
|
+
The \`MPesaExample\` class automatically formats phone numbers for you.
|
|
2057
|
+
|
|
2058
|
+
### Webhook Setup
|
|
2059
|
+
|
|
2060
|
+
1. **Configure Callback URL**
|
|
2061
|
+
- Set \`MPESA_CALLBACK_URL\` to your webhook endpoint
|
|
2062
|
+
- Must be HTTPS in production
|
|
2063
|
+
- Example: \`https://yourdomain.com/api/payments/mpesa/callback\`
|
|
2064
|
+
|
|
2065
|
+
2. **Handle Webhooks**
|
|
2066
|
+
\`\`\`typescript
|
|
2067
|
+
import { PaymentWebhookHandler } from './src/payments/WebhookHandler';
|
|
2068
|
+
|
|
2069
|
+
const webhookHandler = new PaymentWebhookHandler(paymentRegistry);
|
|
2070
|
+
app.post('/api/payments/mpesa/callback', webhookHandler.handleMPesaCallback);
|
|
2071
|
+
\`\`\`
|
|
2072
|
+
|
|
2073
|
+
### Testing M-Pesa
|
|
2074
|
+
|
|
2075
|
+
1. **Sandbox Testing**
|
|
2076
|
+
- Use test credentials from Safaricom
|
|
2077
|
+
- Use test phone numbers: 254708374149, 254711XXXXXX
|
|
2078
|
+
- No real money is charged
|
|
2079
|
+
|
|
2080
|
+
2. **Production Testing**
|
|
2081
|
+
- Use real credentials
|
|
2082
|
+
- Test with small amounts first
|
|
2083
|
+
- Real money will be charged
|
|
2084
|
+
|
|
2085
|
+
## Error Handling
|
|
2086
|
+
|
|
2087
|
+
All payment methods include comprehensive error handling:
|
|
2088
|
+
|
|
2089
|
+
\`\`\`typescript
|
|
2090
|
+
try {
|
|
2091
|
+
const result = await mpesa.processMPesaPayment('0712345678', 100);
|
|
2092
|
+
if (result.success) {
|
|
2093
|
+
console.log('Payment initiated successfully');
|
|
2094
|
+
} else {
|
|
2095
|
+
console.error('Payment failed:', result.error);
|
|
2096
|
+
}
|
|
2097
|
+
} catch (error) {
|
|
2098
|
+
console.error('Payment error:', error.message);
|
|
2099
|
+
}
|
|
2100
|
+
\`\`\`
|
|
2101
|
+
|
|
2102
|
+
## Security Best Practices
|
|
2103
|
+
|
|
2104
|
+
1. **Environment Variables**
|
|
2105
|
+
- Never commit API keys to version control
|
|
2106
|
+
- Use different credentials for sandbox/production
|
|
2107
|
+
- Rotate keys regularly
|
|
2108
|
+
|
|
2109
|
+
2. **Webhook Security**
|
|
2110
|
+
- Validate webhook signatures
|
|
2111
|
+
- Use HTTPS for all webhook URLs
|
|
2112
|
+
- Implement idempotency for webhook processing
|
|
2113
|
+
|
|
2114
|
+
3. **Error Logging**
|
|
2115
|
+
- Log all payment attempts
|
|
2116
|
+
- Monitor failed payments
|
|
2117
|
+
- Set up alerts for unusual activity
|
|
2118
|
+
|
|
2119
|
+
## Production Checklist
|
|
2120
|
+
|
|
2121
|
+
- [ ] M-Pesa production credentials configured
|
|
2122
|
+
- [ ] Webhook URLs are HTTPS
|
|
2123
|
+
- [ ] Error monitoring set up
|
|
2124
|
+
- [ ] Payment logging implemented
|
|
2125
|
+
- [ ] Backup payment method available
|
|
2126
|
+
- [ ] Customer support process defined
|
|
2127
|
+
- [ ] Refund process documented
|
|
2128
|
+
|
|
2129
|
+
## Support
|
|
2130
|
+
|
|
2131
|
+
- **M-Pesa**: developer.safaricom.co.ke
|
|
2132
|
+
- **Stripe**: stripe.com/docs
|
|
2133
|
+
- **PayPal**: developer.paypal.com
|
|
2134
|
+
|
|
2135
|
+
For implementation questions, check the example files in \`src/payments/\`.
|
|
2136
|
+
`;
|
|
2137
|
+
fs.writeFileSync(path.join(projectDir, 'PAYMENTS.md'), content);
|
|
2138
|
+
}
|
|
2139
|
+
getPersonalityPrompt(personality) {
|
|
2140
|
+
const presets = {
|
|
2141
|
+
helpful: 'You are a helpful AI assistant. You are friendly, supportive, and always try to provide useful information.',
|
|
2142
|
+
professional: 'You are a professional AI assistant. You communicate formally and focus on business objectives.',
|
|
2143
|
+
concise: 'You are a concise AI assistant. You provide brief, direct answers without unnecessary elaboration.',
|
|
2144
|
+
creative: 'You are a creative AI assistant. You think outside the box and offer imaginative solutions.',
|
|
2145
|
+
};
|
|
2146
|
+
return presets[personality] || personality;
|
|
2147
|
+
}
|
|
2148
|
+
getDefaultModel(provider) {
|
|
2149
|
+
const models = {
|
|
2150
|
+
openai: 'gpt-4',
|
|
2151
|
+
anthropic: 'claude-3-opus-20240229',
|
|
2152
|
+
local: 'llama2',
|
|
2153
|
+
};
|
|
2154
|
+
return models[provider] || 'gpt-4';
|
|
2155
|
+
}
|
|
2156
|
+
escapeString(str) {
|
|
2157
|
+
return str.replace(/'/g, "\\'").replace(/\n/g, '\\n');
|
|
2158
|
+
}
|
|
2159
|
+
printNextSteps(options) {
|
|
2160
|
+
const deploymentInstructions = this.getDeploymentInstructions(options);
|
|
2161
|
+
console.log(`
|
|
2162
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
2163
|
+
║ 🎉 Success! ║
|
|
2164
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
2165
|
+
|
|
2166
|
+
Next steps:
|
|
2167
|
+
|
|
2168
|
+
cd ${options.projectName}
|
|
2169
|
+
npm install
|
|
2170
|
+
|
|
2171
|
+
# Configure your environment
|
|
2172
|
+
# Edit .env with your API keys and settings
|
|
2173
|
+
|
|
2174
|
+
${options.typescript ? 'npm run dev' : 'npm start'}
|
|
2175
|
+
|
|
2176
|
+
${deploymentInstructions}
|
|
2177
|
+
|
|
2178
|
+
Security Notes:
|
|
2179
|
+
${options.security.includes('https-only') ? ' ✅ HTTPS security headers configured' : ''}
|
|
2180
|
+
${options.security.includes('input-validation') ? ' ✅ Input validation schemas created' : ''}
|
|
2181
|
+
${options.security.includes('jwt') ? ' ✅ JWT authentication ready' : ''}
|
|
2182
|
+
${options.security.includes('rate-limiting') ? ' ✅ Rate limiting configured' : ''}
|
|
2183
|
+
${options.security.includes('audit-logging') ? ' ✅ Audit logging enabled' : ''}
|
|
2184
|
+
|
|
2185
|
+
📚 Documentation:
|
|
2186
|
+
- README.md - Getting started guide
|
|
2187
|
+
- SECURITY.md - Security configuration
|
|
2188
|
+
${options.features.includes('payments') ? ' - PAYMENTS.md - Complete payment integration guide' : ''}
|
|
2189
|
+
${options.deployment.length > 0 ? ' - Deployment files in project root' : ''}
|
|
2190
|
+
|
|
2191
|
+
🔗 Resources:
|
|
2192
|
+
- AgentX Docs: https://agentx.dev/docs
|
|
2193
|
+
- Community: https://github.com/agentx/community
|
|
2194
|
+
`);
|
|
2195
|
+
}
|
|
2196
|
+
getDeploymentInstructions(options) {
|
|
2197
|
+
if (options.deployment.length === 0)
|
|
2198
|
+
return '';
|
|
2199
|
+
const instructions = ['Deployment Options:'];
|
|
2200
|
+
if (options.deployment.includes('docker')) {
|
|
2201
|
+
instructions.push(' 🐳 Docker: npm run docker:build && npm run docker:run');
|
|
2202
|
+
}
|
|
2203
|
+
if (options.deployment.includes('docker-compose')) {
|
|
2204
|
+
instructions.push(' 🐙 Docker Compose: npm run compose:up');
|
|
2205
|
+
}
|
|
2206
|
+
if (options.deployment.includes('kubernetes')) {
|
|
2207
|
+
instructions.push(' ☸️ Kubernetes: npm run k8s:deploy');
|
|
2208
|
+
}
|
|
2209
|
+
if (options.deployment.includes('railway')) {
|
|
2210
|
+
instructions.push(' 🚂 Railway: Connect your GitHub repo at railway.app');
|
|
2211
|
+
}
|
|
2212
|
+
if (options.deployment.includes('vercel')) {
|
|
2213
|
+
instructions.push(' ▲ Vercel: vercel --prod');
|
|
2214
|
+
}
|
|
2215
|
+
if (options.deployment.includes('aws-lambda')) {
|
|
2216
|
+
instructions.push(' λ AWS Lambda: Use serverless framework or AWS SAM');
|
|
2217
|
+
}
|
|
2218
|
+
if (options.deployment.includes('systemd')) {
|
|
2219
|
+
instructions.push(' 🐧 Linux Service: sudo ./install.sh');
|
|
2220
|
+
}
|
|
2221
|
+
return instructions.join('\n');
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
exports.ProjectGenerator = ProjectGenerator;
|
|
2225
|
+
//# sourceMappingURL=InteractiveSetup.js.map
|