2026-01-24 22:01:00 +08:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
# Colors for output
|
|
|
|
|
GREEN='\033[0;32m'
|
|
|
|
|
BLUE='\033[0;34m'
|
|
|
|
|
YELLOW='\033[1;33m'
|
|
|
|
|
NC='\033[0m' # No Color
|
|
|
|
|
|
|
|
|
|
# Get script directory
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
|
|
|
DOCKER_DIR="$PROJECT_ROOT/docker"
|
|
|
|
|
|
|
|
|
|
# Docker Compose command with project name
|
|
|
|
|
COMPOSE_CMD="docker compose -p deer-flow-dev -f docker-compose-dev.yaml"
|
|
|
|
|
|
2026-02-24 08:31:52 +08:00
|
|
|
detect_sandbox_mode() {
|
|
|
|
|
local config_file="$PROJECT_ROOT/config.yaml"
|
|
|
|
|
local sandbox_use=""
|
|
|
|
|
local provisioner_url=""
|
|
|
|
|
|
|
|
|
|
if [ ! -f "$config_file" ]; then
|
|
|
|
|
echo "local"
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
sandbox_use=$(awk '
|
|
|
|
|
/^[[:space:]]*sandbox:[[:space:]]*$/ { in_sandbox=1; next }
|
|
|
|
|
in_sandbox && /^[^[:space:]#]/ { in_sandbox=0 }
|
|
|
|
|
in_sandbox && /^[[:space:]]*use:[[:space:]]*/ {
|
|
|
|
|
line=$0
|
|
|
|
|
sub(/^[[:space:]]*use:[[:space:]]*/, "", line)
|
|
|
|
|
print line
|
|
|
|
|
exit
|
|
|
|
|
}
|
|
|
|
|
' "$config_file")
|
|
|
|
|
|
|
|
|
|
provisioner_url=$(awk '
|
|
|
|
|
/^[[:space:]]*sandbox:[[:space:]]*$/ { in_sandbox=1; next }
|
|
|
|
|
in_sandbox && /^[^[:space:]#]/ { in_sandbox=0 }
|
|
|
|
|
in_sandbox && /^[[:space:]]*provisioner_url:[[:space:]]*/ {
|
|
|
|
|
line=$0
|
|
|
|
|
sub(/^[[:space:]]*provisioner_url:[[:space:]]*/, "", line)
|
|
|
|
|
print line
|
|
|
|
|
exit
|
|
|
|
|
}
|
|
|
|
|
' "$config_file")
|
|
|
|
|
|
refactor: split backend into harness (deerflow.*) and app (app.*) (#1131)
* refactor: extract shared utils to break harness→app cross-layer imports
Move _validate_skill_frontmatter to src/skills/validation.py and
CONVERTIBLE_EXTENSIONS + convert_file_to_markdown to src/utils/file_conversion.py.
This eliminates the two reverse dependencies from client.py (harness layer)
into gateway/routers/ (app layer), preparing for the harness/app package split.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: split backend/src into harness (deerflow.*) and app (app.*)
Physically split the monolithic backend/src/ package into two layers:
- **Harness** (`packages/harness/deerflow/`): publishable agent framework
package with import prefix `deerflow.*`. Contains agents, sandbox, tools,
models, MCP, skills, config, and all core infrastructure.
- **App** (`app/`): unpublished application code with import prefix `app.*`.
Contains gateway (FastAPI REST API) and channels (IM integrations).
Key changes:
- Move 13 harness modules to packages/harness/deerflow/ via git mv
- Move gateway + channels to app/ via git mv
- Rename all imports: src.* → deerflow.* (harness) / app.* (app layer)
- Set up uv workspace with deerflow-harness as workspace member
- Update langgraph.json, config.example.yaml, all scripts, Docker files
- Add build-system (hatchling) to harness pyproject.toml
- Add PYTHONPATH=. to gateway startup commands for app.* resolution
- Update ruff.toml with known-first-party for import sorting
- Update all documentation to reflect new directory structure
Boundary rule enforced: harness code never imports from app.
All 429 tests pass. Lint clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add harness→app boundary check test and update docs
Add test_harness_boundary.py that scans all Python files in
packages/harness/deerflow/ and fails if any `from app.*` or
`import app.*` statement is found. This enforces the architectural
rule that the harness layer never depends on the app layer.
Update CLAUDE.md to document the harness/app split architecture,
import conventions, and the boundary enforcement test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add config versioning with auto-upgrade on startup
When config.example.yaml schema changes, developers' local config.yaml
files can silently become outdated. This adds a config_version field and
auto-upgrade mechanism so breaking changes (like src.* → deerflow.*
renames) are applied automatically before services start.
- Add config_version: 1 to config.example.yaml
- Add startup version check warning in AppConfig.from_file()
- Add scripts/config-upgrade.sh with migration registry for value replacements
- Add `make config-upgrade` target
- Auto-run config-upgrade in serve.sh and start-daemon.sh before starting services
- Add config error hints in service failure messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix comments
* fix: update src.* import in test_sandbox_tools_security to deerflow.*
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle empty config and search parent dirs for config.example.yaml
Address Copilot review comments on PR #1131:
- Guard against yaml.safe_load() returning None for empty config files
- Search parent directories for config.example.yaml instead of only
looking next to config.yaml, fixing detection in common setups
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: correct skills root path depth and config_version type coercion
- loader.py: fix get_skills_root_path() to use 5 parent levels (was 3)
after harness split, file lives at packages/harness/deerflow/skills/
so parent×3 resolved to backend/packages/harness/ instead of backend/
- app_config.py: coerce config_version to int() before comparison in
_check_config_version() to prevent TypeError when YAML stores value
as string (e.g. config_version: "1")
- tests: add regression tests for both fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update test imports from src.* to deerflow.*/app.* after harness refactor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:55:52 +08:00
|
|
|
if [[ "$sandbox_use" == *"deerflow.sandbox.local:LocalSandboxProvider"* ]]; then
|
2026-02-24 08:31:52 +08:00
|
|
|
echo "local"
|
refactor: split backend into harness (deerflow.*) and app (app.*) (#1131)
* refactor: extract shared utils to break harness→app cross-layer imports
Move _validate_skill_frontmatter to src/skills/validation.py and
CONVERTIBLE_EXTENSIONS + convert_file_to_markdown to src/utils/file_conversion.py.
This eliminates the two reverse dependencies from client.py (harness layer)
into gateway/routers/ (app layer), preparing for the harness/app package split.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: split backend/src into harness (deerflow.*) and app (app.*)
Physically split the monolithic backend/src/ package into two layers:
- **Harness** (`packages/harness/deerflow/`): publishable agent framework
package with import prefix `deerflow.*`. Contains agents, sandbox, tools,
models, MCP, skills, config, and all core infrastructure.
- **App** (`app/`): unpublished application code with import prefix `app.*`.
Contains gateway (FastAPI REST API) and channels (IM integrations).
Key changes:
- Move 13 harness modules to packages/harness/deerflow/ via git mv
- Move gateway + channels to app/ via git mv
- Rename all imports: src.* → deerflow.* (harness) / app.* (app layer)
- Set up uv workspace with deerflow-harness as workspace member
- Update langgraph.json, config.example.yaml, all scripts, Docker files
- Add build-system (hatchling) to harness pyproject.toml
- Add PYTHONPATH=. to gateway startup commands for app.* resolution
- Update ruff.toml with known-first-party for import sorting
- Update all documentation to reflect new directory structure
Boundary rule enforced: harness code never imports from app.
All 429 tests pass. Lint clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add harness→app boundary check test and update docs
Add test_harness_boundary.py that scans all Python files in
packages/harness/deerflow/ and fails if any `from app.*` or
`import app.*` statement is found. This enforces the architectural
rule that the harness layer never depends on the app layer.
Update CLAUDE.md to document the harness/app split architecture,
import conventions, and the boundary enforcement test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add config versioning with auto-upgrade on startup
When config.example.yaml schema changes, developers' local config.yaml
files can silently become outdated. This adds a config_version field and
auto-upgrade mechanism so breaking changes (like src.* → deerflow.*
renames) are applied automatically before services start.
- Add config_version: 1 to config.example.yaml
- Add startup version check warning in AppConfig.from_file()
- Add scripts/config-upgrade.sh with migration registry for value replacements
- Add `make config-upgrade` target
- Auto-run config-upgrade in serve.sh and start-daemon.sh before starting services
- Add config error hints in service failure messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix comments
* fix: update src.* import in test_sandbox_tools_security to deerflow.*
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle empty config and search parent dirs for config.example.yaml
Address Copilot review comments on PR #1131:
- Guard against yaml.safe_load() returning None for empty config files
- Search parent directories for config.example.yaml instead of only
looking next to config.yaml, fixing detection in common setups
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: correct skills root path depth and config_version type coercion
- loader.py: fix get_skills_root_path() to use 5 parent levels (was 3)
after harness split, file lives at packages/harness/deerflow/skills/
so parent×3 resolved to backend/packages/harness/ instead of backend/
- app_config.py: coerce config_version to int() before comparison in
_check_config_version() to prevent TypeError when YAML stores value
as string (e.g. config_version: "1")
- tests: add regression tests for both fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update test imports from src.* to deerflow.*/app.* after harness refactor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:55:52 +08:00
|
|
|
elif [[ "$sandbox_use" == *"deerflow.community.aio_sandbox:AioSandboxProvider"* ]]; then
|
2026-02-24 08:31:52 +08:00
|
|
|
if [ -n "$provisioner_url" ]; then
|
|
|
|
|
echo "provisioner"
|
|
|
|
|
else
|
|
|
|
|
echo "aio"
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
echo "local"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 22:01:00 +08:00
|
|
|
# Cleanup function for Ctrl+C
|
|
|
|
|
cleanup() {
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${YELLOW}Operation interrupted by user${NC}"
|
|
|
|
|
exit 130
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Set up trap for Ctrl+C
|
|
|
|
|
trap cleanup INT TERM
|
|
|
|
|
|
2026-03-18 22:06:35 +08:00
|
|
|
docker_available() {
|
|
|
|
|
# Check that the docker CLI exists
|
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Check that the Docker daemon is reachable
|
|
|
|
|
if ! docker info >/dev/null 2>&1; then
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 11:02:09 +08:00
|
|
|
# Initialize: pre-pull the sandbox image so first Pod startup is fast
|
2026-01-24 22:01:00 +08:00
|
|
|
init() {
|
|
|
|
|
echo "=========================================="
|
2026-02-12 11:02:09 +08:00
|
|
|
echo " DeerFlow Init — Pull Sandbox Image"
|
2026-01-24 22:01:00 +08:00
|
|
|
echo "=========================================="
|
|
|
|
|
echo ""
|
|
|
|
|
|
2026-02-12 11:02:09 +08:00
|
|
|
SANDBOX_IMAGE="enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest"
|
2026-01-24 22:01:00 +08:00
|
|
|
|
2026-03-18 22:06:35 +08:00
|
|
|
# Detect sandbox mode from config.yaml
|
|
|
|
|
local sandbox_mode
|
|
|
|
|
sandbox_mode="$(detect_sandbox_mode)"
|
|
|
|
|
|
|
|
|
|
# Skip image pull for local sandbox mode (no container image needed)
|
|
|
|
|
if [ "$sandbox_mode" = "local" ]; then
|
|
|
|
|
echo -e "${GREEN}Detected local sandbox mode — no Docker image required.${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
if docker_available; then
|
|
|
|
|
echo -e "${GREEN}✓ Docker environment is ready.${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${YELLOW}Next step: make docker-start${NC}"
|
|
|
|
|
else
|
|
|
|
|
echo -e "${YELLOW}Docker does not appear to be installed, or the Docker daemon is not reachable.${NC}"
|
|
|
|
|
echo "Local sandbox mode itself does not require Docker, but Docker-based workflows (e.g., docker-start) will fail until Docker is available."
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${YELLOW}Install and start Docker, then run: make docker-init && make docker-start${NC}"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-02-12 11:02:09 +08:00
|
|
|
if ! docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${SANDBOX_IMAGE}$"; then
|
|
|
|
|
echo -e "${BLUE}Pulling sandbox image: $SANDBOX_IMAGE ...${NC}"
|
2026-03-18 22:06:35 +08:00
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
if ! docker pull "$SANDBOX_IMAGE" 2>&1; then
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${YELLOW}⚠ Failed to pull sandbox image.${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo "This is expected if:"
|
|
|
|
|
echo " 1. You are using local sandbox mode (default — no image needed)"
|
|
|
|
|
echo " 2. You are behind a corporate proxy or firewall"
|
|
|
|
|
echo " 3. The registry requires authentication"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${GREEN}The Docker development environment can still be started.${NC}"
|
|
|
|
|
echo "If you need AIO sandbox (container-based execution):"
|
|
|
|
|
echo " - Ensure you have network access to the registry"
|
|
|
|
|
echo " - Or configure a custom sandbox image in config.yaml"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${YELLOW}Next step: make docker-start${NC}"
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
2026-02-12 11:02:09 +08:00
|
|
|
else
|
|
|
|
|
echo -e "${GREEN}Sandbox image already exists locally: $SANDBOX_IMAGE${NC}"
|
2026-01-24 22:01:00 +08:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo ""
|
2026-02-12 11:02:09 +08:00
|
|
|
echo -e "${GREEN}✓ Sandbox image is ready.${NC}"
|
2026-01-24 22:01:00 +08:00
|
|
|
echo ""
|
2026-02-12 11:02:09 +08:00
|
|
|
echo -e "${YELLOW}Next step: make docker-start${NC}"
|
2026-01-24 22:01:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Start Docker development environment
|
|
|
|
|
start() {
|
2026-02-24 08:31:52 +08:00
|
|
|
local sandbox_mode
|
|
|
|
|
local services
|
|
|
|
|
|
2026-01-24 22:01:00 +08:00
|
|
|
echo "=========================================="
|
|
|
|
|
echo " Starting DeerFlow Docker Development"
|
|
|
|
|
echo "=========================================="
|
|
|
|
|
echo ""
|
2026-02-24 08:31:52 +08:00
|
|
|
|
|
|
|
|
sandbox_mode="$(detect_sandbox_mode)"
|
|
|
|
|
|
|
|
|
|
if [ "$sandbox_mode" = "provisioner" ]; then
|
|
|
|
|
services="frontend gateway langgraph provisioner nginx"
|
|
|
|
|
else
|
|
|
|
|
services="frontend gateway langgraph nginx"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo -e "${BLUE}Detected sandbox mode: $sandbox_mode${NC}"
|
|
|
|
|
if [ "$sandbox_mode" = "provisioner" ]; then
|
|
|
|
|
echo -e "${BLUE}Provisioner enabled (Kubernetes mode).${NC}"
|
|
|
|
|
else
|
|
|
|
|
echo -e "${BLUE}Provisioner disabled (not required for this sandbox mode).${NC}"
|
|
|
|
|
fi
|
|
|
|
|
echo ""
|
2026-02-12 11:02:09 +08:00
|
|
|
|
|
|
|
|
# Set DEER_FLOW_ROOT for provisioner if not already set
|
|
|
|
|
if [ -z "$DEER_FLOW_ROOT" ]; then
|
|
|
|
|
export DEER_FLOW_ROOT="$PROJECT_ROOT"
|
|
|
|
|
echo -e "${BLUE}Setting DEER_FLOW_ROOT=$DEER_FLOW_ROOT${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-11 10:03:01 +08:00
|
|
|
# Ensure config.yaml exists before starting.
|
|
|
|
|
if [ ! -f "$PROJECT_ROOT/config.yaml" ]; then
|
|
|
|
|
if [ -f "$PROJECT_ROOT/config.example.yaml" ]; then
|
|
|
|
|
cp "$PROJECT_ROOT/config.example.yaml" "$PROJECT_ROOT/config.yaml"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${YELLOW}============================================================${NC}"
|
|
|
|
|
echo -e "${YELLOW} config.yaml has been created from config.example.yaml.${NC}"
|
|
|
|
|
echo -e "${YELLOW} Please edit config.yaml to set your API keys and model ${NC}"
|
|
|
|
|
echo -e "${YELLOW} configuration before starting DeerFlow. ${NC}"
|
|
|
|
|
echo -e "${YELLOW}============================================================${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${YELLOW} Edit the file: $PROJECT_ROOT/config.yaml${NC}"
|
|
|
|
|
echo -e "${YELLOW} Then run: make docker-start${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
exit 0
|
|
|
|
|
else
|
|
|
|
|
echo -e "${YELLOW}✗ config.yaml not found and no config.example.yaml to copy from.${NC}"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Ensure extensions_config.json exists as a file before mounting.
|
|
|
|
|
# Docker creates a directory when bind-mounting a non-existent host path.
|
|
|
|
|
if [ ! -f "$PROJECT_ROOT/extensions_config.json" ]; then
|
|
|
|
|
if [ -f "$PROJECT_ROOT/extensions_config.example.json" ]; then
|
|
|
|
|
cp "$PROJECT_ROOT/extensions_config.example.json" "$PROJECT_ROOT/extensions_config.json"
|
|
|
|
|
echo -e "${BLUE}Created extensions_config.json from example${NC}"
|
|
|
|
|
else
|
|
|
|
|
echo "{}" > "$PROJECT_ROOT/extensions_config.json"
|
|
|
|
|
echo -e "${BLUE}Created empty extensions_config.json${NC}"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-01-24 22:01:00 +08:00
|
|
|
echo "Building and starting containers..."
|
2026-02-25 22:16:43 +08:00
|
|
|
cd "$DOCKER_DIR" && $COMPOSE_CMD up --build -d --remove-orphans $services
|
2026-01-24 22:01:00 +08:00
|
|
|
echo ""
|
|
|
|
|
echo "=========================================="
|
|
|
|
|
echo " DeerFlow Docker is starting!"
|
|
|
|
|
echo "=========================================="
|
|
|
|
|
echo ""
|
|
|
|
|
echo " 🌐 Application: http://localhost:2026"
|
|
|
|
|
echo " 📡 API Gateway: http://localhost:2026/api/*"
|
|
|
|
|
echo " 🤖 LangGraph: http://localhost:2026/api/langgraph/*"
|
|
|
|
|
echo ""
|
2026-02-09 21:59:13 +08:00
|
|
|
echo " 📋 View logs: make docker-logs"
|
|
|
|
|
echo " 🛑 Stop: make docker-stop"
|
2026-01-24 22:01:00 +08:00
|
|
|
echo ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# View Docker development logs
|
|
|
|
|
logs() {
|
|
|
|
|
local service=""
|
|
|
|
|
|
|
|
|
|
case "$1" in
|
2026-02-09 21:59:13 +08:00
|
|
|
--frontend)
|
|
|
|
|
service="frontend"
|
2026-01-24 22:01:00 +08:00
|
|
|
echo -e "${BLUE}Viewing frontend logs...${NC}"
|
|
|
|
|
;;
|
2026-02-09 21:59:13 +08:00
|
|
|
--gateway)
|
|
|
|
|
service="gateway"
|
|
|
|
|
echo -e "${BLUE}Viewing gateway logs...${NC}"
|
|
|
|
|
;;
|
|
|
|
|
--nginx)
|
|
|
|
|
service="nginx"
|
|
|
|
|
echo -e "${BLUE}Viewing nginx logs...${NC}"
|
2026-01-24 22:01:00 +08:00
|
|
|
;;
|
2026-02-24 08:31:52 +08:00
|
|
|
--provisioner)
|
|
|
|
|
service="provisioner"
|
|
|
|
|
echo -e "${BLUE}Viewing provisioner logs...${NC}"
|
|
|
|
|
;;
|
2026-01-24 22:01:00 +08:00
|
|
|
"")
|
|
|
|
|
echo -e "${BLUE}Viewing all logs...${NC}"
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
echo -e "${YELLOW}Unknown option: $1${NC}"
|
2026-02-24 08:31:52 +08:00
|
|
|
echo "Usage: $0 logs [--frontend|--gateway|--nginx|--provisioner]"
|
2026-01-24 22:01:00 +08:00
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
cd "$DOCKER_DIR" && $COMPOSE_CMD logs -f $service
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Stop Docker development environment
|
|
|
|
|
stop() {
|
2026-03-11 10:03:01 +08:00
|
|
|
# DEER_FLOW_ROOT is referenced in docker-compose-dev.yaml; set it before
|
|
|
|
|
# running compose down to suppress "variable is not set" warnings.
|
|
|
|
|
if [ -z "$DEER_FLOW_ROOT" ]; then
|
|
|
|
|
export DEER_FLOW_ROOT="$PROJECT_ROOT"
|
|
|
|
|
fi
|
2026-01-24 22:01:00 +08:00
|
|
|
echo "Stopping Docker development services..."
|
|
|
|
|
cd "$DOCKER_DIR" && $COMPOSE_CMD down
|
2026-03-11 10:03:01 +08:00
|
|
|
echo "Cleaning up sandbox containers..."
|
|
|
|
|
"$SCRIPT_DIR/cleanup-containers.sh" deer-flow-sandbox 2>/dev/null || true
|
2026-01-24 22:01:00 +08:00
|
|
|
echo -e "${GREEN}✓ Docker services stopped${NC}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Restart Docker development environment
|
|
|
|
|
restart() {
|
|
|
|
|
echo "========================================"
|
|
|
|
|
echo " Restarting DeerFlow Docker Services"
|
|
|
|
|
echo "========================================"
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${BLUE}Restarting containers...${NC}"
|
|
|
|
|
cd "$DOCKER_DIR" && $COMPOSE_CMD restart
|
|
|
|
|
echo ""
|
|
|
|
|
echo -e "${GREEN}✓ Docker services restarted${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " 🌐 Application: http://localhost:2026"
|
2026-03-16 20:58:59 +09:00
|
|
|
echo " 📋 View logs: make docker-logs"
|
2026-01-24 22:01:00 +08:00
|
|
|
echo ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Show help
|
|
|
|
|
help() {
|
|
|
|
|
echo "DeerFlow Docker Management Script"
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Usage: $0 <command> [options]"
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Commands:"
|
2026-02-12 11:02:09 +08:00
|
|
|
echo " init - Pull the sandbox image (speeds up first Pod startup)"
|
2026-02-24 08:31:52 +08:00
|
|
|
echo " start - Start Docker services (auto-detects sandbox mode from config.yaml)"
|
2026-01-24 22:01:00 +08:00
|
|
|
echo " restart - Restart all running Docker services"
|
|
|
|
|
echo " logs [option] - View Docker development logs"
|
2026-02-09 21:59:13 +08:00
|
|
|
echo " --frontend View frontend logs only"
|
2026-02-24 08:31:52 +08:00
|
|
|
echo " --gateway View gateway logs only"
|
|
|
|
|
echo " --nginx View nginx logs only"
|
|
|
|
|
echo " --provisioner View provisioner logs only"
|
2026-01-24 22:01:00 +08:00
|
|
|
echo " stop - Stop Docker development services"
|
|
|
|
|
echo " help - Show this help message"
|
|
|
|
|
echo ""
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 08:31:52 +08:00
|
|
|
main() {
|
|
|
|
|
# Main command dispatcher
|
|
|
|
|
case "$1" in
|
|
|
|
|
init)
|
|
|
|
|
init
|
|
|
|
|
;;
|
|
|
|
|
start)
|
|
|
|
|
start
|
|
|
|
|
;;
|
|
|
|
|
restart)
|
|
|
|
|
restart
|
|
|
|
|
;;
|
|
|
|
|
logs)
|
|
|
|
|
logs "$2"
|
|
|
|
|
;;
|
|
|
|
|
stop)
|
|
|
|
|
stop
|
|
|
|
|
;;
|
|
|
|
|
help|--help|-h|"")
|
|
|
|
|
help
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
echo -e "${YELLOW}Unknown command: $1${NC}"
|
|
|
|
|
echo ""
|
|
|
|
|
help
|
|
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
|
|
|
main "$@"
|
|
|
|
|
fi
|