@feardread/fear 1.0.1

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.
Files changed (99) hide show
  1. package/FEAR.js +459 -0
  2. package/FEARServer.js +280 -0
  3. package/controllers/agent.js +438 -0
  4. package/controllers/auth/index.js +345 -0
  5. package/controllers/auth/token.js +50 -0
  6. package/controllers/blog.js +105 -0
  7. package/controllers/brand.js +10 -0
  8. package/controllers/cart.js +425 -0
  9. package/controllers/category.js +9 -0
  10. package/controllers/coupon.js +63 -0
  11. package/controllers/crud/crud.js +508 -0
  12. package/controllers/crud/index.js +36 -0
  13. package/controllers/email.js +34 -0
  14. package/controllers/enquiry.js +65 -0
  15. package/controllers/events.js +9 -0
  16. package/controllers/order.js +125 -0
  17. package/controllers/payment.js +31 -0
  18. package/controllers/product.js +147 -0
  19. package/controllers/review.js +247 -0
  20. package/controllers/tag.js +10 -0
  21. package/controllers/task.js +10 -0
  22. package/controllers/upload.js +41 -0
  23. package/controllers/user.js +401 -0
  24. package/index.js +7 -0
  25. package/libs/agent/index.js +561 -0
  26. package/libs/agent/modules/ai/ai.js +285 -0
  27. package/libs/agent/modules/ai/chat.js +518 -0
  28. package/libs/agent/modules/ai/config.js +688 -0
  29. package/libs/agent/modules/ai/operations.js +787 -0
  30. package/libs/agent/modules/analyze/api.js +546 -0
  31. package/libs/agent/modules/analyze/dorks.js +395 -0
  32. package/libs/agent/modules/ccard/README.md +454 -0
  33. package/libs/agent/modules/ccard/audit.js +479 -0
  34. package/libs/agent/modules/ccard/checker.js +674 -0
  35. package/libs/agent/modules/ccard/payment-processors.json +16 -0
  36. package/libs/agent/modules/ccard/validator.js +629 -0
  37. package/libs/agent/modules/code/analyzer.js +303 -0
  38. package/libs/agent/modules/code/jquery.js +1093 -0
  39. package/libs/agent/modules/code/react.js +1536 -0
  40. package/libs/agent/modules/code/refactor.js +499 -0
  41. package/libs/agent/modules/crypto/exchange.js +564 -0
  42. package/libs/agent/modules/net/proxy.js +409 -0
  43. package/libs/agent/modules/security/cve.js +442 -0
  44. package/libs/agent/modules/security/monitor.js +360 -0
  45. package/libs/agent/modules/security/scanner.js +300 -0
  46. package/libs/agent/modules/security/vulnerability.js +506 -0
  47. package/libs/agent/modules/security/web.js +465 -0
  48. package/libs/agent/modules/utils/browser.js +492 -0
  49. package/libs/agent/modules/utils/colorizer.js +285 -0
  50. package/libs/agent/modules/utils/manager.js +478 -0
  51. package/libs/cloud/index.js +228 -0
  52. package/libs/config/db.js +21 -0
  53. package/libs/config/validator.js +82 -0
  54. package/libs/db/index.js +318 -0
  55. package/libs/emailer/imap.js +126 -0
  56. package/libs/emailer/info.js +41 -0
  57. package/libs/emailer/smtp.js +77 -0
  58. package/libs/handler/async.js +3 -0
  59. package/libs/handler/error.js +66 -0
  60. package/libs/handler/index.js +161 -0
  61. package/libs/logger/index.js +49 -0
  62. package/libs/logger/morgan.js +24 -0
  63. package/libs/passport/passport.js +109 -0
  64. package/libs/search/api.js +384 -0
  65. package/libs/search/features.js +219 -0
  66. package/libs/search/service.js +64 -0
  67. package/libs/swagger/config.js +18 -0
  68. package/libs/swagger/index.js +35 -0
  69. package/libs/validator/index.js +254 -0
  70. package/models/blog.js +31 -0
  71. package/models/brand.js +12 -0
  72. package/models/cart.js +14 -0
  73. package/models/category.js +11 -0
  74. package/models/coupon.js +9 -0
  75. package/models/customer.js +0 -0
  76. package/models/enquiry.js +29 -0
  77. package/models/events.js +13 -0
  78. package/models/order.js +94 -0
  79. package/models/product.js +32 -0
  80. package/models/review.js +14 -0
  81. package/models/tag.js +10 -0
  82. package/models/task.js +11 -0
  83. package/models/user.js +68 -0
  84. package/package.json +12 -0
  85. package/routes/agent.js +615 -0
  86. package/routes/auth.js +13 -0
  87. package/routes/blog.js +19 -0
  88. package/routes/brand.js +15 -0
  89. package/routes/cart.js +105 -0
  90. package/routes/category.js +16 -0
  91. package/routes/coupon.js +15 -0
  92. package/routes/enquiry.js +14 -0
  93. package/routes/events.js +16 -0
  94. package/routes/mail.js +170 -0
  95. package/routes/order.js +19 -0
  96. package/routes/product.js +22 -0
  97. package/routes/review.js +11 -0
  98. package/routes/task.js +12 -0
  99. package/routes/user.js +17 -0
