@tencent-ai/codebuddy-code 2.109.1 → 2.109.2

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": "@tencent-ai/codebuddy-code",
3
- "version": "2.109.1",
3
+ "version": "2.109.2",
4
4
  "description": "Use CodeBuddy, Tencent's AI assistant, right from your terminal. CodeBuddy can understand your codebase, edit files, run terminal commands, and handle entire workflows for you.",
5
5
  "main": "lib/node/index.js",
6
6
  "typings": "lib/node/index.d.ts",
@@ -824,6 +824,6 @@
824
824
  "SelectImage": true,
825
825
  "SkipToolCallSupportCheck": true
826
826
  },
827
- "commit": "bd65832d49c4ca8fac14fbf709684cc34f73f9a3",
828
- "date": "2026-06-22T01:32:01.952Z"
827
+ "commit": "58a450bdac648b0df71fe5e176e35583306d6003",
828
+ "date": "2026-06-23T04:53:58.543Z"
829
829
  }
@@ -703,6 +703,6 @@
703
703
  }
704
704
  }
705
705
  },
706
- "commit": "bd65832d49c4ca8fac14fbf709684cc34f73f9a3",
707
- "date": "2026-06-22T01:32:01.952Z"
706
+ "commit": "58a450bdac648b0df71fe5e176e35583306d6003",
707
+ "date": "2026-06-23T04:53:58.445Z"
708
708
  }
package/product.ioa.json CHANGED
@@ -1106,6 +1106,6 @@
1106
1106
  }
1107
1107
  }
1108
1108
  },
1109
- "commit": "bd65832d49c4ca8fac14fbf709684cc34f73f9a3",
1110
- "date": "2026-06-22T01:32:02.034Z"
1109
+ "commit": "58a450bdac648b0df71fe5e176e35583306d6003",
1110
+ "date": "2026-06-23T04:53:58.508Z"
1111
1111
  }
package/product.json CHANGED
@@ -1757,6 +1757,6 @@
1757
1757
  "deferLoading": true
1758
1758
  }
1759
1759
  ],
1760
- "commit": "bd65832d49c4ca8fac14fbf709684cc34f73f9a3",
1761
- "date": "2026-06-22T01:32:02.010Z"
1760
+ "commit": "58a450bdac648b0df71fe5e176e35583306d6003",
1761
+ "date": "2026-06-23T04:53:58.431Z"
1762
1762
  }
@@ -339,6 +339,6 @@
339
339
  "ScheduledTasks": true,
340
340
  "SkipToolCallSupportCheck": true
341
341
  },
342
- "commit": "bd65832d49c4ca8fac14fbf709684cc34f73f9a3",
343
- "date": "2026-06-22T01:32:01.965Z"
342
+ "commit": "58a450bdac648b0df71fe5e176e35583306d6003",
343
+ "date": "2026-06-23T04:53:58.498Z"
344
344
  }
