fix: namespace sandbox temp paths by sandbox ID to prevent cross-session collisions

DifyCli and AppAssets used hardcoded absolute paths under /tmp/.dify/,
causing concurrent sandbox sessions on the same host (e.g. SSH provider)
to overwrite each other's config files and CLI binaries.

- Add Sandbox.id property (uuid4 hex) as a stable, path-safe identifier
  independent of provider-specific environment IDs
- Convert DifyCli/AppAssets from static constants to per-sandbox instances
  namespaced under /tmp/.dify/{sandbox.id}/
- Replace all vm.metadata.id references with sandbox.id
- Replace upload_file with heredoc-based pipeline step in session.py to
  reduce round-trips
This commit is contained in:
Harry
2026-03-11 19:19:17 +08:00
parent 6fe221518e
commit 76c02db094
7 changed files with 85 additions and 34 deletions

View File

@ -2,18 +2,45 @@ from typing import Final
class DifyCli:
"""Dify CLI constants (absolute path - hidden in /tmp, not in sandbox workdir)"""
"""Per-sandbox Dify CLI paths, namespaced under ``/tmp/.dify/{env_id}``.
ROOT: Final[str] = "/tmp/.dify"
PATH: Final[str] = "/tmp/.dify/bin/dify"
PATH_PATTERN: Final[str] = "dify-cli-{os}-{arch}"
Every sandbox environment gets its own directory tree so that
concurrent sessions on the same host (e.g. SSH provider) never
collide on config files or CLI binaries.
Class-level constants (``CONFIG_FILENAME``, ``PATH_PATTERN``) are
safe to share; all path attributes are instance-level and derived
from the ``env_id`` passed at construction time.
"""
# --- class-level constants (no path component) ---
CONFIG_FILENAME: Final[str] = ".dify_cli.json"
TOOLS_ROOT: Final[str] = "/tmp/.dify/tools"
GLOBAL_TOOLS_PATH: Final[str] = "/tmp/.dify/tools/global"
PATH_PATTERN: Final[str] = "dify-cli-{os}-{arch}"
# --- instance attributes ---
root: str
bin_path: str
tools_root: str
global_tools_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.tools_root = f"{self.root}/tools"
self.global_tools_path = f"{self.root}/tools/global"
class AppAssets:
"""App Assets constants (relative path - stays in sandbox workdir)"""
"""App Assets constants.
``PATH`` is a relative path resolved by each provider against its
own workspace root — already isolated. ``zip_path`` is an absolute
temp path and must be namespaced per environment to avoid collisions.
"""
PATH: Final[str] = "skills"
ZIP_PATH: Final[str] = "/tmp/assets.zip"
zip_path: str
def __init__(self, env_id: str) -> None:
self.zip_path = f"/tmp/.dify/{env_id}/assets.zip"