mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +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_CONFIG_PATH,
|
||||||
DIFY_CLI_PATH,
|
DIFY_CLI_PATH,
|
||||||
DIFY_CLI_PATH_PATTERN,
|
DIFY_CLI_PATH_PATTERN,
|
||||||
|
DIFY_CLI_ROOT,
|
||||||
)
|
)
|
||||||
from .initializer import AppAssetsInitializer, DifyCliInitializer, SandboxInitializer
|
from .initializer import AppAssetsInitializer, DifyCliInitializer, SandboxInitializer
|
||||||
from .manager import SandboxManager
|
from .manager import SandboxManager
|
||||||
@ -26,6 +27,7 @@ __all__ = [
|
|||||||
"DIFY_CLI_CONFIG_PATH",
|
"DIFY_CLI_CONFIG_PATH",
|
||||||
"DIFY_CLI_PATH",
|
"DIFY_CLI_PATH",
|
||||||
"DIFY_CLI_PATH_PATTERN",
|
"DIFY_CLI_PATH_PATTERN",
|
||||||
|
"DIFY_CLI_ROOT",
|
||||||
"AppAssetsInitializer",
|
"AppAssetsInitializer",
|
||||||
"ArchiveSandboxStorage",
|
"ArchiveSandboxStorage",
|
||||||
"DifyCliBinary",
|
"DifyCliBinary",
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
from typing import Final
|
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_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_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 extensions.ext_storage import storage
|
||||||
from models.app_asset import AppAssets
|
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
|
from .base import SandboxInitializer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -44,7 +44,7 @@ class AppAssetsInitializer(SandboxInitializer):
|
|||||||
with with_connection(env) as conn:
|
with with_connection(env) as conn:
|
||||||
execute(
|
execute(
|
||||||
env,
|
env,
|
||||||
["mkdir", "-p", ".dify/tmp"],
|
["mkdir", "-p", f"{DIFY_CLI_ROOT}/tmp"],
|
||||||
connection=conn,
|
connection=conn,
|
||||||
error_message="Failed to create temp directory",
|
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 core.virtual_environment.__base.virtual_environment import VirtualEnvironment
|
||||||
|
|
||||||
from ..bash.dify_cli import DifyCliLocator
|
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
|
from .base import SandboxInitializer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -18,6 +18,14 @@ class DifyCliInitializer(SandboxInitializer):
|
|||||||
|
|
||||||
def initialize(self, env: VirtualEnvironment) -> None:
|
def initialize(self, env: VirtualEnvironment) -> None:
|
||||||
binary = self._locator.resolve(env.metadata.os, env.metadata.arch)
|
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()))
|
env.upload_file(DIFY_CLI_PATH, BytesIO(binary.path.read_bytes()))
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
|
|||||||
@ -369,8 +369,33 @@ class DockerDaemonEnvironment(VirtualEnvironment):
|
|||||||
return self._working_dir
|
return self._working_dir
|
||||||
return f"{self._working_dir}/{relative.as_posix()}"
|
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:
|
def upload_file(self, path: str, content: BytesIO) -> None:
|
||||||
container = self._get_container()
|
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)
|
relative_path = self._relative_path(path)
|
||||||
if not relative_path.parts:
|
if not relative_path.parts:
|
||||||
raise ValueError("Upload path must point to a file within the workspace.")
|
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:
|
def download_file(self, path: str) -> BytesIO:
|
||||||
container = self._get_container()
|
container = self._get_container()
|
||||||
container_path = self._container_path(path)
|
container_path = self._workspace_path(path)
|
||||||
stream, _ = container.get_archive(container_path)
|
stream, _ = container.get_archive(container_path)
|
||||||
tar_stream = BytesIO()
|
tar_stream = BytesIO()
|
||||||
for chunk in stream:
|
for chunk in stream:
|
||||||
@ -469,7 +494,7 @@ class DockerDaemonEnvironment(VirtualEnvironment):
|
|||||||
raise RuntimeError("Docker container ID is not available for exec.")
|
raise RuntimeError("Docker container ID is not available for exec.")
|
||||||
api_client = self.get_docker_api_client(self.get_docker_sock())
|
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(
|
exec_info: dict[str, object] = cast(
|
||||||
dict[str, object],
|
dict[str, object],
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import os
|
import posixpath
|
||||||
import shlex
|
import shlex
|
||||||
import threading
|
import threading
|
||||||
from collections.abc import Mapping, Sequence
|
from collections.abc import Mapping, Sequence
|
||||||
@ -171,10 +171,9 @@ class E2BEnvironment(VirtualEnvironment):
|
|||||||
path (str): The path to upload the file to.
|
path (str): The path to upload the file to.
|
||||||
content (BytesIO): The content of the file.
|
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: 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:
|
def download_file(self, path: str) -> BytesIO:
|
||||||
"""
|
"""
|
||||||
@ -185,10 +184,9 @@ class E2BEnvironment(VirtualEnvironment):
|
|||||||
Returns:
|
Returns:
|
||||||
BytesIO: The content of the file.
|
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: Sandbox = self.metadata.store[self.StoreKey.SANDBOX]
|
||||||
content = sandbox.files.read(path)
|
content = sandbox.files.read(remote_path)
|
||||||
return BytesIO(content.encode())
|
return BytesIO(content.encode())
|
||||||
|
|
||||||
def list_files(self, directory_path: str, limit: int) -> Sequence[FileState]:
|
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.
|
List files in a directory of the E2B virtual environment.
|
||||||
"""
|
"""
|
||||||
sandbox: Sandbox = self.metadata.store[self.StoreKey.SANDBOX]
|
sandbox: Sandbox = self.metadata.store[self.StoreKey.SANDBOX]
|
||||||
directory_path = os.path.join(self._WORKDIR, directory_path.lstrip("/"))
|
remote_dir = self._workspace_path(directory_path)
|
||||||
files_info = sandbox.files.list(directory_path, depth=self.options.get(self.OptionsKey.E2B_LIST_FILE_DEPTH, 3))
|
files_info = sandbox.files.list(remote_dir, depth=self.options.get(self.OptionsKey.E2B_LIST_FILE_DEPTH, 3))
|
||||||
return [
|
return [
|
||||||
FileState(
|
FileState(
|
||||||
path=os.path.relpath(file_info.path, self._WORKDIR),
|
path=posixpath.relpath(file_info.path, self._WORKDIR),
|
||||||
size=file_info.size,
|
size=file_info.size,
|
||||||
created_at=int(file_info.modified_time.timestamp()),
|
created_at=int(file_info.modified_time.timestamp()),
|
||||||
updated_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()
|
stdout_stream = QueueTransportReadCloser()
|
||||||
stderr_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(
|
threading.Thread(
|
||||||
target=self._cmd_thread,
|
target=self._cmd_thread,
|
||||||
@ -282,6 +280,18 @@ class E2BEnvironment(VirtualEnvironment):
|
|||||||
"""
|
"""
|
||||||
return self.options.get(self.OptionsKey.API_KEY, "")
|
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:
|
def _convert_architecture(self, arch_str: str) -> Arch:
|
||||||
arch_map = {
|
arch_map = {
|
||||||
"x86_64": Arch.AMD64,
|
"x86_64": Arch.AMD64,
|
||||||
|
|||||||
Reference in New Issue
Block a user