






















@@ -1,6 +1,7 @@
11import process from "node:process";
22import { CommanderError } from "commander";
33import { beforeEach, describe, expect, it, vi } from "vitest";
4+import { loggingState } from "../logging/state.js";
45import { runCli, shouldStartProxyForCli } from "./run-main.js";
5667const tryRouteCliMock = vi.hoisted(() => vi.fn());
@@ -269,6 +270,7 @@ describe("runCli exit behavior", () => {
269270resolveManifestCliCommandSurfaceOwnerMock.mockReturnValue(undefined);
270271delete process.env.OPENCLAW_DISABLE_CLI_STARTUP_HELP_FAST_PATH;
271272delete process.env.OPENCLAW_HIDE_BANNER;
273+loggingState.forceConsoleToStderr = false;
272274});
273275274276it("does not force process.exit after successful routed command", async () => {
@@ -596,6 +598,68 @@ describe("runCli exit behavior", () => {
596598expect(parseAsync).toHaveBeenCalledWith(argv);
597599});
598600601+it("routes lazy plugin registration logs to stderr only during --json registration", async () => {
602+tryRouteCliMock.mockResolvedValueOnce(false);
603+resolvePluginCliRootOwnerIdsMock.mockImplementation(
604+({ primaryCommand }: { primaryCommand?: string }) =>
605+primaryCommand === "memory" ? ["memory"] : [],
606+);
607+let stderrDuringPluginRegistration = false;
608+let stderrDuringParse = true;
609+registerPluginCliCommandsFromValidatedConfigMock.mockImplementationOnce(async () => {
610+stderrDuringPluginRegistration = loggingState.forceConsoleToStderr;
611+return {};
612+});
613+const parseAsync = vi.fn().mockImplementationOnce(async () => {
614+stderrDuringParse = loggingState.forceConsoleToStderr;
615+});
616+buildProgramMock.mockReturnValueOnce({
617+commands: [],
618+ parseAsync,
619+});
620+621+await runCli(["node", "openclaw", "memory", "search", "query", "--json"]);
622+623+expect(registerPluginCliCommandsFromValidatedConfigMock).toHaveBeenCalledWith(
624+expect.anything(),
625+undefined,
626+undefined,
627+{ mode: "lazy", primary: "memory" },
628+);
629+expect(stderrDuringPluginRegistration).toBe(true);
630+expect(stderrDuringParse).toBe(false);
631+expect(loggingState.forceConsoleToStderr).toBe(false);
632+});
633+634+it("does not route lazy plugin registration logs for pass-through --json after terminator", async () => {
635+tryRouteCliMock.mockResolvedValueOnce(false);
636+resolvePluginCliRootOwnerIdsMock.mockImplementation(
637+({ primaryCommand }: { primaryCommand?: string }) =>
638+primaryCommand === "memory" ? ["memory"] : [],
639+);
640+let stderrDuringPluginRegistration = true;
641+registerPluginCliCommandsFromValidatedConfigMock.mockImplementationOnce(async () => {
642+stderrDuringPluginRegistration = loggingState.forceConsoleToStderr;
643+return {};
644+});
645+const parseAsync = vi.fn().mockResolvedValueOnce(undefined);
646+buildProgramMock.mockReturnValueOnce({
647+commands: [],
648+ parseAsync,
649+});
650+651+await runCli(["node", "openclaw", "memory", "--", "--json"]);
652+653+expect(registerPluginCliCommandsFromValidatedConfigMock).toHaveBeenCalledWith(
654+expect.anything(),
655+undefined,
656+undefined,
657+{ mode: "lazy", primary: "memory" },
658+);
659+expect(stderrDuringPluginRegistration).toBe(false);
660+expect(loggingState.forceConsoleToStderr).toBe(false);
661+});
662+599663it("fails protected commands when managed proxy activation fails", async () => {
600664startProxyMock.mockRejectedValueOnce(new Error("proxy: enabled but no HTTP proxy URL"));
601665此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。