diff --git a/CHANGELOG.md b/CHANGELOG.md index 372dba86c8e..27bf049d294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Agents/models: keep legacy CLI runtime model refs such as `claude-cli/*` in the configured allowlist after canonical runtime migration, so cron `payload.model` overrides keep working. Fixes #75753. Thanks @RyanSandoval. +- Gateway/watch: keep colored subsystem log prefixes in the managed tmux pane even when the parent shell exports `NO_COLOR`, while preserving explicit `FORCE_COLOR=0` opt-out. Thanks @vincentkoc. - Plugin SDK: re-export `isPrivateIpAddress` from `plugin-sdk/ssrf-runtime`, restoring source-checkout builds for SearXNG and Firecrawl private-network guards. Thanks @vincentkoc. - CLI/directory: report unsupported directory operations for installed channel plugins instead of prompting to reinstall the plugin when it lacks a directory adapter. Fixes #75770. Thanks @lawong888. - Web search/SearXNG: show the JSON API `search.formats` prerequisite during SearXNG setup before prompting for the base URL. Supersedes #65592. Thanks @evanpaul14. diff --git a/docs/help/debugging.md b/docs/help/debugging.md index 9d2766d2898..e8fa6619c57 100644 --- a/docs/help/debugging.md +++ b/docs/help/debugging.md @@ -251,6 +251,8 @@ The tmux wrapper carries common non-secret runtime selectors such as `OPENCLAW_GATEWAY_PORT`, and `OPENCLAW_SKIP_CHANNELS` into the pane. Put provider credentials in your normal profile/config, or use raw foreground mode for one-off ephemeral secrets. +The managed tmux pane also defaults to colored Gateway logs for readability; +set `FORCE_COLOR=0` when starting `pnpm gateway:watch` to disable ANSI output. The watcher restarts on build-relevant files under `src/`, extension source files, extension `package.json` and `openclaw.plugin.json` metadata, `tsconfig.json`, diff --git a/scripts/gateway-watch-tmux.mjs b/scripts/gateway-watch-tmux.mjs index 4ed17f2c310..9e7d586e4c9 100644 --- a/scripts/gateway-watch-tmux.mjs +++ b/scripts/gateway-watch-tmux.mjs @@ -66,6 +66,17 @@ export const resolveGatewayWatchTmuxSessionName = ({ args = [], env = process.en const resolveShell = (env) => env.SHELL || "/bin/sh"; +const resolveColorEnv = (env) => { + const forceColor = env.FORCE_COLOR; + if (forceColor == null || forceColor === "") { + return { assignments: ["FORCE_COLOR=1"], options: ["-u", "NO_COLOR"] }; + } + if (String(forceColor).trim() !== "0") { + return { assignments: [`FORCE_COLOR=${forceColor}`], options: ["-u", "NO_COLOR"] }; + } + return { assignments: [`FORCE_COLOR=${forceColor}`], options: [] }; +}; + export const buildGatewayWatchTmuxCommand = ({ args = [], cwd = process.cwd(), @@ -74,10 +85,13 @@ export const buildGatewayWatchTmuxCommand = ({ sessionName, } = {}) => { const shell = resolveShell(env); + const colorEnv = resolveColorEnv(env); const childEnv = [ "env", + ...colorEnv.options, `OPENCLAW_GATEWAY_WATCH_TMUX_CHILD=1`, `OPENCLAW_GATEWAY_WATCH_SESSION=${sessionName}`, + ...colorEnv.assignments, ...TMUX_CHILD_ENV_KEYS.flatMap((key) => env[key] == null || env[key] === "" ? [] : [`${key}=${env[key]}`], ), diff --git a/src/infra/gateway-watch-tmux.test.ts b/src/infra/gateway-watch-tmux.test.ts index 356b99fee48..cf3ff05ada4 100644 --- a/src/infra/gateway-watch-tmux.test.ts +++ b/src/infra/gateway-watch-tmux.test.ts @@ -53,6 +53,8 @@ describe("gateway-watch tmux wrapper", () => { expect(command).toContain("/repo with spaces/openclaw"); expect(command).toContain("'OPENCLAW_GATEWAY_WATCH_TMUX_CHILD=1'"); expect(command).toContain("'OPENCLAW_GATEWAY_WATCH_SESSION=openclaw-gateway-watch-main'"); + expect(command).toContain("'\\''-u'\\'' '\\''NO_COLOR'\\''"); + expect(command).toContain("'FORCE_COLOR=1'"); expect(command).toContain("'OPENCLAW_GATEWAY_PORT=19001'"); expect(command).toContain("'OPENCLAW_PROFILE=Dev Profile'"); expect(command).toContain("/opt/node"); @@ -62,6 +64,24 @@ describe("gateway-watch tmux wrapper", () => { expect(command).toContain("'a b.jsonl'"); }); + it("preserves an explicit color override for the tmux child", () => { + const command = buildGatewayWatchTmuxCommand({ + args: ["gateway", "--force"], + cwd: "/repo", + env: { + FORCE_COLOR: "0", + NO_COLOR: "1", + SHELL: "/bin/zsh", + }, + nodePath: "/opt/node", + sessionName: "openclaw-gateway-watch-main", + }); + + expect(command).toContain("'FORCE_COLOR=0'"); + expect(command).not.toContain("'\\''-u'\\'' '\\''NO_COLOR'\\''"); + expect(command).not.toContain("'FORCE_COLOR=1'"); + }); + it("creates a detached tmux session when none exists", () => { const stdout = createOutput(); const stderr = createOutput();