diff --git a/CHANGELOG.md b/CHANGELOG.md index 85e505dc91bb..75f7d62ccab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ _Development cycle in progress — entries are added as work merges into `release/v3.8.16` and finalized by the release flow._ +### 🔧 Bug Fixes + +- **fix(mitm):** `getMitmStatus()` in the build-time stub (Docker image) now returns a graceful `{ running: false }` status instead of throwing, so the Agent Bridge UI shows a clean "stopped" state rather than an error banner in containerised deployments. ([#3390](https://github.com/diegosouzapw/OmniRoute/issues/3390)) + --- ## [3.8.15] — 2026-06-07 diff --git a/src/mitm/manager.stub.ts b/src/mitm/manager.stub.ts index 08ffee4e84b4..f8a401224d88 100644 --- a/src/mitm/manager.stub.ts +++ b/src/mitm/manager.stub.ts @@ -1,12 +1,12 @@ // Build-time stub for @/mitm/manager, aliased in by Turbopack during `next build` -// (the Docker image build) so native MITM modules aren't bundled. Routes that -// *statically* import @/mitm/manager get this stub baked in and may reach it at -// runtime in the bundled/container build. Exports that have a safe degraded value -// return it (getCachedPassword/setCachedPassword/clearCachedPassword → null/no-op, -// getAllAgentsStatus → empty list) because MITM needs host access the container -// lacks; getMitmStatus/startMitm/stopMitm throw STUB_ERROR since they can't return -// anything meaningful without the real MITM process. Routes that need real MITM at -// runtime dynamic-import @/mitm/manager.runtime (the real module) instead. +// (the Docker image build) so native MITM modules aren't bundled. Exports that have +// a safe degraded value return it (getCachedPassword/setCachedPassword/clearCachedPassword +// → null/no-op, getAllAgentsStatus → empty list, getMitmStatus → stopped status) because +// MITM needs host access the container lacks. startMitm/stopMitm still throw since they +// cannot do anything meaningful without the real MITM process. Routes that need real MITM +// at runtime dynamic-import @/mitm/manager.runtime (the real module) instead. +// Fix #3390: getMitmStatus no longer throws — it returns running=false so the AgentBridge +// UI shows a graceful "stopped" state instead of an error banner in Docker. const STUB_ERROR = "MITM manager stub reached at runtime — build alias applied incorrectly. " + @@ -15,9 +15,8 @@ const STUB_ERROR = export const getCachedPassword = () => null; export const setCachedPassword = (_pwd: string) => {}; export const clearCachedPassword = () => {}; -export const getMitmStatus = async () => { - throw new Error(STUB_ERROR); -}; +export const getMitmStatus = async () => + ({ running: false, pid: null, dnsConfigured: false, certExists: false }) as const; // Must be exported or the Turbopack build fails ("Export getAllAgentsStatus doesn't // exist") — /api/tools/agent-bridge/state imports it statically. Returns the truthful // empty agent list in the bundled build rather than throwing (see file header). See #3066. diff --git a/tests/unit/mitm-manager-stub.test.ts b/tests/unit/mitm-manager-stub.test.ts new file mode 100644 index 000000000000..f992a39809a6 --- /dev/null +++ b/tests/unit/mitm-manager-stub.test.ts @@ -0,0 +1,37 @@ +/** + * Regression test for #3390: getMitmStatus() in the Docker build stub must + * return a safe "not available" status object instead of throwing STUB_ERROR. + * When the stub throws, the AgentBridge state route propagates the error to + * the UI, showing a red error banner instead of a graceful "stopped" state. + */ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; + +describe("mitm manager.stub — Docker graceful degradation (#3390)", () => { + it("getMitmStatus resolves without throwing", async () => { + const { getMitmStatus } = await import("../../src/mitm/manager.stub.ts"); + await assert.doesNotReject(getMitmStatus); + }); + + it("getMitmStatus returns running=false", async () => { + const { getMitmStatus } = await import("../../src/mitm/manager.stub.ts"); + const status = await getMitmStatus(); + assert.strictEqual(status.running, false); + }); + + it("getMitmStatus returns pid=null", async () => { + const { getMitmStatus } = await import("../../src/mitm/manager.stub.ts"); + const status = await getMitmStatus(); + assert.strictEqual(status.pid, null); + }); + + it("startMitm still throws (MITM unavailable in container)", async () => { + const { startMitm } = await import("../../src/mitm/manager.stub.ts"); + await assert.rejects(async () => startMitm("key", "pwd"), /stub/i); + }); + + it("getAllAgentsStatus returns empty array", async () => { + const { getAllAgentsStatus } = await import("../../src/mitm/manager.stub.ts"); + assert.deepStrictEqual(getAllAgentsStatus(), []); + }); +});