mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-06 15:18:58 +00:00
fix: avoid fetch runtime proxy imports
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import type { Dispatcher } from "undici";
|
||||
import { logWarn } from "../../logger.js";
|
||||
import { captureHttpExchange } from "../../proxy-capture/runtime.js";
|
||||
import { buildTimeoutAbortSignal } from "../../utils/fetch-timeout.js";
|
||||
import { hasProxyEnvConfigured, shouldUseEnvHttpProxyForUrl } from "./proxy-env.js";
|
||||
import { retainSafeHeadersForCrossOriginRedirect as retainSafeRedirectHeaders } from "./redirect-headers.js";
|
||||
@@ -95,6 +94,11 @@ type GuardedFetchPresetOptions = Omit<
|
||||
>;
|
||||
|
||||
const DEFAULT_MAX_REDIRECTS = 3;
|
||||
const OPENCLAW_DEBUG_PROXY_ENABLED = "OPENCLAW_DEBUG_PROXY_ENABLED";
|
||||
|
||||
function isTruthyEnvValue(value: string | undefined): boolean {
|
||||
return value === "1" || value === "true" || value === "yes" || value === "on";
|
||||
}
|
||||
|
||||
export function withStrictGuardedFetchMode(params: GuardedFetchPresetOptions): GuardedFetchOptions {
|
||||
return { ...params, mode: GUARDED_FETCH_MODE.STRICT };
|
||||
@@ -232,6 +236,36 @@ export function retainSafeHeadersForCrossOriginRedirectHeaders(
|
||||
return retainSafeRedirectHeaders(headers);
|
||||
}
|
||||
|
||||
async function captureGuardedFetchExchange(params: {
|
||||
url: string;
|
||||
method: string;
|
||||
requestHeaders?: Headers | Record<string, string> | undefined;
|
||||
requestBody?: BodyInit | Buffer | string | null;
|
||||
response: Response;
|
||||
transport?: "http" | "sse";
|
||||
capture: GuardedFetchOptions["capture"];
|
||||
auditContext?: string;
|
||||
}): Promise<void> {
|
||||
if (params.capture === false || !isTruthyEnvValue(process.env[OPENCLAW_DEBUG_PROXY_ENABLED])) {
|
||||
return;
|
||||
}
|
||||
const { captureHttpExchange } = await import("../../proxy-capture/runtime.js");
|
||||
captureHttpExchange({
|
||||
url: params.url,
|
||||
method: params.method,
|
||||
requestHeaders: params.requestHeaders,
|
||||
requestBody: params.requestBody,
|
||||
response: params.response,
|
||||
transport: params.transport,
|
||||
flowId: params.capture?.flowId,
|
||||
meta: {
|
||||
captureOrigin: "guarded-fetch",
|
||||
...(params.auditContext ? { auditContext: params.auditContext } : {}),
|
||||
...params.capture?.meta,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function retainSafeHeadersForCrossOriginRedirect(init?: RequestInit): RequestInit | undefined {
|
||||
if (!init?.headers) {
|
||||
return init;
|
||||
@@ -433,23 +467,17 @@ export async function fetchWithSsrFGuard(params: GuardedFetchOptions): Promise<G
|
||||
? await fetchWithRuntimeDispatcher(parsedUrl.toString(), init)
|
||||
: await defaultFetch(parsedUrl.toString(), init);
|
||||
|
||||
if (params.capture !== false) {
|
||||
captureHttpExchange({
|
||||
url: parsedUrl.toString(),
|
||||
method: currentInit?.method ?? "GET",
|
||||
requestHeaders: currentInit?.headers as Headers | Record<string, string> | undefined,
|
||||
requestBody:
|
||||
(currentInit as (RequestInit & { body?: BodyInit | null }) | undefined)?.body ?? null,
|
||||
response,
|
||||
transport: "http",
|
||||
flowId: params.capture?.flowId,
|
||||
meta: {
|
||||
captureOrigin: "guarded-fetch",
|
||||
...(params.auditContext ? { auditContext: params.auditContext } : {}),
|
||||
...params.capture?.meta,
|
||||
},
|
||||
});
|
||||
}
|
||||
await captureGuardedFetchExchange({
|
||||
url: parsedUrl.toString(),
|
||||
method: currentInit?.method ?? "GET",
|
||||
requestHeaders: currentInit?.headers as Headers | Record<string, string> | undefined,
|
||||
requestBody:
|
||||
(currentInit as (RequestInit & { body?: BodyInit | null }) | undefined)?.body ?? null,
|
||||
response,
|
||||
transport: "http",
|
||||
capture: params.capture,
|
||||
auditContext: params.auditContext,
|
||||
});
|
||||
|
||||
if (isRedirectStatus(response.status)) {
|
||||
const location = response.headers.get("location");
|
||||
|
||||
38
src/plugin-sdk/fetch-runtime.test.ts
Normal file
38
src/plugin-sdk/fetch-runtime.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("plugin SDK fetch runtime", () => {
|
||||
it("does not initialize the undici global dispatcher on import", () => {
|
||||
const moduleUrl = pathToFileURL(path.resolve("src/plugin-sdk/fetch-runtime.ts")).href;
|
||||
const source = `
|
||||
const dispatcherKey = Symbol.for("undici.globalDispatcher.1");
|
||||
await import(${JSON.stringify(moduleUrl)});
|
||||
if (globalThis[dispatcherKey] !== undefined) {
|
||||
throw new Error("undici global dispatcher was initialized");
|
||||
}
|
||||
console.log("ok");
|
||||
`;
|
||||
const env = { ...process.env };
|
||||
for (const key of [
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"ALL_PROXY",
|
||||
"http_proxy",
|
||||
"https_proxy",
|
||||
"all_proxy",
|
||||
"OPENCLAW_DEBUG_PROXY_ENABLED",
|
||||
]) {
|
||||
delete env[key];
|
||||
}
|
||||
|
||||
const output = execFileSync(
|
||||
process.execPath,
|
||||
["--import", "tsx", "--input-type=module", "--eval", source],
|
||||
{ cwd: process.cwd(), encoding: "utf8", env },
|
||||
);
|
||||
|
||||
expect(output.trim()).toBe("ok");
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { Agent } from "node:http";
|
||||
import { createRequire } from "node:module";
|
||||
import process from "node:process";
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
import {
|
||||
resolveDebugProxyBlobDir,
|
||||
resolveDebugProxyCertDir,
|
||||
@@ -28,6 +28,14 @@ export type DebugProxySettings = {
|
||||
};
|
||||
|
||||
let cachedImplicitSessionId: string | undefined;
|
||||
let cachedHttpsProxyAgent: typeof import("https-proxy-agent").HttpsProxyAgent | undefined;
|
||||
|
||||
function loadHttpsProxyAgent(): typeof import("https-proxy-agent").HttpsProxyAgent {
|
||||
cachedHttpsProxyAgent ??= (
|
||||
createRequire(import.meta.url)("https-proxy-agent") as typeof import("https-proxy-agent")
|
||||
).HttpsProxyAgent;
|
||||
return cachedHttpsProxyAgent;
|
||||
}
|
||||
|
||||
function isTruthy(value: string | undefined): boolean {
|
||||
return value === "1" || value === "true" || value === "yes" || value === "on";
|
||||
@@ -80,6 +88,7 @@ export function createDebugProxyWebSocketAgent(settings: DebugProxySettings): Ag
|
||||
if (!settings.enabled || !settings.proxyUrl) {
|
||||
return undefined;
|
||||
}
|
||||
const HttpsProxyAgent = loadHttpsProxyAgent();
|
||||
return new HttpsProxyAgent(settings.proxyUrl);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user