@dexto/core 1.5.4 → 1.5.6
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/dist/agent/DextoAgent.cjs +61 -155
- package/dist/agent/DextoAgent.d.ts +11 -54
- package/dist/agent/DextoAgent.d.ts.map +1 -1
- package/dist/agent/DextoAgent.js +61 -155
- package/dist/context/compaction/strategies/reactive-overflow.cjs +5 -3
- package/dist/context/compaction/strategies/reactive-overflow.d.ts +1 -0
- package/dist/context/compaction/strategies/reactive-overflow.d.ts.map +1 -1
- package/dist/context/compaction/strategies/reactive-overflow.js +5 -3
- package/dist/context/compaction/types.d.ts +13 -1
- package/dist/context/compaction/types.d.ts.map +1 -1
- package/dist/errors/types.cjs +0 -2
- package/dist/errors/types.d.ts +1 -5
- package/dist/errors/types.d.ts.map +1 -1
- package/dist/errors/types.js +0 -2
- package/dist/events/index.cjs +0 -2
- package/dist/events/index.d.ts +6 -25
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +0 -2
- package/dist/llm/executor/stream-processor.cjs +85 -27
- package/dist/llm/executor/stream-processor.d.ts +4 -0
- package/dist/llm/executor/stream-processor.d.ts.map +1 -1
- package/dist/llm/executor/stream-processor.js +85 -27
- package/dist/llm/executor/turn-executor.cjs +58 -130
- package/dist/llm/executor/turn-executor.d.ts +9 -43
- package/dist/llm/executor/turn-executor.d.ts.map +1 -1
- package/dist/llm/executor/turn-executor.js +58 -130
- package/dist/llm/executor/types.d.ts +0 -28
- package/dist/llm/executor/types.d.ts.map +1 -1
- package/dist/llm/services/vercel.cjs +2 -5
- package/dist/llm/services/vercel.d.ts +1 -6
- package/dist/llm/services/vercel.d.ts.map +1 -1
- package/dist/llm/services/vercel.js +2 -5
- package/dist/logger/logger.cjs +6 -7
- package/dist/logger/logger.d.ts +1 -0
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/logger.js +6 -7
- package/dist/session/chat-session.cjs +19 -12
- package/dist/session/chat-session.d.ts +3 -6
- package/dist/session/chat-session.d.ts.map +1 -1
- package/dist/session/chat-session.js +19 -12
- package/dist/session/session-manager.cjs +0 -135
- package/dist/session/session-manager.d.ts +0 -43
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +0 -135
- package/dist/telemetry/telemetry.cjs +12 -5
- package/dist/telemetry/telemetry.d.ts.map +1 -1
- package/dist/telemetry/telemetry.js +12 -5
- package/dist/tools/schemas.cjs +2 -2
- package/dist/tools/schemas.js +2 -2
- package/package.json +15 -5
- package/dist/filesystem/error-codes.cjs +0 -53
- package/dist/filesystem/error-codes.d.ts +0 -31
- package/dist/filesystem/error-codes.d.ts.map +0 -1
- package/dist/filesystem/error-codes.js +0 -30
- package/dist/filesystem/errors.cjs +0 -303
- package/dist/filesystem/errors.d.ts +0 -109
- package/dist/filesystem/errors.d.ts.map +0 -1
- package/dist/filesystem/errors.js +0 -280
- package/dist/filesystem/filesystem-service.cjs +0 -534
- package/dist/filesystem/filesystem-service.d.ts +0 -97
- package/dist/filesystem/filesystem-service.d.ts.map +0 -1
- package/dist/filesystem/filesystem-service.js +0 -501
- package/dist/filesystem/index.cjs +0 -37
- package/dist/filesystem/index.d.ts +0 -11
- package/dist/filesystem/index.d.ts.map +0 -1
- package/dist/filesystem/index.js +0 -11
- package/dist/filesystem/path-validator.cjs +0 -250
- package/dist/filesystem/path-validator.d.ts +0 -103
- package/dist/filesystem/path-validator.d.ts.map +0 -1
- package/dist/filesystem/path-validator.js +0 -217
- package/dist/filesystem/types.cjs +0 -16
- package/dist/filesystem/types.d.ts +0 -175
- package/dist/filesystem/types.d.ts.map +0 -1
- package/dist/filesystem/types.js +0 -0
- package/dist/process/command-validator.cjs +0 -554
- package/dist/process/command-validator.d.ts +0 -49
- package/dist/process/command-validator.d.ts.map +0 -1
- package/dist/process/command-validator.js +0 -531
- package/dist/process/error-codes.cjs +0 -47
- package/dist/process/error-codes.d.ts +0 -25
- package/dist/process/error-codes.d.ts.map +0 -1
- package/dist/process/error-codes.js +0 -24
- package/dist/process/errors.cjs +0 -244
- package/dist/process/errors.d.ts +0 -87
- package/dist/process/errors.d.ts.map +0 -1
- package/dist/process/errors.js +0 -221
- package/dist/process/index.cjs +0 -37
- package/dist/process/index.d.ts +0 -11
- package/dist/process/index.d.ts.map +0 -1
- package/dist/process/index.js +0 -11
- package/dist/process/process-service.cjs +0 -497
- package/dist/process/process-service.d.ts +0 -69
- package/dist/process/process-service.d.ts.map +0 -1
- package/dist/process/process-service.js +0 -464
- package/dist/process/types.cjs +0 -16
- package/dist/process/types.d.ts +0 -107
- package/dist/process/types.d.ts.map +0 -1
- package/dist/process/types.js +0 -0
- package/dist/session/compaction-service.cjs +0 -139
- package/dist/session/compaction-service.d.ts +0 -81
- package/dist/session/compaction-service.d.ts.map +0 -1
- package/dist/session/compaction-service.js +0 -106
|
@@ -1,501 +0,0 @@
|
|
|
1
|
-
import "../chunk-PTJYTZNU.js";
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { glob } from "glob";
|
|
5
|
-
import { getDextoPath } from "../utils/path.js";
|
|
6
|
-
import { PathValidator } from "./path-validator.js";
|
|
7
|
-
import { FileSystemError } from "./errors.js";
|
|
8
|
-
import { DextoLogComponent } from "../logger/v2/types.js";
|
|
9
|
-
const DEFAULT_ENCODING = "utf-8";
|
|
10
|
-
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
11
|
-
const DEFAULT_MAX_RESULTS = 1e3;
|
|
12
|
-
const DEFAULT_MAX_SEARCH_RESULTS = 100;
|
|
13
|
-
class FileSystemService {
|
|
14
|
-
config;
|
|
15
|
-
pathValidator;
|
|
16
|
-
initialized = false;
|
|
17
|
-
logger;
|
|
18
|
-
constructor(config = {}, logger) {
|
|
19
|
-
this.config = {
|
|
20
|
-
allowedPaths: config.allowedPaths || ["."],
|
|
21
|
-
blockedPaths: config.blockedPaths || [".git", "node_modules/.bin", ".env"],
|
|
22
|
-
blockedExtensions: config.blockedExtensions || [".exe", ".dll", ".so"],
|
|
23
|
-
maxFileSize: config.maxFileSize || DEFAULT_MAX_FILE_SIZE,
|
|
24
|
-
enableBackups: config.enableBackups ?? false,
|
|
25
|
-
backupPath: config.backupPath,
|
|
26
|
-
// Optional absolute override, defaults handled by getBackupDir()
|
|
27
|
-
backupRetentionDays: config.backupRetentionDays || 7,
|
|
28
|
-
workingDirectory: config.workingDirectory
|
|
29
|
-
};
|
|
30
|
-
this.logger = logger.createChild(DextoLogComponent.FILESYSTEM);
|
|
31
|
-
this.pathValidator = new PathValidator(this.config, this.logger);
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Get backup directory path (context-aware with optional override)
|
|
35
|
-
* TODO: Migrate to explicit configuration via CLI enrichment layer (per-agent paths)
|
|
36
|
-
*/
|
|
37
|
-
getBackupDir() {
|
|
38
|
-
return this.config.backupPath || getDextoPath("backups");
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Initialize the service
|
|
42
|
-
*/
|
|
43
|
-
async initialize() {
|
|
44
|
-
if (this.initialized) {
|
|
45
|
-
this.logger.debug("FileSystemService already initialized");
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (this.config.enableBackups) {
|
|
49
|
-
try {
|
|
50
|
-
const backupDir = this.getBackupDir();
|
|
51
|
-
await fs.mkdir(backupDir, { recursive: true });
|
|
52
|
-
this.logger.debug(`Backup directory created/verified: ${backupDir}`);
|
|
53
|
-
} catch (error) {
|
|
54
|
-
this.logger.warn(
|
|
55
|
-
`Failed to create backup directory: ${error instanceof Error ? error.message : String(error)}`
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
this.initialized = true;
|
|
60
|
-
this.logger.info("FileSystemService initialized successfully");
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Check if a file path is within the configured allowed paths.
|
|
64
|
-
* This is a public method for external consumers (like ToolManager) to check
|
|
65
|
-
* if a path requires directory approval before file operations.
|
|
66
|
-
*
|
|
67
|
-
* @param filePath The file path to check (can be relative or absolute)
|
|
68
|
-
* @returns true if the path is within allowed paths, false otherwise
|
|
69
|
-
*/
|
|
70
|
-
async isPathWithinAllowed(filePath) {
|
|
71
|
-
return this.pathValidator.isPathWithinAllowed(filePath);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Get the list of configured allowed paths.
|
|
75
|
-
* Useful for displaying to users when directory approval is needed.
|
|
76
|
-
*
|
|
77
|
-
* @returns Array of allowed path strings
|
|
78
|
-
*/
|
|
79
|
-
getAllowedPaths() {
|
|
80
|
-
return this.pathValidator.getAllowedPaths();
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Get the parent directory of a file path (resolved to absolute).
|
|
84
|
-
* Useful for directory approval requests.
|
|
85
|
-
*
|
|
86
|
-
* @param filePath The file path
|
|
87
|
-
* @returns The absolute parent directory path
|
|
88
|
-
*/
|
|
89
|
-
getParentDirectory(filePath) {
|
|
90
|
-
return path.dirname(path.resolve(filePath));
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Set a callback to check if a path is in an approved directory.
|
|
94
|
-
* This allows PathValidator to consult ApprovalManager without a direct dependency.
|
|
95
|
-
*
|
|
96
|
-
* @param checker Function that returns true if path is in an approved directory
|
|
97
|
-
*/
|
|
98
|
-
setDirectoryApprovalChecker(checker) {
|
|
99
|
-
this.pathValidator.setDirectoryApprovalChecker(checker);
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Read a file with validation and size limits
|
|
103
|
-
*/
|
|
104
|
-
async readFile(filePath, options = {}) {
|
|
105
|
-
if (!this.initialized) {
|
|
106
|
-
throw FileSystemError.notInitialized();
|
|
107
|
-
}
|
|
108
|
-
const validation = await this.pathValidator.validatePath(filePath);
|
|
109
|
-
if (!validation.isValid || !validation.normalizedPath) {
|
|
110
|
-
throw FileSystemError.invalidPath(filePath, validation.error || "Unknown error");
|
|
111
|
-
}
|
|
112
|
-
const normalizedPath = validation.normalizedPath;
|
|
113
|
-
try {
|
|
114
|
-
const stats = await fs.stat(normalizedPath);
|
|
115
|
-
if (!stats.isFile()) {
|
|
116
|
-
throw FileSystemError.invalidPath(normalizedPath, "Path is not a file");
|
|
117
|
-
}
|
|
118
|
-
if (stats.size > this.config.maxFileSize) {
|
|
119
|
-
throw FileSystemError.fileTooLarge(
|
|
120
|
-
normalizedPath,
|
|
121
|
-
stats.size,
|
|
122
|
-
this.config.maxFileSize
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
} catch (error) {
|
|
126
|
-
if (error.code === "ENOENT") {
|
|
127
|
-
throw FileSystemError.fileNotFound(normalizedPath);
|
|
128
|
-
}
|
|
129
|
-
if (error.code === "EACCES") {
|
|
130
|
-
throw FileSystemError.permissionDenied(normalizedPath, "read");
|
|
131
|
-
}
|
|
132
|
-
throw FileSystemError.readFailed(
|
|
133
|
-
normalizedPath,
|
|
134
|
-
error instanceof Error ? error.message : String(error)
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
try {
|
|
138
|
-
const encoding = options.encoding || DEFAULT_ENCODING;
|
|
139
|
-
const content = await fs.readFile(normalizedPath, encoding);
|
|
140
|
-
const lines = content.split("\n");
|
|
141
|
-
const limit = options.limit;
|
|
142
|
-
const offset1 = options.offset;
|
|
143
|
-
let selectedLines;
|
|
144
|
-
let truncated = false;
|
|
145
|
-
if (offset1 && offset1 > 0 || limit !== void 0) {
|
|
146
|
-
const start = offset1 && offset1 > 0 ? Math.max(0, offset1 - 1) : 0;
|
|
147
|
-
const end = limit !== void 0 ? start + limit : lines.length;
|
|
148
|
-
selectedLines = lines.slice(start, end);
|
|
149
|
-
truncated = end < lines.length;
|
|
150
|
-
} else {
|
|
151
|
-
selectedLines = lines;
|
|
152
|
-
}
|
|
153
|
-
return {
|
|
154
|
-
content: selectedLines.join("\n"),
|
|
155
|
-
lines: selectedLines.length,
|
|
156
|
-
encoding,
|
|
157
|
-
truncated,
|
|
158
|
-
size: Buffer.byteLength(content, encoding)
|
|
159
|
-
};
|
|
160
|
-
} catch (error) {
|
|
161
|
-
throw FileSystemError.readFailed(
|
|
162
|
-
normalizedPath,
|
|
163
|
-
error instanceof Error ? error.message : String(error)
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Find files matching a glob pattern
|
|
169
|
-
*/
|
|
170
|
-
async globFiles(pattern, options = {}) {
|
|
171
|
-
if (!this.initialized) {
|
|
172
|
-
throw FileSystemError.notInitialized();
|
|
173
|
-
}
|
|
174
|
-
const cwd = options.cwd || this.config.workingDirectory || process.cwd();
|
|
175
|
-
const maxResults = options.maxResults || DEFAULT_MAX_RESULTS;
|
|
176
|
-
try {
|
|
177
|
-
const files = await glob(pattern, {
|
|
178
|
-
cwd,
|
|
179
|
-
absolute: true,
|
|
180
|
-
nodir: true,
|
|
181
|
-
// Only files
|
|
182
|
-
follow: false
|
|
183
|
-
// Don't follow symlinks
|
|
184
|
-
});
|
|
185
|
-
const validFiles = [];
|
|
186
|
-
for (const file of files) {
|
|
187
|
-
const validation = await this.pathValidator.validatePath(file);
|
|
188
|
-
if (!validation.isValid || !validation.normalizedPath) {
|
|
189
|
-
this.logger.debug(`Skipping invalid path: ${file}`);
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (options.includeMetadata !== false) {
|
|
193
|
-
try {
|
|
194
|
-
const stats = await fs.stat(validation.normalizedPath);
|
|
195
|
-
validFiles.push({
|
|
196
|
-
path: validation.normalizedPath,
|
|
197
|
-
size: stats.size,
|
|
198
|
-
modified: stats.mtime,
|
|
199
|
-
isDirectory: stats.isDirectory()
|
|
200
|
-
});
|
|
201
|
-
} catch (error) {
|
|
202
|
-
this.logger.debug(
|
|
203
|
-
`Failed to stat file ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
} else {
|
|
207
|
-
validFiles.push({
|
|
208
|
-
path: validation.normalizedPath,
|
|
209
|
-
size: 0,
|
|
210
|
-
modified: /* @__PURE__ */ new Date(),
|
|
211
|
-
isDirectory: false
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
if (validFiles.length >= maxResults) {
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
const limited = validFiles.length >= maxResults;
|
|
219
|
-
return {
|
|
220
|
-
files: validFiles,
|
|
221
|
-
truncated: limited,
|
|
222
|
-
totalFound: validFiles.length
|
|
223
|
-
};
|
|
224
|
-
} catch (error) {
|
|
225
|
-
throw FileSystemError.globFailed(
|
|
226
|
-
pattern,
|
|
227
|
-
error instanceof Error ? error.message : String(error)
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Search for content in files (grep-like functionality)
|
|
233
|
-
*/
|
|
234
|
-
async searchContent(pattern, options = {}) {
|
|
235
|
-
if (!this.initialized) {
|
|
236
|
-
throw FileSystemError.notInitialized();
|
|
237
|
-
}
|
|
238
|
-
const searchPath = options.path || this.config.workingDirectory || process.cwd();
|
|
239
|
-
const globPattern = options.glob || "**/*";
|
|
240
|
-
const maxResults = options.maxResults || DEFAULT_MAX_SEARCH_RESULTS;
|
|
241
|
-
const contextLines = options.contextLines || 0;
|
|
242
|
-
try {
|
|
243
|
-
const flags = options.caseInsensitive ? "i" : "";
|
|
244
|
-
const regex = new RegExp(pattern, flags);
|
|
245
|
-
const globResult = await this.globFiles(globPattern, {
|
|
246
|
-
cwd: searchPath,
|
|
247
|
-
maxResults: 1e4
|
|
248
|
-
// Search more files, but limit results
|
|
249
|
-
});
|
|
250
|
-
const matches = [];
|
|
251
|
-
let filesSearched = 0;
|
|
252
|
-
for (const fileInfo of globResult.files) {
|
|
253
|
-
try {
|
|
254
|
-
const fileContent = await this.readFile(fileInfo.path);
|
|
255
|
-
const lines = fileContent.content.split("\n");
|
|
256
|
-
filesSearched++;
|
|
257
|
-
for (let i = 0; i < lines.length; i++) {
|
|
258
|
-
const line = lines[i];
|
|
259
|
-
if (regex.test(line)) {
|
|
260
|
-
let context;
|
|
261
|
-
if (contextLines > 0) {
|
|
262
|
-
const before = [];
|
|
263
|
-
const after = [];
|
|
264
|
-
for (let j = Math.max(0, i - contextLines); j < i; j++) {
|
|
265
|
-
before.push(lines[j]);
|
|
266
|
-
}
|
|
267
|
-
for (let j = i + 1; j < Math.min(lines.length, i + contextLines + 1); j++) {
|
|
268
|
-
after.push(lines[j]);
|
|
269
|
-
}
|
|
270
|
-
context = { before, after };
|
|
271
|
-
}
|
|
272
|
-
matches.push({
|
|
273
|
-
file: fileInfo.path,
|
|
274
|
-
lineNumber: i + 1,
|
|
275
|
-
// 1-based line numbers
|
|
276
|
-
line,
|
|
277
|
-
...context !== void 0 && { context }
|
|
278
|
-
});
|
|
279
|
-
if (matches.length >= maxResults) {
|
|
280
|
-
return {
|
|
281
|
-
matches,
|
|
282
|
-
totalMatches: matches.length,
|
|
283
|
-
truncated: true,
|
|
284
|
-
filesSearched
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
} catch (error) {
|
|
290
|
-
this.logger.debug(
|
|
291
|
-
`Skipping file ${fileInfo.path}: ${error instanceof Error ? error.message : String(error)}`
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return {
|
|
296
|
-
matches,
|
|
297
|
-
totalMatches: matches.length,
|
|
298
|
-
truncated: false,
|
|
299
|
-
filesSearched
|
|
300
|
-
};
|
|
301
|
-
} catch (error) {
|
|
302
|
-
if (error instanceof Error && error.message.includes("Invalid regular expression")) {
|
|
303
|
-
throw FileSystemError.invalidPattern(pattern, "Invalid regular expression syntax");
|
|
304
|
-
}
|
|
305
|
-
throw FileSystemError.searchFailed(
|
|
306
|
-
pattern,
|
|
307
|
-
error instanceof Error ? error.message : String(error)
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Write content to a file
|
|
313
|
-
*/
|
|
314
|
-
async writeFile(filePath, content, options = {}) {
|
|
315
|
-
if (!this.initialized) {
|
|
316
|
-
throw FileSystemError.notInitialized();
|
|
317
|
-
}
|
|
318
|
-
const validation = await this.pathValidator.validatePath(filePath);
|
|
319
|
-
if (!validation.isValid || !validation.normalizedPath) {
|
|
320
|
-
throw FileSystemError.invalidPath(filePath, validation.error || "Unknown error");
|
|
321
|
-
}
|
|
322
|
-
const normalizedPath = validation.normalizedPath;
|
|
323
|
-
const encoding = options.encoding || DEFAULT_ENCODING;
|
|
324
|
-
let backupPath;
|
|
325
|
-
let fileExists = false;
|
|
326
|
-
try {
|
|
327
|
-
await fs.access(normalizedPath);
|
|
328
|
-
fileExists = true;
|
|
329
|
-
} catch {
|
|
330
|
-
}
|
|
331
|
-
if (fileExists && (options.backup ?? this.config.enableBackups)) {
|
|
332
|
-
backupPath = await this.createBackup(normalizedPath);
|
|
333
|
-
}
|
|
334
|
-
try {
|
|
335
|
-
if (options.createDirs) {
|
|
336
|
-
const dir = path.dirname(normalizedPath);
|
|
337
|
-
await fs.mkdir(dir, { recursive: true });
|
|
338
|
-
}
|
|
339
|
-
await fs.writeFile(normalizedPath, content, encoding);
|
|
340
|
-
const bytesWritten = Buffer.byteLength(content, encoding);
|
|
341
|
-
this.logger.debug(`File written: ${normalizedPath} (${bytesWritten} bytes)`);
|
|
342
|
-
return {
|
|
343
|
-
success: true,
|
|
344
|
-
path: normalizedPath,
|
|
345
|
-
bytesWritten,
|
|
346
|
-
backupPath
|
|
347
|
-
};
|
|
348
|
-
} catch (error) {
|
|
349
|
-
throw FileSystemError.writeFailed(
|
|
350
|
-
normalizedPath,
|
|
351
|
-
error instanceof Error ? error.message : String(error)
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Edit a file by replacing text
|
|
357
|
-
*/
|
|
358
|
-
async editFile(filePath, operation, options = {}) {
|
|
359
|
-
if (!this.initialized) {
|
|
360
|
-
throw FileSystemError.notInitialized();
|
|
361
|
-
}
|
|
362
|
-
const validation = await this.pathValidator.validatePath(filePath);
|
|
363
|
-
if (!validation.isValid || !validation.normalizedPath) {
|
|
364
|
-
throw FileSystemError.invalidPath(filePath, validation.error || "Unknown error");
|
|
365
|
-
}
|
|
366
|
-
const normalizedPath = validation.normalizedPath;
|
|
367
|
-
const fileContent = await this.readFile(normalizedPath);
|
|
368
|
-
const originalContent = fileContent.content;
|
|
369
|
-
const occurrences = (originalContent.match(
|
|
370
|
-
new RegExp(operation.oldString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")
|
|
371
|
-
) || []).length;
|
|
372
|
-
if (occurrences === 0) {
|
|
373
|
-
throw FileSystemError.stringNotFound(normalizedPath, operation.oldString);
|
|
374
|
-
}
|
|
375
|
-
if (!operation.replaceAll && occurrences > 1) {
|
|
376
|
-
throw FileSystemError.stringNotUnique(normalizedPath, operation.oldString, occurrences);
|
|
377
|
-
}
|
|
378
|
-
let backupPath;
|
|
379
|
-
if (options.backup ?? this.config.enableBackups) {
|
|
380
|
-
backupPath = await this.createBackup(normalizedPath);
|
|
381
|
-
}
|
|
382
|
-
try {
|
|
383
|
-
let newContent;
|
|
384
|
-
if (operation.replaceAll) {
|
|
385
|
-
newContent = originalContent.replace(
|
|
386
|
-
new RegExp(operation.oldString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
|
|
387
|
-
operation.newString
|
|
388
|
-
);
|
|
389
|
-
} else {
|
|
390
|
-
newContent = originalContent.replace(operation.oldString, operation.newString);
|
|
391
|
-
}
|
|
392
|
-
await fs.writeFile(normalizedPath, newContent, options.encoding || DEFAULT_ENCODING);
|
|
393
|
-
this.logger.debug(`File edited: ${normalizedPath} (${occurrences} replacements)`);
|
|
394
|
-
return {
|
|
395
|
-
success: true,
|
|
396
|
-
path: normalizedPath,
|
|
397
|
-
changesCount: occurrences,
|
|
398
|
-
backupPath,
|
|
399
|
-
originalContent,
|
|
400
|
-
newContent
|
|
401
|
-
};
|
|
402
|
-
} catch (error) {
|
|
403
|
-
throw FileSystemError.editFailed(
|
|
404
|
-
normalizedPath,
|
|
405
|
-
error instanceof Error ? error.message : String(error)
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Create a backup of a file
|
|
411
|
-
*/
|
|
412
|
-
async createBackup(filePath) {
|
|
413
|
-
const backupDir = this.getBackupDir();
|
|
414
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
415
|
-
const basename = path.basename(filePath);
|
|
416
|
-
const backupFilename = `${basename}.${timestamp}.backup`;
|
|
417
|
-
const backupPath = path.join(backupDir, backupFilename);
|
|
418
|
-
try {
|
|
419
|
-
await fs.mkdir(backupDir, { recursive: true });
|
|
420
|
-
await fs.copyFile(filePath, backupPath);
|
|
421
|
-
this.logger.debug(`Backup created: ${backupPath}`);
|
|
422
|
-
await this.cleanupOldBackups();
|
|
423
|
-
return backupPath;
|
|
424
|
-
} catch (error) {
|
|
425
|
-
throw FileSystemError.backupFailed(
|
|
426
|
-
filePath,
|
|
427
|
-
error instanceof Error ? error.message : String(error)
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Clean up old backup files based on retention policy
|
|
433
|
-
*/
|
|
434
|
-
async cleanupOldBackups() {
|
|
435
|
-
if (!this.config.enableBackups) {
|
|
436
|
-
return 0;
|
|
437
|
-
}
|
|
438
|
-
let backupDir;
|
|
439
|
-
try {
|
|
440
|
-
backupDir = this.getBackupDir();
|
|
441
|
-
} catch (error) {
|
|
442
|
-
this.logger.warn(
|
|
443
|
-
`Failed to resolve backup directory: ${error instanceof Error ? error.message : String(error)}`
|
|
444
|
-
);
|
|
445
|
-
return 0;
|
|
446
|
-
}
|
|
447
|
-
try {
|
|
448
|
-
await fs.access(backupDir);
|
|
449
|
-
} catch {
|
|
450
|
-
return 0;
|
|
451
|
-
}
|
|
452
|
-
const cutoffDate = new Date(
|
|
453
|
-
Date.now() - this.config.backupRetentionDays * 24 * 60 * 60 * 1e3
|
|
454
|
-
);
|
|
455
|
-
let deletedCount = 0;
|
|
456
|
-
try {
|
|
457
|
-
const files = await fs.readdir(backupDir);
|
|
458
|
-
const backupFiles = files.filter((file) => file.endsWith(".backup"));
|
|
459
|
-
for (const file of backupFiles) {
|
|
460
|
-
const filePath = path.join(backupDir, file);
|
|
461
|
-
try {
|
|
462
|
-
const stats = await fs.stat(filePath);
|
|
463
|
-
if (stats.mtime < cutoffDate) {
|
|
464
|
-
await fs.unlink(filePath);
|
|
465
|
-
deletedCount++;
|
|
466
|
-
this.logger.debug(`Cleaned up old backup: ${file}`);
|
|
467
|
-
}
|
|
468
|
-
} catch (error) {
|
|
469
|
-
this.logger.warn(
|
|
470
|
-
`Failed to process backup file ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
if (deletedCount > 0) {
|
|
475
|
-
this.logger.info(`Backup cleanup: removed ${deletedCount} old backup files`);
|
|
476
|
-
}
|
|
477
|
-
return deletedCount;
|
|
478
|
-
} catch (error) {
|
|
479
|
-
this.logger.warn(
|
|
480
|
-
`Failed to cleanup backup directory: ${error instanceof Error ? error.message : String(error)}`
|
|
481
|
-
);
|
|
482
|
-
return 0;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* Get service configuration
|
|
487
|
-
*/
|
|
488
|
-
getConfig() {
|
|
489
|
-
return { ...this.config };
|
|
490
|
-
}
|
|
491
|
-
/**
|
|
492
|
-
* Check if a path is allowed (async for non-blocking symlink resolution)
|
|
493
|
-
*/
|
|
494
|
-
async isPathAllowed(filePath) {
|
|
495
|
-
const validation = await this.pathValidator.validatePath(filePath);
|
|
496
|
-
return validation.isValid;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
export {
|
|
500
|
-
FileSystemService
|
|
501
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var filesystem_exports = {};
|
|
20
|
-
__export(filesystem_exports, {
|
|
21
|
-
FileSystemError: () => import_errors.FileSystemError,
|
|
22
|
-
FileSystemErrorCode: () => import_error_codes.FileSystemErrorCode,
|
|
23
|
-
FileSystemService: () => import_filesystem_service.FileSystemService,
|
|
24
|
-
PathValidator: () => import_path_validator.PathValidator
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(filesystem_exports);
|
|
27
|
-
var import_filesystem_service = require("./filesystem-service.js");
|
|
28
|
-
var import_path_validator = require("./path-validator.js");
|
|
29
|
-
var import_errors = require("./errors.js");
|
|
30
|
-
var import_error_codes = require("./error-codes.js");
|
|
31
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
32
|
-
0 && (module.exports = {
|
|
33
|
-
FileSystemError,
|
|
34
|
-
FileSystemErrorCode,
|
|
35
|
-
FileSystemService,
|
|
36
|
-
PathValidator
|
|
37
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FileSystem Module
|
|
3
|
-
*
|
|
4
|
-
* Exports file system service, types, errors, and utilities
|
|
5
|
-
*/
|
|
6
|
-
export { FileSystemService } from './filesystem-service.js';
|
|
7
|
-
export { PathValidator, type DirectoryApprovalChecker } from './path-validator.js';
|
|
8
|
-
export { FileSystemError } from './errors.js';
|
|
9
|
-
export { FileSystemErrorCode } from './error-codes.js';
|
|
10
|
-
export type { FileSystemConfig, FileContent, ReadFileOptions, GlobOptions, GlobResult, GrepOptions, SearchResult, SearchMatch, WriteFileOptions, WriteResult, EditFileOptions, EditResult, EditOperation, FileMetadata, PathValidation, BufferEncoding, } from './types.js';
|
|
11
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filesystem/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EACR,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,WAAW,EACX,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,UAAU,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/filesystem/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import "../chunk-PTJYTZNU.js";
|
|
2
|
-
import { FileSystemService } from "./filesystem-service.js";
|
|
3
|
-
import { PathValidator } from "./path-validator.js";
|
|
4
|
-
import { FileSystemError } from "./errors.js";
|
|
5
|
-
import { FileSystemErrorCode } from "./error-codes.js";
|
|
6
|
-
export {
|
|
7
|
-
FileSystemError,
|
|
8
|
-
FileSystemErrorCode,
|
|
9
|
-
FileSystemService,
|
|
10
|
-
PathValidator
|
|
11
|
-
};
|