@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.
- package/FEAR.js +459 -0
- package/FEARServer.js +280 -0
- package/controllers/agent.js +438 -0
- package/controllers/auth/index.js +345 -0
- package/controllers/auth/token.js +50 -0
- package/controllers/blog.js +105 -0
- package/controllers/brand.js +10 -0
- package/controllers/cart.js +425 -0
- package/controllers/category.js +9 -0
- package/controllers/coupon.js +63 -0
- package/controllers/crud/crud.js +508 -0
- package/controllers/crud/index.js +36 -0
- package/controllers/email.js +34 -0
- package/controllers/enquiry.js +65 -0
- package/controllers/events.js +9 -0
- package/controllers/order.js +125 -0
- package/controllers/payment.js +31 -0
- package/controllers/product.js +147 -0
- package/controllers/review.js +247 -0
- package/controllers/tag.js +10 -0
- package/controllers/task.js +10 -0
- package/controllers/upload.js +41 -0
- package/controllers/user.js +401 -0
- package/index.js +7 -0
- package/libs/agent/index.js +561 -0
- package/libs/agent/modules/ai/ai.js +285 -0
- package/libs/agent/modules/ai/chat.js +518 -0
- package/libs/agent/modules/ai/config.js +688 -0
- package/libs/agent/modules/ai/operations.js +787 -0
- package/libs/agent/modules/analyze/api.js +546 -0
- package/libs/agent/modules/analyze/dorks.js +395 -0
- package/libs/agent/modules/ccard/README.md +454 -0
- package/libs/agent/modules/ccard/audit.js +479 -0
- package/libs/agent/modules/ccard/checker.js +674 -0
- package/libs/agent/modules/ccard/payment-processors.json +16 -0
- package/libs/agent/modules/ccard/validator.js +629 -0
- package/libs/agent/modules/code/analyzer.js +303 -0
- package/libs/agent/modules/code/jquery.js +1093 -0
- package/libs/agent/modules/code/react.js +1536 -0
- package/libs/agent/modules/code/refactor.js +499 -0
- package/libs/agent/modules/crypto/exchange.js +564 -0
- package/libs/agent/modules/net/proxy.js +409 -0
- package/libs/agent/modules/security/cve.js +442 -0
- package/libs/agent/modules/security/monitor.js +360 -0
- package/libs/agent/modules/security/scanner.js +300 -0
- package/libs/agent/modules/security/vulnerability.js +506 -0
- package/libs/agent/modules/security/web.js +465 -0
- package/libs/agent/modules/utils/browser.js +492 -0
- package/libs/agent/modules/utils/colorizer.js +285 -0
- package/libs/agent/modules/utils/manager.js +478 -0
- package/libs/cloud/index.js +228 -0
- package/libs/config/db.js +21 -0
- package/libs/config/validator.js +82 -0
- package/libs/db/index.js +318 -0
- package/libs/emailer/imap.js +126 -0
- package/libs/emailer/info.js +41 -0
- package/libs/emailer/smtp.js +77 -0
- package/libs/handler/async.js +3 -0
- package/libs/handler/error.js +66 -0
- package/libs/handler/index.js +161 -0
- package/libs/logger/index.js +49 -0
- package/libs/logger/morgan.js +24 -0
- package/libs/passport/passport.js +109 -0
- package/libs/search/api.js +384 -0
- package/libs/search/features.js +219 -0
- package/libs/search/service.js +64 -0
- package/libs/swagger/config.js +18 -0
- package/libs/swagger/index.js +35 -0
- package/libs/validator/index.js +254 -0
- package/models/blog.js +31 -0
- package/models/brand.js +12 -0
- package/models/cart.js +14 -0
- package/models/category.js +11 -0
- package/models/coupon.js +9 -0
- package/models/customer.js +0 -0
- package/models/enquiry.js +29 -0
- package/models/events.js +13 -0
- package/models/order.js +94 -0
- package/models/product.js +32 -0
- package/models/review.js +14 -0
- package/models/tag.js +10 -0
- package/models/task.js +11 -0
- package/models/user.js +68 -0
- package/package.json +12 -0
- package/routes/agent.js +615 -0
- package/routes/auth.js +13 -0
- package/routes/blog.js +19 -0
- package/routes/brand.js +15 -0
- package/routes/cart.js +105 -0
- package/routes/category.js +16 -0
- package/routes/coupon.js +15 -0
- package/routes/enquiry.js +14 -0
- package/routes/events.js +16 -0
- package/routes/mail.js +170 -0
- package/routes/order.js +19 -0
- package/routes/product.js +22 -0
- package/routes/review.js +11 -0
- package/routes/task.js +12 -0
- 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();
|