package/FEARServer.js ADDED
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path');
4
+ const express = require('express');
5
+ require("dotenv").config();
6
+
7
+ const FearServer = (function () {
8
+ // Private constants
9
+ const DEFAULT_PATHS = {
10
+ root: path.resolve(),
11
+ app: '/backend/dashboard/build',
12
+ build: 'backend/dashboard/build'
13
+ };
14
+
15
+ const DEFAULT_PORT = 4000;
16
+ const SHUTDOWN_TIMEOUT = 10000; // 10 seconds
17
+
18
+ // Constructor
19
+ function FearServer() {
20
+ this.fear = null;
21
+ this.server = null;
22
+ this.Router = null;
23
+ this.isShuttingDown = false;
24
+ this.rootDir = path.resolve();
25
+ }
26
+
27
+ FearServer.prototype = {
28
+ constructor: FearServer,
29
+
30
+ /**
31
+ * Configure static file serving for React SPA
32
+ * @param {string} root - Root directory path
33
+ * @param {string} app - App directory path
34
+ * @param {string} build - Build directory path
35
+ * @param {string} basePath - Base path for the app (e.g., '/fear/sites/ghap')
36
+ */
37
+ setupStaticFiles(root, app, build, basePath = '') {
38
+ this.rootDir = root || path.resolve();
39
+ const buildPath = path.join(this.rootDir, app);
40
+ const indexPath = path.resolve(this.rootDir, build, "index.html");
41
+
42
+ const normalizedBasePath = basePath
43
+ ? `/${basePath.replace(/^\/+|\/+$/g, '')}`
44
+ : '';
45
+
46
+
47
+ this.fear.getLogger().info(`Serving static files from: ${buildPath}`);
48
+ this.fear.getLogger().info(`Base URL path: ${normalizedBasePath || '/'}`);
49
+
50
+ this.fear.getApp().use(
51
+ normalizedBasePath,
52
+ express.static(buildPath, {
53
+ index: false,
54
+ fallthrough: true
55
+ })
56
+ );
57
+
58
+ this.fear.getApp().get(`${normalizedBasePath}/*`, (req, res) => {
59
+ res.sendFile(indexPath, (err) => {
60
+ if (err) {
61
+ this.fear.getLogger().error('Error serving index.html:', err);
62
+ res.status(500).send('Internal Server Error');
63
+ }
64
+ });
65
+ });
66
+ },
67
+
68
+ /**
69
+ * Setup process event handlers for graceful shutdown
70
+ */
71
+ setupProcessHandlers() {
72
+ // Handle unhandled promise rejections
73
+ process.on("unhandledRejection", (reason, promise) => {
74
+ console.log('Unhandled Rejection at:', promise, 'reason:', reason);
75
+ this.fear.getLogger().error('Unhandled Rejection at:', promise, 'reason:', reason);
76
+ //this.gracefulShutdown('unhandledRejection');
77
+ });
78
+
79
+ // Handle uncaught exceptions
80
+ process.on("uncaughtException", (err) => {
81
+ this.fear.getLogger().error('Uncaught Exception:', err);
82
+ this.gracefulShutdown('uncaughtException');
83
+ });
84
+
85
+ // Handle termination signals
86
+ process.on('SIGTERM', () => {
87
+ this.fear.getLogger().info('SIGTERM received, starting graceful shutdown');
88
+ this.gracefulShutdown('SIGTERM');
89
+ });
90
+
91
+ process.on('SIGINT', () => {
92
+ this.fear.getLogger().info('SIGINT received, starting graceful shutdown');
93
+ this.gracefulShutdown('SIGINT');
94
+ });
95
+ },
96
+
97
+ /**
98
+ * Initialize database connection
99
+ */
100
+ initializeDatabase() {
101
+ return new Promise((resolve, reject) => {
102
+ try {
103
+ this.fear.getDatabase().connect(this.fear.getEnvironment(), (err) => {
104
+ if (err) {
105
+ this.fear.getLogger().error('Database initialization failed:', err);
106
+ return reject(err);
107
+ }
108
+
109
+ this.fear.getLogger().info('Database initialized successfully');
110
+ resolve();
111
+ });
112
+ } catch (error) {
113
+ this.fear.getLogger().error('Database setup error:', error);
114
+ reject(error);
115
+ }
116
+ });
117
+ },
118
+
119
+ /**
120
+ * Start HTTP server on specified port
121
+ */
122
+ startHttpServer(port) {
123
+ return new Promise((resolve, reject) => {
124
+ const server = this.fear.getApp().listen(port, (err) => {
125
+ if (err) {
126
+ this.fear.getLogger().error(`Failed to start server on port ${port}:`, err);
127
+ return reject(err);
128
+ }
129
+ resolve(server);
130
+ });
131
+
132
+ server.on('error', (err) => {
133
+ if (err.code === 'EADDRINUSE') {
134
+ this.fear.getLogger().error(`Port ${port} is already in use`);
135
+ } else {
136
+ this.fear.getLogger().error('Server error:', err);
137
+ }
138
+ reject(err);
139
+ });
140
+ });
141
+ },
142
+
143
+ /**
144
+ * Perform graceful shutdown
145
+ */
146
+ gracefulShutdown(signal) {
147
+ if (this.isShuttingDown) {
148
+ this.fear.getLogger().warn('Shutdown already in progress...');
149
+ return Promise.resolve();
150
+ }
151
+
152
+ this.isShuttingDown = true;
153
+ this.fear.getLogger().info(`Graceful shutdown initiated by: ${signal}`);
154
+
155
+ // Force shutdown after timeout
156
+ const forceShutdownTimeout = setTimeout(() => {
157
+ this.fear.getLogger().error('Forced shutdown after timeout');
158
+ process.exit(1);
159
+ }, SHUTDOWN_TIMEOUT);
160
+
161
+ // Close server connections
162
+ const closeServerPromise = this.server
163
+ ? new Promise((resolve) => this.server.close(resolve))
164
+ : Promise.resolve();
165
+
166
+ return closeServerPromise
167
+ .then(() => {
168
+ // Shutdown FEAR application
169
+ if (this.fear && typeof this.fear.shutdown === 'function') {
170
+ return this.fear.shutdown();
171
+ }
172
+ })
173
+ .then(() => {
174
+ clearTimeout(forceShutdownTimeout);
175
+ this.fear.getLogger().info('Graceful shutdown completed');
176
+ process.exit(0);
177
+ })
178
+ .catch((error) => {
179
+ this.fear.getLogger().error('Error during shutdown:', error);
180
+ process.exit(1);
181
+ });
182
+ },
183
+
184
+ /**
185
+ * Initialize FEAR application
186
+ */
187
+ initialize(paths = DEFAULT_PATHS) {
188
+ try {
189
+ // Import FEAR after dotenv is configured
190
+ const FearFactory = require("./FEAR");
191
+ this.fear = new FearFactory();
192
+ this.Router = this.fear.Router;
193
+
194
+ this.setupStaticFiles(paths.root, paths.app, paths.build, paths.basePath);
195
+ this.setupProcessHandlers();
196
+
197
+ return Promise.resolve(this.fear);
198
+ } catch (error) {
199
+ console.error('Failed to initialize FEAR application:', error);
200
+ process.exit(1);
201
+ }
202
+ },
203
+
204
+ /**
205
+ * Start the server
206
+ */
207
+ startServer() {
208
+ const port = this.fear.getApp().get("PORT") || DEFAULT_PORT;
209
+ const logger = this.fear.getLogger();
210
+
211
+ // Display logo
212
+ if (this.fear.logo) {
213
+ logger.warn(this.fear.logo);
214
+ }
215
+
216
+ // Initialize database connection
217
+ return this.initializeDatabase()
218
+ .then(() => {
219
+ // Start the HTTP server
220
+ return this.startHttpServer(port);
221
+ })
222
+ .then((server) => {
223
+ this.server = server;
224
+ logger.info(`FEAR API Initialized :: Port ${port}`);
225
+ return server;
226
+ })
227
+ .catch((error) => {
228
+ logger.error('Failed to start server:', error);
229
+ throw error;
230
+ });
231
+ },
232
+
233
+ /**
234
+ * Get logger instance
235
+ */
236
+ getLogger() {
237
+ return this.fear ? this.fear.getLogger() : null;
238
+ },
239
+
240
+ /**
241
+ * Get FEAR instance
242
+ */
243
+ getFear() {
244
+ return this.fear;
245
+ },
246
+
247
+ /**
248
+ * Get HTTP server instance
249
+ */
250
+ getServer() {
251
+ return this.server;
252
+ },
253
+
254
+ /**
255
+ * Get Router instance
256
+ */
257
+ getRouter() {
258
+ return this.Router;
259
+ },
260
+
261
+ /**
262
+ * Check if server is shutting down
263
+ */
264
+ getIsShuttingDown() {
265
+ return this.isShuttingDown;
266
+ },
267
+
268
+ /**
269
+ * Get root directory path
270
+ */
271
+ getRootDir() {
272
+ return this.rootDir;
273
+ }
274
+ };
275
+
276
+ return FearServer;
277
+
278
+ })();
279
+
280
+ module.exports = FearServer;
@@ -0,0 +1,438 @@
1
+ const SecurityAgent = require('../libs/agent');
2
+
3
+ class AgentController {
4
+ constructor() {
5
+ this.agentInstance = null;
6
+ this.isInitialized = false;
7
+ }
8
+
9
+ /**
10
+ * Get or create agent instance (singleton pattern)
11
+ */
12
+ getInstance() {
13
+ if (!this.agentInstance) {
14
+ this.agentInstance = new SecurityAgent();
15
+ this.isInitialized = false;
16
+ }
17
+ return this.agentInstance;
18
+ }
19
+
20
+ /**
21
+ * Initialize the security agent
22
+ */
23
+ async initialize(req, res, handler, logger) {
24
+ try {
25
+ const agent = this.getInstance();
26
+
27
+ if (this.isInitialized) {
28
+ return res.status(200).json({
29
+ success: true,
30
+ message: 'Agent already initialized',
31
+ initialized: true
32
+ });
33
+ }
34
+
35
+ // Agent auto-loads modules in constructor
36
+ this.isInitialized = true;
37
+
38
+ logger.info('Agent initialized via API');
39
+
40
+ return handler.success(res, {
41
+ success: true,
42
+ message: 'Agent initialized successfully',
43
+ initialized: true,
44
+ modulesLoaded: Object.keys(agent.modules).length,
45
+ commandsRegistered: Object.keys(agent.commands).length
46
+ });
47
+
48
+ } catch (error) {
49
+ logger.error('Agent initialization error:', error);
50
+ return new handler.error(res, error.message || 'Failed to initialize agent', 500);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Execute a single command
56
+ */
57
+ async executeCommand(req, res, handler, logger) {
58
+ try {
59
+ const { command, args } = req.body;
60
+
61
+ if (!command) {
62
+ return handler.error(res, 'Command is required', 400);
63
+ }
64
+
65
+ const agent = this.getInstance();
66
+
67
+ if (!this.isInitialized) {
68
+ this.isInitialized = true;
69
+ }
70
+
71
+ // Check if command exists
72
+ if (!agent.commands[command]) {
73
+ return handler.error(res, `Unknown command: ${command}`, 400);
74
+ }
75
+
76
+ // Parse args
77
+ const parsedArgs = Array.isArray(args) ? args : (args ? [args] : []);
78
+
79
+ // Execute command and capture output
80
+ const output = await this.captureCommandOutput(
81
+ () => agent.commands[command](parsedArgs)
82
+ );
83
+
84
+ logger.info(`Agent command executed: ${command}`);
85
+
86
+ return handler.success(res, {
87
+ success: true,
88
+ command,
89
+ output,
90
+ executedAt: new Date().toISOString()
91
+ });
92
+
93
+ } catch (error) {
94
+ logger.error('Agent command execution error:', error);
95
+ return new handler.error(res, error.message || 'Command execution failed', 500);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Execute multiple commands in sequence
101
+ */
102
+ async executeBatch(req, res, handler, logger) {
103
+ try {
104
+ const { commands } = req.body;
105
+
106
+ if (!Array.isArray(commands) || commands.length === 0) {
107
+ return handler.error(res, 'Commands array is required', 400);
108
+ }
109
+
110
+ const agent = this.getInstance();
111
+
112
+ if (!this.isInitialized) {
113
+ this.isInitialized = true;
114
+ }
115
+
116
+ const results = [];
117
+
118
+ for (const cmdConfig of commands) {
119
+ const { command, args } = cmdConfig;
120
+
121
+ if (!command) {
122
+ results.push({
123
+ success: false,
124
+ error: 'Command name is required'
125
+ });
126
+ continue;
127
+ }
128
+
129
+ if (!agent.commands[command]) {
130
+ results.push({
131
+ success: false,
132
+ command,
133
+ error: `Unknown command: ${command}`
134
+ });
135
+ continue;
136
+ }
137
+
138
+ try {
139
+ const parsedArgs = Array.isArray(args) ? args : (args ? [args] : []);
140
+ const output = await this.captureCommandOutput(
141
+ () => agent.commands[command](parsedArgs)
142
+ );
143
+
144
+ results.push({
145
+ success: true,
146
+ command,
147
+ output
148
+ });
149
+ } catch (error) {
150
+ results.push({
151
+ success: false,
152
+ command,
153
+ error: error.message
154
+ });
155
+ }
156
+ }
157
+
158
+ logger.info(`Agent batch execution: ${commands.length} commands`);
159
+
160
+ return handler.success(res, {
161
+ success: true,
162
+ totalCommands: commands.length,
163
+ results,
164
+ executedAt: new Date().toISOString()
165
+ });
166
+
167
+ } catch (error) {
168
+ logger.error('Agent batch execution error:', error);
169
+ return handler.error(res, error.message || 'Batch execution failed', 500);
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Get all available commands
175
+ */
176
+ async getCommands(req, res, handler, logger) {
177
+ try {
178
+ const agent = this.getInstance();
179
+
180
+ if (!this.isInitialized) {
181
+ this.isInitialized = true;
182
+ }
183
+
184
+ const commands = {};
185
+
186
+ // Build command list with descriptions
187
+ Object.entries(agent.mappings).forEach(([cmd, config]) => {
188
+ commands[cmd] = {
189
+ description: config.description,
190
+ module: config.module,
191
+ method: config.method
192
+ };
193
+ });
194
+
195
+ // Add built-in commands
196
+ const builtInCommands = {
197
+ 'help': { description: 'Show help information', module: 'core', method: 'showHelp' },
198
+ 'status': { description: 'Show system status', module: 'core', method: 'showStatus' },
199
+ 'history': { description: 'Show command history', module: 'core', method: 'showHistory' },
200
+ 'clear': { description: 'Clear screen', module: 'core', method: 'clearScreen' },
201
+ 'banner': { description: 'Show banner', module: 'core', method: 'showBanner' },
202
+ 'version': { description: 'Show version info', module: 'core', method: 'showVersion' },
203
+ 'tips': { description: 'Show tips and tricks', module: 'core', method: 'showTips' },
204
+ 'exit': { description: 'Exit agent', module: 'core', method: 'exit' }
205
+ };
206
+
207
+ Object.assign(commands, builtInCommands);
208
+
209
+ return handler.success(res, {
210
+ success: true,
211
+ totalCommands: Object.keys(commands).length,
212
+ commands
213
+ });
214
+
215
+ } catch (error) {
216
+ logger.error('Error fetching commands:', error);
217
+ return handler.error(res, 'Failed to fetch commands', 500);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Check if a specific command exists
223
+ */
224
+ async checkCommand(req, res, handler, logger) {
225
+ try {
226
+ const { command } = req.params;
227
+ const agent = this.getInstance();
228
+
229
+ if (!this.isInitialized) {
230
+ this.isInitialized = true;
231
+ }
232
+
233
+ const exists = !!agent.commands[command];
234
+ const config = agent.mappings[command];
235
+
236
+ return handler.success(res, {
237
+ success: true,
238
+ command,
239
+ exists,
240
+ details: config || null
241
+ });
242
+
243
+ } catch (error) {
244
+ logger.error('Error checking command:', error);
245
+ return handler.error(res, 'Failed to check command', 500);
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Get agent and module status
251
+ */
252
+ async getStatus(req, res, handler, logger) {
253
+ try {
254
+ const agent = this.getInstance();
255
+
256
+ if (!this.isInitialized) {
257
+ return handler.success(res, {
258
+ success: true,
259
+ initialized: false,
260
+ message: 'Agent not initialized'
261
+ });
262
+ }
263
+
264
+ // Get module status
265
+ const modules = {};
266
+ agent.definitions.forEach(moduleDef => {
267
+ const module = agent.modules[moduleDef.name];
268
+ modules[moduleDef.name] = {
269
+ displayName: moduleDef.displayName,
270
+ loaded: !!module,
271
+ status: module ? 'ready' : 'unavailable'
272
+ };
273
+
274
+ // Special handling for AI modules
275
+ if (moduleDef.name === 'aiAnalyzer' && module) {
276
+ modules[moduleDef.name].configured = module.isConfigured ? module.isConfigured() : false;
277
+ modules[moduleDef.name].provider = module.getProviderName ? module.getProviderName() : null;
278
+ }
279
+ if (moduleDef.name === 'aiChat' && module) {
280
+ modules[moduleDef.name].configured = module.isConfigured ? module.isConfigured() : false;
281
+ }
282
+ });
283
+
284
+ // Get background services status
285
+ const services = {};
286
+ agent.backgroundServices.forEach((service, name) => {
287
+ services[name] = agent.serviceStatus.get(name) || 'stopped';
288
+ });
289
+
290
+ const version = this.getVersionInfo(agent);
291
+
292
+ return handler.success(res, {
293
+ success: true,
294
+ initialized: true,
295
+ version,
296
+ modules,
297
+ services,
298
+ stats: {
299
+ modulesLoaded: Object.keys(agent.modules).length,
300
+ commandsAvailable: Object.keys(agent.commands).length,
301
+ servicesRunning: Array.from(agent.serviceStatus.values()).filter(s => s === 'running').length,
302
+ commandHistory: agent.commandHistory.length
303
+ }
304
+ });
305
+
306
+ } catch (error) {
307
+ logger.error('Error fetching agent status:', error);
308
+ return handler.error(res, 'Failed to fetch status', 500);
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Get command history
314
+ */
315
+ async getHistory(req, res, handler, logger) {
316
+ try {
317
+ const { limit } = req.query;
318
+ const agent = this.getInstance();
319
+
320
+ if (!this.isInitialized) {
321
+ return handler.success(res, {
322
+ success: true,
323
+ history: [],
324
+ message: 'Agent not initialized'
325
+ });
326
+ }
327
+
328
+ const limitNum = limit ? parseInt(limit) : 20;
329
+ const history = agent.commandHistory.slice(-limitNum);
330
+
331
+ return handler.success(res, {
332
+ success: true,
333
+ total: agent.commandHistory.length,
334
+ limit: limitNum,
335
+ history
336
+ });
337
+
338
+ } catch (error) {
339
+ logger.error('Error fetching history:', error);
340
+ return handler.error(res, 'Failed to fetch history', 500);
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Get agent version information
346
+ */
347
+ async getVersion(req, res, handler, logger) {
348
+ try {
349
+ const agent = this.getInstance();
350
+ const version = this.getVersionInfo(agent);
351
+
352
+ return handler.success(res, {
353
+ success: true,
354
+ ...version
355
+ });
356
+
357
+ } catch (error) {
358
+ logger.error('Error fetching version:', error);
359
+ return handler.error(res, 'Failed to fetch version', 500);
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Shutdown the agent
365
+ */
366
+ async shutdown(req, res, handler, logger) {
367
+ try {
368
+ const agent = this.getInstance();
369
+
370
+ // Stop all background services
371
+ const stoppedServices = [];
372
+ agent.backgroundServices.forEach((service, name) => {
373
+ if (agent.serviceStatus.get(name) === 'running') {
374
+ agent.stopService(name);
375
+ stoppedServices.push(name);
376
+ }
377
+ });
378
+
379
+ // Cleanup modules
380
+ if (agent.modules.trafficMonitor && agent.modules.trafficMonitor.stopMonitoring) {
381
+ agent.modules.trafficMonitor.stopMonitoring();
382
+ }
383
+
384
+ this.isInitialized = false;
385
+ this.agentInstance = null;
386
+
387
+ logger.info('Agent shutdown via API');
388
+
389
+ return handler.success(res, {
390
+ success: true,
391
+ message: 'Agent shutdown successfully',
392
+ servicesStopped: stoppedServices
393
+ });
394
+
395
+ } catch (error) {
396
+ logger.error('Agent shutdown error:', error);
397
+ return handler.error(res, 'Failed to shutdown agent', 500);
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Helper: Capture command output
403
+ */
404
+ async captureCommandOutput(commandFn) {
405
+ const originalLog = console.log;
406
+ const output = [];
407
+
408
+ console.log = (...args) => {
409
+ output.push(args.map(arg =>
410
+ typeof arg === 'string' ? arg : JSON.stringify(arg)
411
+ ).join(' '));
412
+ };
413
+
414
+ try {
415
+ await commandFn();
416
+ return output.join('\n');
417
+ } finally {
418
+ console.log = originalLog;
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Helper: Get version info
424
+ */
425
+ getVersionInfo(agent) {
426
+ return {
427
+ version: '2.4.0',
428
+ nodeVersion: process.version,
429
+ platform: process.platform,
430
+ architecture: process.arch,
431
+ modulesLoaded: Object.keys(agent.modules).length,
432
+ commandsAvailable: Object.keys(agent.commands).length
433
+ };
434
+ }
435
+ }
436
+
437
+ // Export singleton instance
438
+ module.exports = new AgentController();