@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/CHANGELOG.md +14 -0
- package/dist/codebuddy-headless.js +161 -99
- package/dist/codebuddy.js +173 -111
- package/dist/web-ui/docs/cn/cli/prewarm.md +0 -1
- package/dist/web-ui/docs/en/cli/prewarm.md +0 -1
- package/dist/web-ui/docs/search-index-zh.json +1 -1
- package/package.json +1 -1
- package/product.cloudhosted.json +2 -2
- package/product.internal.json +2 -2
- package/product.ioa.json +2 -2
- package/product.json +2 -2
- package/product.selfhosted.json +2 -2
- package/vendor/shim/genie-safe-delete.cjs +53 -0
- package/vendor/shim/safe-bin/safe-delete-common.sh +108 -0
- package/vendor/shim/sitecustomize.py +62 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tencent-ai/codebuddy-code",
|
|
3
|
-
"version": "2.109.
|
|
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",
|
package/product.cloudhosted.json
CHANGED
|
@@ -824,6 +824,6 @@
|
|
|
824
824
|
"SelectImage": true,
|
|
825
825
|
"SkipToolCallSupportCheck": true
|
|
826
826
|
},
|
|
827
|
-
"commit": "
|
|
828
|
-
"date": "2026-06-
|
|
827
|
+
"commit": "58a450bdac648b0df71fe5e176e35583306d6003",
|
|
828
|
+
"date": "2026-06-23T04:53:58.543Z"
|
|
829
829
|
}
|
package/product.internal.json
CHANGED
package/product.ioa.json
CHANGED
package/product.json
CHANGED
package/product.selfhosted.json
CHANGED
|
@@ -339,6 +339,6 @@
|
|
|
339
339
|
"ScheduledTasks": true,
|
|
340
340
|
"SkipToolCallSupportCheck": true
|
|
341
341
|
},
|
|
342
|
-
"commit": "
|
|
343
|
-
"date": "2026-06-
|
|
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 错误 → 让原生抛
|