feat: 编辑器实时探测并显示已关联虚拟环境
fix: 按系统 ANSI 代码页解码子进程输出,修复中文日志乱码
This commit is contained in:
@@ -27,9 +27,12 @@ $env:TASK_PILOT_DATABASE_URL = "sqlite://data/task_pilot.db"
|
||||
$env:TASK_PILOT_LOG_DIR = "logs"
|
||||
$env:TASK_PILOT_LOG_RETENTION_DAYS = "30"
|
||||
$env:TASK_PILOT_LOG_MAX_TOTAL_MB = "512"
|
||||
$env:TASK_PILOT_LOG_ENCODING = "auto"
|
||||
$env:TASK_PILOT_STATIC_DIR = "web"
|
||||
```
|
||||
|
||||
`TASK_PILOT_LOG_ENCODING` 默认 `auto`,会按 UTF-8 → 系统 ANSI 代码页(简体中文为 GBK)的顺序解码子进程输出;日志出现乱码时可显式指定 `gbk` 或 `utf-8`。
|
||||
|
||||
## 安全边界
|
||||
|
||||
- 服务只监听 `127.0.0.1`。
|
||||
|
||||
@@ -31,6 +31,15 @@
|
||||
- 仅按目录名判断不可靠,必须验证 venv 结构。
|
||||
- Windows 环境变量大小写不敏感,必须主动处理 `PATH`/`Path` 重复。
|
||||
|
||||
## 编辑器内的环境探测
|
||||
|
||||
任务编辑器需要在用户填写工作目录时就显示"会关联到哪个虚拟环境",否则用户只能靠事后查看日志猜测实际使用的解释器。
|
||||
|
||||
1. 新增 `POST /api/v1/python/venv` 探测接口,请求只包含 `working_dir`、`interpreter` 和可选的 `venv`。
|
||||
2. 探测与执行共用同一个 `resolve_venv`,解析顺序完全一致:显式配置 → 解释器路径推断 → 从工作目录向上搜索。
|
||||
3. 响应区分 `linked`、`missing`、`invalid`、`not_applicable`、`incomplete` 五种状态,并携带相对工作目录的展示路径,例如 `.venv\Scripts\python.exe`。
|
||||
4. 探测是只读的文件系统检查,属于写操作语义,因此同样要求管理令牌;工作目录不存在或非 Python 解释器时不会误报为"已关联"。
|
||||
|
||||
## 边界
|
||||
|
||||
- Conda 环境不会被识别为标准 venv;当前可通过显式解释器路径运行,但不会自动设置 `CONDA_PREFIX`。
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# ADR-0004:运行日志的输出编码
|
||||
|
||||
- 状态:已接受
|
||||
- 日期:2026-10-24
|
||||
|
||||
## 背景
|
||||
|
||||
任务日志原先按 UTF-8 无损方式(`String::from_utf8_lossy`)解码子进程输出。中文 Windows 上多数程序并不输出 UTF-8:Python 默认使用系统 ANSI 代码页(简体中文为 GBK/936),`cmd.exe`、部分 CLI 使用控制台代码页。结果日志里出现成片的 `����`,例如 `release/TaskPilot/logs/copyparty/1.log` 中真实的 `copyparty ������ (PID: 22056)`。
|
||||
|
||||
同一处还有两个次要问题:读取日志按 64 KiB 切块时可能切开多字节字符;终态任务只要日志超过 64 KiB,前端拿到首块就停止轮询,导致尾部丢失。
|
||||
|
||||
## 决策
|
||||
|
||||
1. 在写入侧解码:子进程每读完一行就解码为 UTF-8 再落盘,日志文件因此始终是合法 UTF-8,字节 offset 语义不变。
|
||||
2. 默认 `auto`:先按 UTF-8 严格解码,失败时回退到系统 ANSI 代码页(`GetACP`),并按代码页映射到 `encoding_rs` 编码(936→GBK、950→Big5、932→Shift_JIS、949→EUC-KR、125x→windows-125x)。
|
||||
3. 允许用 `TASK_PILOT_LOG_ENCODING` 指定编码标签(如 `gbk`、`utf-8`、`windows-1251`);显式指定时不再嗅探,行为完全确定。
|
||||
4. HTTP 任务响应体沿用同一解码器。
|
||||
5. 读取侧只做 UTF-8 边界对齐:块尾落在多字节字符中间时,把不完整字节留给下一次读取;只有真正非法的字节才替换为 `U+FFFD`。
|
||||
6. `complete` 改为"运行已结束且已读到文件末尾",修复长日志被截断的问题。
|
||||
|
||||
## 原因
|
||||
|
||||
- 解码放在写入侧,只需处理一次,SSE、增量读取、下载、`grep` 得到的内容完全一致。
|
||||
- 先 UTF-8 再回退,是因为 UTF-8 合法序列有强约束,误判概率低;反过来(先 GBK)会把大量 UTF-8 日志解坏。
|
||||
- 使用系统代码页而不是硬编码 GBK,保证非中文 Windows 上的行为同样正确。
|
||||
- 逐行解码避免多字节字符被缓冲区边界切开,且不受行尾差异影响。
|
||||
|
||||
## 边界
|
||||
|
||||
- 逐行启发式无法覆盖"一行内混用多种编码",也无法识别恰好构成合法 UTF-8 的 ANSI 字节序列;这两种情况需要显式设置 `TASK_PILOT_LOG_ENCODING`。
|
||||
- 修复前写入的历史日志已经是替换字符,不会恢复。
|
||||
- 非 Windows 平台回退编码为 UTF-8,不做代码页探测。
|
||||
Reference in New Issue
Block a user