@loxia-labs/loxia-autopilot-one 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/LICENSE +267 -0
- package/README.md +509 -0
- package/bin/cli.js +117 -0
- package/package.json +94 -0
- package/scripts/install-scanners.js +236 -0
- package/src/analyzers/CSSAnalyzer.js +297 -0
- package/src/analyzers/ConfigValidator.js +690 -0
- package/src/analyzers/ESLintAnalyzer.js +320 -0
- package/src/analyzers/JavaScriptAnalyzer.js +261 -0
- package/src/analyzers/PrettierFormatter.js +247 -0
- package/src/analyzers/PythonAnalyzer.js +266 -0
- package/src/analyzers/SecurityAnalyzer.js +729 -0
- package/src/analyzers/TypeScriptAnalyzer.js +247 -0
- package/src/analyzers/codeCloneDetector/analyzer.js +344 -0
- package/src/analyzers/codeCloneDetector/detector.js +203 -0
- package/src/analyzers/codeCloneDetector/index.js +160 -0
- package/src/analyzers/codeCloneDetector/parser.js +199 -0
- package/src/analyzers/codeCloneDetector/reporter.js +148 -0
- package/src/analyzers/codeCloneDetector/scanner.js +59 -0
- package/src/core/agentPool.js +1474 -0
- package/src/core/agentScheduler.js +2147 -0
- package/src/core/contextManager.js +709 -0
- package/src/core/messageProcessor.js +732 -0
- package/src/core/orchestrator.js +548 -0
- package/src/core/stateManager.js +877 -0
- package/src/index.js +631 -0
- package/src/interfaces/cli.js +549 -0
- package/src/interfaces/webServer.js +2162 -0
- package/src/modules/fileExplorer/controller.js +280 -0
- package/src/modules/fileExplorer/index.js +37 -0
- package/src/modules/fileExplorer/middleware.js +92 -0
- package/src/modules/fileExplorer/routes.js +125 -0
- package/src/modules/fileExplorer/types.js +44 -0
- package/src/services/aiService.js +1232 -0
- package/src/services/apiKeyManager.js +164 -0
- package/src/services/benchmarkService.js +366 -0
- package/src/services/budgetService.js +539 -0
- package/src/services/contextInjectionService.js +247 -0
- package/src/services/conversationCompactionService.js +637 -0
- package/src/services/errorHandler.js +810 -0
- package/src/services/fileAttachmentService.js +544 -0
- package/src/services/modelRouterService.js +366 -0
- package/src/services/modelsService.js +322 -0
- package/src/services/qualityInspector.js +796 -0
- package/src/services/tokenCountingService.js +536 -0
- package/src/tools/agentCommunicationTool.js +1344 -0
- package/src/tools/agentDelayTool.js +485 -0
- package/src/tools/asyncToolManager.js +604 -0
- package/src/tools/baseTool.js +800 -0
- package/src/tools/browserTool.js +920 -0
- package/src/tools/cloneDetectionTool.js +621 -0
- package/src/tools/dependencyResolverTool.js +1215 -0
- package/src/tools/fileContentReplaceTool.js +875 -0
- package/src/tools/fileSystemTool.js +1107 -0
- package/src/tools/fileTreeTool.js +853 -0
- package/src/tools/imageTool.js +901 -0
- package/src/tools/importAnalyzerTool.js +1060 -0
- package/src/tools/jobDoneTool.js +248 -0
- package/src/tools/seekTool.js +956 -0
- package/src/tools/staticAnalysisTool.js +1778 -0
- package/src/tools/taskManagerTool.js +2873 -0
- package/src/tools/terminalTool.js +2304 -0
- package/src/tools/webTool.js +1430 -0
- package/src/types/agent.js +519 -0
- package/src/types/contextReference.js +972 -0
- package/src/types/conversation.js +730 -0
- package/src/types/toolCommand.js +747 -0
- package/src/utilities/attachmentValidator.js +292 -0
- package/src/utilities/configManager.js +582 -0
- package/src/utilities/constants.js +722 -0
- package/src/utilities/directoryAccessManager.js +535 -0
- package/src/utilities/fileProcessor.js +307 -0
- package/src/utilities/logger.js +436 -0
- package/src/utilities/tagParser.js +1246 -0
- package/src/utilities/toolConstants.js +317 -0
- package/web-ui/build/index.html +15 -0
- package/web-ui/build/logo.png +0 -0
- package/web-ui/build/logo2.png +0 -0
- package/web-ui/build/static/index-CjkkcnFA.js +344 -0
- package/web-ui/build/static/index-Dy2bYbOa.css +1 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Explorer Controller
|
|
3
|
+
* Handles all file system operations for the file explorer module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { promises as fs } from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
|
|
10
|
+
class FileExplorerController {
|
|
11
|
+
constructor(config = {}) {
|
|
12
|
+
this.config = {
|
|
13
|
+
showHidden: false,
|
|
14
|
+
allowedExtensions: [], // Empty array = all extensions allowed
|
|
15
|
+
maxDepth: 50, // Prevent infinite directory traversal
|
|
16
|
+
restrictedPaths: [], // Paths that cannot be accessed
|
|
17
|
+
...config
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get file/directory stats safely
|
|
23
|
+
* @param {string} filePath - Path to check
|
|
24
|
+
* @returns {Promise<{stats: fs.Stats | null, error?: string}>}
|
|
25
|
+
*/
|
|
26
|
+
async getFileStats(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
const stats = await fs.stat(filePath);
|
|
29
|
+
return { stats };
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return {
|
|
32
|
+
stats: null,
|
|
33
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if path is safe and accessible
|
|
40
|
+
* @param {string} requestedPath - Path to validate
|
|
41
|
+
* @returns {boolean}
|
|
42
|
+
*/
|
|
43
|
+
isSafePath(requestedPath) {
|
|
44
|
+
try {
|
|
45
|
+
const resolvedPath = path.resolve(requestedPath);
|
|
46
|
+
|
|
47
|
+
// Check if path is in restricted list
|
|
48
|
+
if (this.config.restrictedPaths.some(restricted =>
|
|
49
|
+
resolvedPath.startsWith(path.resolve(restricted))
|
|
50
|
+
)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Filter items based on configuration
|
|
62
|
+
* @param {string} itemName - Item name to check
|
|
63
|
+
* @param {fs.Stats} stats - File stats
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
shouldIncludeItem(itemName, stats) {
|
|
67
|
+
// Check hidden files
|
|
68
|
+
if (!this.config.showHidden && itemName.startsWith('.')) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check file extensions (only for files)
|
|
73
|
+
if (stats.isFile() && this.config.allowedExtensions.length > 0) {
|
|
74
|
+
const ext = path.extname(itemName).toLowerCase();
|
|
75
|
+
if (!this.config.allowedExtensions.includes(ext)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Browse directory contents
|
|
85
|
+
* @param {string} requestedPath - Directory path to browse
|
|
86
|
+
* @param {Object} options - Browse options
|
|
87
|
+
* @returns {Promise<{success: boolean, data?: BrowseResponse, error?: string}>}
|
|
88
|
+
*/
|
|
89
|
+
async browseDirectory(requestedPath = process.cwd(), options = {}) {
|
|
90
|
+
try {
|
|
91
|
+
const normalizedPath = path.resolve(requestedPath);
|
|
92
|
+
|
|
93
|
+
// Security checks
|
|
94
|
+
if (!this.isSafePath(normalizedPath)) {
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
error: 'Access to this path is restricted'
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check if path exists and is directory
|
|
102
|
+
const { stats, error } = await this.getFileStats(normalizedPath);
|
|
103
|
+
if (error || !stats) {
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
error: 'Path not found or inaccessible'
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!stats.isDirectory()) {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
error: 'Path is not a directory'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Read directory contents
|
|
118
|
+
const items = await fs.readdir(normalizedPath);
|
|
119
|
+
const fileItems = [];
|
|
120
|
+
|
|
121
|
+
// Process each item
|
|
122
|
+
for (const item of items) {
|
|
123
|
+
const itemPath = path.join(normalizedPath, item);
|
|
124
|
+
const { stats: itemStats } = await this.getFileStats(itemPath);
|
|
125
|
+
|
|
126
|
+
if (itemStats && this.shouldIncludeItem(item, itemStats)) {
|
|
127
|
+
const fileItem = {
|
|
128
|
+
name: item,
|
|
129
|
+
path: itemPath,
|
|
130
|
+
type: itemStats.isDirectory() ? 'directory' : 'file',
|
|
131
|
+
size: itemStats.isFile() ? itemStats.size : undefined,
|
|
132
|
+
lastModified: itemStats.mtime,
|
|
133
|
+
extension: itemStats.isFile() ? path.extname(item).toLowerCase() : undefined
|
|
134
|
+
};
|
|
135
|
+
fileItems.push(fileItem);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Sort: directories first, then files, both alphabetically
|
|
140
|
+
fileItems.sort((a, b) => {
|
|
141
|
+
if (a.type === b.type) {
|
|
142
|
+
return a.name.localeCompare(b.name);
|
|
143
|
+
}
|
|
144
|
+
return a.type === 'directory' ? -1 : 1;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Calculate parent path
|
|
148
|
+
const parentPath = path.dirname(normalizedPath);
|
|
149
|
+
const hasParent = parentPath !== normalizedPath;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
data: {
|
|
154
|
+
currentPath: normalizedPath,
|
|
155
|
+
parentPath: hasParent ? parentPath : null,
|
|
156
|
+
items: fileItems,
|
|
157
|
+
totalItems: fileItems.length,
|
|
158
|
+
directories: fileItems.filter(item => item.type === 'directory').length,
|
|
159
|
+
files: fileItems.filter(item => item.type === 'file').length
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
error: error instanceof Error ? error.message : 'Server error'
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get file information
|
|
173
|
+
* @param {string} filePath - File path to get info for
|
|
174
|
+
* @returns {Promise<{success: boolean, data?: FileItem, error?: string}>}
|
|
175
|
+
*/
|
|
176
|
+
async getFileInfo(filePath) {
|
|
177
|
+
try {
|
|
178
|
+
if (!filePath || !this.isSafePath(filePath)) {
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
error: 'Invalid file path'
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const { stats, error } = await this.getFileStats(filePath);
|
|
186
|
+
if (error || !stats) {
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
error: 'File not found'
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const fileInfo = {
|
|
194
|
+
name: path.basename(filePath),
|
|
195
|
+
path: filePath,
|
|
196
|
+
type: stats.isDirectory() ? 'directory' : 'file',
|
|
197
|
+
size: stats.isFile() ? stats.size : undefined,
|
|
198
|
+
lastModified: stats.mtime,
|
|
199
|
+
extension: stats.isFile() ? path.extname(filePath).toLowerCase() : undefined
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
data: fileInfo
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
} catch (error) {
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
error: error instanceof Error ? error.message : 'Server error'
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get current working directory info
|
|
217
|
+
* @returns {{success: boolean, data: {cwd: string, platform: string, homedir: string}}}
|
|
218
|
+
*/
|
|
219
|
+
getCurrentWorkingDirectory() {
|
|
220
|
+
return {
|
|
221
|
+
success: true,
|
|
222
|
+
data: {
|
|
223
|
+
cwd: process.cwd(),
|
|
224
|
+
platform: process.platform,
|
|
225
|
+
homedir: os.homedir()
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Create directory
|
|
232
|
+
* @param {string} dirPath - Directory path to create
|
|
233
|
+
* @param {Object} options - Creation options
|
|
234
|
+
* @returns {Promise<{success: boolean, data?: {path: string}, error?: string}>}
|
|
235
|
+
*/
|
|
236
|
+
async createDirectory(dirPath, options = {}) {
|
|
237
|
+
try {
|
|
238
|
+
if (!this.isSafePath(dirPath)) {
|
|
239
|
+
return {
|
|
240
|
+
success: false,
|
|
241
|
+
error: 'Invalid directory path'
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
await fs.mkdir(dirPath, { recursive: options.recursive || false });
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
success: true,
|
|
249
|
+
data: {
|
|
250
|
+
path: dirPath,
|
|
251
|
+
relativePath: path.relative(process.cwd(), dirPath)
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
} catch (error) {
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
error: error instanceof Error ? error.message : 'Failed to create directory',
|
|
259
|
+
code: error.code
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Health check
|
|
266
|
+
* @returns {{success: boolean, data: {status: string, timestamp: string}}}
|
|
267
|
+
*/
|
|
268
|
+
healthCheck() {
|
|
269
|
+
return {
|
|
270
|
+
success: true,
|
|
271
|
+
data: {
|
|
272
|
+
status: 'healthy',
|
|
273
|
+
timestamp: new Date().toISOString(),
|
|
274
|
+
module: 'fileExplorer'
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export default FileExplorerController;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Explorer Module
|
|
3
|
+
* Main entry point for the file explorer functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createFileExplorerRouter, defaultConfig } from './routes.js';
|
|
7
|
+
import FileExplorerController from './controller.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Initialize file explorer module
|
|
11
|
+
* @param {Object} config - Configuration options
|
|
12
|
+
* @returns {Object} Module interface
|
|
13
|
+
*/
|
|
14
|
+
export function initFileExplorerModule(config = {}) {
|
|
15
|
+
const mergedConfig = { ...defaultConfig, ...config };
|
|
16
|
+
const router = createFileExplorerRouter(mergedConfig);
|
|
17
|
+
const controller = new FileExplorerController(mergedConfig);
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
router,
|
|
21
|
+
controller,
|
|
22
|
+
config: mergedConfig
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Export individual components for advanced usage
|
|
27
|
+
export { default as FileExplorerController } from './controller.js';
|
|
28
|
+
export { createFileExplorerRouter, defaultConfig } from './routes.js';
|
|
29
|
+
export * from './middleware.js';
|
|
30
|
+
|
|
31
|
+
// Default export
|
|
32
|
+
export default {
|
|
33
|
+
init: initFileExplorerModule,
|
|
34
|
+
Controller: FileExplorerController,
|
|
35
|
+
createRouter: createFileExplorerRouter,
|
|
36
|
+
defaultConfig
|
|
37
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Explorer Middleware
|
|
3
|
+
* Contains middleware functions specific to file explorer operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validate path parameter middleware
|
|
8
|
+
* @param {import('express').Request} req
|
|
9
|
+
* @param {import('express').Response} res
|
|
10
|
+
* @param {import('express').NextFunction} next
|
|
11
|
+
*/
|
|
12
|
+
export function validatePath(req, res, next) {
|
|
13
|
+
const { path } = req.query;
|
|
14
|
+
|
|
15
|
+
if (path && typeof path !== 'string') {
|
|
16
|
+
return res.status(400).json({
|
|
17
|
+
success: false,
|
|
18
|
+
error: 'Path parameter must be a string'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
next();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Rate limiting middleware for file operations
|
|
27
|
+
* Prevents excessive file system requests
|
|
28
|
+
* @param {number} maxRequests - Max requests per window
|
|
29
|
+
* @param {number} windowMs - Time window in milliseconds
|
|
30
|
+
*/
|
|
31
|
+
export function createRateLimit(maxRequests = 100, windowMs = 60000) {
|
|
32
|
+
const requests = new Map();
|
|
33
|
+
|
|
34
|
+
return (req, res, next) => {
|
|
35
|
+
const clientIp = req.ip || req.connection.remoteAddress || 'unknown';
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const windowStart = now - windowMs;
|
|
38
|
+
|
|
39
|
+
// Get or create request history for this IP
|
|
40
|
+
if (!requests.has(clientIp)) {
|
|
41
|
+
requests.set(clientIp, []);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const clientRequests = requests.get(clientIp);
|
|
45
|
+
|
|
46
|
+
// Remove old requests outside the window
|
|
47
|
+
const validRequests = clientRequests.filter(timestamp => timestamp > windowStart);
|
|
48
|
+
requests.set(clientIp, validRequests);
|
|
49
|
+
|
|
50
|
+
// Check if limit exceeded
|
|
51
|
+
if (validRequests.length >= maxRequests) {
|
|
52
|
+
return res.status(429).json({
|
|
53
|
+
success: false,
|
|
54
|
+
error: 'Too many requests',
|
|
55
|
+
retryAfter: Math.ceil(windowMs / 1000)
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add current request
|
|
60
|
+
validRequests.push(now);
|
|
61
|
+
|
|
62
|
+
next();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Security headers middleware for file explorer
|
|
68
|
+
*/
|
|
69
|
+
export function securityHeaders(req, res, next) {
|
|
70
|
+
// Prevent caching of file system data
|
|
71
|
+
res.set({
|
|
72
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
73
|
+
'Pragma': 'no-cache',
|
|
74
|
+
'Expires': '0'
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
next();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Request logging middleware for file explorer
|
|
82
|
+
*/
|
|
83
|
+
export function requestLogger(req, res, next) {
|
|
84
|
+
const start = Date.now();
|
|
85
|
+
|
|
86
|
+
res.on('finish', () => {
|
|
87
|
+
const duration = Date.now() - start;
|
|
88
|
+
console.log(`[FileExplorer] ${req.method} ${req.originalUrl} - ${res.statusCode} (${duration}ms)`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
next();
|
|
92
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Explorer Routes
|
|
3
|
+
* Defines all API endpoints for file system operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import FileExplorerController from './controller.js';
|
|
8
|
+
import { validatePath, createRateLimit, securityHeaders, requestLogger } from './middleware.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create file explorer router with all endpoints
|
|
12
|
+
* @param {Object} config - Configuration options for the file explorer
|
|
13
|
+
* @returns {express.Router} Configured express router
|
|
14
|
+
*/
|
|
15
|
+
export function createFileExplorerRouter(config = {}) {
|
|
16
|
+
const router = express.Router();
|
|
17
|
+
const controller = new FileExplorerController(config);
|
|
18
|
+
|
|
19
|
+
// Apply middleware to all routes
|
|
20
|
+
router.use(requestLogger);
|
|
21
|
+
router.use(securityHeaders);
|
|
22
|
+
router.use(createRateLimit(100, 60000)); // 100 requests per minute
|
|
23
|
+
|
|
24
|
+
// Health check endpoint
|
|
25
|
+
router.get('/health', (req, res) => {
|
|
26
|
+
const result = controller.healthCheck();
|
|
27
|
+
res.json(result);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Get current working directory
|
|
31
|
+
router.get('/cwd', (req, res) => {
|
|
32
|
+
const result = controller.getCurrentWorkingDirectory();
|
|
33
|
+
res.json(result);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Browse directory contents
|
|
37
|
+
router.get('/browse', validatePath, async (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const requestedPath = req.query.path;
|
|
40
|
+
const options = {
|
|
41
|
+
showHidden: req.query.showHidden === 'true'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const result = await controller.browseDirectory(requestedPath, options);
|
|
45
|
+
|
|
46
|
+
if (result.success) {
|
|
47
|
+
res.json(result);
|
|
48
|
+
} else {
|
|
49
|
+
const statusCode = result.error.includes('restricted') ? 403 :
|
|
50
|
+
result.error.includes('not found') ? 404 : 400;
|
|
51
|
+
res.status(statusCode).json(result);
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
res.status(500).json({
|
|
55
|
+
success: false,
|
|
56
|
+
error: 'Internal server error'
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Get file information
|
|
62
|
+
router.get('/file-info', validatePath, async (req, res) => {
|
|
63
|
+
try {
|
|
64
|
+
const filePath = req.query.path;
|
|
65
|
+
const result = await controller.getFileInfo(filePath);
|
|
66
|
+
|
|
67
|
+
if (result.success) {
|
|
68
|
+
res.json(result);
|
|
69
|
+
} else {
|
|
70
|
+
const statusCode = result.error.includes('not found') ? 404 : 400;
|
|
71
|
+
res.status(statusCode).json(result);
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
res.status(500).json({
|
|
75
|
+
success: false,
|
|
76
|
+
error: 'Internal server error'
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Create directory
|
|
82
|
+
router.post('/mkdir', express.json(), async (req, res) => {
|
|
83
|
+
try {
|
|
84
|
+
const { path: dirPath, recursive = false } = req.body;
|
|
85
|
+
|
|
86
|
+
if (!dirPath) {
|
|
87
|
+
return res.status(400).json({
|
|
88
|
+
success: false,
|
|
89
|
+
error: 'Directory path is required'
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const result = await controller.createDirectory(dirPath, { recursive });
|
|
94
|
+
|
|
95
|
+
if (result.success) {
|
|
96
|
+
res.json(result);
|
|
97
|
+
} else {
|
|
98
|
+
const statusCode = result.code === 'EEXIST' ? 409 : 400;
|
|
99
|
+
res.status(statusCode).json(result);
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
res.status(500).json({
|
|
103
|
+
success: false,
|
|
104
|
+
error: 'Internal server error'
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return router;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Default configuration for file explorer
|
|
114
|
+
*/
|
|
115
|
+
export const defaultConfig = {
|
|
116
|
+
showHidden: false,
|
|
117
|
+
allowedExtensions: [], // Empty = all extensions
|
|
118
|
+
maxDepth: 50,
|
|
119
|
+
restrictedPaths: [
|
|
120
|
+
// Add system paths that should be restricted
|
|
121
|
+
// Example: '/etc', '/var', '/usr/bin'
|
|
122
|
+
]
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export default createFileExplorerRouter;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Explorer Module Types
|
|
3
|
+
* Defines types and interfaces for file system operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} FileItem
|
|
8
|
+
* @property {string} name - File or directory name
|
|
9
|
+
* @property {string} path - Full absolute path
|
|
10
|
+
* @property {'file'|'directory'} type - Item type
|
|
11
|
+
* @property {number} [size] - File size in bytes (files only)
|
|
12
|
+
* @property {Date} [lastModified] - Last modification date
|
|
13
|
+
* @property {string} [extension] - File extension (files only)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} BrowseResponse
|
|
18
|
+
* @property {string} currentPath - Current directory path
|
|
19
|
+
* @property {string} parentPath - Parent directory path
|
|
20
|
+
* @property {FileItem[]} items - Directory contents
|
|
21
|
+
* @property {number} totalItems - Total number of items
|
|
22
|
+
* @property {number} directories - Number of directories
|
|
23
|
+
* @property {number} files - Number of files
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} ApiResponse
|
|
28
|
+
* @property {boolean} success - Operation success status
|
|
29
|
+
* @property {*} [data] - Response data
|
|
30
|
+
* @property {string} [error] - Error message if failed
|
|
31
|
+
* @property {string} [code] - Error code if applicable
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} FileExplorerConfig
|
|
36
|
+
* @property {boolean} showHidden - Show hidden files/directories
|
|
37
|
+
* @property {string[]} allowedExtensions - Allowed file extensions (empty = all)
|
|
38
|
+
* @property {number} maxDepth - Maximum directory traversal depth
|
|
39
|
+
* @property {string[]} restrictedPaths - Paths that cannot be accessed
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
// Export types as JSDoc comments for runtime usage
|
|
44
|
+
};
|