diff --git a/CHANGELOG.md b/CHANGELOG.md index 106c4c19206..67f693db163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Docs: https://docs.openclaw.ai - Plugins/startup: install bundled runtime dependencies into each plugin's own runtime directory, reuse source-checkout repair caches after rebuilds, and log only packages that were actually installed so repeated Gateway starts stay quiet once deps are present. - Plugins/startup: ignore pnpm's `npm_execpath` when repairing bundled plugin runtime dependencies and skip workspace-only package specs so npm-only install flags or local workspace links do not break packaged plugin startup. - MCP: block interpreter-startup env keys such as `NODE_OPTIONS` for stdio servers while preserving ordinary credential and proxy env vars. (#69540) Thanks @drobison00. +- Agents/shell: ignore non-interactive placeholder shells like `/usr/bin/false` and `/sbin/nologin`, falling back to `sh` so service-user exec runs no longer exit immediately. (#69308) Thanks @sk7n4k3d. - Setup/TUI: relaunch the setup hatch TUI in a fresh process while preserving the configured gateway target and auth source, so onboarding recovers terminal state cleanly without exposing gateway secrets on command-line args. (#69524) Thanks @shakkernerd. - Codex: avoid re-exposing the image-generation tool on native vision turns with inbound images, and keep bare image-model overrides on the configured image provider. (#65061) Thanks @zhulijin1991. - Sessions/reset: clear auto-sourced model, provider, and auth-profile overrides on `/new` and `/reset` while preserving explicit user selections, so channel sessions stop staying pinned to runtime fallback choices. (#69419) Thanks @sk7n4k3d. diff --git a/src/agents/shell-utils.test.ts b/src/agents/shell-utils.test.ts index e03867c2768..28909f66a8e 100644 --- a/src/agents/shell-utils.test.ts +++ b/src/agents/shell-utils.test.ts @@ -3,7 +3,12 @@ import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { captureEnv } from "../test-utils/env.js"; -import { getShellConfig, resolvePowerShellPath, resolveShellFromPath } from "./shell-utils.js"; +import { + detectRuntimeShell, + getShellConfig, + resolvePowerShellPath, + resolveShellFromPath, +} from "./shell-utils.js"; const isWin = process.platform === "win32"; @@ -139,6 +144,45 @@ describe("resolveShellFromPath", () => { }); }); +describe("detectRuntimeShell", () => { + let envSnapshot: ReturnType; + + beforeEach(() => { + envSnapshot = captureEnv([ + "OPENCLAW_SHELL", + "SHELL", + "POWERSHELL_DISTRIBUTION_CHANNEL", + "BASH_VERSION", + "ZSH_VERSION", + "FISH_VERSION", + "KSH_VERSION", + "NU_VERSION", + "NUSHELL_VERSION", + ]); + delete process.env.OPENCLAW_SHELL; + delete process.env.POWERSHELL_DISTRIBUTION_CHANNEL; + delete process.env.BASH_VERSION; + delete process.env.ZSH_VERSION; + delete process.env.FISH_VERSION; + delete process.env.KSH_VERSION; + delete process.env.NU_VERSION; + delete process.env.NUSHELL_VERSION; + }); + + afterEach(() => { + envSnapshot.restore(); + }); + + if (!isWin) { + it("ignores non-interactive SHELL placeholders and falls through to runtime hints", () => { + process.env.SHELL = "/usr/bin/false"; + process.env.BASH_VERSION = "5.2.0"; + + expect(detectRuntimeShell()).toBe("bash"); + }); + } +}); + describe("resolvePowerShellPath", () => { let envSnapshot: ReturnType; const tempDirs: string[] = [];