diff --git a/scripts/clawdock/clawdock-helpers.sh b/scripts/clawdock/clawdock-helpers.sh index 7c392c6569b2..7b8c5ae81daf 100755 --- a/scripts/clawdock/clawdock-helpers.sh +++ b/scripts/clawdock/clawdock-helpers.sh @@ -73,6 +73,27 @@ _clawdock_mask_value() { printf "%s" "" } +_clawdock_browser_url_for_gateway_url() { + local url="$1" + case "$url" in + http://127.0.0.1:18789*|http://localhost:18789*|https://127.0.0.1:18789*|https://localhost:18789*) ;; + *) + printf "%s" "$url" + return 0 + ;; + esac + + local published published_port + published=$(_clawdock_compose port openclaw-gateway 18789 2>/dev/null | head -n 1 | tr -d '\r') + published_port="${published##*:}" + if [[ ! "$published_port" =~ ^[0-9]+$ ]]; then + printf "%s" "$url" + return 0 + fi + + printf "%s" "${url/:18789/:${published_port}}" +} + _clawdock_read_config_dir() { if [[ ! -f "$CLAWDOCK_CONFIG" ]]; then return 1 @@ -375,7 +396,7 @@ clawdock-dashboard() { echo "🦞 Getting dashboard URL..." local output exit_status url - output=$(_clawdock_compose run --rm openclaw-cli dashboard --no-open 2>&1) + output=$(_clawdock_compose run --rm --no-deps openclaw-cli dashboard --no-open 2>&1) exit_status=$? url=$(printf "%s\n" "$output" | _clawdock_filter_warnings | grep -o 'http[s]\?://[^[:space:]]*' | head -n 1) if [[ $exit_status -ne 0 ]]; then @@ -385,6 +406,7 @@ clawdock-dashboard() { fi if [[ -n "$url" ]]; then + url=$(_clawdock_browser_url_for_gateway_url "$url") echo -e "✅ Opening: ${_CLR_CYAN}${url}${_CLR_RESET}" open "$url" 2>/dev/null || xdg-open "$url" 2>/dev/null || echo -e " Please open manually: ${_CLR_CYAN}${url}${_CLR_RESET}" echo "" diff --git a/test/scripts/clawdock-helpers.test.ts b/test/scripts/clawdock-helpers.test.ts index 135d8d06ed58..62ea6cfa99db 100644 --- a/test/scripts/clawdock-helpers.test.ts +++ b/test/scripts/clawdock-helpers.test.ts @@ -10,6 +10,10 @@ import { describe, expect, it } from "vitest"; const execFileAsync = promisify(execFile); const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +async function writeExecutable(file: string, content: string) { + await writeFile(file, content, { mode: 0o755 }); +} + describe("scripts/clawdock/clawdock-helpers.sh", () => { it("loads the standard docker-compose.override.yml before ClawDock extra overrides", async () => { const tempDir = await mkdtemp(path.join(tmpdir(), "openclaw-clawdock-")); @@ -22,12 +26,11 @@ describe("scripts/clawdock/clawdock-helpers.sh", () => { await writeFile(path.join(projectDir, "docker-compose.yml"), "services: {}\n"); await writeFile(path.join(projectDir, "docker-compose.override.yml"), "services: {}\n"); await writeFile(path.join(projectDir, "docker-compose.extra.yml"), "services: {}\n"); - await writeFile( + await writeExecutable( path.join(binDir, "docker"), `#!/usr/bin/env bash printf '%s\\n' "$@" > "$CLAWDOCK_DOCKER_ARGS_FILE" `, - { mode: 0o755 }, ); await execFileAsync( @@ -62,4 +65,79 @@ printf '%s\\n' "$@" > "$CLAWDOCK_DOCKER_ARGS_FILE" await rm(tempDir, { force: true, recursive: true }); } }); + + it("opens dashboard URLs through the published gateway port without starting dependencies", async () => { + const tempDir = await mkdtemp(path.join(tmpdir(), "openclaw-clawdock-")); + try { + const projectDir = path.join(tempDir, "project"); + const binDir = path.join(tempDir, "bin"); + const argsFile = path.join(tempDir, "docker-args.txt"); + const openedUrlFile = path.join(tempDir, "opened-url.txt"); + await mkdir(projectDir); + await mkdir(binDir); + await writeFile(path.join(projectDir, "docker-compose.yml"), "services: {}\n"); + await writeExecutable( + path.join(binDir, "docker"), + `#!/usr/bin/env bash +printf '%s\\n' "$@" >> "$CLAWDOCK_DOCKER_ARGS_FILE" +printf '%s\\n' '---' >> "$CLAWDOCK_DOCKER_ARGS_FILE" +if [[ "$*" == *" port openclaw-gateway 18789" ]]; then + printf '%s\\n' '0.0.0.0:19001' +else + printf '%s\\n' 'Dashboard: http://127.0.0.1:18789/?token=test-token' +fi +`, + ); + await writeExecutable( + path.join(binDir, "open"), + `#!/usr/bin/env bash +printf '%s\\n' "$1" > "$CLAWDOCK_OPENED_URL_FILE" +`, + ); + + await execFileAsync( + "bash", + ["-c", "source scripts/clawdock/clawdock-helpers.sh; clawdock-dashboard"], + { + cwd: repoRoot, + env: { + ...process.env, + CLAWDOCK_DIR: projectDir, + CLAWDOCK_DOCKER_ARGS_FILE: argsFile, + CLAWDOCK_OPENED_URL_FILE: openedUrlFile, + HOME: path.join(tempDir, "home"), + PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}`, + }, + }, + ); + + await expect(readFile(openedUrlFile, "utf8")).resolves.toBe( + "http://127.0.0.1:19001/?token=test-token\n", + ); + await expect(readFile(argsFile, "utf8")).resolves.toBe( + [ + "compose", + "-f", + path.join(projectDir, "docker-compose.yml"), + "run", + "--rm", + "--no-deps", + "openclaw-cli", + "dashboard", + "--no-open", + "---", + "compose", + "-f", + path.join(projectDir, "docker-compose.yml"), + "port", + "openclaw-gateway", + "18789", + "---", + "", + ].join("\n"), + ); + } finally { + await rm(tempDir, { force: true, recursive: true }); + } + }); });