mirror of
https://github.com/langgenius/dify.git
synced 2026-03-30 02:20:16 +08:00
feat(constants): introduce DIFY_CLI_ROOT and update paths for Dify CLI and app assets
- Added DIFY_CLI_ROOT constant for the root directory of Dify CLI. - Updated DIFY_CLI_PATH and DIFY_CLI_CONFIG_PATH to use absolute paths. - Modified app asset initialization to create directories under DIFY_CLI_ROOT. - Enhanced Docker and E2B environment file handling to use workspace paths.
This commit is contained in:
@ -11,6 +11,7 @@ from .constants import (
|
||||
DIFY_CLI_CONFIG_PATH,
|
||||
DIFY_CLI_PATH,
|
||||
DIFY_CLI_PATH_PATTERN,
|
||||
DIFY_CLI_ROOT,
|
||||
)
|
||||
from .initializer import AppAssetsInitializer, DifyCliInitializer, SandboxInitializer
|
||||
from .manager import SandboxManager
|
||||
@ -26,6 +27,7 @@ __all__ = [
|
||||
"DIFY_CLI_CONFIG_PATH",
|
||||
"DIFY_CLI_PATH",
|
||||
"DIFY_CLI_PATH_PATTERN",
|
||||
"DIFY_CLI_ROOT",
|
||||
"AppAssetsInitializer",
|
||||
"ArchiveSandboxStorage",
|
||||
"DifyCliBinary",
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
from typing import Final
|
||||
|
||||
DIFY_CLI_PATH: Final[str] = ".dify/bin/dify"
|
||||
# Dify CLI (absolute path - hidden in /tmp, not in sandbox workdir)
|
||||
DIFY_CLI_ROOT: Final[str] = "/tmp/.dify"
|
||||
DIFY_CLI_PATH: Final[str] = "/tmp/.dify/bin/dify"
|
||||
|
||||
DIFY_CLI_PATH_PATTERN: Final[str] = "dify-cli-{os}-{arch}"
|
||||
|
||||
DIFY_CLI_CONFIG_PATH: Final[str] = ".dify_cli.json"
|
||||
DIFY_CLI_CONFIG_PATH: Final[str] = "/tmp/.dify/.dify_cli.json"
|
||||
|
||||
# App Assets
|
||||
# App Assets (relative path - stays in sandbox workdir)
|
||||
APP_ASSETS_PATH: Final[str] = "assets"
|
||||
APP_ASSETS_ZIP_PATH: Final[str] = ".dify/tmp/assets.zip"
|
||||
APP_ASSETS_ZIP_PATH: Final[str] = "/tmp/.dify/tmp/assets.zip"
|
||||
|
||||
@ -10,7 +10,7 @@ from extensions.ext_database import db
|
||||
from extensions.ext_storage import storage
|
||||
from models.app_asset import AppAssets
|
||||
|
||||
from ..constants import APP_ASSETS_PATH, APP_ASSETS_ZIP_PATH
|
||||
from ..constants import APP_ASSETS_PATH, APP_ASSETS_ZIP_PATH, DIFY_CLI_ROOT
|
||||
from .base import SandboxInitializer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -44,7 +44,7 @@ class AppAssetsInitializer(SandboxInitializer):
|
||||
with with_connection(env) as conn:
|
||||
execute(
|
||||
env,
|
||||
["mkdir", "-p", ".dify/tmp"],
|
||||
["mkdir", "-p", f"{DIFY_CLI_ROOT}/tmp"],
|
||||
connection=conn,
|
||||
error_message="Failed to create temp directory",
|
||||
)
|
||||
|
||||
@ -6,7 +6,7 @@ from core.virtual_environment.__base.helpers import execute
|
||||
from core.virtual_environment.__base.virtual_environment import VirtualEnvironment
|
||||
|
||||
from ..bash.dify_cli import DifyCliLocator
|
||||
from ..constants import DIFY_CLI_PATH
|
||||
from ..constants import DIFY_CLI_PATH, DIFY_CLI_ROOT
|
||||
from .base import SandboxInitializer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -18,6 +18,14 @@ class DifyCliInitializer(SandboxInitializer):
|
||||
|
||||
def initialize(self, env: VirtualEnvironment) -> None:
|
||||
binary = self._locator.resolve(env.metadata.os, env.metadata.arch)
|
||||
|
||||
execute(
|
||||
env,
|
||||
["mkdir", "-p", f"{DIFY_CLI_ROOT}/bin"],
|
||||
timeout=10,
|
||||
error_message="Failed to create dify CLI directory",
|
||||
)
|
||||
|
||||
env.upload_file(DIFY_CLI_PATH, BytesIO(binary.path.read_bytes()))
|
||||
|
||||
execute(
|
||||
|
||||
@ -369,8 +369,33 @@ class DockerDaemonEnvironment(VirtualEnvironment):
|
||||
return self._working_dir
|
||||
return f"{self._working_dir}/{relative.as_posix()}"
|
||||
|
||||
def _workspace_path(self, path: str) -> str:
|
||||
"""
|
||||
Convert a path to an absolute path in the Docker container.
|
||||
Absolute paths are returned as-is, relative paths are joined with _working_dir.
|
||||
"""
|
||||
normalized = PurePosixPath(path)
|
||||
if normalized.is_absolute():
|
||||
return str(normalized)
|
||||
return self._container_path(path)
|
||||
|
||||
def upload_file(self, path: str, content: BytesIO) -> None:
|
||||
container = self._get_container()
|
||||
normalized = PurePosixPath(path)
|
||||
|
||||
if normalized.is_absolute():
|
||||
parent_dir = str(normalized.parent)
|
||||
file_name = normalized.name
|
||||
payload = content.getvalue()
|
||||
tar_stream = BytesIO()
|
||||
with tarfile.open(fileobj=tar_stream, mode="w") as tar:
|
||||
tar_info = tarfile.TarInfo(name=file_name)
|
||||
tar_info.size = len(payload)
|
||||
tar.addfile(tar_info, BytesIO(payload))
|
||||
tar_stream.seek(0)
|
||||
container.put_archive(parent_dir, tar_stream.read()) # pyright: ignore[reportUnknownMemberType] #
|
||||
return
|
||||
|
||||
relative_path = self._relative_path(path)
|
||||
if not relative_path.parts:
|
||||
raise ValueError("Upload path must point to a file within the workspace.")
|
||||
@ -386,7 +411,7 @@ class DockerDaemonEnvironment(VirtualEnvironment):
|
||||
|
||||
def download_file(self, path: str) -> BytesIO:
|
||||
container = self._get_container()
|
||||
container_path = self._container_path(path)
|
||||
container_path = self._workspace_path(path)
|
||||
stream, _ = container.get_archive(container_path)
|
||||
tar_stream = BytesIO()
|
||||
for chunk in stream:
|
||||
@ -469,7 +494,7 @@ class DockerDaemonEnvironment(VirtualEnvironment):
|
||||
raise RuntimeError("Docker container ID is not available for exec.")
|
||||
api_client = self.get_docker_api_client(self.get_docker_sock())
|
||||
|
||||
working_dir = self._container_path(cwd) if cwd else self._working_dir
|
||||
working_dir = self._workspace_path(cwd) if cwd else self._working_dir
|
||||
|
||||
exec_info: dict[str, object] = cast(
|
||||
dict[str, object],
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import os
|
||||
import posixpath
|
||||
import shlex
|
||||
import threading
|
||||
from collections.abc import Mapping, Sequence
|
||||
@ -171,10 +171,9 @@ class E2BEnvironment(VirtualEnvironment):
|
||||
path (str): The path to upload the file to.
|
||||
content (BytesIO): The content of the file.
|
||||
"""
|
||||
path = os.path.join(self._WORKDIR, path.lstrip("/"))
|
||||
|
||||
remote_path = self._workspace_path(path)
|
||||
sandbox: Sandbox = self.metadata.store[self.StoreKey.SANDBOX]
|
||||
sandbox.files.write(path, content) # pyright: ignore[reportUnknownMemberType] #
|
||||
sandbox.files.write(remote_path, content) # pyright: ignore[reportUnknownMemberType] #
|
||||
|
||||
def download_file(self, path: str) -> BytesIO:
|
||||
"""
|
||||
@ -185,10 +184,9 @@ class E2BEnvironment(VirtualEnvironment):
|
||||
Returns:
|
||||
BytesIO: The content of the file.
|
||||
"""
|
||||
path = os.path.join(self._WORKDIR, path.lstrip("/"))
|
||||
|
||||
remote_path = self._workspace_path(path)
|
||||
sandbox: Sandbox = self.metadata.store[self.StoreKey.SANDBOX]
|
||||
content = sandbox.files.read(path)
|
||||
content = sandbox.files.read(remote_path)
|
||||
return BytesIO(content.encode())
|
||||
|
||||
def list_files(self, directory_path: str, limit: int) -> Sequence[FileState]:
|
||||
@ -196,11 +194,11 @@ class E2BEnvironment(VirtualEnvironment):
|
||||
List files in a directory of the E2B virtual environment.
|
||||
"""
|
||||
sandbox: Sandbox = self.metadata.store[self.StoreKey.SANDBOX]
|
||||
directory_path = os.path.join(self._WORKDIR, directory_path.lstrip("/"))
|
||||
files_info = sandbox.files.list(directory_path, depth=self.options.get(self.OptionsKey.E2B_LIST_FILE_DEPTH, 3))
|
||||
remote_dir = self._workspace_path(directory_path)
|
||||
files_info = sandbox.files.list(remote_dir, depth=self.options.get(self.OptionsKey.E2B_LIST_FILE_DEPTH, 3))
|
||||
return [
|
||||
FileState(
|
||||
path=os.path.relpath(file_info.path, self._WORKDIR),
|
||||
path=posixpath.relpath(file_info.path, self._WORKDIR),
|
||||
size=file_info.size,
|
||||
created_at=int(file_info.modified_time.timestamp()),
|
||||
updated_at=int(file_info.modified_time.timestamp()),
|
||||
@ -225,7 +223,7 @@ class E2BEnvironment(VirtualEnvironment):
|
||||
stdout_stream = QueueTransportReadCloser()
|
||||
stderr_stream = QueueTransportReadCloser()
|
||||
|
||||
working_dir = os.path.join(self._WORKDIR, cwd) if cwd else self._WORKDIR
|
||||
working_dir = self._workspace_path(cwd) if cwd else self._WORKDIR
|
||||
|
||||
threading.Thread(
|
||||
target=self._cmd_thread,
|
||||
@ -282,6 +280,18 @@ class E2BEnvironment(VirtualEnvironment):
|
||||
"""
|
||||
return self.options.get(self.OptionsKey.API_KEY, "")
|
||||
|
||||
def _workspace_path(self, path: str) -> str:
|
||||
"""
|
||||
Convert a path to an absolute path in the E2B environment.
|
||||
Absolute paths are returned as-is, relative paths are joined with _WORKDIR.
|
||||
"""
|
||||
normalized = posixpath.normpath(path)
|
||||
if normalized in ("", "."):
|
||||
return self._WORKDIR
|
||||
if normalized.startswith("/"):
|
||||
return normalized
|
||||
return posixpath.join(self._WORKDIR, normalized)
|
||||
|
||||
def _convert_architecture(self, arch_str: str) -> Arch:
|
||||
arch_map = {
|
||||
"x86_64": Arch.AMD64,
|
||||
|
||||
Reference in New Issue
Block a user