From 2aff0ada8fab7e0662cec2b4e99d0dbf8a02e6e7 Mon Sep 17 00:00:00 2001 From: Harry Date: Thu, 12 Mar 2026 16:03:01 +0800 Subject: [PATCH] fix: centralize sandbox temp path management --- api/core/sandbox/bash/session.py | 4 ++-- api/core/sandbox/entities/config.py | 16 ++++++++++++++-- .../initializer/app_assets_initializer.py | 8 ++++---- .../sandbox/initializer/dify_cli_initializer.py | 8 ++++---- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/api/core/sandbox/bash/session.py b/api/core/sandbox/bash/session.py index 5a920b9688..4ff03eb7b6 100644 --- a/api/core/sandbox/bash/session.py +++ b/api/core/sandbox/bash/session.py @@ -72,14 +72,14 @@ class SandboxBashSession: tools: ToolDependencies, cli_api_session: CliApiSession, ) -> str: - node_tools_path = f"{cli.tools_root}/{node_id}" + node_tools_path = cli.node_tools_path(node_id) config_json = json.dumps( DifyCliConfig.create(session=cli_api_session, tenant_id=self._tenant_id, tool_deps=tools).model_dump( mode="json" ), ensure_ascii=False, ) - config_path = shlex.quote(f"{node_tools_path}/{DifyCli.CONFIG_FILENAME}") + config_path = shlex.quote(cli.node_config_path(node_id)) vm = self._sandbox.vm # Merge mkdir + config write into a single pipeline to reduce round-trips. diff --git a/api/core/sandbox/entities/config.py b/api/core/sandbox/entities/config.py index ba33b64feb..3bc5dd2512 100644 --- a/api/core/sandbox/entities/config.py +++ b/api/core/sandbox/entities/config.py @@ -19,15 +19,25 @@ class DifyCli: # --- instance attributes --- root: str + bin_dir: str bin_path: str tools_root: str global_tools_path: str + global_config_path: str def __init__(self, env_id: str) -> None: self.root = f"/tmp/.dify/{env_id}" - self.bin_path = f"{self.root}/bin/dify" + self.bin_dir = f"{self.root}/bin" + self.bin_path = f"{self.bin_dir}/dify" self.tools_root = f"{self.root}/tools" self.global_tools_path = f"{self.root}/tools/global" + self.global_config_path = f"{self.global_tools_path}/{DifyCli.CONFIG_FILENAME}" + + def node_tools_path(self, node_id: str) -> str: + return f"{self.tools_root}/{node_id}" + + def node_config_path(self, node_id: str) -> str: + return f"{self.node_tools_path(node_id)}/{DifyCli.CONFIG_FILENAME}" class AppAssets: @@ -40,7 +50,9 @@ class AppAssets: PATH: Final[str] = "skills" + root: str zip_path: str def __init__(self, env_id: str) -> None: - self.zip_path = f"/tmp/.dify/{env_id}/assets.zip" + self.root = f"/tmp/.dify/{env_id}" + self.zip_path = f"{self.root}/assets.zip" diff --git a/api/core/sandbox/initializer/app_assets_initializer.py b/api/core/sandbox/initializer/app_assets_initializer.py index 624b75872f..a4d281dae7 100644 --- a/api/core/sandbox/initializer/app_assets_initializer.py +++ b/api/core/sandbox/initializer/app_assets_initializer.py @@ -26,11 +26,11 @@ class AppAssetsInitializer(AsyncSandboxInitializer): ( pipeline(vm) .add( - ["mkdir", "-p", f"/tmp/.dify/{sandbox.id}"], + ["mkdir", "-p", assets.root], error_message="Failed to create assets temp directory", ) .add( - ["curl", "-fsSL", download_url, "-o", assets.zip_path], + ["sh", "-c", 'curl -fsSL "$1" -o "$2"', "sh", download_url, assets.zip_path], error_message="Failed to download assets zip", ) # Create the assets directory first to ensure it exists even if zip is empty @@ -41,12 +41,12 @@ class AppAssetsInitializer(AsyncSandboxInitializer): # unzip with silent error and return 1 if the zip is empty # FIXME(Mairuis): should use a more robust way to check if the zip is empty .add( - ["sh", "-c", f"unzip {assets.zip_path} -d {AppAssets.PATH} 2>/dev/null || [ $? -eq 1 ]"], + ["sh", "-c", 'unzip "$1" -d "$2" 2>/dev/null || [ $? -eq 1 ]', "sh", assets.zip_path, AppAssets.PATH], error_message="Failed to unzip assets", ) # Ensure directories have execute permission for traversal and files are readable .add( - ["sh", "-c", f"chmod -R u+rwX,go+rX {AppAssets.PATH}"], + ["sh", "-c", 'chmod -R u+rwX,go+rX "$1"', "sh", AppAssets.PATH], error_message="Failed to set permissions on assets", ) .execute(timeout=APP_ASSETS_DOWNLOAD_TIMEOUT, raise_on_error=True) diff --git a/api/core/sandbox/initializer/dify_cli_initializer.py b/api/core/sandbox/initializer/dify_cli_initializer.py index cb7d62be4f..2b01d898fd 100644 --- a/api/core/sandbox/initializer/dify_cli_initializer.py +++ b/api/core/sandbox/initializer/dify_cli_initializer.py @@ -33,9 +33,9 @@ class DifyCliInitializer(AsyncSandboxInitializer): # FIXME(Mairuis): should be more robust, effectively. binary = self._locator.resolve(vm.metadata.os, vm.metadata.arch) - pipeline(vm).add( - ["mkdir", "-p", f"{cli.root}/bin"], error_message="Failed to create dify CLI directory" - ).execute(raise_on_error=True) + pipeline(vm).add(["mkdir", "-p", cli.bin_dir], error_message="Failed to create dify CLI directory").execute( + raise_on_error=True + ) vm.upload_file(cli.bin_path, BytesIO(binary.path.read_bytes())) @@ -69,7 +69,7 @@ class DifyCliInitializer(AsyncSandboxInitializer): config = DifyCliConfig.create(self._cli_api_session, ctx.tenant_id, bundle.get_tool_dependencies()) config_json = json.dumps(config.model_dump(mode="json"), ensure_ascii=False) - config_path = f"{cli.global_tools_path}/{DifyCli.CONFIG_FILENAME}" + config_path = cli.global_config_path vm.upload_file(config_path, BytesIO(config_json.encode("utf-8"))) pipeline(vm, cwd=cli.global_tools_path).add(