From 44e31bd49c366b11372c8c4eafe626349fcb4f88 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Mon, 27 Apr 2026 22:27:18 +0800 Subject: [PATCH] feat(shell): implement background command execution with detached shell command support Co-authored-by: Copilot --- .../core/computer/booters/shell_background.py | 18 ++++++++++++++++++ astrbot/core/computer/booters/shipyard.py | 5 ++--- astrbot/core/computer/booters/shipyard_neo.py | 5 ++--- astrbot/core/tools/computer_tools/shell.py | 3 +-- 4 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 astrbot/core/computer/booters/shell_background.py diff --git a/astrbot/core/computer/booters/shell_background.py b/astrbot/core/computer/booters/shell_background.py new file mode 100644 index 000000000..6fe94c133 --- /dev/null +++ b/astrbot/core/computer/booters/shell_background.py @@ -0,0 +1,18 @@ +import shlex + +_BACKGROUND_SPAWN_SCRIPT = ( + "import subprocess, sys; " + "p = subprocess.Popen(" + "['bash', '-lc', sys.argv[1]], " + "stdin=subprocess.DEVNULL, " + "stdout=subprocess.DEVNULL, " + "stderr=subprocess.DEVNULL, " + "start_new_session=True, " + "close_fds=True" + "); " + "print(p.pid)" +) + + +def build_detached_shell_command(command: str) -> str: + return f"python3 -c {shlex.quote(_BACKGROUND_SPAWN_SCRIPT)} {shlex.quote(command)}" diff --git a/astrbot/core/computer/booters/shipyard.py b/astrbot/core/computer/booters/shipyard.py index 098511bba..a8375544d 100644 --- a/astrbot/core/computer/booters/shipyard.py +++ b/astrbot/core/computer/booters/shipyard.py @@ -10,6 +10,7 @@ from astrbot.api import logger from ..olayer import FileSystemComponent, PythonComponent, ShellComponent from .base import ComputerBooter +from .shell_background import build_detached_shell_command from .shipyard_search_file_util import search_files_via_shell @@ -52,9 +53,7 @@ class ShipyardShellWrapper: run_command = f"{env_prefix} {run_command}" if background: - run_command = ( - f"nohup sh -lc {shlex.quote(run_command)} >/dev/null 2>&1 & echo $!" - ) + run_command = build_detached_shell_command(run_command) result = await self._shell.exec( run_command, diff --git a/astrbot/core/computer/booters/shipyard_neo.py b/astrbot/core/computer/booters/shipyard_neo.py index 813befe44..cec3c3a36 100644 --- a/astrbot/core/computer/booters/shipyard_neo.py +++ b/astrbot/core/computer/booters/shipyard_neo.py @@ -13,6 +13,7 @@ from ..olayer import ( ShellComponent, ) from .base import ComputerBooter +from .shell_background import build_detached_shell_command from .shipyard_search_file_util import search_files_via_shell try: @@ -116,9 +117,7 @@ class NeoShellComponent(ShellComponent): run_command = f"{env_prefix} {run_command}" if background: - run_command = ( - f"nohup sh -lc {shlex.quote(run_command)} >/dev/null 2>&1 & echo $!" - ) + run_command = build_detached_shell_command(run_command) result = await self._sandbox.shell.exec( run_command, diff --git a/astrbot/core/tools/computer_tools/shell.py b/astrbot/core/tools/computer_tools/shell.py index 11f1692c5..e8fff4b50 100644 --- a/astrbot/core/tools/computer_tools/shell.py +++ b/astrbot/core/tools/computer_tools/shell.py @@ -60,7 +60,7 @@ class ExecuteShellTool(FunctionTool): }, "background": { "type": "boolean", - "description": "Run the command in the background. Use the file read tool to read the output later.", + "description": "Run the command in the background. Use the file read tool to read the output later. For long running commands, using this option.", "default": False, }, "timeout": { @@ -123,7 +123,6 @@ class ExecuteShellTool(FunctionTool): timeout=timeout or 300, ) if stdout_file: - result["stdout_file"] = stdout_file result["stdout"] = ( f"Command is running in the background. stdout/stderr is being " f"written to `{stdout_file}`. Use astrbot_file_read_tool to read it."