CoolFace
Apppublic

q81801/agent

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
openclaw-entrypoint.sh580 linesDownload Raw Back to scripts
1#!/usr/bin/env bash2set -euo pipefail3 4OPENCLAW_USER="${OPENCLAW_USER:-root}"5OPENCLAW_GROUP="${OPENCLAW_GROUP:-root}"6OPENCLAW_HOME="${OPENCLAW_HOME:-/root}"7OPENCLAW_STATE_DIR="${OPENCLAW_STATE_DIR:-/root/.openclaw}"8OPENCLAW_CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${OPENCLAW_STATE_DIR}/openclaw.json}"9OPENCLAW_WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-${OPENCLAW_STATE_DIR}/workspace}"10OPENCLAW_GATEWAY_BIND="${OPENCLAW_GATEWAY_BIND:-lan}"11OPENCLAW_GATEWAY_PORT="${OPENCLAW_GATEWAY_PORT:-7860}"12OPENCLAW_INIT_GATEWAY_MODE="${OPENCLAW_INIT_GATEWAY_MODE:-local}"13OPENCLAW_GATEWAY_AUTH_MODE="${OPENCLAW_GATEWAY_AUTH_MODE:-token}"14OPENCLAW_LLM_PROVIDER="${OPENCLAW_LLM_PROVIDER:-thirdparty}"15OPENCLAW_LLM_MODEL="${OPENCLAW_LLM_MODEL:-}"16OPENCLAW_LLM_API="${OPENCLAW_LLM_API:-openai-completions}"17OPENCLAW_LLM_BASE_URL_ENV="${OPENCLAW_LLM_BASE_URL_ENV:-OPENCLAW_LLM_BASE_URL}"18OPENCLAW_LLM_API_KEY_ENV="${OPENCLAW_LLM_API_KEY_ENV:-OPENCLAW_LLM_API_KEY}"19OPENCLAW_BACKUP_ENABLED="${OPENCLAW_BACKUP_ENABLED:-true}"20OPENCLAW_BACKUP_CRON="${OPENCLAW_BACKUP_CRON:-*/30 * * * *}"21OPENCLAW_BACKUP_SOURCE_DIR="${OPENCLAW_BACKUP_SOURCE_DIR:-${OPENCLAW_STATE_DIR}}"22OPENCLAW_BACKUP_ROOT_CONFIG_DIR="${OPENCLAW_BACKUP_ROOT_CONFIG_DIR:-/root/.config}"23OPENCLAW_BACKUP_ROOT_CODEX_DIR="${OPENCLAW_BACKUP_ROOT_CODEX_DIR:-/root/.codex}"24OPENCLAW_BACKUP_ROOT_CLAUDE_DIR="${OPENCLAW_BACKUP_ROOT_CLAUDE_DIR:-/root/.claude}"25OPENCLAW_BACKUP_ROOT_AGENTS_DIR="${OPENCLAW_BACKUP_ROOT_AGENTS_DIR:-/root/.agents}"26OPENCLAW_BACKUP_ROOT_SSH_DIR="${OPENCLAW_BACKUP_ROOT_SSH_DIR:-/root/.ssh}"27OPENCLAW_BACKUP_ROOT_NPM_DIR="${OPENCLAW_BACKUP_ROOT_NPM_DIR:-/root/.npm}"28OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR="${OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR:-/root/.lark-cli}"29OPENCLAW_BACKUP_ENV_FILE_PATH="${OPENCLAW_BACKUP_ENV_FILE_PATH:-/etc/openclaw-backup.env}"30OPENCLAW_STDOUT_LOG_PATH="${OPENCLAW_STDOUT_LOG_PATH:-/var/log/openclaw/gateway.stdout.log}"31OPENCLAW_STDERR_LOG_PATH="${OPENCLAW_STDERR_LOG_PATH:-/var/log/openclaw/gateway.stderr.log}"32OPENCLAW_SSHX_AUTO_START="${OPENCLAW_SSHX_AUTO_START:-false}"33OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH="${OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH:-false}"34OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH="${OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH:-false}"35OPENCLAW_CHILD_PID=""36OPENCLAW_SSHX_PID=""37OPENCLAW_SHUTDOWN_BACKUP_DONE=038 39export HOME="$OPENCLAW_HOME"40export OPENCLAW_HOME41export OPENCLAW_STATE_DIR42export OPENCLAW_CONFIG_PATH43export OPENCLAW_BACKUP_ENV_FILE_PATH44OPENCLAW_STEP_INDEX=045OPENCLAW_ENTRYPOINT_TAG="openclaw-entrypoint"46 47timestamp_utc() {48  date -u +"%Y-%m-%dT%H:%M:%SZ"49}50 51log_info() {52  printf '[%s] %s: %s\n' "$(timestamp_utc)" "$OPENCLAW_ENTRYPOINT_TAG" "$*"53}54 55log_warn() {56  printf '[%s] %s: %s\n' "$(timestamp_utc)" "$OPENCLAW_ENTRYPOINT_TAG" "$*" >&257}58 59run_step() {60  local description="$1"61  shift62  OPENCLAW_STEP_INDEX=$((OPENCLAW_STEP_INDEX + 1))63  log_info "step ${OPENCLAW_STEP_INDEX} start: ${description}"64  "$@"65  log_info "step ${OPENCLAW_STEP_INDEX} done: ${description}"66}67 68is_true() {69  local value70  value="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')"71  [[ "$value" == "1" || "$value" == "true" || "$value" == "yes" || "$value" == "on" ]]72}73 74generate_gateway_token() {75  if command -v openssl >/dev/null 2>&1; then76    openssl rand -hex 3277  else78    python3 - <<'PY'79import secrets80print(secrets.token_hex(32))81PY82  fi83}84 85mkdir_state_dirs() {86  mkdir -p "$OPENCLAW_STATE_DIR" "$OPENCLAW_WORKSPACE_DIR"87  mkdir -p "$OPENCLAW_STATE_DIR/identity"88  mkdir -p "$OPENCLAW_STATE_DIR/agents/main/agent"89  mkdir -p "$OPENCLAW_STATE_DIR/agents/main/sessions"90  mkdir -p "$OPENCLAW_STATE_DIR/logs"91  mkdir -p "$(dirname "$OPENCLAW_STDOUT_LOG_PATH")" "$(dirname "$OPENCLAW_STDERR_LOG_PATH")"92  touch "$OPENCLAW_STDOUT_LOG_PATH" "$OPENCLAW_STDERR_LOG_PATH"93}94 95fix_permissions() {96  local path97 98  if [[ "$(id -u)" -ne 0 ]]; then99    return 0100  fi101 102  if [[ -e "$OPENCLAW_HOME" ]]; then103    chown "$OPENCLAW_USER:$OPENCLAW_GROUP" "$OPENCLAW_HOME"104  fi105 106  for path in \107    "$OPENCLAW_STATE_DIR" \108    "$OPENCLAW_WORKSPACE_DIR" \109    "$OPENCLAW_STDOUT_LOG_PATH" \110    "$OPENCLAW_STDERR_LOG_PATH" \111    "$(dirname "$OPENCLAW_STDOUT_LOG_PATH")" \112    "$(dirname "$OPENCLAW_STDERR_LOG_PATH")"; do113    if [[ -e "$path" ]]; then114      chown -R "$OPENCLAW_USER:$OPENCLAW_GROUP" "$path"115    fi116  done117}118 119write_or_update_config() {120  local existing_token121  existing_token=""122 123  if [[ -f "$OPENCLAW_CONFIG_PATH" ]]; then124    existing_token="$(python3 - <<'PY'125import json126import os127 128config_path = os.environ["OPENCLAW_CONFIG_PATH"]129try:130    with open(config_path, "r", encoding="utf-8") as fh:131        cfg = json.load(fh)132    token = cfg.get("gateway", {}).get("auth", {}).get("token", "")133    if isinstance(token, str):134        print(token.strip())135except Exception:136    pass137PY138)"139  fi140 141  if [[ -z "${OPENCLAW_GATEWAY_TOKEN:-}" ]]; then142    if [[ -n "$existing_token" ]]; then143      OPENCLAW_GATEWAY_TOKEN="$existing_token"144    else145      OPENCLAW_GATEWAY_TOKEN="$(generate_gateway_token)"146    fi147  fi148  export OPENCLAW_GATEWAY_TOKEN149 150  OPENCLAW_GATEWAY_PORT="$OPENCLAW_GATEWAY_PORT" \151  OPENCLAW_GATEWAY_BIND="$OPENCLAW_GATEWAY_BIND" \152  OPENCLAW_INIT_GATEWAY_MODE="$OPENCLAW_INIT_GATEWAY_MODE" \153  OPENCLAW_CONFIG_PATH="$OPENCLAW_CONFIG_PATH" \154  OPENCLAW_GATEWAY_TOKEN="$OPENCLAW_GATEWAY_TOKEN" \155  OPENCLAW_GATEWAY_PASSWORD="${OPENCLAW_GATEWAY_PASSWORD:-}" \156  OPENCLAW_GATEWAY_AUTH_MODE="$OPENCLAW_GATEWAY_AUTH_MODE" \157  OPENCLAW_LLM_PROVIDER="$OPENCLAW_LLM_PROVIDER" \158  OPENCLAW_LLM_MODEL="$OPENCLAW_LLM_MODEL" \159  OPENCLAW_LLM_API="$OPENCLAW_LLM_API" \160  OPENCLAW_LLM_BASE_URL_ENV="$OPENCLAW_LLM_BASE_URL_ENV" \161  OPENCLAW_LLM_API_KEY_ENV="$OPENCLAW_LLM_API_KEY_ENV" \162  OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK="${OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK:-true}" \163  OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH="$OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH" \164  OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH="$OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH" \165  OPENCLAW_INIT_CONTROL_UI_ALLOWED_ORIGINS="${OPENCLAW_INIT_CONTROL_UI_ALLOWED_ORIGINS:-}" \166  python3 - <<'PY'167import json168import os169import sys170from pathlib import Path171 172config_path = Path(os.environ["OPENCLAW_CONFIG_PATH"])173config = {}174if config_path.exists():175    try:176        config = json.loads(config_path.read_text(encoding="utf-8"))177    except Exception:178        config = {}179 180provider = os.environ.get("OPENCLAW_LLM_PROVIDER", "thirdparty").strip() or "thirdparty"181model = (os.environ.get("OPENCLAW_LLM_MODEL") or "").strip()182api = os.environ.get("OPENCLAW_LLM_API", "openai-completions").strip() or "openai-completions"183base_env_name = os.environ.get("OPENCLAW_LLM_BASE_URL_ENV", "OPENCLAW_LLM_BASE_URL").strip() or "OPENCLAW_LLM_BASE_URL"184key_env_name = os.environ.get("OPENCLAW_LLM_API_KEY_ENV", "OPENCLAW_LLM_API_KEY").strip() or "OPENCLAW_LLM_API_KEY"185base_url_value = (os.environ.get(base_env_name) or "").strip()186api_key_value = (os.environ.get(key_env_name) or "").strip()187 188missing_llm_env = []189if not model:190    missing_llm_env.append("OPENCLAW_LLM_MODEL")191if not base_url_value:192    missing_llm_env.append(base_env_name)193if not api_key_value:194    missing_llm_env.append(key_env_name)195custom_llm_ready = not missing_llm_env196model_ref = f"{provider}/{model}" if custom_llm_ready else ""197 198if missing_llm_env:199    print(200        "openclaw: skip custom LLM model configuration because missing: "201        + ", ".join(missing_llm_env),202        file=sys.stderr,203    )204    sshx_auto_start = (os.environ.get("OPENCLAW_SSHX_AUTO_START") or "false").strip().lower() in {205        "1",206        "true",207        "yes",208        "on",209    }210    if not sshx_auto_start:211        print(212            "openclaw: if you want to configure LLM via sshx, consider setting OPENCLAW_SSHX_AUTO_START=true",213            file=sys.stderr,214        )215 216fallback_allowed_origins = [f"http://127.0.0.1:{os.environ.get('OPENCLAW_GATEWAY_PORT', '7860')}"]217allowed_origins = fallback_allowed_origins218raw_allowed_origins = (os.environ.get("OPENCLAW_INIT_CONTROL_UI_ALLOWED_ORIGINS") or "").strip()219if raw_allowed_origins:220    try:221        parsed = json.loads(raw_allowed_origins)222        if isinstance(parsed, list):223            normalized = []224            for value in parsed:225                if isinstance(value, str):226                    item = value.strip()227                    if item:228                        normalized.append(item)229            if normalized:230                allowed_origins = normalized231    except Exception:232        pass233 234fallback_origin = (os.environ.get("OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK") or "true").lower() in {235    "1",236    "true",237    "yes",238    "on",239}240allow_insecure_auth = (os.environ.get("OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH") or "false").lower() in {241    "1",242    "true",243    "yes",244    "on",245}246disable_device_auth = (os.environ.get("OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH") or "false").lower() in {247    "1",248    "true",249    "yes",250    "on",251}252 253gateway = config.get("gateway")254if not isinstance(gateway, dict):255    gateway = {}256gateway["mode"] = os.environ.get("OPENCLAW_INIT_GATEWAY_MODE", "local")257gateway["bind"] = os.environ.get("OPENCLAW_GATEWAY_BIND", "lan")258auth = gateway.get("auth")259if not isinstance(auth, dict):260    auth = {}261 262token_value = (os.environ.get("OPENCLAW_GATEWAY_TOKEN") or "").strip()263password_value = (os.environ.get("OPENCLAW_GATEWAY_PASSWORD") or "").strip()264if token_value:265    auth["token"] = token_value266if password_value:267    auth["password"] = password_value268 269valid_auth_modes = {"none", "token", "password", "trusted-proxy"}270mode_env = (os.environ.get("OPENCLAW_GATEWAY_AUTH_MODE") or "").strip().lower()271existing_mode = auth.get("mode")272if mode_env in valid_auth_modes:273    auth["mode"] = mode_env274elif password_value and not token_value:275    auth["mode"] = "password"276elif token_value and not password_value:277    auth["mode"] = "token"278elif token_value and password_value:279    auth["mode"] = "token"280elif isinstance(existing_mode, str) and existing_mode.strip().lower() in valid_auth_modes:281    auth["mode"] = existing_mode.strip().lower()282else:283    auth["mode"] = "token"284 285gateway["auth"] = auth286gateway.setdefault("controlUi", {})287gateway["controlUi"]["allowedOrigins"] = allowed_origins288gateway["controlUi"]["dangerouslyAllowHostHeaderOriginFallback"] = fallback_origin289gateway["controlUi"]["allowInsecureAuth"] = allow_insecure_auth290gateway["controlUi"]["dangerouslyDisableDeviceAuth"] = disable_device_auth291config["gateway"] = gateway292 293agents = config.get("agents")294if not isinstance(agents, dict):295    agents = {}296defaults = agents.get("defaults")297if not isinstance(defaults, dict):298    defaults = {}299if custom_llm_ready:300    defaults["model"] = {"primary": model_ref}301    allow_models = defaults.get("models")302    if not isinstance(allow_models, dict):303        allow_models = {}304    allow_models.setdefault(model_ref, {"alias": "Default"})305    defaults["models"] = allow_models306agents["defaults"] = defaults307config["agents"] = agents308 309if custom_llm_ready:310    models_cfg = config.get("models")311    if not isinstance(models_cfg, dict):312        models_cfg = {}313    models_cfg["mode"] = "merge"314    providers = models_cfg.get("providers")315    if not isinstance(providers, dict):316        providers = {}317    provider_cfg = providers.get(provider)318    if not isinstance(provider_cfg, dict):319        provider_cfg = {}320    provider_cfg["baseUrl"] = f"${{{base_env_name}}}"321    provider_cfg["apiKey"] = f"${{{key_env_name}}}"322    provider_cfg["api"] = api323    provider_cfg["models"] = [{"id": model, "name": model}]324    providers[provider] = provider_cfg325    models_cfg["providers"] = providers326    config["models"] = models_cfg327 328config_path.parent.mkdir(parents=True, exist_ok=True)329config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")330os.chmod(config_path, 0o600)331PY332}333 334write_backup_env_file() {335  local backup_env_file336  local keys=(337    HF_TOKEN338    HUGGINGFACE_HUB_TOKEN339    OPENCLAW_BACKUP_DATASET_REPO340    OPENCLAW_BACKUP_REPO_TYPE341    OPENCLAW_BACKUP_PATH_PREFIX342    OPENCLAW_BACKUP_LATEST_NAME343    OPENCLAW_BACKUP_KEEP_COUNT344    OPENCLAW_BACKUP_PRIVATE345    OPENCLAW_BACKUP_WORK_DIR346    OPENCLAW_BACKUP_SOURCE_DIR347    OPENCLAW_BACKUP_ROOT_CONFIG_DIR348    OPENCLAW_BACKUP_ROOT_CODEX_DIR349    OPENCLAW_BACKUP_ROOT_CLAUDE_DIR350    OPENCLAW_BACKUP_ROOT_AGENTS_DIR351    OPENCLAW_BACKUP_ROOT_SSH_DIR352    OPENCLAW_BACKUP_ROOT_NPM_DIR353    OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR354    OPENCLAW_STATE_DIR355  )356 357  backup_env_file="${OPENCLAW_BACKUP_ENV_FILE_PATH:-/etc/openclaw-backup.env}"358  if [[ "$(id -u)" -ne 0 && "$backup_env_file" == "/etc/openclaw-backup.env" ]]; then359    backup_env_file="${OPENCLAW_STATE_DIR}/openclaw-backup.env"360  fi361 362  mkdir -p "$(dirname "$backup_env_file")"363  if ! : > "$backup_env_file"; then364    echo "openclaw: cannot write backup env file at $backup_env_file" >&2365    return 1366  fi367 368  for key in "${keys[@]}"; do369    printf '%s=%q\n' "$key" "${!key:-}" >> "$backup_env_file"370  done371  OPENCLAW_BACKUP_ENV_FILE_PATH="$backup_env_file"372  export OPENCLAW_BACKUP_ENV_FILE_PATH373 374  if [[ "$(id -u)" -eq 0 ]]; then375    chown "$OPENCLAW_USER:$OPENCLAW_GROUP" "$backup_env_file"376    chmod 640 "$backup_env_file"377  else378    chmod 600 "$backup_env_file"379  fi380}381 382setup_backup_cron() {383  if [[ "$(id -u)" -ne 0 ]]; then384    echo "openclaw: backup cron requires root; skip cron setup" >&2385    return 0386  fi387 388  if ! is_true "$OPENCLAW_BACKUP_ENABLED"; then389    return 0390  fi391 392  if [[ -z "${OPENCLAW_BACKUP_DATASET_REPO:-}" ]]; then393    echo "openclaw: backup enabled but OPENCLAW_BACKUP_DATASET_REPO is empty; skip cron" >&2394    return 0395  fi396 397  if ! write_backup_env_file; then398    echo "openclaw: backup env file unavailable; skip cron setup" >&2399    return 0400  fi401 402  cat > /etc/cron.d/openclaw-backup <<EOFCRON403SHELL=/bin/bash404PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin405OPENCLAW_BACKUP_ENV_FILE_PATH=${OPENCLAW_BACKUP_ENV_FILE_PATH}406${OPENCLAW_BACKUP_CRON} ${OPENCLAW_USER} /usr/local/bin/openclaw-backup-cron.sh >> /var/log/openclaw/backup.log 2>&1407EOFCRON408  chmod 0644 /etc/cron.d/openclaw-backup409  touch /var/log/openclaw/backup.log410  chown "$OPENCLAW_USER:$OPENCLAW_GROUP" /var/log/openclaw/backup.log411 412  if pgrep -x cron >/dev/null 2>&1; then413    return 0414  fi415 416  cron417}418 419restore_from_backup_on_startup() {420  if [[ -z "${OPENCLAW_BACKUP_DATASET_REPO:-}" ]]; then421    echo "openclaw: startup restore skipped (OPENCLAW_BACKUP_DATASET_REPO is empty)" >&2422    return 0423  fi424 425  if ! write_backup_env_file; then426    echo "openclaw: backup env file unavailable; skip startup restore" >&2427    return 0428  fi429  if /usr/local/bin/openclaw-restore.sh; then430    echo "openclaw: restore finished"431  else432    echo "openclaw: restore skipped or failed, continue startup" >&2433  fi434}435 436run_as_node() {437  local cmd=("$@")438  if [[ "$(id -u)" -eq 0 ]]; then439    exec gosu "$OPENCLAW_USER:$OPENCLAW_GROUP" "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH"440  fi441  exec "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH"442}443 444run_as_node_background() {445  local cmd=("$@")446  if [[ "$(id -u)" -eq 0 ]]; then447    gosu "$OPENCLAW_USER:$OPENCLAW_GROUP" "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH" &448  else449    "${cmd[@]}" >>"$OPENCLAW_STDOUT_LOG_PATH" 2>>"$OPENCLAW_STDERR_LOG_PATH" &450  fi451  OPENCLAW_CHILD_PID="$!"452}453 454start_sshx_background() {455  if ! is_true "$OPENCLAW_SSHX_AUTO_START"; then456    return 0457  fi458 459  if ! command -v sshx >/dev/null 2>&1; then460    echo "openclaw: OPENCLAW_SSHX_AUTO_START=true but sshx not found; skip auto start" >&2461    return 0462  fi463 464  if [[ "$(id -u)" -eq 0 ]]; then465    gosu "$OPENCLAW_USER:$OPENCLAW_GROUP" sshx >/proc/1/fd/1 2>/proc/1/fd/2 &466  else467    sshx >/proc/1/fd/1 2>/proc/1/fd/2 &468  fi469  OPENCLAW_SSHX_PID="$!"470  echo "openclaw: sshx started in background (pid=$OPENCLAW_SSHX_PID)"471}472 473stop_sshx_background() {474  if [[ -n "$OPENCLAW_SSHX_PID" ]] && kill -0 "$OPENCLAW_SSHX_PID" >/dev/null 2>&1; then475    echo "openclaw: stopping sshx background process (pid=$OPENCLAW_SSHX_PID)"476    kill "$OPENCLAW_SSHX_PID" >/dev/null 2>&1 || true477    wait "$OPENCLAW_SSHX_PID" >/dev/null 2>&1 || true478  fi479}480 481backup_on_shutdown() {482  if [[ "$OPENCLAW_SHUTDOWN_BACKUP_DONE" -eq 1 ]]; then483    return 0484  fi485  OPENCLAW_SHUTDOWN_BACKUP_DONE=1486 487  if [[ -z "${OPENCLAW_BACKUP_DATASET_REPO:-}" ]]; then488    echo "openclaw: shutdown backup skipped (OPENCLAW_BACKUP_DATASET_REPO is empty)" >&2489    return 0490  fi491 492  if ! write_backup_env_file; then493    echo "openclaw: backup env file unavailable; skip shutdown backup" >&2494    return 0495  fi496  echo "openclaw: running shutdown backup"497  if /usr/local/bin/openclaw-backup-cron.sh; then498    echo "openclaw: shutdown backup completed"499  else500    echo "openclaw: shutdown backup failed, container will still exit" >&2501  fi502}503 504run_gateway_with_shutdown_backup() {505  local gateway_exit_code=0506  local shutting_down=0507 508  on_gateway_signal() {509    local signal="$1"510    if [[ "$shutting_down" -eq 1 ]]; then511      return 0512    fi513    shutting_down=1514    log_warn "received ${signal}, preparing graceful shutdown"515 516    if [[ -n "$OPENCLAW_CHILD_PID" ]] && kill -0 "$OPENCLAW_CHILD_PID" >/dev/null 2>&1; then517      kill -s "$signal" "$OPENCLAW_CHILD_PID" >/dev/null 2>&1 || kill "$OPENCLAW_CHILD_PID" >/dev/null 2>&1 || true518      if ! wait "$OPENCLAW_CHILD_PID"; then519        gateway_exit_code=$?520      fi521    fi522 523    log_info "gateway process stopped with code ${gateway_exit_code}"524    stop_sshx_background525    backup_on_shutdown526    trap - TERM INT QUIT527    exit "$gateway_exit_code"528  }529 530  trap 'on_gateway_signal TERM' TERM531  trap 'on_gateway_signal INT' INT532  trap 'on_gateway_signal QUIT' QUIT533 534  log_info "starting gateway process (bind=${OPENCLAW_GATEWAY_BIND}, port=${OPENCLAW_GATEWAY_PORT})"535  run_as_node_background openclaw gateway --allow-unconfigured --bind "$OPENCLAW_GATEWAY_BIND" --port "$OPENCLAW_GATEWAY_PORT" "$@"536  log_info "gateway process started (pid=${OPENCLAW_CHILD_PID})"537  if ! wait "$OPENCLAW_CHILD_PID"; then538    gateway_exit_code=$?539  fi540  log_info "gateway process exited (pid=${OPENCLAW_CHILD_PID}, code=${gateway_exit_code})"541 542  stop_sshx_background543  backup_on_shutdown544  trap - TERM INT QUIT545  return "$gateway_exit_code"546}547 548main() {549  if [[ "$#" -eq 0 ]]; then550    set -- gateway551  fi552 553  local subcommand="$1"554  shift || true555 556  if [[ "$subcommand" == "gateway" ]]; then557    log_info "starting gateway bootstrap sequence"558    run_step "prepare runtime directories" mkdir_state_dirs559    run_step "restore state from backup (if configured)" restore_from_backup_on_startup560    run_step "write or update gateway config" write_or_update_config561    run_step "fix file ownership and permissions" fix_permissions562    run_step "setup backup cron (if enabled)" setup_backup_cron563    run_step "start sshx background service (if enabled)" start_sshx_background564    OPENCLAW_STEP_INDEX=$((OPENCLAW_STEP_INDEX + 1))565    log_info "step ${OPENCLAW_STEP_INDEX} start: launch gateway"566    run_gateway_with_shutdown_backup "$@"567    local gateway_exit_code=$?568    if [[ "$gateway_exit_code" -ne 0 ]]; then569      log_warn "gateway exited with non-zero code ${gateway_exit_code}"570      return "$gateway_exit_code"571    fi572    log_info "step ${OPENCLAW_STEP_INDEX} done: launch gateway"573  else574    log_info "starting subcommand: openclaw ${subcommand}"575    run_as_node openclaw "$subcommand" "$@"576  fi577}578 579main "$@"580