diff --git a/CHANGELOG.md b/CHANGELOG.md index 76b0789ffc2..96c5d4d0630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai - Channels: add Yuanbao channel docs entrance so the Tencent Yuanbao bot appears in the channel listing and sidebar navigation. (#73443) Thanks @loongfay. - Active Memory: add optional per-conversation `allowedChatIds` and `deniedChatIds` filters so operators can enable recall only for selected direct, group, or channel conversations while keeping broad sessions skipped. (#67977) Thanks @quengh. - Active Memory: return bounded partial recall summaries when the hidden memory sub-agent times out, including the default temporary-transcript path, so useful recovered context is not discarded. (#73219) Thanks @joeykrug. +- Docker setup: add `OPENCLAW_SKIP_ONBOARDING` so automated Docker installs can skip the interactive onboarding step while still applying gateway defaults. (#55518) Thanks @jinjimz. ### Fixes diff --git a/docs/install/docker.md b/docs/install/docker.md index 85672edf0d8..a65abe58872 100644 --- a/docs/install/docker.md +++ b/docs/install/docker.md @@ -131,6 +131,7 @@ The setup script accepts these optional environment variables: | `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume | | `OPENCLAW_PLUGIN_STAGE_DIR` | Container path for generated bundled plugin deps and mirrors | | `OPENCLAW_SANDBOX` | Opt in to sandbox bootstrap (`1`, `true`, `yes`, `on`) | +| `OPENCLAW_SKIP_ONBOARDING` | Skip the interactive onboarding step (`1`, `true`, `yes`, `on`) | | `OPENCLAW_DOCKER_SOCKET` | Override Docker socket path | | `OPENCLAW_DISABLE_BONJOUR` | Disable Bonjour/mDNS advertising (defaults to `1` for Docker) | | `OPENCLAW_DISABLE_BUNDLED_SOURCE_OVERLAYS` | Disable bundled plugin source bind-mount overlays | diff --git a/scripts/docker/setup.sh b/scripts/docker/setup.sh index fb2cb217bc9..76ee72f1d3b 100755 --- a/scripts/docker/setup.sh +++ b/scripts/docker/setup.sh @@ -12,6 +12,8 @@ RAW_SANDBOX_SETTING="${OPENCLAW_SANDBOX:-}" SANDBOX_ENABLED="" DOCKER_SOCKET_PATH="${OPENCLAW_DOCKER_SOCKET:-}" TIMEZONE="${OPENCLAW_TZ:-}" +RAW_SKIP_ONBOARDING="${OPENCLAW_SKIP_ONBOARDING:-}" +SKIP_ONBOARDING="" fail() { echo "ERROR: $*" >&2 @@ -232,6 +234,9 @@ fi if is_truthy_value "$RAW_SANDBOX_SETTING"; then SANDBOX_ENABLED="1" fi +if is_truthy_value "$RAW_SKIP_ONBOARDING"; then + SKIP_ONBOARDING="1" +fi OPENCLAW_CONFIG_DIR="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}" OPENCLAW_WORKSPACE_DIR="${OPENCLAW_WORKSPACE_DIR:-$HOME/.openclaw/workspace}" @@ -295,6 +300,7 @@ export OTEL_EXPORTER_OTLP_PROTOCOL="${OTEL_EXPORTER_OTLP_PROTOCOL:-}" export OTEL_SERVICE_NAME="${OTEL_SERVICE_NAME:-}" export OTEL_SEMCONV_STABILITY_OPT_IN="${OTEL_SEMCONV_STABILITY_OPT_IN:-}" export OPENCLAW_OTEL_PRELOADED="${OPENCLAW_OTEL_PRELOADED:-}" +export OPENCLAW_SKIP_ONBOARDING="$SKIP_ONBOARDING" # Detect Docker socket GID for sandbox group_add. DOCKER_GID="" @@ -489,7 +495,8 @@ upsert_env "$ENV_FILE" \ OTEL_EXPORTER_OTLP_PROTOCOL \ OTEL_SERVICE_NAME \ OTEL_SEMCONV_STABILITY_OPT_IN \ - OPENCLAW_OTEL_PRELOADED + OPENCLAW_OTEL_PRELOADED \ + OPENCLAW_SKIP_ONBOARDING if [[ "$IMAGE_NAME" == "openclaw:local" ]]; then echo "==> Building Docker image: $IMAGE_NAME" @@ -525,22 +532,26 @@ run_prestart_gateway --user root --entrypoint sh openclaw-gateway -c \ [ -d /home/node/.openclaw/workspace/.openclaw ] && chown -R node:node /home/node/.openclaw/workspace/.openclaw || true' echo "" -echo "==> Onboarding (interactive)" -echo "Docker setup pins Gateway mode to local." -echo "Gateway runtime bind comes from OPENCLAW_GATEWAY_BIND (default: lan)." -echo "Current runtime bind: $OPENCLAW_GATEWAY_BIND" -if is_truthy_value "$OPENCLAW_DISABLE_BONJOUR"; then - echo "Bonjour/mDNS advertising: force disabled (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)." -elif [[ -z "$OPENCLAW_DISABLE_BONJOUR" ]]; then - echo "Bonjour/mDNS advertising: auto (disabled inside the Gateway container unless explicitly enabled)." +if [[ -n "$SKIP_ONBOARDING" ]]; then + echo "==> Skipping onboarding (OPENCLAW_SKIP_ONBOARDING is set)" else - echo "Bonjour/mDNS advertising: explicitly enabled (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)." + echo "==> Onboarding (interactive)" + echo "Docker setup pins Gateway mode to local." + echo "Gateway runtime bind comes from OPENCLAW_GATEWAY_BIND (default: lan)." + echo "Current runtime bind: $OPENCLAW_GATEWAY_BIND" + if is_truthy_value "$OPENCLAW_DISABLE_BONJOUR"; then + echo "Bonjour/mDNS advertising: force disabled (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)." + elif [[ -z "$OPENCLAW_DISABLE_BONJOUR" ]]; then + echo "Bonjour/mDNS advertising: auto (disabled inside the Gateway container unless explicitly enabled)." + else + echo "Bonjour/mDNS advertising: explicitly enabled (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)." + fi + echo "Gateway token: $OPENCLAW_GATEWAY_TOKEN" + echo "Tailscale exposure: Off (use host-level tailnet/Tailscale setup separately)." + echo "Install Gateway daemon: No (managed by Docker Compose)" + echo "" + run_prestart_cli onboard --mode local --no-install-daemon fi -echo "Gateway token: $OPENCLAW_GATEWAY_TOKEN" -echo "Tailscale exposure: Off (use host-level tailnet/Tailscale setup separately)." -echo "Install Gateway daemon: No (managed by Docker Compose)" -echo "" -run_prestart_cli onboard --mode local --no-install-daemon echo "" echo "==> Docker gateway defaults" diff --git a/src/docker-setup.e2e.test.ts b/src/docker-setup.e2e.test.ts index 162dbc4dfb5..ef3d54747be 100644 --- a/src/docker-setup.e2e.test.ts +++ b/src/docker-setup.e2e.test.ts @@ -520,6 +520,40 @@ describe("scripts/docker/setup.sh", () => { expect(result.stderr).toContain("OPENCLAW_TZ must match a timezone in /usr/share/zoneinfo"); }); + it("skips onboarding when OPENCLAW_SKIP_ONBOARDING is set", async () => { + const activeSandbox = requireSandbox(sandbox); + await resetDockerLog(activeSandbox); + + const result = runDockerSetup(activeSandbox, { + OPENCLAW_SKIP_ONBOARDING: "1", + }); + + expect(result.status).toBe(0); + const log = await readDockerLog(activeSandbox); + expect(log).not.toContain("onboard"); + // Gateway defaults (config set) and control UI allowlist should still run. + expect(log).toContain("config set --batch-json"); + expect(log).toContain('"path":"gateway.mode","value":"local"'); + expect(log).toContain('"path":"gateway.bind","value":"lan"'); + const envFile = await readFile(join(activeSandbox.rootDir, ".env"), "utf8"); + expect(envFile).toContain("OPENCLAW_SKIP_ONBOARDING=1"); + }); + + it("treats OPENCLAW_SKIP_ONBOARDING=0 as disabled and runs onboarding", async () => { + const activeSandbox = requireSandbox(sandbox); + await resetDockerLog(activeSandbox); + + const result = runDockerSetup(activeSandbox, { + OPENCLAW_SKIP_ONBOARDING: "0", + }); + + expect(result.status).toBe(0); + const log = await readDockerLog(activeSandbox); + expect(log).toContain("onboard --mode local --no-install-daemon"); + const envFile = await readFile(join(activeSandbox.rootDir, ".env"), "utf8"); + expect(envFile).toMatch(/OPENCLAW_SKIP_ONBOARDING=\n/); + }); + it("avoids associative arrays so the script remains Bash 3.2-compatible", async () => { const script = await readFile(join(repoRoot, "scripts", "docker", "setup.sh"), "utf8"); expect(script).not.toMatch(/^\s*declare -A\b/m);