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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatcc-agent",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "CCLink Agent - bridges Claude Code CLI with instant messaging",
5
5
  "bin": {
6
6
  "chatcc": "src/cli.js"
@@ -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) return;
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
- console.error('[FileService] Tree reply failed (likely too large), sending names only:', sendErr.message, sendErr.stack);
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
- console.error('[FileService] Error reply also failed:', replyErr.message, replyErr.stack);
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
- console.log(`[FileService] mkdir: ${fullPath} (by ${from})`);
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,