chatcc-agent 0.5.3 → 0.5.4
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/package.json +1 -1
- package/src/file-service.js +56 -4
package/package.json
CHANGED
package/src/file-service.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const crypto = require('crypto');
|
|
5
5
|
const { BLOCKED_ROOTS } = require('./security');
|
|
6
|
+
const logUploader = require('./log-uploader');
|
|
6
7
|
|
|
7
8
|
const FILE_TREE_MAX_DEPTH = 3;
|
|
8
9
|
const FILE_READ_MAX_LINES = 100;
|
|
@@ -32,6 +33,10 @@ const SENSITIVE_FILE_NAMES = new Set([
|
|
|
32
33
|
]);
|
|
33
34
|
const HOME_DIR = os.homedir();
|
|
34
35
|
|
|
36
|
+
function fileLog(level, msg, extra) {
|
|
37
|
+
logUploader.collect(level, msg, { tag: 'FileService', module: 'file-service', ...(extra || {}) });
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
function isBinary(filePath) {
|
|
36
41
|
const ext = path.extname(filePath).toLowerCase();
|
|
37
42
|
if (BINARY_EXTENSIONS.has(ext)) return true;
|
|
@@ -177,7 +182,10 @@ class FileService {
|
|
|
177
182
|
handlePermissionResponse(data) {
|
|
178
183
|
const { request_id, approved, remember } = data;
|
|
179
184
|
const pending = this._pendingPermissions.get(request_id);
|
|
180
|
-
if (!pending)
|
|
185
|
+
if (!pending) {
|
|
186
|
+
fileLog('WARN', 'permission response without pending request', { request_id });
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
181
189
|
|
|
182
190
|
clearTimeout(pending.timer);
|
|
183
191
|
this._pendingPermissions.delete(request_id);
|
|
@@ -193,12 +201,14 @@ class FileService {
|
|
|
193
201
|
const depth = Math.min(Math.max(1, data.depth || FILE_TREE_MAX_DEPTH), FILE_TREE_MAX_DEPTH);
|
|
194
202
|
const resolved = resolvePath(dirPath);
|
|
195
203
|
if (!resolved) {
|
|
204
|
+
fileLog('WARN', 'file tree invalid path', { from, request_id, path: dirPath });
|
|
196
205
|
await this._reply(from, 'file_tree_response', { request_id, error: 'Invalid path' });
|
|
197
206
|
return;
|
|
198
207
|
}
|
|
199
208
|
|
|
200
209
|
const safePath = await this.checkPermission(from, resolved, 'list', sessionConstraint);
|
|
201
210
|
if (!safePath) {
|
|
211
|
+
fileLog('WARN', 'file tree access denied', { from, request_id, path: dirPath });
|
|
202
212
|
await this._reply(from, 'file_tree_response', { request_id, error: 'Access denied' });
|
|
203
213
|
return;
|
|
204
214
|
}
|
|
@@ -221,7 +231,13 @@ class FileService {
|
|
|
221
231
|
});
|
|
222
232
|
} catch (sendErr) {
|
|
223
233
|
// Payload too large — retry with minimal info
|
|
224
|
-
|
|
234
|
+
fileLog('WARN', 'file tree reply too large; retrying light tree', {
|
|
235
|
+
from,
|
|
236
|
+
request_id,
|
|
237
|
+
path: dirPath,
|
|
238
|
+
msg: sendErr.message,
|
|
239
|
+
stack: sendErr.stack,
|
|
240
|
+
});
|
|
225
241
|
const lightTree = { name: tree.name, type: 'directory', children: tree.children.map(c => ({ name: c.name, type: c.type })) };
|
|
226
242
|
await this._reply(from, 'file_tree_response', {
|
|
227
243
|
request_id,
|
|
@@ -236,7 +252,13 @@ class FileService {
|
|
|
236
252
|
error: e.message,
|
|
237
253
|
});
|
|
238
254
|
} catch (replyErr) {
|
|
239
|
-
|
|
255
|
+
fileLog('ERROR', 'file tree error reply failed', {
|
|
256
|
+
from,
|
|
257
|
+
request_id,
|
|
258
|
+
path: dirPath,
|
|
259
|
+
msg: replyErr.message,
|
|
260
|
+
stack: replyErr.stack,
|
|
261
|
+
});
|
|
240
262
|
}
|
|
241
263
|
}
|
|
242
264
|
}
|
|
@@ -290,12 +312,14 @@ class FileService {
|
|
|
290
312
|
const { request_id, path: filePath, start_line = 1, end_line } = data;
|
|
291
313
|
const resolved = resolvePath(filePath);
|
|
292
314
|
if (!resolved) {
|
|
315
|
+
fileLog('WARN', 'file read invalid path', { from, request_id, path: filePath });
|
|
293
316
|
await this._reply(from, 'file_read_response', { request_id, error: 'Invalid path' });
|
|
294
317
|
return;
|
|
295
318
|
}
|
|
296
319
|
|
|
297
320
|
const safePath = await this.checkPermission(from, resolved, 'read', sessionConstraint);
|
|
298
321
|
if (!safePath) {
|
|
322
|
+
fileLog('WARN', 'file read access denied', { from, request_id, path: filePath });
|
|
299
323
|
await this._reply(from, 'file_read_response', { request_id, error: 'Access denied' });
|
|
300
324
|
return;
|
|
301
325
|
}
|
|
@@ -354,6 +378,13 @@ class FileService {
|
|
|
354
378
|
language: ext || 'text',
|
|
355
379
|
});
|
|
356
380
|
} catch (e) {
|
|
381
|
+
fileLog('ERROR', 'file read failed', {
|
|
382
|
+
from,
|
|
383
|
+
request_id,
|
|
384
|
+
path: filePath,
|
|
385
|
+
msg: e.message,
|
|
386
|
+
stack: e.stack,
|
|
387
|
+
});
|
|
357
388
|
await this._reply(from, 'file_read_response', {
|
|
358
389
|
request_id,
|
|
359
390
|
error: e.message,
|
|
@@ -368,6 +399,7 @@ class FileService {
|
|
|
368
399
|
if (!name || typeof name !== 'string' || name.length > 255 ||
|
|
369
400
|
name.includes('/') || name.includes('\0') ||
|
|
370
401
|
name === '.' || name === '..') {
|
|
402
|
+
fileLog('WARN', 'file mkdir invalid folder name', { from, request_id, path: parentPath, name });
|
|
371
403
|
await this._reply(from, 'file_mkdir_response', {
|
|
372
404
|
request_id,
|
|
373
405
|
error: 'Invalid folder name',
|
|
@@ -377,12 +409,14 @@ class FileService {
|
|
|
377
409
|
|
|
378
410
|
const resolved = resolvePath(parentPath);
|
|
379
411
|
if (!resolved) {
|
|
412
|
+
fileLog('WARN', 'file mkdir invalid parent path', { from, request_id, path: parentPath, name });
|
|
380
413
|
await this._reply(from, 'file_mkdir_response', { request_id, error: 'Invalid parent path' });
|
|
381
414
|
return;
|
|
382
415
|
}
|
|
383
416
|
|
|
384
417
|
const safePath = await this.checkPermission(from, resolved, 'mkdir', sessionConstraint);
|
|
385
418
|
if (!safePath) {
|
|
419
|
+
fileLog('WARN', 'file mkdir access denied', { from, request_id, path: parentPath, name });
|
|
386
420
|
await this._reply(from, 'file_mkdir_response', { request_id, error: 'Access denied' });
|
|
387
421
|
return;
|
|
388
422
|
}
|
|
@@ -401,13 +435,21 @@ class FileService {
|
|
|
401
435
|
}
|
|
402
436
|
|
|
403
437
|
fs.mkdirSync(fullPath, { recursive: false });
|
|
404
|
-
|
|
438
|
+
fileLog('INFO', 'file mkdir created', { from, request_id, path: fullPath, name });
|
|
405
439
|
await this._reply(from, 'file_mkdir_response', {
|
|
406
440
|
request_id,
|
|
407
441
|
path: fullPath,
|
|
408
442
|
name,
|
|
409
443
|
});
|
|
410
444
|
} catch (e) {
|
|
445
|
+
fileLog('ERROR', 'file mkdir failed', {
|
|
446
|
+
from,
|
|
447
|
+
request_id,
|
|
448
|
+
path: parentPath,
|
|
449
|
+
name,
|
|
450
|
+
msg: e.message,
|
|
451
|
+
stack: e.stack,
|
|
452
|
+
});
|
|
411
453
|
await this._reply(from, 'file_mkdir_response', { request_id, error: e.message });
|
|
412
454
|
}
|
|
413
455
|
}
|
|
@@ -416,12 +458,14 @@ class FileService {
|
|
|
416
458
|
const { request_id, pattern, path: searchPath } = data;
|
|
417
459
|
const resolved = resolvePath(searchPath);
|
|
418
460
|
if (!resolved) {
|
|
461
|
+
fileLog('WARN', 'file search invalid path', { from, request_id, path: searchPath, pattern });
|
|
419
462
|
await this._reply(from, 'file_search_response', { request_id, error: 'Invalid path' });
|
|
420
463
|
return;
|
|
421
464
|
}
|
|
422
465
|
|
|
423
466
|
const safePath = await this.checkPermission(from, resolved, 'search', sessionConstraint);
|
|
424
467
|
if (!safePath) {
|
|
468
|
+
fileLog('WARN', 'file search access denied', { from, request_id, path: searchPath, pattern });
|
|
425
469
|
await this._reply(from, 'file_search_response', { request_id, error: 'Access denied' });
|
|
426
470
|
return;
|
|
427
471
|
}
|
|
@@ -436,6 +480,14 @@ class FileService {
|
|
|
436
480
|
})),
|
|
437
481
|
});
|
|
438
482
|
} catch (e) {
|
|
483
|
+
fileLog('ERROR', 'file search failed', {
|
|
484
|
+
from,
|
|
485
|
+
request_id,
|
|
486
|
+
path: searchPath,
|
|
487
|
+
pattern,
|
|
488
|
+
msg: e.message,
|
|
489
|
+
stack: e.stack,
|
|
490
|
+
});
|
|
439
491
|
await this._reply(from, 'file_search_response', {
|
|
440
492
|
request_id,
|
|
441
493
|
error: e.message,
|