fix(cli): hide banners for implicit JSON output (#129204)

This commit is contained in:
Peter Steinberger
2026-08-25 02:46:07 -07:00
committed by GitHub
parent ef7b6653a8
commit 33e556fe8a
5 changed files with 45 additions and 7 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ describe("command-execution-startup", () => {
commandPath: ["status"],
startupPolicy: {
suppressDoctorStdout: true,
hideBanner: false,
hideBanner: true,
skipConfigGuard: true,
loadPlugins: false,
pluginRegistry: { scope: "channels" },
+4 -1
View File
@@ -328,6 +328,9 @@ describe("command-startup-policy", () => {
}).hideBanner,
).toBe(true);
expect(resolvePolicy({ commandPath: ["status"], env: {} }).hideBanner).toBe(false);
expect(
resolvePolicy({ commandPath: ["status"], jsonOutputMode: true, env: {} }).hideBanner,
).toBe(true);
});
it("uses process env banner suppression when startup env is omitted", () => {
@@ -366,7 +369,7 @@ describe("command-startup-policy", () => {
}),
).toEqual({
suppressDoctorStdout: true,
hideBanner: false,
hideBanner: true,
skipConfigGuard: true,
loadPlugins: false,
pluginRegistry: { scope: "channels" },
+2 -1
View File
@@ -35,9 +35,10 @@ export function resolveCliStartupPolicy(params: {
? commandPolicy.configGuard({ argv: params.argv ?? [], commandPath: params.commandPath })
: commandPolicy.configGuard;
const env = params.env ?? process.env;
const hideBanner = params.jsonOutputMode || commandPolicy.hideBanner;
return {
suppressDoctorStdout,
hideBanner: isTruthyEnvValue(env.OPENCLAW_HIDE_BANNER) || commandPolicy.hideBanner,
hideBanner: hideBanner || isTruthyEnvValue(env.OPENCLAW_HIDE_BANNER),
skipConfigGuard:
configGuard === "skip" || (configGuard === "when-suppressed" && suppressDoctorStdout),
...(configGuard === "validate" ? { validateConfigOnly: true } : {}),
+1
View File
@@ -891,6 +891,7 @@ describe("registerPreActionHooks", () => {
}
expect(routeLogsToStderrMock).toHaveBeenCalledOnce();
expect(emitCliBannerMock).not.toHaveBeenCalled();
expect(observedMachineOutputStdoutIsTTY).toBe(false);
expect(ensureConfigReadyMock).toHaveBeenCalledWith({
runtime: runtimeMock,
+37 -4
View File
@@ -97,7 +97,29 @@ describe("cli json stdout contract", () => {
commander: true,
},
{
name: "dual-TTY JSON",
name: "dual-TTY implicit JSON",
args: ["cron", "edit", "job-1", "--enable", "--disable"],
tty: true,
},
{
name: "dual-TTY automation alias implicit JSON",
args: ["automations", "edit", "job-1", "--enable", "--disable"],
tty: true,
},
{
name: "dual-TTY implicit JSON command sibling",
args: ["cron", "runs", "--id", "job-1", "--limit", "invalid"],
message: "Invalid --limit (must be a positive integer).",
tty: true,
},
{
name: "dual-TTY raw-output command sibling",
args: ["cron", "scratch", "job-1", "--set", "updated", "--unset"],
message: "choose only one of --set, --file, or --unset",
tty: true,
},
{
name: "dual-TTY explicit JSON",
args: ["cron", "edit", "job-1", "--enable", "--disable", "--json"],
tty: true,
},
@@ -107,6 +129,13 @@ describe("cli json stdout contract", () => {
message: "--agent must not be blank",
human: true,
},
{
name: "dual-TTY human-output sibling",
args: ["cron", "list", "--agent", ""],
message: "--agent must not be blank",
human: true,
tty: true,
},
])("renders cron edit failures through the shared owner for $name", async (testCase) => {
await withTempHome(
async (tempHome) => {
@@ -139,10 +168,14 @@ describe("cli json stdout contract", () => {
: (testCase.message ?? "Choose --enable or --disable, not both");
expect(result.status, result.stderr).toBe(1);
expect(result.stdout, result.stderr).not.toMatch(/[\u001B\u0007]/u);
if ("human" in testCase) {
expect(result.stdout).toBe("");
if ("tty" in testCase) {
expect(result.stdout).toContain("OpenClaw");
} else {
expect(result.stdout).toBe("");
}
} else {
expect(result.stdout, result.stderr).not.toMatch(/[\u001B\u0007]/u);
expect(JSON.parse(result.stdout)).toEqual({
ok: false,
error: { type: "cli_error", message },
@@ -155,7 +188,7 @@ describe("cli json stdout contract", () => {
expect(result.stderr).not.toContain(gatewayError);
await expect(fs.stat(stateDir)).rejects.toMatchObject({ code: "ENOENT" });
}
if ("tty" in testCase) {
if ("tty" in testCase && !("human" in testCase)) {
expect(result.stderr).toContain("\u001B[?25h");
}
await expect(fs.stat(configPath)).rejects.toMatchObject({ code: "ENOENT" });