bash tweaks
This commit is contained in:
parent
2f5d6ba447
commit
ae328d338e
2 changed files with 41 additions and 33 deletions
|
|
@ -90,11 +90,7 @@ export const BashTool = Tool.define("bash", async () => {
|
||||||
parameters: z.object({
|
parameters: z.object({
|
||||||
command: z.string().describe("The command to execute"),
|
command: z.string().describe("The command to execute"),
|
||||||
timeout: z.number().describe("Optional timeout in milliseconds").optional(),
|
timeout: z.number().describe("Optional timeout in milliseconds").optional(),
|
||||||
workdir: z
|
workdir: z.string().default(Instance.directory).describe("The working directory to execute the command in"),
|
||||||
.string()
|
|
||||||
.default(Instance.directory)
|
|
||||||
.describe("The working directory to execute the command in")
|
|
||||||
.optional(),
|
|
||||||
description: z
|
description: z
|
||||||
.string()
|
.string()
|
||||||
.describe(
|
.describe(
|
||||||
|
|
@ -102,6 +98,7 @@ export const BashTool = Tool.define("bash", async () => {
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
async execute(params, ctx) {
|
async execute(params, ctx) {
|
||||||
|
const cwd = params.workdir || Instance.directory
|
||||||
if (params.timeout !== undefined && params.timeout < 0) {
|
if (params.timeout !== undefined && params.timeout < 0) {
|
||||||
throw new Error(`Invalid timeout value: ${params.timeout}. Timeout must be a positive number.`)
|
throw new Error(`Invalid timeout value: ${params.timeout}. Timeout must be a positive number.`)
|
||||||
}
|
}
|
||||||
|
|
@ -111,6 +108,37 @@ export const BashTool = Tool.define("bash", async () => {
|
||||||
throw new Error("Failed to parse command")
|
throw new Error("Failed to parse command")
|
||||||
}
|
}
|
||||||
const agent = await Agent.get(ctx.agent)
|
const agent = await Agent.get(ctx.agent)
|
||||||
|
|
||||||
|
const checkExternalDirectory = async (dir: string) => {
|
||||||
|
if (Filesystem.contains(Instance.directory, dir)) return
|
||||||
|
const title = `This command references paths outside of ${Instance.directory}`
|
||||||
|
if (agent.permission.external_directory === "ask") {
|
||||||
|
await Permission.ask({
|
||||||
|
type: "external_directory",
|
||||||
|
pattern: [dir, path.join(dir, "*")],
|
||||||
|
sessionID: ctx.sessionID,
|
||||||
|
messageID: ctx.messageID,
|
||||||
|
callID: ctx.callID,
|
||||||
|
title,
|
||||||
|
metadata: {
|
||||||
|
command: params.command,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else if (agent.permission.external_directory === "deny") {
|
||||||
|
throw new Permission.RejectedError(
|
||||||
|
ctx.sessionID,
|
||||||
|
"external_directory",
|
||||||
|
ctx.callID,
|
||||||
|
{
|
||||||
|
command: params.command,
|
||||||
|
},
|
||||||
|
`${title} so this command is not allowed to be executed.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await checkExternalDirectory(cwd)
|
||||||
|
|
||||||
const permissions = agent.permission.bash
|
const permissions = agent.permission.bash
|
||||||
|
|
||||||
const askPatterns = new Set<string>()
|
const askPatterns = new Set<string>()
|
||||||
|
|
@ -137,6 +165,7 @@ export const BashTool = Tool.define("bash", async () => {
|
||||||
for (const arg of command.slice(1)) {
|
for (const arg of command.slice(1)) {
|
||||||
if (arg.startsWith("-") || (command[0] === "chmod" && arg.startsWith("+"))) continue
|
if (arg.startsWith("-") || (command[0] === "chmod" && arg.startsWith("+"))) continue
|
||||||
const resolved = await $`realpath ${arg}`
|
const resolved = await $`realpath ${arg}`
|
||||||
|
.cwd(cwd)
|
||||||
.quiet()
|
.quiet()
|
||||||
.nothrow()
|
.nothrow()
|
||||||
.text()
|
.text()
|
||||||
|
|
@ -149,32 +178,7 @@ export const BashTool = Tool.define("bash", async () => {
|
||||||
? resolved.replace(/^\/([a-z])\//, (_, drive) => `${drive.toUpperCase()}:\\`).replace(/\//g, "\\")
|
? resolved.replace(/^\/([a-z])\//, (_, drive) => `${drive.toUpperCase()}:\\`).replace(/\//g, "\\")
|
||||||
: resolved
|
: resolved
|
||||||
|
|
||||||
if (!Filesystem.contains(Instance.directory, normalized)) {
|
await checkExternalDirectory(path.dirname(normalized))
|
||||||
const parentDir = path.dirname(normalized)
|
|
||||||
if (agent.permission.external_directory === "ask") {
|
|
||||||
await Permission.ask({
|
|
||||||
type: "external_directory",
|
|
||||||
pattern: [parentDir, path.join(parentDir, "*")],
|
|
||||||
sessionID: ctx.sessionID,
|
|
||||||
messageID: ctx.messageID,
|
|
||||||
callID: ctx.callID,
|
|
||||||
title: `This command references paths outside of ${Instance.directory}`,
|
|
||||||
metadata: {
|
|
||||||
command: params.command,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else if (agent.permission.external_directory === "deny") {
|
|
||||||
throw new Permission.RejectedError(
|
|
||||||
ctx.sessionID,
|
|
||||||
"external_directory",
|
|
||||||
ctx.callID,
|
|
||||||
{
|
|
||||||
command: params.command,
|
|
||||||
},
|
|
||||||
`This command references paths outside of ${Instance.directory} so it is not allowed to be executed.`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +224,7 @@ export const BashTool = Tool.define("bash", async () => {
|
||||||
|
|
||||||
const proc = spawn(params.command, {
|
const proc = spawn(params.command, {
|
||||||
shell,
|
shell,
|
||||||
cwd: params.workdir || Instance.directory,
|
cwd,
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,16 @@ Before executing the command, please follow these steps:
|
||||||
Usage notes:
|
Usage notes:
|
||||||
- The command argument is required.
|
- The command argument is required.
|
||||||
- You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 120000ms (2 minutes).
|
- You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 120000ms (2 minutes).
|
||||||
|
- The `workdir` parameter specifies the working directory for the command. Defaults to the current working directory. Prefer setting `workdir` over using `cd` in your commands.
|
||||||
- It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
|
- It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
|
||||||
- If the output exceeds 30000 characters, output will be truncated before being returned to you.
|
- If the output exceeds 30000 characters, output will be truncated before being returned to you.
|
||||||
- VERY IMPORTANT: You MUST avoid using search commands like `find` and `grep`. Instead use Grep, Glob, or Task to search. You MUST avoid read tools like `cat`, `head`, `tail`, and `ls`, and use Read and List to read files.
|
- VERY IMPORTANT: You MUST avoid using search commands like `find` and `grep`. Instead use Grep, Glob, or Task to search. You MUST avoid read tools like `cat`, `head`, `tail`, and `ls`, and use Read and List to read files.
|
||||||
- If you _still_ need to run `grep`, STOP. ALWAYS USE ripgrep at `rg` (or /usr/bin/rg) first, which all opencode users have pre-installed.
|
- If you _still_ need to run `grep`, STOP. ALWAYS USE ripgrep at `rg` (or /usr/bin/rg) first, which all opencode users have pre-installed.
|
||||||
- When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings).
|
- When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings).
|
||||||
- Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly requests it.
|
- Avoid using `cd` to change directories when possible. Instead, set the `workdir` parameter or use absolute paths in your commands.
|
||||||
|
<good-example>
|
||||||
|
workdir="/foo/bar", command="pytest tests"
|
||||||
|
</good-example>
|
||||||
<good-example>
|
<good-example>
|
||||||
pytest /foo/bar/tests
|
pytest /foo/bar/tests
|
||||||
</good-example>
|
</good-example>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue