feat: 升级至 v0.3.12 — 文件与代码类工具全面优化(16 项)
【阶段一 · 紧急修复 F1】 - F1-1 file_editor regex 计数 bug:强制 g 标志 + 同一 RegExp 实例做 match+replace - F1-2 code_search 统一 safeResolvePath(含路径遍历 + MEMORY.md 拦截) - F1-3 git_commit amend 模式:message 可选 + --no-edit 防止编辑器 hang - F1-4 write_file append 模式原子化:显式 open(O_APPEND)+write+fsync+close 【阶段二 · 增强现有工具 F2】 - F2-1 read_file 智能编码检测:BOM(UTF-8/UTF-16 LE/BE) + GBK 降级 - F2-2 read_file 支持 tail 模式(读取末尾 N 行,适用日志) - F2-3 search_files 二进制过滤 + 智能编码检测 - F2-4 search_files 多 glob 匹配(逗号分隔,如 *.ts,*.js) - F2-5 file_editor 新增 find_replace 操作(字面量替换,规避正则歧义) - F2-6 file_editor regex 支持跨行匹配(multiline 参数) - F2-7 file_editor 新增 backup 参数(编辑前 .bak 备份) 【阶段三 · 新增工具 F3】 - F3-1 file_move:双路径校验 + overwrite + 自动建父目录 + rename 原子 - F3-2 file_info:大小/时间/类型/编码/二进制/权限位 【阶段四 · 优化 F4】 - F4-1 diff_viewer Uint32Array→Uint16Array(省一半内存)+ safeResolvePath + 智能编码 - F4-2 错误处理统一:diff-viewer/file-editor/code-search catch 块改用 extractErrorMessage 【阶段五 · 验证 F5】 - tsc --noEmit 类型检查通过 - 人工审查通过:路径校验/错误处理/原子性/资源释放/权限策略/导出注册 工具数量:13 → 15(新增 file_move / file_info)
This commit is contained in:
@@ -8,11 +8,14 @@
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
import { resolve } from 'path';
|
||||
import type { IMetonaTool, ToolExecutionContext } from '../../types/metona-tool';
|
||||
import type { MetonaToolDef } from '../../../harness/types';
|
||||
import { MetonaToolCategory, MetonaRiskLevel } from '../../../harness/types';
|
||||
import { isProtectedWorkspaceFile, isPathWithinWorkspace } from './file-guard';
|
||||
import {
|
||||
safeResolvePath,
|
||||
extractErrorMessage,
|
||||
decodeBufferWithDetection,
|
||||
} from './file-guard';
|
||||
|
||||
interface DiffLine {
|
||||
type: 'context' | 'added' | 'removed';
|
||||
@@ -30,8 +33,9 @@ function computeDiff(oldLines: string[], newLines: string[]): DiffLine[] {
|
||||
const sm = oldSliced.length;
|
||||
const sn = newSliced.length;
|
||||
|
||||
// D4.1: 使用 Uint32Array 一维数组替代 number[][],节省内存
|
||||
const lcs = new Uint32Array((sm + 1) * (sn + 1));
|
||||
// F4-1: 使用 Uint16Array 一维数组替代 Uint32Array,节省一半内存
|
||||
// LCS 长度最大值为 min(sm, sn) <= 5000,远小于 Uint16Array 上限 65535,安全
|
||||
const lcs = new Uint16Array((sm + 1) * (sn + 1));
|
||||
const idx = (i: number, j: number): number => i * (sn + 1) + j;
|
||||
|
||||
for (let i = 1; i <= sm; i++) {
|
||||
@@ -183,21 +187,24 @@ export class DiffViewerTool implements IMetonaTool {
|
||||
return { success: false, error: 'file_a and file_b are required for files mode' };
|
||||
}
|
||||
|
||||
// 安全校验
|
||||
const pathA = resolve(context.workspacePath, fileA);
|
||||
const pathB = resolve(context.workspacePath, fileB);
|
||||
if (!isPathWithinWorkspace(fileA, context.workspacePath) || !isPathWithinWorkspace(fileB, context.workspacePath)) {
|
||||
return { success: false, error: 'Path traversal detected' };
|
||||
}
|
||||
if (isProtectedWorkspaceFile(fileA, context.workspacePath) || isProtectedWorkspaceFile(fileB, context.workspacePath)) {
|
||||
return { success: false, error: 'Access denied: MEMORY.md is protected' };
|
||||
// F4-1: 用 safeResolvePath 统一路径校验(含 isPathWithinWorkspace + MEMORY.md 拦截)
|
||||
let pathA: string;
|
||||
let pathB: string;
|
||||
try {
|
||||
pathA = safeResolvePath(fileA, context.workspacePath);
|
||||
pathB = safeResolvePath(fileB, context.workspacePath);
|
||||
} catch (error) {
|
||||
return { success: false, error: extractErrorMessage(error) };
|
||||
}
|
||||
|
||||
try {
|
||||
contentA = await readFile(pathA, 'utf-8');
|
||||
contentB = await readFile(pathB, 'utf-8');
|
||||
// F4-1: 用智能编码检测读取文件(支持 GBK/UTF-16 等非 UTF-8 编码)
|
||||
const bufferA = await readFile(pathA);
|
||||
const bufferB = await readFile(pathB);
|
||||
contentA = decodeBufferWithDetection(bufferA).content;
|
||||
contentB = decodeBufferWithDetection(bufferB).content;
|
||||
} catch (err) {
|
||||
return { success: false, error: `Failed to read files: ${(err as Error).message}` };
|
||||
return { success: false, error: `Failed to read files: ${extractErrorMessage(err)}` };
|
||||
}
|
||||
labelA = fileA;
|
||||
labelB = fileB;
|
||||
@@ -252,7 +259,8 @@ export class DiffViewerTool implements IMetonaTool {
|
||||
diff_lines: diff.slice(0, 500),
|
||||
};
|
||||
} catch (err) {
|
||||
return { success: false, error: (err as Error).message };
|
||||
// F4-2: 统一错误处理,避免 non-Error 抛出值被 String 强转丢失信息
|
||||
return { success: false, error: extractErrorMessage(err) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user