Apply PR #11605: fix: prevent Windows reserved device names from being added to directory checks and simplify bash description parameter

This commit is contained in:
opencode-agent[bot]
2026-02-01 14:06:38 +00:00

View File

@@ -1,3 +1,29 @@
/**
* 文件用途Bash工具实现提供命令执行、权限管理、输出截断等功能
* 作者TRAE
* 创建日期2026-02-01
*
* 输入输出签名:
* - 输入command命令字符串、timeout超时毫秒、workdir工作目录、description命令描述
* - 输出:{ title, metadata: { output, exit, description }, output }
*
* 依赖列表:
* - zod@latest
* - web-tree-sitter@latest
* - tree-sitter-bash@latest
* - bun@latest
*
* 与其他模块交互方式:
* - 调用Instance.directory获取项目根目录
* - 调用ctx.ask()请求权限external_directory、bash
* - 调用ctx.metadata()更新元数据
* - 调用ctx.abort监听中止事件
* - 调用Shell.killTree()终止进程树
*
* 其他备注:
* - 相关文件bash.txt工具描述模板
* - 支持Windows、Linux、macOS多平台
*/
import z from "zod"
import { spawn } from "child_process"
import { Tool } from "./tool"
@@ -22,6 +48,35 @@ const DEFAULT_TIMEOUT = Flag.OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS || 2
export const log = Log.create({ service: "bash-tool" })
const WINDOWS_RESERVED_DEVICE_NAMES = new Set([
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
])
/**
* 实现说明Windows保留设备名称检查
*
* Windows系统保留以下设备名称不能作为文件名使用
* - CON, PRN, AUX, NUL标准设备
* - COM1-9串行端口
* - LPT1-9并行端口
*
* 检查逻辑:
* 1. 提取路径的基本名称(去除目录部分)
* 2. 转换为大写Windows文件系统不区分大小写
* 3. 检查是否在保留名称集合中
*
* 注意事项:
* - 此检查应在调用realpath之前进行避免无效命令
* - 只检查基本名称,不检查路径中间的保留名称
* - 适用于Windows平台process.platform === 'win32'
*/
const isWindowsReservedDeviceName = (name: string): boolean => {
const baseName = path.basename(name).toUpperCase()
return WINDOWS_RESERVED_DEVICE_NAMES.has(baseName)
}
const resolveWasm = (asset: string) => {
if (asset.startsWith("file://")) return fileURLToPath(asset)
if (asset.startsWith("/") || /^[a-z]:/i.test(asset)) return asset
@@ -71,7 +126,7 @@ export const BashTool = Tool.define("bash", async () => {
description: z
.string()
.describe(
"Clear, concise description of what this command does in 5-10 words. Examples:\nInput: ls\nOutput: Lists files in current directory\n\nInput: git status\nOutput: Shows working tree status\n\nInput: npm install\nOutput: Installs package dependencies\n\nInput: mkdir foo\nOutput: Creates directory 'foo'",
"Clear, concise description of what this command does in 5-10 words",
),
}),
async execute(params, ctx) {
@@ -115,6 +170,10 @@ export const BashTool = Tool.define("bash", async () => {
if (["cd", "rm", "cp", "mv", "mkdir", "touch", "chmod", "chown", "cat"].includes(command[0])) {
for (const arg of command.slice(1)) {
if (arg.startsWith("-") || (command[0] === "chmod" && arg.startsWith("+"))) continue
if (process.platform === "win32" && isWindowsReservedDeviceName(arg)) {
log.info("skipping Windows reserved device name", { arg })
continue
}
const resolved = await $`realpath ${arg}`
.cwd(cwd)
.quiet()