From f0542df9f0088a6b64bb6eb4741e817ade8bcb55 Mon Sep 17 00:00:00 2001 From: Vignesh Natarajan Date: Sun, 22 Feb 2026 16:33:02 -0800 Subject: [PATCH] Docker: precreate identity dir in docker setup --- CHANGELOG.md | 1 + docker-setup.sh | 3 +++ src/docker-setup.test.ts | 17 ++++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36474795ba5..fd1da947ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Install/Discord Voice: make `@discordjs/opus` an optional dependency so `openclaw` install/update no longer hard-fails when native Opus builds fail, while keeping `opusscript` as the runtime fallback decoder for Discord voice flows. (#23737, #23733, #23703) Thanks @jeadland, @Sheetaa, and @Breakyman. +- Docker/Setup: precreate `$OPENCLAW_CONFIG_DIR/identity` during `docker-setup.sh` so CLI commands that need device identity (for example `devices list`) avoid `EACCES ... /home/node/.openclaw/identity` failures on restrictive bind mounts. (#23948) Thanks @ackson-beep. - Exec/Background: stop applying the default exec timeout to background sessions (`background: true` or explicit `yieldMs`) when no explicit timeout is set, so long-running background jobs are no longer terminated at the default timeout boundary. (#23303) - Slack/Threading: sessions: keep parent-session forking and thread-history context active beyond first turn by removing first-turn-only gates in session init, thread-history fetch, and reply prompt context injection. (#23843, #23090) Thanks @vincentkoc and @Taskle. - Slack/Threading: respect `replyToMode` when Slack auto-populates top-level `thread_ts`, and ignore inline `replyToId` directive tags when `replyToMode` is `off` so thread forcing stays disabled unless explicitly configured. (#23839, #23320, #23513) Thanks @vincentkoc and @dorukardahan. diff --git a/docker-setup.sh b/docker-setup.sh index 00c3cf1924f..8c67dc0962d 100755 --- a/docker-setup.sh +++ b/docker-setup.sh @@ -82,6 +82,9 @@ fi mkdir -p "$OPENCLAW_CONFIG_DIR" mkdir -p "$OPENCLAW_WORKSPACE_DIR" +# Seed device-identity parent eagerly for Docker Desktop/Windows bind mounts +# that reject creating new subdirectories from inside the container. +mkdir -p "$OPENCLAW_CONFIG_DIR/identity" export OPENCLAW_CONFIG_DIR export OPENCLAW_WORKSPACE_DIR diff --git a/src/docker-setup.test.ts b/src/docker-setup.test.ts index 710e920fe61..20f754990e3 100644 --- a/src/docker-setup.test.ts +++ b/src/docker-setup.test.ts @@ -1,5 +1,5 @@ import { spawnSync } from "node:child_process"; -import { chmod, copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { chmod, copyFile, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; @@ -153,6 +153,21 @@ describe("docker-setup.sh", () => { expect(log).toContain("--build-arg OPENCLAW_DOCKER_APT_PACKAGES=ffmpeg build-essential"); }); + it("precreates config identity dir for CLI device auth writes", async () => { + const activeSandbox = requireSandbox(sandbox); + const configDir = join(activeSandbox.rootDir, "config-identity"); + const workspaceDir = join(activeSandbox.rootDir, "workspace-identity"); + + const result = runDockerSetup(activeSandbox, { + OPENCLAW_CONFIG_DIR: configDir, + OPENCLAW_WORKSPACE_DIR: workspaceDir, + }); + + expect(result.status).toBe(0); + const identityDirStat = await stat(join(configDir, "identity")); + expect(identityDirStat.isDirectory()).toBe(true); + }); + it("rejects injected multiline OPENCLAW_EXTRA_MOUNTS values", async () => { const activeSandbox = requireSandbox(sandbox);