@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,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DirectoryAccessManager - Manage directory access permissions for agents
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Control agent access to directories and files
|
|
6
|
+
* - Distinguish between read-only and write-enabled directories
|
|
7
|
+
* - Validate paths against access permissions
|
|
8
|
+
* - Provide working directory management
|
|
9
|
+
* - Support both absolute and relative path resolution
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import fs from 'fs/promises';
|
|
14
|
+
import os from 'os';
|
|
15
|
+
|
|
16
|
+
class DirectoryAccessManager {
|
|
17
|
+
constructor(config = {}, logger = null) {
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
this.config = config;
|
|
20
|
+
|
|
21
|
+
// Default system restrictions
|
|
22
|
+
this.systemRestrictedPaths = [
|
|
23
|
+
// System directories
|
|
24
|
+
'/etc',
|
|
25
|
+
'/var',
|
|
26
|
+
'/usr',
|
|
27
|
+
'/bin',
|
|
28
|
+
'/sbin',
|
|
29
|
+
'/boot',
|
|
30
|
+
'/dev',
|
|
31
|
+
'/proc',
|
|
32
|
+
'/sys',
|
|
33
|
+
// Windows system directories
|
|
34
|
+
'C:\\Windows',
|
|
35
|
+
'C:\\Program Files',
|
|
36
|
+
'C:\\Program Files (x86)',
|
|
37
|
+
// User sensitive directories
|
|
38
|
+
path.join(os.homedir(), '.ssh'),
|
|
39
|
+
path.join(os.homedir(), '.aws'),
|
|
40
|
+
path.join(os.homedir(), '.config'),
|
|
41
|
+
// Common package managers
|
|
42
|
+
'node_modules/.bin',
|
|
43
|
+
'.git/objects',
|
|
44
|
+
'.git/hooks'
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create directory access configuration for an agent
|
|
50
|
+
* @param {Object} options - Access configuration options
|
|
51
|
+
* @returns {Object} Directory access configuration
|
|
52
|
+
*/
|
|
53
|
+
createDirectoryAccess(options = {}) {
|
|
54
|
+
const {
|
|
55
|
+
workingDirectory = process.cwd(),
|
|
56
|
+
readOnlyDirectories = [],
|
|
57
|
+
writeEnabledDirectories = [],
|
|
58
|
+
restrictToProject = true,
|
|
59
|
+
allowSystemAccess = false,
|
|
60
|
+
customRestrictions = []
|
|
61
|
+
} = options;
|
|
62
|
+
|
|
63
|
+
// Normalize all paths to absolute
|
|
64
|
+
const workingDir = path.resolve(workingDirectory);
|
|
65
|
+
const readOnlyDirs = readOnlyDirectories.map(dir => this.normalizePath(dir, workingDir));
|
|
66
|
+
const writeEnabledDirs = writeEnabledDirectories.map(dir => this.normalizePath(dir, workingDir));
|
|
67
|
+
|
|
68
|
+
// If restrict to project, ensure working directory is included
|
|
69
|
+
const finalReadOnlyDirs = restrictToProject
|
|
70
|
+
? [...new Set([...readOnlyDirs, workingDir])]
|
|
71
|
+
: readOnlyDirs;
|
|
72
|
+
|
|
73
|
+
const finalWriteEnabledDirs = restrictToProject
|
|
74
|
+
? writeEnabledDirs.filter(dir => this.isPathWithinDirectory(dir, workingDir))
|
|
75
|
+
: writeEnabledDirs;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
workingDirectory: workingDir,
|
|
79
|
+
readOnlyDirectories: finalReadOnlyDirs,
|
|
80
|
+
writeEnabledDirectories: finalWriteEnabledDirs,
|
|
81
|
+
restrictToProject,
|
|
82
|
+
allowSystemAccess,
|
|
83
|
+
customRestrictions: customRestrictions.map(restriction => path.resolve(restriction)),
|
|
84
|
+
createdAt: new Date().toISOString(),
|
|
85
|
+
version: '1.0'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Validate if a path can be accessed for reading
|
|
91
|
+
* @param {string} targetPath - Path to validate
|
|
92
|
+
* @param {Object} accessConfig - Directory access configuration
|
|
93
|
+
* @returns {Object} Validation result
|
|
94
|
+
*/
|
|
95
|
+
validateReadAccess(targetPath, accessConfig) {
|
|
96
|
+
try {
|
|
97
|
+
const resolvedPath = this.resolvePath(targetPath, accessConfig.workingDirectory);
|
|
98
|
+
|
|
99
|
+
// Check system restrictions first
|
|
100
|
+
if (!accessConfig.allowSystemAccess && this.isSystemRestrictedPath(resolvedPath)) {
|
|
101
|
+
return {
|
|
102
|
+
allowed: false,
|
|
103
|
+
reason: 'System path access denied',
|
|
104
|
+
path: resolvedPath,
|
|
105
|
+
category: 'system_restricted'
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check custom restrictions
|
|
110
|
+
if (this.isCustomRestricted(resolvedPath, accessConfig.customRestrictions)) {
|
|
111
|
+
return {
|
|
112
|
+
allowed: false,
|
|
113
|
+
reason: 'Custom restriction applied',
|
|
114
|
+
path: resolvedPath,
|
|
115
|
+
category: 'custom_restricted'
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If restricting to project, ensure path is within allowed boundaries
|
|
120
|
+
if (accessConfig.restrictToProject) {
|
|
121
|
+
const isWithinProject = this.isPathWithinAnyDirectory(resolvedPath, [
|
|
122
|
+
...accessConfig.readOnlyDirectories,
|
|
123
|
+
...accessConfig.writeEnabledDirectories
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
if (!isWithinProject) {
|
|
127
|
+
return {
|
|
128
|
+
allowed: false,
|
|
129
|
+
reason: 'Path outside project scope',
|
|
130
|
+
path: resolvedPath,
|
|
131
|
+
category: 'project_restricted'
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check if path is within any allowed directory
|
|
137
|
+
const isWithinAllowed = this.isPathWithinAnyDirectory(resolvedPath, [
|
|
138
|
+
...accessConfig.readOnlyDirectories,
|
|
139
|
+
...accessConfig.writeEnabledDirectories
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
if (!isWithinAllowed && accessConfig.readOnlyDirectories.length > 0) {
|
|
143
|
+
return {
|
|
144
|
+
allowed: false,
|
|
145
|
+
reason: 'Path not in allowed directories',
|
|
146
|
+
path: resolvedPath,
|
|
147
|
+
category: 'directory_restricted'
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
allowed: true,
|
|
153
|
+
path: resolvedPath,
|
|
154
|
+
category: 'allowed'
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return {
|
|
159
|
+
allowed: false,
|
|
160
|
+
reason: `Path validation error: ${error.message}`,
|
|
161
|
+
path: targetPath,
|
|
162
|
+
category: 'validation_error'
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Validate if a path can be accessed for writing
|
|
169
|
+
* @param {string} targetPath - Path to validate
|
|
170
|
+
* @param {Object} accessConfig - Directory access configuration
|
|
171
|
+
* @returns {Object} Validation result
|
|
172
|
+
*/
|
|
173
|
+
validateWriteAccess(targetPath, accessConfig) {
|
|
174
|
+
// First check read access
|
|
175
|
+
const readResult = this.validateReadAccess(targetPath, accessConfig);
|
|
176
|
+
if (!readResult.allowed) {
|
|
177
|
+
return {
|
|
178
|
+
...readResult,
|
|
179
|
+
writeAllowed: false
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const resolvedPath = readResult.path;
|
|
184
|
+
|
|
185
|
+
// Check if path is within write-enabled directories
|
|
186
|
+
const isWithinWriteEnabled = this.isPathWithinAnyDirectory(
|
|
187
|
+
resolvedPath,
|
|
188
|
+
accessConfig.writeEnabledDirectories
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (!isWithinWriteEnabled) {
|
|
192
|
+
// Check if it's in read-only directories
|
|
193
|
+
const isWithinReadOnly = this.isPathWithinAnyDirectory(
|
|
194
|
+
resolvedPath,
|
|
195
|
+
accessConfig.readOnlyDirectories
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (isWithinReadOnly) {
|
|
199
|
+
return {
|
|
200
|
+
allowed: false,
|
|
201
|
+
writeAllowed: false,
|
|
202
|
+
reason: 'Path is in read-only directory',
|
|
203
|
+
path: resolvedPath,
|
|
204
|
+
category: 'read_only_restricted'
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
allowed: false,
|
|
210
|
+
writeAllowed: false,
|
|
211
|
+
reason: 'Path not in write-enabled directories',
|
|
212
|
+
path: resolvedPath,
|
|
213
|
+
category: 'write_restricted'
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
allowed: true,
|
|
219
|
+
writeAllowed: true,
|
|
220
|
+
path: resolvedPath,
|
|
221
|
+
category: 'write_allowed'
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get the effective working directory for an agent
|
|
227
|
+
* @param {Object} accessConfig - Directory access configuration
|
|
228
|
+
* @returns {string} Working directory path
|
|
229
|
+
*/
|
|
230
|
+
getWorkingDirectory(accessConfig) {
|
|
231
|
+
return accessConfig.workingDirectory || process.cwd();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* List accessible directories for an agent
|
|
236
|
+
* @param {Object} accessConfig - Directory access configuration
|
|
237
|
+
* @returns {Object} Directory listing with permissions
|
|
238
|
+
*/
|
|
239
|
+
getAccessibleDirectories(accessConfig) {
|
|
240
|
+
return {
|
|
241
|
+
workingDirectory: accessConfig.workingDirectory,
|
|
242
|
+
readOnly: [...accessConfig.readOnlyDirectories],
|
|
243
|
+
writeEnabled: [...accessConfig.writeEnabledDirectories],
|
|
244
|
+
projectRestricted: accessConfig.restrictToProject,
|
|
245
|
+
systemAccessAllowed: accessConfig.allowSystemAccess,
|
|
246
|
+
totalDirectories: accessConfig.readOnlyDirectories.length + accessConfig.writeEnabledDirectories.length
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Update directory access configuration
|
|
252
|
+
* @param {Object} currentConfig - Current access configuration
|
|
253
|
+
* @param {Object} updates - Updates to apply
|
|
254
|
+
* @returns {Object} Updated configuration
|
|
255
|
+
*/
|
|
256
|
+
updateDirectoryAccess(currentConfig, updates) {
|
|
257
|
+
const updatedConfig = { ...currentConfig };
|
|
258
|
+
|
|
259
|
+
if (updates.workingDirectory) {
|
|
260
|
+
updatedConfig.workingDirectory = path.resolve(updates.workingDirectory);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (updates.readOnlyDirectories !== undefined) {
|
|
264
|
+
updatedConfig.readOnlyDirectories = updates.readOnlyDirectories.map(dir =>
|
|
265
|
+
this.normalizePath(dir, updatedConfig.workingDirectory)
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (updates.writeEnabledDirectories !== undefined) {
|
|
270
|
+
updatedConfig.writeEnabledDirectories = updates.writeEnabledDirectories.map(dir =>
|
|
271
|
+
this.normalizePath(dir, updatedConfig.workingDirectory)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (updates.restrictToProject !== undefined) {
|
|
276
|
+
updatedConfig.restrictToProject = updates.restrictToProject;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (updates.allowSystemAccess !== undefined) {
|
|
280
|
+
updatedConfig.allowSystemAccess = updates.allowSystemAccess;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (updates.customRestrictions !== undefined) {
|
|
284
|
+
updatedConfig.customRestrictions = updates.customRestrictions.map(restriction =>
|
|
285
|
+
path.resolve(restriction)
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
updatedConfig.version = currentConfig.version || '1.0';
|
|
290
|
+
updatedConfig.updatedAt = new Date().toISOString();
|
|
291
|
+
|
|
292
|
+
return updatedConfig;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Validate directory access configuration
|
|
297
|
+
* @param {Object} accessConfig - Configuration to validate
|
|
298
|
+
* @returns {Object} Validation result
|
|
299
|
+
*/
|
|
300
|
+
validateAccessConfiguration(accessConfig) {
|
|
301
|
+
const errors = [];
|
|
302
|
+
const warnings = [];
|
|
303
|
+
|
|
304
|
+
// Validate working directory exists
|
|
305
|
+
if (!accessConfig.workingDirectory) {
|
|
306
|
+
errors.push('Working directory is required');
|
|
307
|
+
} else {
|
|
308
|
+
// Convert relative paths to absolute paths relative to process.cwd()
|
|
309
|
+
if (!path.isAbsolute(accessConfig.workingDirectory)) {
|
|
310
|
+
accessConfig.workingDirectory = path.resolve(process.cwd(), accessConfig.workingDirectory);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Validate directory arrays
|
|
315
|
+
if (!Array.isArray(accessConfig.readOnlyDirectories)) {
|
|
316
|
+
errors.push('readOnlyDirectories must be an array');
|
|
317
|
+
} else {
|
|
318
|
+
// Convert relative paths to absolute paths
|
|
319
|
+
accessConfig.readOnlyDirectories = accessConfig.readOnlyDirectories.map(dir =>
|
|
320
|
+
path.isAbsolute(dir) ? dir : path.resolve(process.cwd(), dir)
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (!Array.isArray(accessConfig.writeEnabledDirectories)) {
|
|
325
|
+
errors.push('writeEnabledDirectories must be an array');
|
|
326
|
+
} else {
|
|
327
|
+
// Convert relative paths to absolute paths
|
|
328
|
+
accessConfig.writeEnabledDirectories = accessConfig.writeEnabledDirectories.map(dir =>
|
|
329
|
+
path.isAbsolute(dir) ? dir : path.resolve(process.cwd(), dir)
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Check for overlapping directories
|
|
334
|
+
if (accessConfig.readOnlyDirectories && accessConfig.writeEnabledDirectories) {
|
|
335
|
+
const overlapping = this.findOverlappingPaths(
|
|
336
|
+
accessConfig.readOnlyDirectories,
|
|
337
|
+
accessConfig.writeEnabledDirectories
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
if (overlapping.length > 0) {
|
|
341
|
+
warnings.push(`Overlapping directories found: ${overlapping.join(', ')}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Check for system path access
|
|
346
|
+
if (accessConfig.allowSystemAccess) {
|
|
347
|
+
warnings.push('System path access is enabled - use with caution');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Validate paths exist (async check would be needed in real implementation)
|
|
351
|
+
const allPaths = [
|
|
352
|
+
...accessConfig.readOnlyDirectories,
|
|
353
|
+
...accessConfig.writeEnabledDirectories
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
for (const dirPath of allPaths) {
|
|
357
|
+
if (!path.isAbsolute(dirPath)) {
|
|
358
|
+
errors.push(`Directory path must be absolute: ${dirPath}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
valid: errors.length === 0,
|
|
364
|
+
errors,
|
|
365
|
+
warnings,
|
|
366
|
+
summary: {
|
|
367
|
+
readOnlyCount: accessConfig.readOnlyDirectories?.length || 0,
|
|
368
|
+
writeEnabledCount: accessConfig.writeEnabledDirectories?.length || 0,
|
|
369
|
+
restrictToProject: accessConfig.restrictToProject,
|
|
370
|
+
allowSystemAccess: accessConfig.allowSystemAccess
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Create relative path from absolute path within accessible directories
|
|
377
|
+
* @param {string} absolutePath - Absolute path to convert
|
|
378
|
+
* @param {Object} accessConfig - Directory access configuration
|
|
379
|
+
* @returns {string} Relative path or original if not within accessible directories
|
|
380
|
+
*/
|
|
381
|
+
createRelativePath(absolutePath, accessConfig) {
|
|
382
|
+
const allDirectories = [
|
|
383
|
+
...accessConfig.readOnlyDirectories,
|
|
384
|
+
...accessConfig.writeEnabledDirectories,
|
|
385
|
+
accessConfig.workingDirectory
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
for (const dir of allDirectories) {
|
|
389
|
+
if (this.isPathWithinDirectory(absolutePath, dir)) {
|
|
390
|
+
return path.relative(dir, absolutePath);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return absolutePath;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Resolve path relative to working directory or as absolute
|
|
399
|
+
* @private
|
|
400
|
+
*/
|
|
401
|
+
resolvePath(targetPath, workingDirectory) {
|
|
402
|
+
if (path.isAbsolute(targetPath)) {
|
|
403
|
+
return path.normalize(targetPath);
|
|
404
|
+
}
|
|
405
|
+
return path.resolve(workingDirectory, targetPath);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Normalize path to absolute, resolving relative to working directory
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
normalizePath(targetPath, workingDirectory) {
|
|
413
|
+
if (path.isAbsolute(targetPath)) {
|
|
414
|
+
return path.normalize(targetPath);
|
|
415
|
+
}
|
|
416
|
+
return path.resolve(workingDirectory, targetPath);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Check if path is within a directory
|
|
421
|
+
* @private
|
|
422
|
+
*/
|
|
423
|
+
isPathWithinDirectory(targetPath, parentDirectory) {
|
|
424
|
+
const relative = path.relative(parentDirectory, targetPath);
|
|
425
|
+
return !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Check if path is within any of the provided directories
|
|
430
|
+
* @private
|
|
431
|
+
*/
|
|
432
|
+
isPathWithinAnyDirectory(targetPath, directories) {
|
|
433
|
+
return directories.some(dir => this.isPathWithinDirectory(targetPath, dir));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Check if path is system restricted
|
|
438
|
+
* @private
|
|
439
|
+
*/
|
|
440
|
+
isSystemRestrictedPath(targetPath) {
|
|
441
|
+
return this.systemRestrictedPaths.some(restrictedPath => {
|
|
442
|
+
return targetPath.startsWith(restrictedPath) || targetPath === restrictedPath;
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Check if path is custom restricted
|
|
448
|
+
* @private
|
|
449
|
+
*/
|
|
450
|
+
isCustomRestricted(targetPath, customRestrictions) {
|
|
451
|
+
if (!customRestrictions || customRestrictions.length === 0) {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return customRestrictions.some(restriction => {
|
|
456
|
+
return targetPath.startsWith(restriction) || targetPath === restriction;
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Find overlapping paths between two arrays
|
|
462
|
+
* @private
|
|
463
|
+
*/
|
|
464
|
+
findOverlappingPaths(paths1, paths2) {
|
|
465
|
+
const overlapping = [];
|
|
466
|
+
|
|
467
|
+
for (const path1 of paths1) {
|
|
468
|
+
for (const path2 of paths2) {
|
|
469
|
+
if (this.isPathWithinDirectory(path1, path2) || this.isPathWithinDirectory(path2, path1)) {
|
|
470
|
+
overlapping.push(`${path1} <-> ${path2}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return overlapping;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Get directory access summary for logging/debugging
|
|
480
|
+
* @param {Object} accessConfig - Directory access configuration
|
|
481
|
+
* @returns {Object} Summary object
|
|
482
|
+
*/
|
|
483
|
+
getAccessSummary(accessConfig) {
|
|
484
|
+
return {
|
|
485
|
+
workingDirectory: accessConfig.workingDirectory,
|
|
486
|
+
readOnlyCount: accessConfig.readOnlyDirectories.length,
|
|
487
|
+
writeEnabledCount: accessConfig.writeEnabledDirectories.length,
|
|
488
|
+
projectRestricted: accessConfig.restrictToProject,
|
|
489
|
+
systemAccessAllowed: accessConfig.allowSystemAccess,
|
|
490
|
+
customRestrictionsCount: accessConfig.customRestrictions?.length || 0,
|
|
491
|
+
configVersion: accessConfig.version || 'unknown',
|
|
492
|
+
lastUpdated: accessConfig.updatedAt || accessConfig.createdAt
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Create default directory access for project-based agents
|
|
498
|
+
* @param {string} projectDir - Project directory path
|
|
499
|
+
* @returns {Object} Default directory access configuration
|
|
500
|
+
*/
|
|
501
|
+
static createProjectDefaults(projectDir) {
|
|
502
|
+
const resolvedProject = path.resolve(projectDir);
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
workingDirectory: resolvedProject,
|
|
506
|
+
readOnlyDirectories: [resolvedProject],
|
|
507
|
+
writeEnabledDirectories: [resolvedProject],
|
|
508
|
+
restrictToProject: true,
|
|
509
|
+
allowSystemAccess: false,
|
|
510
|
+
customRestrictions: [],
|
|
511
|
+
createdAt: new Date().toISOString(),
|
|
512
|
+
version: '1.0'
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Create permissive directory access (use with caution)
|
|
518
|
+
* @param {string} workingDir - Working directory
|
|
519
|
+
* @returns {Object} Permissive directory access configuration
|
|
520
|
+
*/
|
|
521
|
+
static createPermissiveDefaults(workingDir = process.cwd()) {
|
|
522
|
+
return {
|
|
523
|
+
workingDirectory: path.resolve(workingDir),
|
|
524
|
+
readOnlyDirectories: [os.homedir()],
|
|
525
|
+
writeEnabledDirectories: [path.resolve(workingDir)],
|
|
526
|
+
restrictToProject: false,
|
|
527
|
+
allowSystemAccess: false,
|
|
528
|
+
customRestrictions: [],
|
|
529
|
+
createdAt: new Date().toISOString(),
|
|
530
|
+
version: '1.0'
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export default DirectoryAccessManager;
|