From de86be4cde12472807b2c6fc0dbd1135dfe63b30 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sat, 1 Aug 2026 23:04:36 +0800 Subject: [PATCH] fix(slack): preserve debug capture with proxies --- extensions/slack/src/client-options.ts | 5 ++++- extensions/slack/src/client.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/extensions/slack/src/client-options.ts b/extensions/slack/src/client-options.ts index c23fcff8965a..cb1a2e914c19 100644 --- a/extensions/slack/src/client-options.ts +++ b/extensions/slack/src/client-options.ts @@ -6,6 +6,7 @@ import { resolveFetch, resolveEnvHttpProxyAgentOptions, } from "openclaw/plugin-sdk/fetch-runtime"; +import { isDebugProxyGlobalFetchPatchInstalled } from "openclaw/plugin-sdk/proxy-capture"; import type { EnvHttpProxyAgent } from "undici"; type SlackUndiciRuntime = Pick; @@ -57,7 +58,9 @@ export function resolveSlackProxyDispatcher(): SlackProxyDispatcher | undefined function buildSlackFetch( dispatcher?: SlackProxyDispatcher, ): NonNullable | undefined { - if (!dispatcher) { + if (!dispatcher || isDebugProxyGlobalFetchPatchInstalled()) { + // Debug capture patches global fetch after installing its proxy-aware dispatcher. + // A package-owned fetch would bypass capture whenever ambient proxy env is present. return resolveFetch() as NonNullable | undefined; } const { fetch: slackFetch } = loadSlackUndiciRuntime(); diff --git a/extensions/slack/src/client.test.ts b/extensions/slack/src/client.test.ts index 10775a0f37a1..bdd8db9a2961 100644 --- a/extensions/slack/src/client.test.ts +++ b/extensions/slack/src/client.test.ts @@ -5,6 +5,14 @@ import path from "node:path"; import type { WebClientOptions } from "@slack/web-api"; import { afterEach, beforeAll, beforeEach, describe, expect, expectTypeOf, it, vi } from "vitest"; +const { isDebugProxyGlobalFetchPatchInstalledMock } = vi.hoisted(() => ({ + isDebugProxyGlobalFetchPatchInstalledMock: vi.fn(() => false), +})); + +vi.mock("openclaw/plugin-sdk/proxy-capture", () => ({ + isDebugProxyGlobalFetchPatchInstalled: isDebugProxyGlobalFetchPatchInstalledMock, +})); + vi.mock("@slack/web-api", () => { const WebClient = vi.fn(function WebClientMock( this: Record, @@ -119,6 +127,7 @@ beforeEach(() => { WebClient.mockClear(); clearSlackWriteClientCacheForTest(); clearSlackApiUrlEnvForTest(); + isDebugProxyGlobalFetchPatchInstalledMock.mockReturnValue(false); }); afterEach(() => { @@ -406,6 +415,23 @@ describe("slack proxy dispatcher", () => { await dispatcher?.close(); }); + it("keeps the capture-patched global fetch with ambient proxy env", async () => { + process.env.HTTPS_PROXY = "http://proxy.example.com:3128"; + isDebugProxyGlobalFetchPatchInstalledMock.mockReturnValue(true); + const globalFetch = vi + .spyOn(globalThis, "fetch") + .mockResolvedValue(new Response(null, { status: 200 })); + const dispatcher = resolveSlackProxyDispatcher(); + try { + const options = resolveSlackWebClientOptions({}, dispatcher); + await requireFetch(options)("https://slack.com/api/auth.test"); + expect(globalFetch).toHaveBeenCalledOnce(); + } finally { + globalFetch.mockRestore(); + await dispatcher?.close(); + } + }); + it("creates the dispatcher while managed proxy CA trust is active", async () => { const caFile = writeTempCa("slack-managed-proxy-ca"); process.env.HTTPS_PROXY = "https://proxy.example.com:8443";