mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-15 10:00:28 +00:00
Split interactive run into dedicated runtime, footer, and scrollback components so local and attach mode share one flow, with the related polish and tests folded into one change.
144 lines
3.9 KiB
TypeScript
144 lines
3.9 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import type { PermissionRequest } from "@opencode-ai/sdk/v2"
|
|
import {
|
|
createPermissionBodyState,
|
|
permissionAlwaysLines,
|
|
permissionCancel,
|
|
permissionEscape,
|
|
permissionInfo,
|
|
permissionReject,
|
|
permissionRun,
|
|
} from "../../../src/cli/cmd/run/permission.shared"
|
|
|
|
function req(input: Partial<PermissionRequest> = {}): PermissionRequest {
|
|
return {
|
|
id: "perm-1",
|
|
sessionID: "session-1",
|
|
permission: "read",
|
|
patterns: [],
|
|
metadata: {},
|
|
always: [],
|
|
...input,
|
|
}
|
|
}
|
|
|
|
describe("run permission shared", () => {
|
|
test("replies immediately for allow once", () => {
|
|
const out = permissionRun(createPermissionBodyState("perm-1"), "perm-1", "once")
|
|
|
|
expect(out.reply).toEqual({
|
|
requestID: "perm-1",
|
|
reply: "once",
|
|
})
|
|
})
|
|
|
|
test("requires confirmation for allow always", () => {
|
|
const next = permissionRun(createPermissionBodyState("perm-1"), "perm-1", "always")
|
|
expect(next.state.stage).toBe("always")
|
|
expect(next.state.selected).toBe("confirm")
|
|
expect(next.reply).toBeUndefined()
|
|
|
|
expect(permissionRun(next.state, "perm-1", "confirm").reply).toEqual({
|
|
requestID: "perm-1",
|
|
reply: "always",
|
|
})
|
|
|
|
expect(permissionRun(next.state, "perm-1", "cancel").state).toMatchObject({
|
|
stage: "permission",
|
|
selected: "always",
|
|
})
|
|
})
|
|
|
|
test("builds trimmed reject replies and stage transitions", () => {
|
|
const next = permissionRun(createPermissionBodyState("perm-1"), "perm-1", "reject")
|
|
expect(next.state.stage).toBe("reject")
|
|
|
|
const out = permissionReject({ ...next.state, message: " use rg " }, "perm-1")
|
|
expect(out).toEqual({
|
|
requestID: "perm-1",
|
|
reply: "reject",
|
|
message: "use rg",
|
|
})
|
|
|
|
expect(permissionCancel(next.state)).toMatchObject({
|
|
stage: "permission",
|
|
selected: "reject",
|
|
})
|
|
|
|
expect(permissionEscape(createPermissionBodyState("perm-1"))).toMatchObject({
|
|
stage: "reject",
|
|
selected: "reject",
|
|
})
|
|
|
|
expect(permissionEscape({ ...next.state, stage: "always", selected: "confirm" })).toMatchObject({
|
|
stage: "permission",
|
|
selected: "always",
|
|
})
|
|
})
|
|
|
|
test("maps supported permission types into display info", () => {
|
|
expect(
|
|
permissionInfo(
|
|
req({
|
|
permission: "bash",
|
|
metadata: {
|
|
input: {
|
|
command: "git status --short",
|
|
},
|
|
},
|
|
}),
|
|
),
|
|
).toMatchObject({
|
|
title: "Shell command",
|
|
lines: ["$ git status --short"],
|
|
})
|
|
|
|
expect(
|
|
permissionInfo(
|
|
req({
|
|
permission: "task",
|
|
metadata: {
|
|
description: "investigate stream",
|
|
subagent_type: "general",
|
|
},
|
|
}),
|
|
),
|
|
).toMatchObject({
|
|
title: "General Task",
|
|
lines: ["◉ investigate stream"],
|
|
})
|
|
|
|
expect(
|
|
permissionInfo(
|
|
req({
|
|
permission: "external_directory",
|
|
patterns: ["/tmp/work/**/*.ts", "/tmp/work/**/*.tsx"],
|
|
}),
|
|
),
|
|
).toMatchObject({
|
|
title: "Access external directory /tmp/work",
|
|
lines: ["- /tmp/work/**/*.ts", "- /tmp/work/**/*.tsx"],
|
|
})
|
|
|
|
expect(permissionInfo(req({ permission: "doom_loop" }))).toMatchObject({
|
|
title: "Continue after repeated failures",
|
|
})
|
|
|
|
expect(permissionInfo(req({ permission: "custom_tool" }))).toMatchObject({
|
|
title: "Call tool custom_tool",
|
|
lines: ["Tool: custom_tool"],
|
|
})
|
|
})
|
|
|
|
test("formats always-allow copy for wildcard and explicit patterns", () => {
|
|
expect(permissionAlwaysLines(req({ permission: "bash", always: ["*"] }))).toEqual([
|
|
"This will allow bash until OpenCode is restarted.",
|
|
])
|
|
|
|
expect(permissionAlwaysLines(req({ always: ["src/**/*.ts", "src/**/*.tsx"] }))).toEqual([
|
|
"This will allow the following patterns until OpenCode is restarted.",
|
|
"- src/**/*.ts",
|
|
"- src/**/*.tsx",
|
|
])
|
|
})
|
|
})
|