OpenClaw use Docker setup.sh instead of openckaw_ctl

This commit is contained in:
Trevor SANDY
2026-05-15 00:20:47 +02:00
parent 6ff1d8a4df
commit 9f534d088b
4 changed files with 338 additions and 1436 deletions
+17 -4
View File
@@ -20,16 +20,29 @@
"program": "${command:SelectScriptName}",
"args": []
},
{
"type": "bashdb",
"request": "launch",
"name": "Bash-Debug (openclaw_setup)",
"cwd": "${workspaceFolder}/openclaw",
"program": "${workspaceFolder}/openclaw/scripts/docker/setup.sh",
"env": {
"OPENCLAW_SANDBOX": "1",
"OPENCLAW_SKIP_ONBOARDING": "1",
"OPENCLAW_CONFIG_DIR": "/mnt/c/Users/Trevor/.openclaw",
"OPENCLAW_WORKSPACE_DIR": "/mnt/c/Users/Trevor/.openclaw/workspace"
}
},
{
"type": "bashdb",
"request": "launch",
"name": "Bash-Debug (openclaw_ctl)",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/openclaw_ctl",
"args": ["--gateway", "--sandbox"],
//"args": ["--configuration", "--sandbox"]
//"args": ["--onboarding", "--sandbox"]
//"args": ["--setenv", "--sandbox"]
//"args": ["--gateway", "--sandbox"],
"args": ["--configuration", "--sandbox"],
//"args": ["--onboarding", "--sandbox"],
//"args": ["--setenv", "--sandbox"],
},
{
"name": "PowerShell Launch Current File",
+44 -5
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# Trevor SANDY
# Last Update April, 28 2026
# Last Update May, 03 2026
# Copyright (C) 2026 by Trevor SANDY
#
# Auto-configure, with user prompts, self-hosted AI-Suite with Caddy/Nginx proxy and
@@ -399,7 +399,7 @@ user_confirm='Default'
config_mode="Interactive"
up_ver="v1.1.0"
up_bin='./access/url-parser'
yq_ver="v4.45.4" # v4.52.4
yq_ver="v4.53.2" # v4.45.4
yq_bin='./access/yq'
PLATFORM='unknown'
@@ -1871,7 +1871,8 @@ openclaw_compose_path="./openclaw/docker-compose.yml"
if [[ -f "$openclaw_compose_path" ]]; then
log_info "${HEADER}Rebuild OpenClaw Services"
#-------------------------------------------
# Rebuild OpenClaw services
# Rebuild OpenClaw services with service name and container names
log_info "${BODY}Rebuild services with service name and container names"
# shellcheck disable=SC2016
openclaw_service_yaml='
{
@@ -1901,6 +1902,44 @@ if [[ -f "$openclaw_compose_path" ]]; then
)
'
update_yaml_file "$openclaw_service_yaml" "$openclaw_compose_path"
# Add openclaw-gateway build args and set pull_policy for locally built image
if [[ -n ${AC_OPENCLAW_SANDBOX+x} ]]; then
log_info "${BODY}Add openclaw-gateway build args and set image pull_policy"
openclaw_service_yaml='
.services."openclaw-gateway" |= (
. as $orig |
# Rebuild in desired order, then merge original
{
"image": "${OPENCLAW_IMAGE:-openclaw:local}",
"pull_policy": "never"
}
* $orig
# Normalize/enrich build
| .build |= (
(select(tag == "!!str") | {
"context": .,
"args": {
"OPENCLAW_INSTALL_DOCKER_CLI": "${OPENCLAW_INSTALL_DOCKER_CLI:-}"
}
})
//
(. * {
"args": {
"OPENCLAW_INSTALL_DOCKER_CLI": "${OPENCLAW_INSTALL_DOCKER_CLI:-}"
}
})
)
) |
.services."openclaw-cli" |= (
{
"image": "openclaw:local",
"pull_policy": "never"
} * .
)
'
update_yaml_file "$openclaw_service_yaml" "$openclaw_compose_path"
fi
fi
log_info "${HEADER}Configure Proxy Service"
@@ -3127,8 +3166,8 @@ PSH
echo "$ps_payload" > "$posix_script_path" && ps_payload=""
log_debug "Hosts edit script: $win_script_path"
cat "$posix_script_path"
#log_debug "Hosts edit script: $win_script_path"
#cat "$posix_script_path"
#-----------------------------
# Elevation and Restricted check before PowerShell invocation
-1182
View File
File diff suppressed because it is too large Load Diff
+277 -245
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""
Trevor SANDY
Last Update April 29, 2026
Last Update May 03, 2026
Copyright (c) 2025-Present by Trevor SANDY
AI-Suite uses this script for the installation command that handles the AI-Suite
@@ -68,6 +68,7 @@ import queue
import re
import requests
import secrets
import shlex
import shutil
import subprocess
import tarfile
@@ -324,8 +325,8 @@ def run_command(cmd, cwd=None, re_raise=None):
cwd=cwd,
check=True
)
if result.returncode != 0 and result.stderr:
log.error(f"{result.stderr.strip()}")
# if result.returncode != 0:
# log.error(f"Command return code: {result.returncode}")
except Exception as e:
log.error(f"Exception: {e}.")
if re_raise:
@@ -1442,7 +1443,7 @@ def check_llama_cpp_model(operation, env_vars, using_hf):
log.info(f"Using {llama} model: {model_name}...")
return model_name
def check_llama_process(operation=None, env_vars={}):
def check_llama_process(operation=None, env_vars=None):
"""Check for Ollama/LLaMA.cpp (on host) and attempt to launch if not running."""
if not attempted_launch:
log.info(f"Checking for {llama} process on host...")
@@ -1494,6 +1495,9 @@ def check_llama_process(operation=None, env_vars={}):
sys.exit(1)
log.info(f"{llama} is not running...", extra=log_bright)
if start_llama:
if not env_vars:
log.critical("The env_vars dictionary is empty - exiting...")
sys.exit(1)
if llama_found:
log.info(f"Attempting to launch {llama} on host...")
llama_args = []
@@ -1586,10 +1590,19 @@ def clone_openclaw_repo():
os.chdir(repo_dir)
git("sparse-checkout", "init", "--cone")
git("sparse-checkout", "set",
"scripts/docker/setup.sh",
"scripts/lib/docker-build.sh",
"scripts/clawdock",
"docs"
"apps",
"assets",
"docs",
"extensions",
"packages",
"patches",
"qa",
"scripts",
"security",
"skills",
"src",
"ui",
"vendor"
)
git("checkout", "main")
else:
@@ -1684,7 +1697,7 @@ def prepare_supabase_env(env_vars):
built_env_vars['COMPOSE_IGNORE_ORPHANS'] = 'true'
write_dotenv_file(env_file, built_env_vars)
def prepare_openclaw_env(cwd):
def prepare_openclaw_env(cwd, sandbox=True):
"""
Creates a .env file from env.example with required values set.
"""
@@ -1700,12 +1713,20 @@ def prepare_openclaw_env(cwd):
bridge_port = 18790
gateway_bind = "lan"
gateway_token = secrets.token_hex(32) # 32 bytes -> 64 hex chars
openclaw_image = "ghcr.io/openclaw/openclaw:latest"
gateway_password = secrets.token_hex(16) # 16 bytes -> 32 hex chars
openapi_key = "llamacpp-local" if llama_cpp else "ollama-local"
cwd = "./openclaw" if not cwd else cwd
example_path = os.path.join(cwd, ".env.example")
output_path=os.path.join(cwd, ".env")
openclaw_image = "ghcr.io/openclaw/openclaw:latest"
openclaw_image_comment = "Use a remote image instead of building locally"
openclaw_docker_cli = 0
openclaw_sandbox = 0
if sandbox:
openclaw_sandbox = 1
openclaw_docker_cli = 1
openclaw_image = "openclaw:local"
openclaw_image_comment = "Build image locally for sandbox"
add_home_dir = True
add_config_dir = True
add_config_path = True
@@ -1715,18 +1736,11 @@ def prepare_openclaw_env(cwd):
add_bridge_port = True
add_gateway_bind = True
add_gateway_token = True
add_remote_image = True
add_openclaw_image = True
add_gateway_password = True
add_openai_api_key = True
add_extra_mounts = True
add_home_volume = True
add_sandbox = True
add_docker_socket = True
add_docker_gid = True
add_install_docker_cli = True
add_timezone = True
sandbox = False
update_env = False
add_docker_cli = True
log.info(f"Writing .env file to {output_path}...")
debug_style = LSHF.style(logging.WARNING)
try:
@@ -1782,15 +1796,29 @@ def prepare_openclaw_env(cwd):
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_GATEWAY_TOKEN="):
add_gateway_password = False
gateway_password = None
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and not key_value[1]:
add_gateway_token = False
modified_lines.append(f"OPENCLAW_GATEWAY_TOKEN={gateway_token}\n")
log.debug(f"OPENCLAW_GATEWAY_TOKEN={elide(gateway_token)}", extra=debug_style)
else:
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_SANDBOX="):
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_sandbox = False
modified_lines.append(f"OPENCLAW_SANDBOX={openclaw_sandbox}\n")
else:
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_INSTALL_DOCKER_CLI="):
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_docker_cli = False
modified_lines.append(f"OPENCLAW_INSTALL_DOCKER_CLI={openclaw_docker_cli}\n")
else:
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_IMAGE="):
add_remote_image = False
add_openclaw_image = False
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and not key_value[1]:
modified_lines.append(f"OPENCLAW_IMAGE={openclaw_image}\n")
@@ -1798,11 +1826,11 @@ def prepare_openclaw_env(cwd):
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_GATEWAY_PASSWORD="):
add_gateway_token = False
gateway_token = None
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and not key_value[1]:
add_gateway_password = False
modified_lines.append(f"OPENCLAW_GATEWAY_PASSWORD={gateway_password}\n")
log.debug(f"OPENCLAW_GATEWAY_PASSWORD={elide(gateway_password)}", extra=debug_style)
else:
modified_lines.append(line)
elif modified_line.startswith("OPENAI_API_KEY="):
@@ -1812,45 +1840,10 @@ def prepare_openclaw_env(cwd):
modified_lines.append(f"OPENAI_API_KEY={openapi_key}\n")
else:
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_EXTRA_MOUNTS="):
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_extra_mounts = False
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_HOME_VOLUME="):
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_home_volume = False
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_SANDBOX="):
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_sandbox = False
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_DOCKER_SOCKET="):
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_docker_socket = False
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_DOCKER_GID=") and sandbox:
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_docker_gid = False
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_INSTALL_DOCKER_CLI=") and sandbox:
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_install_docker_cli = False
modified_lines.append(line)
elif modified_line.startswith("OPENCLAW_TZ="):
key_value = modified_line.split('=', 1)
if len(key_value) == 2 and key_value[1]:
add_timezone = False
modified_lines.append(line)
else:
modified_lines.append(line)
default_paths = add_config_dir or add_workspace_dir
if add_remote_image or default_paths:
if add_openclaw_image or default_paths:
lines = modified_lines
modified_lines = []
section_header = False
@@ -1860,13 +1853,13 @@ def prepare_openclaw_env(cwd):
section_header = True
default_path_insert = modified_line.startswith("# Optional path overrides ")
auto_configure_settings = modified_line.startswith("# Model provider API keys ")
if section_header and add_remote_image:
if section_header and add_openclaw_image:
section_header = False
add_remote_image = False
add_openclaw_image = False
modified_lines.append("# " + "-" * 77 + "\n")
modified_lines.append("# Prebuilt Image\n")
modified_lines.append("# OpenClaw Image\n")
modified_lines.append("# " + "-" * 77 + "\n")
modified_lines.append("# Use a remote image instead of building locally.\n")
modified_lines.append(f"# {openclaw_image_comment}.\n")
modified_lines.append(f"OPENCLAW_IMAGE={openclaw_image}\n")
modified_lines.append("\n")
modified_lines.append(line)
@@ -1880,8 +1873,8 @@ def prepare_openclaw_env(cwd):
modified_lines.append(f"OPENCLAW_WORKSPACE_DIR={workspace_dir}\n")
elif add_gateway_password and modified_line.startswith("# OPENCLAW_GATEWAY_PASSWORD="):
add_gateway_password = False
gateway_token = None
modified_lines.append(f"OPENCLAW_GATEWAY_PASSWORD={gateway_password}\n")
log.debug(f"OPENCLAW_GATEWAY_PASSWORD={elide(gateway_password)}", extra=debug_style)
elif add_openai_api_key and modified_line.startswith("# OPENAI_API_KEY="):
add_openai_api_key = False
modified_lines.append(f"OPENAI_API_KEY={openapi_key}\n")
@@ -1902,6 +1895,11 @@ def prepare_openclaw_env(cwd):
section_header = False
modified_lines.append("# Auto-configure settings\n")
modified_lines.append("# " + "-" * 77 + "\n")
modified_lines.append("COMPOSE_IGNORE_ORPHANS=true\n")
if add_sandbox and sandbox:
modified_lines.append(f"OPENCLAW_SANDBOX={openclaw_sandbox}\n")
if add_docker_cli and sandbox:
modified_lines.append(f"OPENCLAW_INSTALL_DOCKER_CLI={openclaw_docker_cli}\n")
if add_gateway_port:
modified_lines.append(f"OPENCLAW_GATEWAY_PORT={gateway_port}\n")
if add_bridge_port:
@@ -1909,29 +1907,8 @@ def prepare_openclaw_env(cwd):
if add_gateway_bind:
modified_lines.append(f"OPENCLAW_GATEWAY_BIND={gateway_bind}\n")
if add_gateway_token:
gateway_password = None
modified_lines.append(f"OPENCLAW_GATEWAY_TOKEN={gateway_token}\n")
log.debug(f"OPENCLAW_GATEWAY_TOKEN={elide(gateway_token)}", extra=debug_style)
if add_extra_mounts:
update_env = True
modified_lines.append("OPENCLAW_EXTRA_MOUNTS=\n")
if add_home_volume:
update_env = True
modified_lines.append("OPENCLAW_HOME_VOLUME=\n")
if add_sandbox:
update_env = True
modified_lines.append("OPENCLAW_SANDBOX=\n")
if add_docker_socket:
update_env = True
modified_lines.append("OPENCLAW_DOCKER_SOCKET=\n")
if add_docker_gid:
update_env = True
modified_lines.append("OPENCLAW_DOCKER_GID=\n")
if add_install_docker_cli:
update_env = True
modified_lines.append("OPENCLAW_INSTALL_DOCKER_CLI=\n")
if add_timezone:
update_env = True
modified_lines.append("OPENCLAW_TZ=\n")
modified_lines.append("\n")
modified_lines.append("# " + "-" * 77 + "\n")
modified_lines.append(line)
@@ -1946,24 +1923,13 @@ def prepare_openclaw_env(cwd):
except Exception as e:
log.error(f"Exception: OpenClaw Setup: {e}")
return False
if update_env:
oc_script = os.path.normpath("openclaw_ctl")
if not os.path.exists(oc_script):
log.error(f"OpenClaw setup script not found at {oc_script}")
return
cmd = ["bash", "-c"]
if system == 'Windows':
convert_line_endings(oc_script)
oc_script = oc_script.replace("\\", "/")
oc_script = "".join(["./", oc_script])
if system == "Windows":
cmd = ["wsl", "-e"] + cmd
cmd_args = ["--sandbox"]
env_cmd = cmd + [
f'{oc_script} --setenv {" ".join(cmd_args)}'
]
run_command(env_cmd)
if gateway_token:
log.debug(f"OPENCLAW_GATEWAY_TOKEN={elide(gateway_token)}", extra=debug_style)
if gateway_password:
log.debug(f"OPENCLAW_GATEWAY_PASSWORD={elide(gateway_password)}", extra=debug_style)
log.debug(f"OPENCLAW_SANDBOX={openclaw_sandbox}", extra=debug_style)
log.debug(f"OPENCLAW_IMAGE={openclaw_image}", extra=debug_style)
log.debug(f"OPENCLAW_INSTALL_DOCKER_CLI={openclaw_docker_cli}", extra=debug_style)
log.debug(f"OPENCLAW_HOME={home_dir}", extra=debug_style)
log.debug(f"OPENCLAW_CONFIG_DIR={config_dir}", extra=debug_style)
log.debug(f"OPENCLAW_WORKSPACE_DIR={workspace_dir}", extra=debug_style)
@@ -1972,10 +1938,13 @@ def prepare_openclaw_env(cwd):
log.debug(f"OPENCLAW_GATEWAY_BIND={gateway_bind}", extra=debug_style)
log.debug(f"OPENAI_API_KEY={openapi_key}", extra=debug_style)
log.info(f".env file created at {output_path}", extra=log_bright)
_openclaw_add_compose_updates()
_openclaw_inject_env_vars_logging()
# if sandbox:
# _openclaw_enable_sandbox()
return True
def prepare_openclaw_config(cwd, env_vars):
def prepare_openclaw_config(cwd, env_vars, onboard_store):
log.info("Starting OpenClaw config preparation...")
src_path = pathlib.Path("./.openclaw.example.json")
dst_dir = pathlib.Path.home() / ".openclaw"
@@ -1987,18 +1956,20 @@ def prepare_openclaw_config(cwd, env_vars):
config = json.load(f)
cwd = "./openclaw" if not cwd else cwd
oc_env_file=os.path.join(cwd, ".env")
oc_env_vars = get_dotenv_vars(oc_env_file)
if not oc_env_vars:
if not onboard_store['b']:
set_dotenv_var(oc_env_file, 'OPENCLAW_SKIP_ONBOARDING', '1', None)
oc_env : dict[str, str] = get_dotenv_vars(oc_env_file)
if not oc_env:
log.error("No OpenClaw environment variables detected!")
return
if env_vars is None:
log.error("The env_vars dictionary is empty!")
return
token = oc_env_vars.get("OPENCLAW_GATEWAY_TOKEN")
token = oc_env.get("OPENCLAW_GATEWAY_TOKEN")
log.info("Updating gateway token")
config["gateway"]["auth"]["token"] = token
openapi_key = oc_env_vars.get("OPENAI_API_KEY")
openapi_key = oc_env.get("OPENAI_API_KEY")
if llama_cpp:
log.info("Configuring llama.cpp provider")
@@ -2126,6 +2097,156 @@ def prepare_openclaw_config(cwd, env_vars):
raise
log.info("OpenClaw .json configuration complete")
def _openclaw_add_compose_updates(setup_path=None):
"""
Set compose project to ai-suite and update installed Docker CLI check.
"""
if setup_path is None:
setup_path = "./openclaw/scripts/docker/setup.sh"
path = pathlib.Path(setup_path)
if not path.exists():
raise FileNotFoundError(f"{setup_path} not found")
try:
with open(path, "r", newline="\n") as f:
lines = f.readlines()
updated_lines: list[str] = []
# Set compose project argument
compose_args = False
compose_project = 'COMPOSE_ARGS=("-p" "ai-suite")'
for line in lines:
stripped = line.lstrip()
if stripped.startswith(compose_project):
updated_lines = lines
compose_args = True
break
if stripped.startswith('COMPOSE_ARGS=()'):
updated_lines.append(f'{compose_project}\n')
compose_args = True
else:
updated_lines.append(line)
if compose_args:
log.info(f"Add compose project in {path}", extra=log_bright)
else:
log.error(f"COMPOSE_ARGS=() not found in {path}", extra=log_bright)
# Update installed Docker CLI check
lines = updated_lines
updated_lines = []
check_cli = False
cli_check = 'if ! docker compose "${COMPOSE_ARGS[@]}" run --rm --entrypoint docker openclaw-gateway --version'
cli_check_update = 'elif ! docker compose "${COMPOSE_ARGS[@]}" exec -T openclaw-gateway docker --version'
for line in lines:
stripped = line.lstrip()
if stripped.startswith(cli_check_update):
updated_lines = lines
check_cli = True
break
if stripped.startswith(cli_check):
check_cli = True
updated_lines.append(' if ! docker compose "${COMPOSE_ARGS[@]}" ps --status running | grep -q openclaw-gateway; then\n')
updated_lines.append(' echo "WARNING: openclaw-gateway is not running. Skipping sandbox setup." >&2\n')
updated_lines.append(' SANDBOX_ENABLED=""\n')
updated_lines.append(f' {cli_check_update} >/dev/null 2>&1; then\n')
else:
updated_lines.append(line)
if check_cli:
log.info(f"Update Docker CLI ckeck in {path}", extra=log_bright)
else:
log.error(f"Docker CLI ckeck not found in {path}")
with open(path, "w", newline="\n") as f:
f.writelines(updated_lines)
log.info(f"Perform compose updates in {path}", extra=log_bright)
except Exception as e:
log.error(f"Exception: OpenClaw compose updates: {e}")
def _openclaw_inject_env_vars_logging(setup_path=None):
"""
Inject logging statements to display env vars.
"""
if setup_path is None:
setup_path = "./openclaw/scripts/docker/setup.sh"
path = pathlib.Path(setup_path)
if not path.exists():
raise FileNotFoundError(f"{setup_path} not found")
try:
with open(path, "r", newline="\n") as f:
lines = f.readlines()
updated_lines: list[str] = []
inject_env_logging = False
performed_injection = False
for line in lines:
stripped = line.lstrip()
if stripped.startswith('# Injected environment variable logging.'):
updated_lines = lines
performed_injection = True
break
if stripped.startswith('upsert_env() {'):
inject_env_logging = True
updated_lines.append(line)
continue
if inject_env_logging:
if stripped.startswith('tmp="$(mktemp)"'):
updated_lines.append(line)
updated_lines.append(" # Injected environment variable logging.\n")
updated_lines.append(' echo "==> OpenClaw environment variables:"\n')
updated_lines.append(' echo " - ROOT_DIR: ${ROOT_DIR:-}"\n')
updated_lines.append(' echo " - COMPOSE_FILE: ${COMPOSE_FILE:-}"\n')
updated_lines.append(' echo " - EXTRA_COMPOSE_FILE: ${EXTRA_COMPOSE_FILE:-}"\n')
continue
elif stripped.startswith('mv "$tmp" "$file"'):
updated_lines.append(line)
inject_env_logging = False
continue
if stripped.startswith('if [[ "$key" == "$k" ]]; then'):
updated_lines.append(line)
updated_lines.append(' echo " - $k: ${!k-}"\n')
performed_injection = True
elif stripped.startswith('if [[ "$seen" != *" $k "* ]]; then'):
updated_lines.append(line)
updated_lines.append(' echo " - $k: ${!k-}"\n')
performed_injection = True
else:
updated_lines.append(line)
else:
updated_lines.append(line)
with open(path, "w", newline="\n") as f:
f.writelines(updated_lines)
if performed_injection:
log.info(f"OpenClaw add env vars logging in {path}", extra=log_bright)
else:
log.warning(f"Function upsert_env not found - skip logging injection.")
except Exception as e:
log.error(f"Exception: OpenClaw env vars logging: {e}")
def _openclaw_enable_sandbox(compose_path=None):
"""
Enable OpenClaw sandbox-related settings in a docker-compose.yml file by
uncommenting the required lines.
"""
if compose_path is None:
compose_path = "./openclaw/docker-compose.yml"
path = pathlib.Path(compose_path)
if not path.exists():
raise FileNotFoundError(f"{compose_path} not found")
try:
with open(path, "r", newline="\n") as f:
lines = f.readlines()
updated_lines: list[str] = []
for line in lines:
stripped = line.lstrip()
if stripped.startswith("# - /var/run/docker.sock:/var/run/docker.sock"):
updated_lines.append(line.replace("# ", "", 1))
elif stripped.startswith("# group_add:"):
updated_lines.append(line.replace("# ", "", 1))
elif stripped.startswith('# - "${DOCKER_GID:-999}"'):
updated_lines.append(line.replace("# ", "", 1))
else:
updated_lines.append(line)
with open(path, "w", newline="\n") as f:
f.writelines(updated_lines)
log.info(f"Sandbox enabled in {path}", extra=log_bright)
except Exception as e:
log.error(f"Exception: OpenClaw enable sandbox: {e}")
def _openclaw_normalize(name: str):
if not isinstance(name, str):
raise TypeError("Model name must be a string")
@@ -2365,139 +2486,42 @@ def start_open_webui_tools_filesystem(environment=None, build=False):
compose_file = "open-webui/tools/servers/filesystem/compose.yaml"
start_built_container(compose_file, environment, build)
def start_openclaw(
onboard_store,
environment=None,
build=False,
cwd=None,
*,
non_interactive=False,
on_failure="continue", # "continue" | "quit"
on_max="continue", # "continue" | "quit"
):
"""Start the OpenClaw services (using its compose file)."""
def start_openclaw(onboard_store, build=False, cwd=None, oc_sandbox=None):
"""Start the OpenClaw services (using its setup file)."""
log.info("Starting OpenClaw services...")
oc_script = os.path.normpath("openclaw_ctl")
if not os.path.exists(oc_script):
log.error(f"OpenClaw setup script not found at {oc_script}")
oc_dir = "openclaw"
oc_script = os.path.normpath("scripts/docker/setup.sh")
oc_path = os.path.normpath(os.path.join(oc_dir, oc_script))
if not os.path.exists(oc_path):
log.error(f"OpenClaw setup script not found at {oc_path}")
return
cmd = ["bash", "-c"]
if system == 'Windows':
convert_line_endings(oc_script)
oc_script = oc_script.replace("\\", "/")
oc_prefix = "../" if cwd else "./"
oc_script = "".join([oc_prefix, oc_script])
if system == "Windows":
cmd = ["wsl", "-e"] + cmd
cmd_args = ["--sandbox"]
if build:
cmd_args.append("--build")
if onboard_store['b']:
# --- Onboarding + Configuration loop ---
onboarding_cmd = cmd + [
f'{oc_script} --onboarding {" ".join(cmd_args)}'
]
_openclaw_run_with_retries(
onboarding_cmd,
cwd,
"Onboarding",
non_interactive=non_interactive,
on_failure=on_failure,
on_max=on_max,
)
else:
# --- Configuration loop ---
config_cmd = cmd + [
f'{oc_script} --configuration {" ".join(cmd_args)}'
]
_openclaw_run_with_retries(
config_cmd,
cwd,
"Configuration",
non_interactive=non_interactive,
on_failure=on_failure,
on_max=on_max,
)
# --- Gateway start ---
if environment == "public":
cmd_args.extend(["--environment", "public"])
gateway_cmd = cmd + [
f'{oc_script} --gateway {" ".join(cmd_args)}'
]
run_command(gateway_cmd, cwd=cwd)
def _openclaw_prompt_max(action_name, non_interactive, on_max):
"""Prompt user after max attempts reached."""
if non_interactive:
log.info(
f"{action_name} reached max attempts → non-interactive mode: auto '{on_max}'."
)
return on_max
while True:
choice = input(
f"{action_name} reached max attempts. "
"What do you want to do? [c]ontinue / [q]uit: "
).strip().lower()
if choice in ("c", "continue"):
return "continue"
elif choice in ("q", "quit"):
return "quit"
def _openclaw_prompt_retry(action_name, non_interactive, on_failure):
"""Prompt user after a failed attempt (before max attempts)."""
if non_interactive:
log.info(
f"{action_name} failed → non-interactive mode: auto '{on_failure}'."
)
return on_failure
while True:
choice = input(
f"{action_name} failed. What do you want to do? "
"[r]etry / [c]ontinue / [q]uit: "
).strip().lower()
if choice in ("r", "retry"):
return "retry"
elif choice in ("c", "continue"):
return "continue"
elif choice in ("q", "quit"):
return "quit"
def _openclaw_run_with_retries(
cmd,
cwd,
action_name,
max_attempts=3,
non_interactive=False,
on_failure="continue", # or "quit"
on_max="continue", # or "quit"
):
"""Run a command with controlled retry behavior for run_command that raises exceptions on failure."""
attempts = 0
while attempts < max_attempts:
try:
run_command(cmd, cwd=cwd, re_raise=True)
log.info(f"{action_name} completed successfully.")
return 0 # success → exit loop
except Exception as e:
attempts += 1
log.warning(f"{action_name} attempt {attempts} failed {e}")
if attempts < max_attempts:
decision = _openclaw_prompt_retry(action_name, non_interactive, on_failure)
if decision == "retry":
continue
elif decision == "continue":
return 1
elif decision == "quit":
raise SystemExit(f"{action_name} aborted by user.")
else:
log.error(f"{action_name} reached max attempts ({max_attempts}).")
decision = _openclaw_prompt_max(action_name, non_interactive, on_max)
if decision == "continue":
return 1
elif decision == "quit":
raise SystemExit(f"{action_name} aborted after max attempts.")
return 1 # fallback, should not reach here
convert_line_endings(oc_path)
oc_script = oc_script.replace("\\", "/")
oc_env_file=os.path.join(oc_dir, ".env")
oc_env : dict[str, str] = get_dotenv_vars(oc_env_file)
if not oc_env:
log.error(f"OpenClaw .env file not found at {oc_env_file}")
env = {}
for key in ['OPENCLAW_IMAGE', 'OPENCLAW_CONFIG_DIR', 'OPENCLAW_WORKSPACE_DIR']:
val = oc_env.get(key)
if val is not None:
env[key] = val
if oc_sandbox:
for key in ['OPENCLAW_SANDBOX', 'OPENCLAW_INSTALL_DOCKER_CLI']:
val = oc_env.get(key)
if val is not None:
env[key] = val
if not onboard_store['b']:
val = oc_env.get('OPENCLAW_SKIP_ONBOARDING')
if val is not None:
env['OPENCLAW_SKIP_ONBOARDING'] = val
env_prefix = " ".join(f"{k}={shlex.quote(v)}" for k, v in env.items())
cmd_str = f"{env_prefix} {oc_script}" if env_prefix else oc_script
oc_cmd = cmd + [cmd_str]
run_command(oc_cmd, cwd=cwd)
def start_ai_suite(profile=None, environment=None, build=False):
"""Start the AI-Suite services (using its compose file) for the specified
@@ -3158,13 +3182,15 @@ def docker_container_is_running(container):
log.error(f"Exception: {e.stderr}.")
return False
def display_service_endpoints(profile, supabase, openclaw, env_vars={}):
def display_service_endpoints(profile, supabase, openclaw, env_vars=None):
"""Display AI-Suite installation or operation status"""
if not profile:
log.error("Profile required to display service endpoints")
return
debug = log_level == logging.DEBUG
if env_vars is None:
env_vars = {}
host = "localhost" if debug else env_vars.get('AC_DOMAIN', 'undefined')
private = str(env_vars.get('AC_LOCAL')).lower()
protocol = 'http' if private else 'https'
@@ -3431,7 +3457,7 @@ def display_ac_env_vars(ac_env_vars):
log.info(raw_msg, extra=env_var_style)
log.info("="*60, extra=line_style)
def setup_ai_suite_ac_auto_config(prompt_store, onboard_store, env_vars:dict={}):
def setup_ai_suite_ac_auto_config(prompt_store, onboard_store, env_vars=None):
"""Setup env_vars for self-hosted AI-Suite with Caddy/Nginx proxy and Authelia
2FA identity and access management.
"""
@@ -3931,14 +3957,17 @@ def main():
# Setup OpenClaw repository if using OpenClaw
openclaw = \
any(p for p in args.profile if p in ['openclaw', 'ai-all'])
ocwd = None
oc_cwd = None
oc_sandbox = None
onboard_store = {'b':False}
base_dir = pathlib.Path(__file__).parent
if openclaw:
if build:
clone_openclaw_repo()
ocwd = os.path.join(base_dir, "openclaw")
prepare_openclaw_env(ocwd)
oc_cwd = os.path.join(base_dir, "openclaw")
env = os.getenv("OPENCLAW_SANDBOX")
oc_sandbox = True if env is None or int(env) == 1 else False
prepare_openclaw_env(oc_cwd, oc_sandbox)
# Setup Open WebUI Functions and Tools Filesystem repository
open_webui = \
@@ -4034,7 +4063,10 @@ def main():
ac_subdomains.append(profile)
if ac_subdomains:
ac_env_vars.append(f'AC_SUBDOMAINS="{" ".join(ac_subdomains)}"')
# Miscalleanous environment variables
# OpenClaw sandbox
if oc_sandbox:
ac_env_vars.append(f'AC_OPENCLAW_SANDBOX={str(True).lower()}')
# Miscellaneous environment variables
ac_env_vars.append(f'AC_LLAMA={str(False).lower()}')
ac_env_vars.append(f'AC_LLAMACPP={str(llama_cpp).lower()}')
ac_env_vars.append(f'AC_SEARXNG={str(False).lower()}')
@@ -4260,7 +4292,7 @@ def main():
# Setup OpenClaw configuration
if openclaw:
prepare_openclaw_config(ocwd, env_vars)
prepare_openclaw_config(oc_cwd, env_vars, onboard_store)
# Setup Open WebUI Functions and Tools Filesystem repos
if open_webui:
@@ -4289,11 +4321,11 @@ def main():
start_supabase(args.environment, build)
# Give Supabase some time to initialize
log.info("Waiting for Supabase to initialize...", extra=log_bright)
wait_with_progress(10)
wait_with_progress(5)
# Start OpenClaw
if openclaw:
start_openclaw(onboard_store, args.environment, build, ocwd)
start_openclaw(onboard_store, build, oc_cwd, oc_sandbox)
# Give OpenClaw some time to initialize
log.info("Waiting for OpenClaw to initialize...", extra=log_bright)
wait_with_progress(5)