From 6f4fa5076fd21229e8d0846f7e33e1abbc3b1e80 Mon Sep 17 00:00:00 2001 From: shuiyu67 Date: Fri, 7 Aug 2026 19:53:16 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=E8=B7=A8=E5=B9=B3=E5=8F=B0=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E4=B8=B2=E5=8F=B0=EF=BC=88Linux/Mac=20=E9=A3=8E?= =?UTF-8?q?=E6=A0=BC=E8=B7=AF=E5=BE=84=E5=9C=A8=20Windows=20=E4=B8=8B?= =?UTF-8?q?=E8=A2=AB=E6=8B=BC=E6=88=90=20C:/home/user/...=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 _resolve_cross_platform_path:Windows 下将 /home/<用户>/…、/Users/<用户>/…、/root/… 映射到当前用户家目录对应位置 - file_manager 全部 10 个操作的路径解析改用该归一化函数 - 更新 CHANGELOG v8.0.2 条目(覆盖 8.0.2 发行) --- CHANGELOG.md | 4 ++++ plugins/file_manager.py | 45 ++++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc728c..7b0052f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ - 上一版修复导致聊天界面完全看不到情话,改为:非 TTY 在请求开始输出一句随机情话(`💭` 前缀,只打一次、零残帧) - `love.txt` 读取时过滤 `http/https` 行,避免把反战标语 / 网址当情话甩出 - 真 TTY 逐帧情话 + spinner 动画、TUI 模式均不受影响 +- **修复文件管理器跨平台路径串台**(本次提交) + - 模型在 Windows 主机上常给出 Linux/Mac 风格绝对路径(如 `/home/user/Desktop`、`/Users/<谁>/Desktop`、`/root/Desktop`),`os.path.abspath` 在 Windows 下会错误地拼成 `C:\home\user\Desktop` 并报「路径不存在」 + - 新增 `_resolve_cross_platform_path`:Windows 下把 `/home/<用户>/…`、`/Users/<用户>/…`、`/root/…` 统一映射到当前用户家目录对应位置(`~/Desktop` 等);Linux/Mac 下这些路径本身合法,原样保留 + - `file_manager` 全部 10 个操作(list/read/write/append/copy/move/delete/search/info/mkdir)的路径解析均改用该归一化函数 ### 🛠 工程 diff --git a/plugins/file_manager.py b/plugins/file_manager.py index 5c009b4..872f5d7 100644 --- a/plugins/file_manager.py +++ b/plugins/file_manager.py @@ -3,6 +3,7 @@ 聚焦文件系统操作,代码编辑交给 code_editor """ import os +import re import json import shutil import sys @@ -10,6 +11,30 @@ from datetime import datetime from pathlib import Path from typing import List + +# ── 跨平台路径归一化 ────────────────────────────────────────────── +# 模型常给出 Linux/Mac 风格绝对路径(如 /home//Desktop、 +# /Users//Desktop、/root/Desktop)。在 Windows 下 os.path.abspath 会 +# 错误地拼成 C:\home\user\Desktop。这里把这类路径映射到当前用户家目录 +# 对应位置;Linux/Mac 下这些路径本身合法,原样保留。 +_HOME_OR_USER_RE = re.compile(r'^/(?:home|Users)/[^/]+(/.*)?$') +_ROOT_RE = re.compile(r'^/root(/.*)?$') + + +def _resolve_cross_platform_path(path: str) -> str: + """把模型给出的路径归一化到当前平台后再转绝对路径。""" + if os.name == 'nt' and path: + p = path.replace('\\', '/') + m = _HOME_OR_USER_RE.match(p) + if m: + rest = (m.group(1) or '').lstrip('/') + return os.path.join(os.path.expanduser('~'), rest) + m = _ROOT_RE.match(p) + if m: + rest = (m.group(1) or '').lstrip('/') + return os.path.join(os.path.expanduser('~'), rest) + return os.path.abspath(path) + # 添加项目根目录到 path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from xcli_core.tool_result import ToolResult, ErrorCode @@ -158,7 +183,7 @@ class Liugin: path = args[i] i += 1 - path = os.path.abspath(path) + path = _resolve_cross_platform_path(path) if not os.path.isdir(path): return ToolResult.not_found(f"目录: {path}") @@ -193,7 +218,7 @@ class Liugin: def _op_read(self, args: List[str]): if not args: return ToolResult.missing("文件路径") - file_path = os.path.abspath(args[0]) + file_path = _resolve_cross_platform_path(args[0]) max_lines = None i = 1 while i < len(args): @@ -235,7 +260,7 @@ class Liugin: def _op_write(self, args: List[str]): if len(args) < 2: return ToolResult.fail("格式: write <文件> -c <内容>", ErrorCode.MISSING_ARGS) - fp = os.path.abspath(args[0]) + fp = _resolve_cross_platform_path(args[0]) content = None i = 1 while i < len(args): @@ -256,7 +281,7 @@ class Liugin: def _op_append(self, args: List[str]): if len(args) < 2: return ToolResult.fail("格式: append <文件> -c <内容>", ErrorCode.MISSING_ARGS) - fp = os.path.abspath(args[0]) + fp = _resolve_cross_platform_path(args[0]) content = None i = 1 while i < len(args): @@ -278,7 +303,7 @@ class Liugin: def _op_copy(self, args: List[str]): if len(args) < 2: return ToolResult.missing("源和目标路径") - src, dst = os.path.abspath(args[0]), os.path.abspath(args[1]) + src, dst = _resolve_cross_platform_path(args[0]), _resolve_cross_platform_path(args[1]) if not os.path.exists(src): return ToolResult.not_found(f"源: {src}") os.makedirs(os.path.dirname(dst) or ".", exist_ok=True) @@ -291,7 +316,7 @@ class Liugin: def _op_move(self, args: List[str]): if len(args) < 2: return ToolResult.missing("源和目标路径") - src, dst = os.path.abspath(args[0]), os.path.abspath(args[1]) + src, dst = _resolve_cross_platform_path(args[0]), _resolve_cross_platform_path(args[1]) if not os.path.exists(src): return ToolResult.not_found(f"源: {src}") os.makedirs(os.path.dirname(dst) or ".", exist_ok=True) @@ -301,7 +326,7 @@ class Liugin: def _op_delete(self, args: List[str]): if not args: return ToolResult.missing("路径") - path = os.path.abspath(args[0]) + path = _resolve_cross_platform_path(args[0]) recursive = "-r" in args if not os.path.exists(path): return ToolResult.not_found(f"路径: {path}") @@ -321,7 +346,7 @@ class Liugin: def _op_search(self, args: List[str]): if not args: return ToolResult.missing("搜索目录") - directory = os.path.abspath(args[0]) + directory = _resolve_cross_platform_path(args[0]) pattern, content_search = None, None i = 1 while i < len(args): @@ -363,7 +388,7 @@ class Liugin: def _op_info(self, args: List[str]): if not args: return ToolResult.missing("路径") - path = os.path.abspath(args[0]) + path = _resolve_cross_platform_path(args[0]) if not os.path.exists(path): return ToolResult.not_found(f"路径: {path}") @@ -383,7 +408,7 @@ class Liugin: def _op_mkdir(self, args: List[str]): if not args: return ToolResult.missing("目录路径") - path = os.path.abspath(args[0]) + path = _resolve_cross_platform_path(args[0]) if os.path.exists(path): return ToolResult.fail(f"已存在: {path}", ErrorCode.INVALID_ARGS) os.makedirs(path, exist_ok=True)