diff --git a/src/discord/monitor/provider.ts b/src/discord/monitor/provider.ts index e61627e1555..0691e084879 100644 --- a/src/discord/monitor/provider.ts +++ b/src/discord/monitor/provider.ts @@ -4,7 +4,6 @@ import { Routes } from "discord-api-types/v10"; import { inspect } from "node:util"; import type { HistoryEntry } from "../../auto-reply/reply/history.js"; import type { OpenClawConfig, ReplyToMode } from "../../config/config.js"; -import type { RuntimeEnv } from "../../runtime.js"; import { resolveTextChunkLimit } from "../../auto-reply/chunk.js"; import { listNativeCommandSpecsForConfig } from "../../auto-reply/commands-registry.js"; import { listSkillCommandsForAgents } from "../../auto-reply/skill-commands.js"; @@ -19,6 +18,7 @@ import { danger, logVerbose, shouldLogVerbose, warn } from "../../globals.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { createDiscordRetryRunner } from "../../infra/retry-policy.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; +import { createNonExitingRuntime, type RuntimeEnv } from "../../runtime.js"; import { resolveDiscordAccount } from "../accounts.js"; import { attachDiscordGatewayLogging } from "../gateway-logging.js"; import { getDiscordGatewayEmitter, waitForDiscordGatewayStop } from "../monitor.gateway.js"; @@ -137,13 +137,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) { ); } - const runtime: RuntimeEnv = opts.runtime ?? { - log: console.log, - error: console.error, - exit: (code: number): never => { - throw new Error(`exit ${code}`); - }, - }; + const runtime: RuntimeEnv = opts.runtime ?? createNonExitingRuntime(); const discordCfg = account.config; const dmConfig = discordCfg.dm; diff --git a/src/imessage/monitor/runtime.ts b/src/imessage/monitor/runtime.ts index 67ee2e4ac44..004fc2fdd9c 100644 --- a/src/imessage/monitor/runtime.ts +++ b/src/imessage/monitor/runtime.ts @@ -1,16 +1,8 @@ -import type { RuntimeEnv } from "../../runtime.js"; import type { MonitorIMessageOpts } from "./types.js"; +import { createNonExitingRuntime, type RuntimeEnv } from "../../runtime.js"; export function resolveRuntime(opts: MonitorIMessageOpts): RuntimeEnv { - return ( - opts.runtime ?? { - log: console.log, - error: console.error, - exit: (code: number): never => { - throw new Error(`exit ${code}`); - }, - } - ); + return opts.runtime ?? createNonExitingRuntime(); } export function normalizeAllowList(list?: Array) { diff --git a/src/runtime.ts b/src/runtime.ts index c8eab74ec6a..1c6fcec30ff 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -7,8 +7,22 @@ export type RuntimeEnv = { exit: (code: number) => never; }; +function shouldEmitRuntimeLog(env: NodeJS.ProcessEnv = process.env): boolean { + if (env.VITEST !== "true") { + return true; + } + if (env.OPENCLAW_TEST_RUNTIME_LOG === "1") { + return true; + } + const maybeMockedLog = console.log as unknown as { mock?: unknown }; + return typeof maybeMockedLog.mock === "object"; +} + export const defaultRuntime: RuntimeEnv = { log: (...args: Parameters) => { + if (!shouldEmitRuntimeLog()) { + return; + } clearActiveProgressLine(); console.log(...args); }, @@ -22,3 +36,22 @@ export const defaultRuntime: RuntimeEnv = { throw new Error("unreachable"); // satisfies tests when mocked }, }; + +export function createNonExitingRuntime(): RuntimeEnv { + return { + log: (...args: Parameters) => { + if (!shouldEmitRuntimeLog()) { + return; + } + clearActiveProgressLine(); + console.log(...args); + }, + error: (...args: Parameters) => { + clearActiveProgressLine(); + console.error(...args); + }, + exit: (code: number): never => { + throw new Error(`exit ${code}`); + }, + }; +} diff --git a/src/signal/monitor.ts b/src/signal/monitor.ts index aabe0021b43..85f5cac66d1 100644 --- a/src/signal/monitor.ts +++ b/src/signal/monitor.ts @@ -1,12 +1,12 @@ import type { ReplyPayload } from "../auto-reply/types.js"; import type { OpenClawConfig } from "../config/config.js"; import type { SignalReactionNotificationMode } from "../config/types.js"; -import type { RuntimeEnv } from "../runtime.js"; import { chunkTextWithMode, resolveChunkMode, resolveTextChunkLimit } from "../auto-reply/chunk.js"; import { DEFAULT_GROUP_HISTORY_LIMIT, type HistoryEntry } from "../auto-reply/reply/history.js"; import { loadConfig } from "../config/config.js"; import { waitForTransportReady } from "../infra/transport-ready.js"; import { saveMediaBuffer } from "../media/store.js"; +import { createNonExitingRuntime, type RuntimeEnv } from "../runtime.js"; import { normalizeE164 } from "../utils.js"; import { resolveSignalAccount } from "./accounts.js"; import { signalCheck, signalRpcRequest } from "./client.js"; @@ -57,15 +57,7 @@ export type MonitorSignalOpts = { }; function resolveRuntime(opts: MonitorSignalOpts): RuntimeEnv { - return ( - opts.runtime ?? { - log: console.log, - error: console.error, - exit: (code: number): never => { - throw new Error(`exit ${code}`); - }, - } - ); + return opts.runtime ?? createNonExitingRuntime(); } function normalizeAllowList(raw?: Array): string[] { diff --git a/src/slack/monitor/provider.ts b/src/slack/monitor/provider.ts index 6c544655cca..64768ee0ae3 100644 --- a/src/slack/monitor/provider.ts +++ b/src/slack/monitor/provider.ts @@ -1,7 +1,6 @@ import type { IncomingMessage, ServerResponse } from "node:http"; import SlackBolt from "@slack/bolt"; import type { SessionScope } from "../../config/sessions.js"; -import type { RuntimeEnv } from "../../runtime.js"; import type { MonitorSlackOpts } from "./types.js"; import { resolveTextChunkLimit } from "../../auto-reply/chunk.js"; import { DEFAULT_GROUP_HISTORY_LIMIT } from "../../auto-reply/reply/history.js"; @@ -10,6 +9,7 @@ import { loadConfig } from "../../config/config.js"; import { warn } from "../../globals.js"; import { installRequestBodyLimitGuard } from "../../infra/http-body.js"; import { normalizeMainKey } from "../../routing/session-key.js"; +import { createNonExitingRuntime, type RuntimeEnv } from "../../runtime.js"; import { resolveSlackAccount } from "../accounts.js"; import { resolveSlackWebClientOptions } from "../client.js"; import { normalizeSlackWebhookPath, registerSlackHttpHandler } from "../http/index.js"; @@ -81,13 +81,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { ); } - const runtime: RuntimeEnv = opts.runtime ?? { - log: console.log, - error: console.error, - exit: (code: number): never => { - throw new Error(`exit ${code}`); - }, - }; + const runtime: RuntimeEnv = opts.runtime ?? createNonExitingRuntime(); const slackCfg = account.config; const dmConfig = slackCfg.dm;