


























@@ -23,6 +23,9 @@ const restoreTerminalStateMock = vi.hoisted(() => vi.fn());
2323const hasEnvHttpProxyAgentConfiguredMock = vi.hoisted(() => vi.fn(() => false));
2424const ensureGlobalUndiciEnvProxyDispatcherMock = vi.hoisted(() => vi.fn());
2525const runCrestodianMock = vi.hoisted(() => vi.fn(async () => {}));
26+const commanderParseAsyncMock = vi.hoisted(() => vi.fn(async () => {}));
27+const addGatewayRunCommandMock = vi.hoisted(() => vi.fn((command: unknown) => command));
28+const emitCliBannerMock = vi.hoisted(() => vi.fn());
2629const progressDoneMock = vi.hoisted(() => vi.fn());
2730const createCliProgressMock = vi.hoisted(() =>
2831vi.fn(() => ({
@@ -35,10 +38,49 @@ const maybeRunCliInContainerMock = vi.hoisted(() =>
3538>((argv: string[]) => ({ handled: false, argv })),
3639);
374041+vi.mock("commander", () => {
42+class MockCommanderError extends Error {
43+exitCode: number;
44+code: string;
45+46+constructor(exitCode: number, code: string, message: string) {
47+super(message);
48+this.exitCode = exitCode;
49+this.code = code;
50+}
51+}
52+53+class MockCommand {
54+name = vi.fn(() => this);
55+enablePositionalOptions = vi.fn(() => this);
56+exitOverride = vi.fn(() => this);
57+description = vi.fn(() => this);
58+command = vi.fn(() => new MockCommand());
59+parseAsync = commanderParseAsyncMock;
60+}
61+62+return {
63+Command: MockCommand,
64+CommanderError: MockCommanderError,
65+};
66+});
67+3868vi.mock("./route.js", () => ({
3969tryRouteCli: tryRouteCliMock,
4070}));
417172+vi.mock("./gateway-cli/run.js", () => ({
73+addGatewayRunCommand: addGatewayRunCommandMock,
74+}));
75+76+vi.mock("../version.js", () => ({
77+VERSION: "9.9.9-test",
78+}));
79+80+vi.mock("./banner.js", () => ({
81+emitCliBanner: emitCliBannerMock,
82+}));
83+4284vi.mock("./container-target.js", () => ({
4385maybeRunCliInContainer: maybeRunCliInContainerMock,
4486parseCliContainerArgs: (argv: string[]) => ({ ok: true, container: null, argv }),
@@ -132,6 +174,7 @@ describe("runCli exit behavior", () => {
132174hasEnvHttpProxyAgentConfiguredMock.mockReturnValue(false);
133175getProgramContextMock.mockReturnValue(null);
134176delete process.env.OPENCLAW_DISABLE_CLI_STARTUP_HELP_FAST_PATH;
177+delete process.env.OPENCLAW_HIDE_BANNER;
135178});
136179137180it("does not force process.exit after successful routed command", async () => {
@@ -151,6 +194,32 @@ describe("runCli exit behavior", () => {
151194exitSpy.mockRestore();
152195});
153196197+it("emits the startup banner before gateway foreground fast-path startup", async () => {
198+await runCli(["node", "openclaw", "gateway", "--force"]);
199+200+expect(tryRouteCliMock).not.toHaveBeenCalled();
201+expect(emitCliBannerMock).toHaveBeenCalledWith("9.9.9-test", {
202+argv: ["node", "openclaw", "gateway", "--force"],
203+});
204+expect(addGatewayRunCommandMock).toHaveBeenCalledTimes(2);
205+expect(commanderParseAsyncMock).toHaveBeenCalledWith([
206+"node",
207+"openclaw",
208+"gateway",
209+"--force",
210+]);
211+});
212+213+it("honors banner suppression on the gateway foreground fast path", async () => {
214+process.env.OPENCLAW_HIDE_BANNER = "1";
215+216+await runCli(["node", "openclaw", "gateway"]);
217+218+expect(tryRouteCliMock).not.toHaveBeenCalled();
219+expect(emitCliBannerMock).not.toHaveBeenCalled();
220+expect(commanderParseAsyncMock).toHaveBeenCalledWith(["node", "openclaw", "gateway"]);
221+});
222+154223it("renders browser help from startup metadata without building the full program", async () => {
155224outputPrecomputedBrowserHelpTextMock.mockReturnValueOnce(true);
156225const exitSpy = vi.spyOn(process, "exit").mockImplementation(((code?: number) => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。