Skip to content

Commit 20c3461

Browse files
committed
f
1 parent f868719 commit 20c3461

3 files changed

Lines changed: 42 additions & 6 deletions

File tree

packages/opencode/src/tool/shell.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export { Parameters } from "./shell/prompt"
2626

2727
const MAX_METADATA_LENGTH = 30_000
2828
const DEFAULT_TIMEOUT = Flag.OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS || 2 * 60 * 1000
29-
const CWD = new Set(["cd", "push-location", "set-location"])
29+
const CWD = new Set(["cd", "chdir", "popd", "pushd", "push-location", "set-location"])
3030
const FILES = new Set([
3131
...CWD,
3232
"rm",
@@ -49,6 +49,7 @@ const FILES = new Set([
4949
"new-item",
5050
"rename-item",
5151
])
52+
const CMD_FILES = new Set(["copy", "del", "dir", "erase", "md", "mkdir", "move", "rd", "ren", "rename", "rmdir", "type"])
5253
const FLAGS = new Set(["-destination", "-literalpath", "-path"])
5354
const SWITCHES = new Set(["-confirm", "-debug", "-force", "-nonewline", "-recurse", "-verbose", "-whatif"])
5455

@@ -174,11 +175,16 @@ function prefix(text: string) {
174175
return text.slice(0, match.index)
175176
}
176177

177-
function pathArgs(list: Part[], ps: boolean) {
178+
function pathArgs(list: Part[], ps: boolean, cmd = false) {
178179
if (!ps) {
179180
return list
180181
.slice(1)
181-
.filter((item) => !item.text.startsWith("-") && !(list[0]?.text === "chmod" && item.text.startsWith("+")))
182+
.filter(
183+
(item) =>
184+
!item.text.startsWith("-") &&
185+
!(cmd && item.text.startsWith("/")) &&
186+
!(list[0]?.text === "chmod" && item.text.startsWith("+")),
187+
)
182188
.map((item) => item.text)
183189
}
184190

@@ -363,8 +369,8 @@ export const ShellTool = Tool.define(
363369
const tokens = command.map((item) => item.text)
364370
const cmd = ps ? tokens[0]?.toLowerCase() : tokens[0]
365371

366-
if (cmd && FILES.has(cmd)) {
367-
for (const arg of pathArgs(command, ps)) {
372+
if (cmd && (FILES.has(cmd) || (shellKind === "cmd" && CMD_FILES.has(cmd)))) {
373+
for (const arg of pathArgs(command, ps, shellKind === "cmd")) {
368374
const resolved = yield* argPath(arg, cwd, ps, shell)
369375
log.info("resolved path", { arg, resolved })
370376
if (!resolved || Instance.containsPath(resolved)) continue

packages/opencode/src/tool/shell/id.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export namespace ShellKind {
2-
export const ids = ["bash", "pwsh", "powershell"] as const
2+
export const ids = ["bash", "pwsh", "powershell", "cmd"] as const
33
export type ID = (typeof ids)[number]
44

55
const kind = new Set<string>(ids)

packages/opencode/test/tool/shell.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const shells = (() => {
7070
})()
7171
const PS = new Set(["pwsh", "powershell"])
7272
const ps = shells.filter((item) => PS.has(item.label))
73+
const cmdShell = shells.find((item) => item.label === "cmd")
7374

7475
const sh = () => Shell.name(Shell.acceptable())
7576
const evalarg = (text: string) => (sh() === "cmd" ? quote(text) : squote(text))
@@ -736,6 +737,35 @@ describe("tool.shell permissions", () => {
736737
}
737738
}
738739

740+
if (process.platform === "win32" && cmdShell) {
741+
test(
742+
"asks for external_directory permission for cmd file commands [cmd]",
743+
withShell(cmdShell, async () => {
744+
await Instance.provide({
745+
directory: projectRoot,
746+
fn: async () => {
747+
const bash = await initShell()
748+
const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
749+
await Effect.runPromise(
750+
bash.execute(
751+
{
752+
command: `type "${path.join(process.env.WINDIR!, "win.ini")}"`,
753+
description: "Read Windows ini with cmd",
754+
},
755+
capture(requests),
756+
),
757+
)
758+
const extDirReq = requests.find((r) => r.permission === "external_directory")
759+
expect(extDirReq).toBeDefined()
760+
expect(extDirReq!.patterns).toContain(
761+
Filesystem.normalizePathPattern(path.join(process.env.WINDIR!, "*")),
762+
)
763+
},
764+
})
765+
}),
766+
)
767+
}
768+
739769
each("asks for external_directory permission when cd to parent", async () => {
740770
await using tmp = await tmpdir()
741771
await Instance.provide({

0 commit comments

Comments
 (0)