From 342bf4838e85405b2b28cd2cd54258d7c6f79ee0 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 2 Mar 2026 03:09:23 +0000 Subject: [PATCH] fix(cli): preserve json stdout while keeping doctor migration (#24368) (thanks @altaywtf) --- CHANGELOG.md | 1 + src/cli/program/config-guard.ts | 11 +++++++++-- src/terminal/note.ts | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5ae172c..5a9049360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,7 @@ Docs: https://docs.openclaw.ai - Discord/Application ID fallback: parse bot application IDs from token prefixes without numeric precision loss and use token fallback only on transport/timeout failures when probing `/oauth2/applications/@me`. Landed from contributor PR #29695 by @dhananjai1729. Thanks @dhananjai1729. - Discord/EventQueue timeout config: expose per-account `channels.discord.accounts..eventQueue.listenerTimeout` (and related queue options) so long-running handlers can avoid Carbon listener timeout drops. Landed from contributor PR #28945 by @Glucksberg. Thanks @Glucksberg. - CLI/Cron run exit code: return exit code `0` only when `cron run` reports `{ ok: true, ran: true }`, and `1` for non-run/error outcomes so scripting/debugging reflects actual execution status. Landed from contributor PR #31121 by @Sid-Qin. Thanks @Sid-Qin. +- CLI/JSON preflight output: keep `--json` command stdout machine-readable by suppressing doctor preflight note output while still running legacy migration/config doctor flow. (#24368) Thanks @altaywtf. - Nodes/Screen recording guardrails: cap `nodes` tool `screen_record` `durationMs` to 5 minutes at both schema-validation and runtime invocation layers to prevent long-running blocking captures from unbounded durations. Landed from contributor PR #31106 by @BlueBirdBack. Thanks @BlueBirdBack. - Telegram/Empty final replies: skip outbound send for null/undefined final text payloads without media so Telegram typing indicators do not linger on `text must be non-empty` errors, with added regression coverage for undefined final payload dispatch. Landed from contributor PRs #30969 by @haosenwang1018 and #30746 by @rylena. Thanks @haosenwang1018 and @rylena. - Telegram/Proxy dispatcher preservation: preserve proxy-aware global undici dispatcher behavior in Telegram network workarounds so proxy-backed Telegram + model traffic is not broken by dispatcher replacement. Landed from contributor PR #30367 by @Phineas1500. Thanks @Phineas1500. diff --git a/src/cli/program/config-guard.ts b/src/cli/program/config-guard.ts index fac92899e..42d56ff35 100644 --- a/src/cli/program/config-guard.ts +++ b/src/cli/program/config-guard.ts @@ -52,12 +52,19 @@ export async function ensureConfigReady(params: { if (!params.suppressDoctorStdout) { await runDoctorConfigFlow(); } else { - const originalStdoutWrite = process.stdout.write; - process.stdout.write = ((() => true) as unknown) as typeof process.stdout.write; + const originalStdoutWrite = process.stdout.write.bind(process.stdout); + const originalSuppressNotes = process.env.OPENCLAW_SUPPRESS_NOTES; + process.stdout.write = (() => true) as unknown as typeof process.stdout.write; + process.env.OPENCLAW_SUPPRESS_NOTES = "1"; try { await runDoctorConfigFlow(); } finally { process.stdout.write = originalStdoutWrite; + if (originalSuppressNotes === undefined) { + delete process.env.OPENCLAW_SUPPRESS_NOTES; + } else { + process.env.OPENCLAW_SUPPRESS_NOTES = originalSuppressNotes; + } } } } diff --git a/src/terminal/note.ts b/src/terminal/note.ts index e1dc5717f..5b5ccb4a9 100644 --- a/src/terminal/note.ts +++ b/src/terminal/note.ts @@ -6,6 +6,17 @@ const URL_PREFIX_RE = /^(https?:\/\/|file:\/\/)/i; const WINDOWS_DRIVE_RE = /^[a-zA-Z]:[\\/]/; const FILE_LIKE_RE = /^[a-zA-Z0-9._-]+$/; +function isSuppressedByEnv(value: string | undefined): boolean { + if (!value) { + return false; + } + const normalized = value.trim().toLowerCase(); + if (!normalized) { + return false; + } + return normalized !== "0" && normalized !== "false" && normalized !== "off"; +} + function splitLongWord(word: string, maxLen: number): string[] { if (maxLen <= 0) { return [word]; @@ -130,5 +141,8 @@ export function wrapNoteMessage( } export function note(message: string, title?: string) { + if (isSuppressedByEnv(process.env.OPENCLAW_SUPPRESS_NOTES)) { + return; + } clackNote(wrapNoteMessage(message), stylePromptTitle(title)); }