diff --git a/src/cli/config-cli.test.ts b/src/cli/config-cli.test.ts index 6e9cc07bf7e..954dbdc3d51 100644 --- a/src/cli/config-cli.test.ts +++ b/src/cli/config-cli.test.ts @@ -34,13 +34,19 @@ const mockExit = vi.fn((code: number) => { throw new Error(`__exit__:${code} - ${errorMessages}`); }); -vi.mock("../runtime.js", () => ({ - defaultRuntime: { - log: (...args: unknown[]) => mockLog(...args), - error: (...args: unknown[]) => mockError(...args), - exit: (code: number) => mockExit(code), - }, -})); +vi.mock("../runtime.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + defaultRuntime: { + log: (...args: unknown[]) => mockLog(...args), + error: (...args: unknown[]) => mockError(...args), + writeJson: (value: unknown, space = 2) => + mockLog(JSON.stringify(value, null, space > 0 ? space : undefined)), + exit: (code: number) => mockExit(code), + }, + }; +}); function buildSnapshot(params: { resolved: OpenClawConfig; diff --git a/src/cli/cron-cli.test.ts b/src/cli/cron-cli.test.ts index a6b20ca5b3d..ca4ccd0472f 100644 --- a/src/cli/cron-cli.test.ts +++ b/src/cli/cron-cli.test.ts @@ -29,6 +29,7 @@ vi.mock("../runtime.js", () => ({ defaultRuntime: { log: vi.fn(), error: vi.fn(), + writeJson: vi.fn(), exit: (code: number) => { throw new Error(`__exit__:${code}`); }, diff --git a/src/cli/daemon-cli/install.integration.test.ts b/src/cli/daemon-cli/install.integration.test.ts index e4b49003286..db6df6a429e 100644 --- a/src/cli/daemon-cli/install.integration.test.ts +++ b/src/cli/daemon-cli/install.integration.test.ts @@ -28,6 +28,8 @@ vi.mock("../../runtime.js", () => ({ defaultRuntime: { log: (message: string) => runtimeLogs.push(message), error: (message: string) => runtimeErrors.push(message), + writeJson: (value: unknown, space = 2) => + runtimeLogs.push(JSON.stringify(value, null, space > 0 ? space : undefined)), exit: (code: number) => { throw new Error(`__exit__:${code}`); }, diff --git a/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts b/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts index 6e2a93d5633..989f254d962 100644 --- a/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts +++ b/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts @@ -1,13 +1,13 @@ import { vi } from "vitest"; import type { GatewayService } from "../../../daemon/service.js"; -import type { RuntimeEnv } from "../../../runtime.js"; +import type { OutputRuntimeEnv } from "../../../runtime.js"; import type { MockFn } from "../../../test-utils/vitest-mock-fn.js"; export const runtimeLogs: string[] = []; -type LifecycleRuntimeHarness = RuntimeEnv & { - error: MockFn; - exit: MockFn; +type LifecycleRuntimeHarness = OutputRuntimeEnv & { + error: MockFn; + exit: MockFn; }; type LifecycleServiceHarness = GatewayService & { @@ -24,6 +24,12 @@ export const defaultRuntime: LifecycleRuntimeHarness = { log: (...args: unknown[]) => { runtimeLogs.push(args.map((arg) => String(arg)).join(" ")); }, + writeStdout: (value: string) => { + runtimeLogs.push(value); + }, + writeJson: (value: unknown, space = 2) => { + runtimeLogs.push(JSON.stringify(value, null, space > 0 ? space : undefined)); + }, error: vi.fn(), exit: vi.fn((code: number) => { throw new Error(`__exit__:${code}`); diff --git a/src/cli/devices-cli.test.ts b/src/cli/devices-cli.test.ts index 81ca7d3c37f..b5882c3129c 100644 --- a/src/cli/devices-cli.test.ts +++ b/src/cli/devices-cli.test.ts @@ -14,6 +14,7 @@ const withProgress = vi.fn(async (_opts: unknown, fn: () => Promise) => const runtime = { log: vi.fn(), error: vi.fn(), + writeJson: vi.fn(), exit: vi.fn(), }; diff --git a/src/cli/directory-cli.test.ts b/src/cli/directory-cli.test.ts index d5a92b44c35..4e11a0f648d 100644 --- a/src/cli/directory-cli.test.ts +++ b/src/cli/directory-cli.test.ts @@ -39,6 +39,8 @@ vi.mock("../runtime.js", () => ({ defaultRuntime: { log: (...args: unknown[]) => mocks.log(...args), error: (...args: unknown[]) => mocks.error(...args), + writeJson: (value: unknown, space = 2) => + mocks.log(JSON.stringify(value, null, space > 0 ? space : undefined)), exit: (...args: unknown[]) => mocks.exit(...args), }, })); diff --git a/src/cli/mcp-cli.test.ts b/src/cli/mcp-cli.test.ts index 3369ba29cac..1a1077f1c08 100644 --- a/src/cli/mcp-cli.test.ts +++ b/src/cli/mcp-cli.test.ts @@ -15,6 +15,8 @@ vi.mock("../runtime.js", () => ({ defaultRuntime: { log: (...args: unknown[]) => mockLog(...args), error: (...args: unknown[]) => mockError(...args), + writeJson: (value: unknown, space = 2) => + mockLog(JSON.stringify(value, null, space > 0 ? space : undefined)), exit: (code: number) => mockExit(code), }, })); diff --git a/src/cli/qr-cli.test.ts b/src/cli/qr-cli.test.ts index 1bc8a645719..8d984c857ff 100644 --- a/src/cli/qr-cli.test.ts +++ b/src/cli/qr-cli.test.ts @@ -6,6 +6,9 @@ const mocks = vi.hoisted(() => ({ runtime: { log: vi.fn(), error: vi.fn(), + writeJson: vi.fn((value: unknown, space = 2) => { + mocks.runtime.log(JSON.stringify(value, null, space > 0 ? space : undefined)); + }), exit: vi.fn(() => { throw new Error("exit"); }), diff --git a/src/cli/test-runtime-capture.ts b/src/cli/test-runtime-capture.ts index f3a0e537645..ddd9a0db047 100644 --- a/src/cli/test-runtime-capture.ts +++ b/src/cli/test-runtime-capture.ts @@ -1,9 +1,9 @@ -import type { RuntimeEnv } from "../runtime.js"; +import type { OutputRuntimeEnv } from "../runtime.js"; export type CliRuntimeCapture = { runtimeLogs: string[]; runtimeErrors: string[]; - defaultRuntime: Pick; + defaultRuntime: Pick; resetRuntimeCapture: () => void; }; @@ -21,6 +21,12 @@ export function createCliRuntimeCapture(): CliRuntimeCapture { error: (...args: unknown[]) => { runtimeErrors.push(stringifyArgs(args)); }, + writeStdout: (value: string) => { + runtimeLogs.push(value); + }, + writeJson: (value: unknown, space = 2) => { + runtimeLogs.push(JSON.stringify(value, null, space > 0 ? space : undefined)); + }, exit: (code: number) => { throw new Error(`__exit__:${code}`); }, diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index e5351553d6b..4781137fad5 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -25,6 +25,12 @@ const formatPortDiagnostics = vi.fn(); const pathExists = vi.fn(); const syncPluginsForUpdateChannel = vi.fn(); const updateNpmInstalledPlugins = vi.fn(); +const runtimeLog = vi.fn(); +const runtimeError = vi.fn(); +const runtimeExit = vi.fn(); +const runtimeWriteJson = vi.fn((value: unknown, space = 2) => + runtimeLog(JSON.stringify(value, null, space > 0 ? space : undefined)), +); vi.mock("@clack/prompts", () => ({ confirm, @@ -131,9 +137,10 @@ vi.mock("./daemon-cli.js", () => ({ // Mock the runtime vi.mock("../runtime.js", () => ({ defaultRuntime: { - log: vi.fn(), - error: vi.fn(), - exit: vi.fn(), + log: runtimeLog, + error: runtimeError, + writeJson: runtimeWriteJson, + exit: runtimeExit, }, })); diff --git a/src/process/exec.ts b/src/process/exec.ts index 7692fb9ad21..140582d541a 100644 --- a/src/process/exec.ts +++ b/src/process/exec.ts @@ -320,11 +320,17 @@ export async function runCommandWithTimeout( : signal != null ? "signal" : "exit"; + const normalizedCode = + termination === "timeout" || termination === "no-output-timeout" + ? code === 0 + ? 124 + : code + : code; resolve({ pid: child.pid ?? undefined, stdout, stderr, - code, + code: normalizedCode, signal, killed: child.killed, termination,