


































@@ -20,6 +20,15 @@ const getProgramContextMock = vi.hoisted(() => vi.fn(() => null));
2020const registerCoreCliByNameMock = vi.hoisted(() => vi.fn());
2121const registerSubCliByNameMock = vi.hoisted(() => vi.fn());
2222const restoreTerminalStateMock = vi.hoisted(() => vi.fn());
23+const hasEnvHttpProxyConfiguredMock = vi.hoisted(() => vi.fn(() => false));
24+const ensureGlobalUndiciEnvProxyDispatcherMock = vi.hoisted(() => vi.fn());
25+const runCrestodianMock = vi.hoisted(() => vi.fn(async () => {}));
26+const progressDoneMock = vi.hoisted(() => vi.fn());
27+const createCliProgressMock = vi.hoisted(() =>
28+vi.fn(() => ({
29+done: progressDoneMock,
30+})),
31+);
2332const maybeRunCliInContainerMock = vi.hoisted(() =>
2433vi.fn<
2534(argv: string[]) => { handled: true; exitCode: number } | { handled: false; argv: string[] }
@@ -96,12 +105,29 @@ vi.mock("../terminal/restore.js", () => ({
96105restoreTerminalState: restoreTerminalStateMock,
97106}));
98107108+vi.mock("../infra/net/proxy-env.js", () => ({
109+hasEnvHttpProxyConfigured: hasEnvHttpProxyConfiguredMock,
110+}));
111+112+vi.mock("../infra/net/undici-global-dispatcher.js", () => ({
113+ensureGlobalUndiciEnvProxyDispatcher: ensureGlobalUndiciEnvProxyDispatcherMock,
114+}));
115+116+vi.mock("../crestodian/crestodian.js", () => ({
117+runCrestodian: runCrestodianMock,
118+}));
119+120+vi.mock("./progress.js", () => ({
121+createCliProgress: createCliProgressMock,
122+}));
123+99124describe("runCli exit behavior", () => {
100125beforeEach(() => {
101126vi.clearAllMocks();
102127hasMemoryRuntimeMock.mockReturnValue(false);
103128outputPrecomputedBrowserHelpTextMock.mockReturnValue(false);
104129outputPrecomputedRootHelpTextMock.mockReturnValue(false);
130+hasEnvHttpProxyConfiguredMock.mockReturnValue(false);
105131getProgramContextMock.mockReturnValue(null);
106132delete process.env.OPENCLAW_DISABLE_CLI_STARTUP_HELP_FAST_PATH;
107133});
@@ -146,6 +172,17 @@ describe("runCli exit behavior", () => {
146172exitSpy.mockRestore();
147173});
148174175+it("keeps root help on the precomputed path without proxy bootstrap", async () => {
176+outputPrecomputedRootHelpTextMock.mockReturnValueOnce(true);
177+178+await runCli(["node", "openclaw", "--help"]);
179+180+expect(outputPrecomputedRootHelpTextMock).toHaveBeenCalledTimes(1);
181+expect(hasEnvHttpProxyConfiguredMock).not.toHaveBeenCalled();
182+expect(ensureGlobalUndiciEnvProxyDispatcherMock).not.toHaveBeenCalled();
183+expect(runCrestodianMock).not.toHaveBeenCalled();
184+});
185+149186it("renders root help without building the full program", async () => {
150187const exitSpy = vi.spyOn(process, "exit").mockImplementation(((code?: number) => {
151188throw new Error(`unexpected process.exit(${String(code)})`);
@@ -163,6 +200,52 @@ describe("runCli exit behavior", () => {
163200exitSpy.mockRestore();
164201});
165202203+it("bootstraps env proxy before bare Crestodian startup", async () => {
204+hasEnvHttpProxyConfiguredMock.mockReturnValue(true);
205+const stdinTty = Object.getOwnPropertyDescriptor(process.stdin, "isTTY");
206+const stdoutTty = Object.getOwnPropertyDescriptor(process.stdout, "isTTY");
207+Object.defineProperty(process.stdin, "isTTY", { configurable: true, value: true });
208+Object.defineProperty(process.stdout, "isTTY", { configurable: true, value: true });
209+210+try {
211+await runCli(["node", "openclaw"]);
212+} finally {
213+if (stdinTty) {
214+Object.defineProperty(process.stdin, "isTTY", stdinTty);
215+} else {
216+delete (process.stdin as { isTTY?: boolean }).isTTY;
217+}
218+if (stdoutTty) {
219+Object.defineProperty(process.stdout, "isTTY", stdoutTty);
220+} else {
221+delete (process.stdout as { isTTY?: boolean }).isTTY;
222+}
223+}
224+225+expect(ensureGlobalUndiciEnvProxyDispatcherMock).toHaveBeenCalledTimes(1);
226+expect(runCrestodianMock).toHaveBeenCalledWith({ onReady: expect.any(Function) });
227+expect(ensureGlobalUndiciEnvProxyDispatcherMock.mock.invocationCallOrder[0]).toBeLessThan(
228+runCrestodianMock.mock.invocationCallOrder[0],
229+);
230+});
231+232+it("bootstraps env proxy before modern onboard Crestodian startup", async () => {
233+hasEnvHttpProxyConfiguredMock.mockReturnValue(true);
234+235+await runCli(["node", "openclaw", "onboard", "--modern", "--json"]);
236+237+expect(ensureGlobalUndiciEnvProxyDispatcherMock).toHaveBeenCalledTimes(1);
238+expect(runCrestodianMock).toHaveBeenCalledWith({
239+message: undefined,
240+yes: false,
241+json: true,
242+interactive: true,
243+});
244+expect(ensureGlobalUndiciEnvProxyDispatcherMock.mock.invocationCallOrder[0]).toBeLessThan(
245+runCrestodianMock.mock.invocationCallOrder[0],
246+);
247+});
248+166249it("closes memory managers when a runtime was registered", async () => {
167250tryRouteCliMock.mockResolvedValueOnce(true);
168251hasMemoryRuntimeMock.mockReturnValue(true);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。