Skip to content

Commit 0a798af

Browse files
committed
fix: preserve gateway watch log colors
1 parent 0680c0b commit 0a798af

4 files changed

Lines changed: 37 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Docs: https://docs.openclaw.ai
2525
### Fixes
2626

2727
- 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.
28+
- 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.
2829
- Plugin SDK: re-export `isPrivateIpAddress` from `plugin-sdk/ssrf-runtime`, restoring source-checkout builds for SearXNG and Firecrawl private-network guards. Thanks @vincentkoc.
2930
- 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.
3031
- Web search/SearXNG: show the JSON API `search.formats` prerequisite during SearXNG setup before prompting for the base URL. Supersedes #65592. Thanks @evanpaul14.

docs/help/debugging.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ The tmux wrapper carries common non-secret runtime selectors such as
251251
`OPENCLAW_GATEWAY_PORT`, and `OPENCLAW_SKIP_CHANNELS` into the pane. Put
252252
provider credentials in your normal profile/config, or use raw foreground mode
253253
for one-off ephemeral secrets.
254+
The managed tmux pane also defaults to colored Gateway logs for readability;
255+
set `FORCE_COLOR=0` when starting `pnpm gateway:watch` to disable ANSI output.
254256

255257
The watcher restarts on build-relevant files under `src/`, extension source files,
256258
extension `package.json` and `openclaw.plugin.json` metadata, `tsconfig.json`,

scripts/gateway-watch-tmux.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,17 @@ export const resolveGatewayWatchTmuxSessionName = ({ args = [], env = process.en
6666

6767
const resolveShell = (env) => env.SHELL || "/bin/sh";
6868

69+
const resolveColorEnv = (env) => {
70+
const forceColor = env.FORCE_COLOR;
71+
if (forceColor == null || forceColor === "") {
72+
return { assignments: ["FORCE_COLOR=1"], options: ["-u", "NO_COLOR"] };
73+
}
74+
if (String(forceColor).trim() !== "0") {
75+
return { assignments: [`FORCE_COLOR=${forceColor}`], options: ["-u", "NO_COLOR"] };
76+
}
77+
return { assignments: [`FORCE_COLOR=${forceColor}`], options: [] };
78+
};
79+
6980
export const buildGatewayWatchTmuxCommand = ({
7081
args = [],
7182
cwd = process.cwd(),
@@ -74,10 +85,13 @@ export const buildGatewayWatchTmuxCommand = ({
7485
sessionName,
7586
} = {}) => {
7687
const shell = resolveShell(env);
88+
const colorEnv = resolveColorEnv(env);
7789
const childEnv = [
7890
"env",
91+
...colorEnv.options,
7992
`OPENCLAW_GATEWAY_WATCH_TMUX_CHILD=1`,
8093
`OPENCLAW_GATEWAY_WATCH_SESSION=${sessionName}`,
94+
...colorEnv.assignments,
8195
...TMUX_CHILD_ENV_KEYS.flatMap((key) =>
8296
env[key] == null || env[key] === "" ? [] : [`${key}=${env[key]}`],
8397
),

src/infra/gateway-watch-tmux.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ describe("gateway-watch tmux wrapper", () => {
5353
expect(command).toContain("/repo with spaces/openclaw");
5454
expect(command).toContain("'OPENCLAW_GATEWAY_WATCH_TMUX_CHILD=1'");
5555
expect(command).toContain("'OPENCLAW_GATEWAY_WATCH_SESSION=openclaw-gateway-watch-main'");
56+
expect(command).toContain("'\\''-u'\\'' '\\''NO_COLOR'\\''");
57+
expect(command).toContain("'FORCE_COLOR=1'");
5658
expect(command).toContain("'OPENCLAW_GATEWAY_PORT=19001'");
5759
expect(command).toContain("'OPENCLAW_PROFILE=Dev Profile'");
5860
expect(command).toContain("/opt/node");
@@ -62,6 +64,24 @@ describe("gateway-watch tmux wrapper", () => {
6264
expect(command).toContain("'a b.jsonl'");
6365
});
6466

67+
it("preserves an explicit color override for the tmux child", () => {
68+
const command = buildGatewayWatchTmuxCommand({
69+
args: ["gateway", "--force"],
70+
cwd: "/repo",
71+
env: {
72+
FORCE_COLOR: "0",
73+
NO_COLOR: "1",
74+
SHELL: "/bin/zsh",
75+
},
76+
nodePath: "/opt/node",
77+
sessionName: "openclaw-gateway-watch-main",
78+
});
79+
80+
expect(command).toContain("'FORCE_COLOR=0'");
81+
expect(command).not.toContain("'\\''-u'\\'' '\\''NO_COLOR'\\''");
82+
expect(command).not.toContain("'FORCE_COLOR=1'");
83+
});
84+
6585
it("creates a detached tmux session when none exists", () => {
6686
const stdout = createOutput();
6787
const stderr = createOutput();

0 commit comments

Comments
 (0)