Skip to content

Commit 6586e5b

Browse files
authored
Merge pull request #73 from browser-use/feat/run-mode-browser-execute-styling
feat(run): style browser_execute in headless run mode like bash
2 parents 5af4383 + 1a00390 commit 6586e5b

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

  • packages/opencode/src/cli/cmd/run

packages/opencode/src/cli/cmd/run/tool.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { ToolPart } from "@opencode-ai/sdk/v2"
1919
import type * as Tool from "@/tool/tool"
2020
import type { ApplyPatchTool } from "@/tool/apply_patch"
2121
import type { ShellTool as BashTool } from "@/tool/shell"
22+
import type { BrowserExecuteTool } from "@/tool/browser-execute"
2223
import type { EditTool } from "@/tool/edit"
2324
import type { GlobTool } from "@/tool/glob"
2425
import type { GrepTool } from "@/tool/grep"
@@ -94,6 +95,7 @@ type ToolPermissionCtx = {
9495
type ToolDefs = {
9596
invalid: typeof InvalidTool
9697
bash: typeof BashTool
98+
browser_execute: typeof BrowserExecuteTool
9799
write: typeof WriteTool
98100
edit: typeof EditTool
99101
apply_patch: typeof ApplyPatchTool
@@ -682,6 +684,40 @@ function scrollBashFinal(p: ToolProps<typeof BashTool>): string {
682684
return `bash completed (exit ${code})${time ? ` · ${time}` : ""}`
683685
}
684686

687+
// Mimic a JS REPL: "> " on the first line, " " on continuations. Skip
688+
// leading/trailing blank lines so a snippet that starts with "\n" doesn't
689+
// render an empty "> " row. Matches the TUI BrowserExecute renderer.
690+
function promptedCode(code: string): string {
691+
const src = code.replace(/^\n+|\n+$/g, "")
692+
if (!src) {
693+
return ""
694+
}
695+
696+
return src
697+
.split("\n")
698+
.map((line, i) => (i === 0 ? "> " : " ") + line)
699+
.join("\n")
700+
}
701+
702+
function scrollBrowserExecuteStart(p: ToolProps<typeof BrowserExecuteTool>): string {
703+
const desc = p.input.description || "Browser execute"
704+
const code = promptedCode(p.input.code ?? "")
705+
if (!code) {
706+
return `# ${desc}`
707+
}
708+
709+
return `# ${desc}\n${code}`
710+
}
711+
712+
function scrollBrowserExecuteProgress(p: ToolProps<typeof BrowserExecuteTool>): string {
713+
return stripAnsi(p.frame.raw).replace(/^\n+/, "").replace(/\n+$/, "")
714+
}
715+
716+
function scrollBrowserExecuteFinal(p: ToolProps<typeof BrowserExecuteTool>): string {
717+
const time = span(p.frame.state)
718+
return time ? `browser_execute completed · ${time}` : "browser_execute completed"
719+
}
720+
685721
function scrollReadStart(p: ToolProps<typeof ReadTool>): string {
686722
const file = toolPath(p.input.filePath)
687723
const extra = info(p.frame.input, ["filePath"])
@@ -973,6 +1009,16 @@ function permBash(p: ToolPermissionProps<typeof BashTool>): ToolPermissionInfo {
9731009
}
9741010
}
9751011

1012+
function permBrowserExecute(p: ToolPermissionProps<typeof BrowserExecuteTool>): ToolPermissionInfo {
1013+
const title = p.input.description || "Browser execute"
1014+
const code = promptedCode(p.input.code ?? "")
1015+
return {
1016+
icon: "#",
1017+
title,
1018+
lines: code ? code.split("\n") : p.patterns.map((item) => `- ${item}`),
1019+
}
1020+
}
1021+
9761022
function permTask(p: ToolPermissionProps<typeof TaskTool>): ToolPermissionInfo {
9771023
const type = p.input.subagent_type || "general"
9781024
const desc = p.input.description
@@ -1042,6 +1088,19 @@ const TOOL_RULES = {
10421088
},
10431089
permission: permBash,
10441090
},
1091+
browser_execute: {
1092+
view: {
1093+
output: true,
1094+
final: false,
1095+
},
1096+
run: runBrowserExecute,
1097+
scroll: {
1098+
start: scrollBrowserExecuteStart,
1099+
progress: scrollBrowserExecuteProgress,
1100+
final: scrollBrowserExecuteFinal,
1101+
},
1102+
permission: permBrowserExecute,
1103+
},
10451104
write: {
10461105
view: {
10471106
output: false,
@@ -1277,6 +1336,15 @@ function runBash(p: ToolProps<typeof BashTool>): ToolInline {
12771336
}
12781337
}
12791338

1339+
function runBrowserExecute(p: ToolProps<typeof BrowserExecuteTool>): ToolInline {
1340+
return {
1341+
icon: ">",
1342+
title: p.input.description || "Browser execute",
1343+
mode: "block",
1344+
body: p.frame.status === "completed" ? text(p.frame.state.output).trim() : undefined,
1345+
}
1346+
}
1347+
12801348
export function toolView(name?: string): ToolView {
12811349
return (
12821350
rule(name)?.view ?? {

0 commit comments

Comments
 (0)