fix(acp): keep server logs off stdout

This commit is contained in:
Vincent Koc
2026-04-26 14:42:22 -07:00
parent 018f2e78ba
commit d46de6cff7
2 changed files with 28 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ const mockState = vi.hoisted(() => ({
gatewayAuth: [] as GatewayClientAuth[],
agentSideConnectionCtor: vi.fn(),
agentStart: vi.fn(),
routeLogsToStderr: vi.fn(),
resolveGatewayClientBootstrap: vi.fn<ResolveGatewayClientBootstrap>(async (_params) => ({
url: "ws://127.0.0.1:18789",
urlSource: "local loopback",
@@ -104,6 +105,10 @@ vi.mock("../infra/is-main.js", () => ({
isMainModule: () => false,
}));
vi.mock("../logging/console.js", () => ({
routeLogsToStderr: () => mockState.routeLogsToStderr(),
}));
vi.mock("./translator.js", () => ({
AcpGatewayAgent: class {
start(): void {
@@ -166,6 +171,7 @@ describe("serveAcpGateway startup", () => {
mockState.gatewayAuth.length = 0;
mockState.agentSideConnectionCtor.mockReset();
mockState.agentStart.mockReset();
mockState.routeLogsToStderr.mockReset();
mockState.resolveGatewayClientBootstrap.mockReset();
mockState.resolveGatewayClientBootstrap.mockResolvedValue({
url: "ws://127.0.0.1:18789",
@@ -192,6 +198,26 @@ describe("serveAcpGateway startup", () => {
}
});
it("routes logs to stderr before loading gateway config", async () => {
const { signalHandlers, onceSpy } = captureProcessSignalHandlers();
try {
const servePromise = serveAcpGateway({});
await Promise.resolve();
expect(mockState.routeLogsToStderr).toHaveBeenCalledTimes(1);
expect(mockState.routeLogsToStderr.mock.invocationCallOrder[0]).toBeLessThan(
mockState.resolveGatewayClientBootstrap.mock.invocationCallOrder[0] ??
Number.MAX_SAFE_INTEGER,
);
await emitHelloAndWaitForAgentSideConnection();
await stopServeWithSigint(signalHandlers, servePromise);
} finally {
onceSpy.mockRestore();
}
});
it("rejects startup when gateway connect fails before hello", async () => {
const onceSpy = vi
.spyOn(process, "once")

View File

@@ -7,12 +7,14 @@ import { resolveGatewayClientBootstrap } from "../gateway/client-bootstrap.js";
import { GatewayClient } from "../gateway/client.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../gateway/protocol/client-info.js";
import { isMainModule } from "../infra/is-main.js";
import { routeLogsToStderr } from "../logging/console.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { readSecretFromFile } from "./secret-file.js";
import { AcpGatewayAgent } from "./translator.js";
import { normalizeAcpProvenanceMode, type AcpServerOptions } from "./types.js";
export async function serveAcpGateway(opts: AcpServerOptions = {}): Promise<void> {
routeLogsToStderr();
const cfg = loadConfig();
const bootstrap = await resolveGatewayClientBootstrap({
config: cfg,