From 7fd9ef3d22a61ff39436227a88081d7548227dae Mon Sep 17 00:00:00 2001 From: Harry Date: Wed, 21 Jan 2026 01:25:21 +0800 Subject: [PATCH] fix(dify_cli): solve the permission error on e2b --- .../sandbox/initializer/dify_cli_initializer.py | 13 +++++++++++++ api/core/virtual_environment/__base/entities.py | 9 +++++++++ api/core/virtual_environment/__base/exec.py | 2 +- api/core/workflow/nodes/command/node.py | 5 +++-- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/api/core/sandbox/initializer/dify_cli_initializer.py b/api/core/sandbox/initializer/dify_cli_initializer.py index 8150d2b38c..33c2be633a 100644 --- a/api/core/sandbox/initializer/dify_cli_initializer.py +++ b/api/core/sandbox/initializer/dify_cli_initializer.py @@ -49,6 +49,19 @@ class DifyCliInitializer(SandboxInitializer): env.upload_file(DIFY_CLI_PATH, BytesIO(binary.path.read_bytes())) + # Use 'cp' with mode preservation workaround: copy file to itself to claim ownership, + # then use 'install' to set executable permission + pipeline(env).add( + [ + "sh", + "-c", + f"cat '{DIFY_CLI_PATH}' > '{DIFY_CLI_PATH}.tmp' && " + f"mv '{DIFY_CLI_PATH}.tmp' '{DIFY_CLI_PATH}' && " + f"chmod +x '{DIFY_CLI_PATH}'", + ], + error_message="Failed to mark dify CLI as executable", + ).execute(raise_on_error=True) + logger.info("Dify CLI uploaded to sandbox, path=%s", DIFY_CLI_PATH) artifact = SkillManager.load_tool_artifact(self._tenant_id, self._app_id, self._assets_id) diff --git a/api/core/virtual_environment/__base/entities.py b/api/core/virtual_environment/__base/entities.py index 7d8617ec6e..463f1c7c01 100644 --- a/api/core/virtual_environment/__base/entities.py +++ b/api/core/virtual_environment/__base/entities.py @@ -89,3 +89,12 @@ class CommandResult(BaseModel): @property def info_message(self) -> str: return self.stdout.decode("utf-8", errors="replace") if self.stdout else "" + + @property + def debug_message(self) -> str: + return ( + f"stdout: {self.stdout.decode('utf-8', errors='replace')}\n" + f"stderr: {self.stderr.decode('utf-8', errors='replace')}\n" + f"exit_code: {self.exit_code}\n" + f"pid: {self.pid}" + ) diff --git a/api/core/virtual_environment/__base/exec.py b/api/core/virtual_environment/__base/exec.py index f74baccaab..3a833045a7 100644 --- a/api/core/virtual_environment/__base/exec.py +++ b/api/core/virtual_environment/__base/exec.py @@ -30,7 +30,7 @@ class SandboxConfigValidationError(ValueError): pass -class CommandExecutionError(Exception): +class CommandExecutionError(ValueError): """Raised when a command execution fails.""" result: CommandResult diff --git a/api/core/workflow/nodes/command/node.py b/api/core/workflow/nodes/command/node.py index 35e1a1340b..52df7e9351 100644 --- a/api/core/workflow/nodes/command/node.py +++ b/api/core/workflow/nodes/command/node.py @@ -1,5 +1,4 @@ import logging -import shlex from collections.abc import Mapping, Sequence from typing import Any @@ -90,7 +89,7 @@ class CommandNode(Node[CommandNodeData]): try: with with_connection(sandbox) as conn: - command = shlex.split(raw_command) + command = ["bash", "-c", raw_command] sandbox_debug("command_node", "command", command) @@ -105,6 +104,8 @@ class CommandNode(Node[CommandNodeData]): } process_data = {"command": command, "working_directory": working_directory} + sandbox_debug("command_node", "outputs", result.debug_message) + if result.exit_code not in (None, 0): stderr_text = result.stderr.decode("utf-8", errors="replace") error_message = f"{stderr_text}\n\nCommand exited with code {result.exit_code}"