mirror of
https://github.com/anomalyco/opencode.git
synced 2026-02-01 22:48:16 +00:00
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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user