@@ -37,6 +37,52 @@ if (!SESSION_ID) {
37
37
  const PLATFORM = process.platform; // 'darwin' | 'win32' | 'linux' | ...
38
38
  const REPORT_PATH = process.env.CODEBUDDY_SAFE_DELETE_REPORT_PATH;
39
39
 
40
+ function normalizePathForCompare(filePath) {
41
+ const resolved = path.resolve(filePath);
42
+ return PLATFORM === 'win32' ? resolved.toLowerCase() : resolved;
43
+ }
44
+
45
+ function isUsableTempRoot(filePath) {
46
+ try {
47
+ const resolved = path.resolve(filePath);
48
+ if (path.parse(resolved).root === resolved) return false;
49
+ return fs.statSync(resolved).isDirectory();
50
+ } catch (_) {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ function buildOsTmpDirs() {
56
+ const candidates = [
57
+ os.tmpdir(),
58
+ ];
59
+ if (PLATFORM !== 'win32') {
60
+ candidates.push('/tmp');
61
+ candidates.push('/private/tmp');
62
+ }
63
+
64
+ const roots = [];
65
+ const seen = new Set();
66
+ for (const candidate of candidates) {
67
+ if (!candidate || !isUsableTempRoot(candidate)) continue;
68
+ const root = normalizePathForCompare(candidate);
69
+ if (seen.has(root)) continue;
70
+ seen.add(root);
71
+ roots.push(root);
72
+ }
73
+ return roots;
74
+ }
75
+
76
+ const OS_TMP_DIRS = buildOsTmpDirs();
77
+
78
+ function isUnderOsTmpDir(absPath) {
79
+ const target = normalizePathForCompare(absPath);
80
+ return OS_TMP_DIRS.some(root => {
81
+ const rel = path.relative(root, target);
82
+ return !!rel && rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
83
+ });
84
+ }
85
+
40
86
  function recordTrash(absPath) {
41
87
  if (!REPORT_PATH) return;
42
88
  try {
@@ -417,6 +463,7 @@ function toAbsPath(filePath) {
417
463
  function makeSyncWrapper(orig, requireDir) {
418
464
  return function (filePath, ...rest) {
419
465
  const absPath = toAbsPath(filePath);
466
+ if (isUnderOsTmpDir(absPath)) return orig(filePath, ...rest);
420
467
  if (requireDir) {
421
468
  const st = safeLstat(absPath);
422
469
  if (st === null) return orig(filePath, ...rest); // ENOENT → 原生抛
@@ -467,6 +514,7 @@ function makeCallbackWrapper(orig, requireDir) {
467
514
  return orig(filePath, ...rest);
468
515
  }
469
516
  const absPath = toAbsPath(filePath);
517
+ if (isUnderOsTmpDir(absPath)) return orig(filePath, ...rest);
470
518
 
471
519
  const st = safeLstat(absPath);
472
520
  if (st === null) return orig(filePath, ...rest); // ENOENT → 原生抛
@@ -495,6 +543,7 @@ function makeCallbackWrapper(orig, requireDir) {
495
543
  function makePromiseWrapper(orig, requireDir) {
496
544
  return async function (filePath, ...rest) {
497
545
  const absPath = toAbsPath(filePath);
546
+ if (isUnderOsTmpDir(absPath)) return orig(filePath, ...rest);
498
547
  const st = safeLstat(absPath);
499
548
  if (st === null) return orig(filePath, ...rest); // ENOENT → 原生抛
500
549
  if (st) {
@@ -519,6 +568,10 @@ function makePromiseWrapper(orig, requireDir) {
519
568
  * throws → 回收站操作失败(fail-closed),调用方不得真删
520
569
  */
521
570
  function tryRm(absPath, opts) {
571
+ if (isUnderOsTmpDir(absPath)) {
572
+ return { done: false };
573
+ }
574
+
522
575
  const recursive = !!(opts && opts.recursive);
523
576
  const force = !!(opts && opts.force);
524
577
 
@@ -50,6 +50,93 @@ is_dir_empty() {
50
50
  [ -z "$(ls -A "$p" 2>/dev/null)" ]
51
51
  }
52
52
 
53
+ safe_delete_normalize_path() {
54
+ local p="$1"
55
+ while [ "$p" != "/" ] && [ "${p%/}" != "$p" ]; do
56
+ p="${p%/}"
57
+ done
58
+ printf '%s' "$p"
59
+ }
60
+
61
+ safe_delete_to_unix_path_if_possible() {
62
+ local p="$1"
63
+ if command -v cygpath >/dev/null 2>&1; then
64
+ cygpath -u "$p" 2>/dev/null && return 0
65
+ fi
66
+ printf '%s' "$p"
67
+ }
68
+
69
+ safe_delete_resolve_os_tmp_dir() {
70
+ local tmp="${TMPDIR:-${TMP:-${TEMP:-/tmp}}}"
71
+ safe_delete_to_unix_path_if_possible "$tmp"
72
+ }
73
+
74
+ safe_delete_abs_path() {
75
+ local p="$1"
76
+ local abs
77
+ if command -v python3 >/dev/null 2>&1; then
78
+ abs="$(python3 -c 'import os,sys; print(os.path.abspath(sys.argv[1]))' "$p" 2>/dev/null)" && {
79
+ safe_delete_to_unix_path_if_possible "$abs"
80
+ return 0
81
+ }
82
+ elif command -v python >/dev/null 2>&1; then
83
+ abs="$(python -c 'import os,sys; print(os.path.abspath(sys.argv[1]))' "$p" 2>/dev/null)" && {
84
+ safe_delete_to_unix_path_if_possible "$abs"
85
+ return 0
86
+ }
87
+ fi
88
+ case "$p" in
89
+ /*|[A-Za-z]:/*) abs="$p" ;;
90
+ *) abs="$PWD/$p" ;;
91
+ esac
92
+ abs="$(CDPATH= cd -P -- "$(dirname "$abs")" 2>/dev/null && printf '%s/%s' "$PWD" "$(basename "$abs")" || printf '%s' "$abs")"
93
+ safe_delete_to_unix_path_if_possible "$abs"
94
+ }
95
+
96
+ safe_delete_tmp_root_allowed() {
97
+ local root="$1"
98
+ [ -n "$root" ] || return 1
99
+ [ "$root" != "/" ] || return 1
100
+ [ -d "$root" ] || return 1
101
+ return 0
102
+ }
103
+
104
+ safe_delete_build_os_tmp_dirs() {
105
+ local seen="|" candidate root
106
+ for candidate in \
107
+ "$(safe_delete_resolve_os_tmp_dir)" \
108
+ "/tmp" \
109
+ "/private/tmp"
110
+ do
111
+ [ -n "$candidate" ] || continue
112
+ root="$(safe_delete_normalize_path "$(safe_delete_abs_path "$candidate")")"
113
+ safe_delete_tmp_root_allowed "$root" || continue
114
+ case "$seen" in
115
+ *"|$root|"*) ;;
116
+ *)
117
+ seen="${seen}${root}|"
118
+ printf '%s\n' "$root"
119
+ ;;
120
+ esac
121
+ done
122
+ }
123
+
124
+ SAFE_DELETE_OS_TMP_DIRS="$(safe_delete_build_os_tmp_dirs)"
125
+
126
+ safe_delete_is_under_os_tmp_dir() {
127
+ local abs
128
+ abs="$(safe_delete_normalize_path "$(safe_delete_abs_path "$1")")"
129
+ while IFS= read -r tmp_dir; do
130
+ [ -n "$tmp_dir" ] || continue
131
+ case "$abs" in
132
+ "$tmp_dir"/*) return 0 ;;
133
+ esac
134
+ done <<EOF
135
+ $SAFE_DELETE_OS_TMP_DIRS
136
+ EOF
137
+ return 1
138
+ }
139
+
53
140
  # ---- platform-specific trash helpers -----------------------------------------
54
141
 
55
142
  trash_darwin() {
@@ -344,6 +431,16 @@ safe_delete_rm() {
344
431
  continue
345
432
  fi
346
433
  fi
434
+ if safe_delete_is_under_os_tmp_dir "$p"; then
435
+ local native_args=()
436
+ $force && native_args+=("-f")
437
+ $recursive && native_args+=("-r")
438
+ $allow_dir && native_args+=("-d")
439
+ if ! "$REAL_RM" "${native_args[@]}" -- "$p"; then
440
+ _exit=1
441
+ fi
442
+ continue
443
+ fi
347
444
  if ! try_trash "$p"; then
348
445
  _exit=1
349
446
  fi
@@ -390,6 +487,11 @@ safe_delete_unlink() {
390
487
  return 1
391
488
  fi
392
489
 
490
+ if safe_delete_is_under_os_tmp_dir "$target"; then
491
+ "$REAL_UNLINK" -- "$target"
492
+ return $?
493
+ fi
494
+
393
495
  try_trash "$target"
394
496
  return $?
395
497
  }
@@ -444,6 +546,12 @@ safe_delete_rmdir() {
444
546
  _exit=1
445
547
  continue
446
548
  fi
549
+ if safe_delete_is_under_os_tmp_dir "$p"; then
550
+ if ! "$REAL_RMDIR" -- "$p"; then
551
+ _exit=1
552
+ fi
553
+ continue
554
+ fi
447
555
  if ! try_trash "$p"; then
448
556
  _exit=1
449
557
  fi
@@ -17,6 +17,7 @@ import json
17
17
  import os
18
18
  import stat
19
19
  import sys
20
+ import tempfile
20
21
  import time
21
22
 
22
23
  # ---------------------------------------------------------------------------
@@ -275,6 +276,40 @@ if _SESSION_ID:
275
276
  _orig_path_rmdir = pathlib.Path.rmdir
276
277
  _report_path = os.environ.get("CODEBUDDY_SAFE_DELETE_REPORT_PATH")
277
278
 
279
+ def _path_for_compare(path):
280
+ return os.path.normcase(os.path.abspath(path))
281
+
282
+ def _is_usable_temp_root(path):
283
+ try:
284
+ root = _path_for_compare(path)
285
+ if os.path.dirname(root) == root:
286
+ return False
287
+ return os.path.isdir(root)
288
+ except Exception:
289
+ return False
290
+
291
+ def _build_os_tmp_dirs():
292
+ candidates = [
293
+ tempfile.gettempdir(),
294
+ ]
295
+ if sys.platform != "win32":
296
+ candidates.append("/tmp")
297
+ candidates.append("/private/tmp")
298
+
299
+ roots = []
300
+ seen = set()
301
+ for candidate in candidates:
302
+ if not candidate or not _is_usable_temp_root(candidate):
303
+ continue
304
+ root = _path_for_compare(candidate)
305
+ if root in seen:
306
+ continue
307
+ seen.add(root)
308
+ roots.append(root)
309
+ return roots
310
+
311
+ _os_tmp_dirs = _build_os_tmp_dirs()
312
+
278
313
  def _record_trash(abs_path):
279
314
  if not _report_path:
280
315
  return
@@ -388,6 +423,20 @@ if _SESSION_ID:
388
423
  except OSError:
389
424
  return False
390
425
 
426
+ def _is_under_os_tmp_dir(abs_path):
427
+ try:
428
+ target = _path_for_compare(os.fsdecode(abs_path))
429
+ except (TypeError, UnicodeError):
430
+ return False
431
+ for tmp_dir in _os_tmp_dirs:
432
+ try:
433
+ rel = os.path.relpath(target, tmp_dir)
434
+ except ValueError:
435
+ continue
436
+ if rel not in ("", ".") and rel != ".." and not rel.startswith(".." + os.sep) and not os.path.isabs(rel):
437
+ return True
438
+ return False
439
+
391
440
  # -----------------------------------------------------------------------
392
441
  # 包装:os.remove / os.unlink (文件,不能是目录)
393
442
  # -----------------------------------------------------------------------
@@ -397,6 +446,8 @@ if _SESSION_ID:
397
446
  if dir_fd is not None:
398
447
  return _orig_remove(path, dir_fd=dir_fd)
399
448
  abs_path = os.path.abspath(os.fspath(path))
449
+ if _is_under_os_tmp_dir(abs_path):
450
+ return _orig_remove(path)
400
451
  st = _safe_lstat(abs_path)
401
452
  if not st:
402
453
  return _orig_remove(path) # ENOENT / stat 错误 → 让原生抛
@@ -412,6 +463,8 @@ if _SESSION_ID:
412
463
  if dir_fd is not None:
413
464
  return _orig_rmdir(path, dir_fd=dir_fd)
414
465
  abs_path = os.path.abspath(os.fspath(path))
466
+ if _is_under_os_tmp_dir(abs_path):
467
+ return _orig_rmdir(path)
415
468
  st = _safe_lstat(abs_path)
416
469
  if not st:
417
470
  return _orig_rmdir(path) # ENOENT / stat 错误
@@ -434,6 +487,11 @@ if _SESSION_ID:
434
487
  path, ignore_errors=ignore_errors, onerror=onerror, **kwargs
435
488
  )
436
489
 
490
+ if _is_under_os_tmp_dir(abs_path):
491
+ return _orig_shutil_rmtree(
492
+ path, ignore_errors=ignore_errors, onerror=onerror, **kwargs
493
+ )
494
+
437
495
  st = _safe_lstat(abs_path)
438
496
  if not st:
439
497
  return _orig_shutil_rmtree(
@@ -461,6 +519,8 @@ if _SESSION_ID:
461
519
 
462
520
  def _safe_path_unlink(self, missing_ok=False):
463
521
  abs_path = os.path.abspath(str(self))
522
+ if _is_under_os_tmp_dir(abs_path):
523
+ return _orig_path_unlink(self, missing_ok=missing_ok)
464
524
  st = _safe_lstat(abs_path)
465
525
  if not st:
466
526
  if st is None and missing_ok:
@@ -472,6 +532,8 @@ if _SESSION_ID:
472
532
 
473
533
  def _safe_path_rmdir(self):
474
534
  abs_path = os.path.abspath(str(self))
535
+ if _is_under_os_tmp_dir(abs_path):
536
+ return _orig_path_rmdir(self)
475
537
  st = _safe_lstat(abs_path)
476
538
  if not st:
477
539
  return _orig_path_rmdir(self) # ENOENT / stat 错误 → 让原生抛