From 0eabcccce9774a982e9a68fe5249b4884e1a9686 Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Mon, 15 Jun 2026 15:59:06 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20review=20=E2=80=94=20Brave=20API=20key?= =?UTF-8?q?=20+=20webFetchHttpTimeoutMs=20=E8=81=94=E5=8A=A8=20+=20Tavily?= =?UTF-8?q?=20URL=20=E6=8E=A8=E5=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - braveAdapter: 读取 settings.braveApiKey (优先于环境变量) - webFetch utils: getFetchTimeoutMs() 统一读取 settings.webFetchHttpTimeoutMs,HTTP/Tavily 两条路径均生效 - tavilyAdapter: 自定义端点自动追加 /search 路径(与 fetchContentWithTavily 一致) Co-Authored-By: deepseek-v4-pro --- .../src/tools/WebFetchTool/utils.ts | 17 +++++++++++++---- .../WebSearchTool/adapters/braveAdapter.ts | 9 +++++++++ .../WebSearchTool/adapters/tavilyAdapter.ts | 6 +++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/builtin-tools/src/tools/WebFetchTool/utils.ts b/packages/builtin-tools/src/tools/WebFetchTool/utils.ts index 7c419a0ff..1ec2466a3 100644 --- a/packages/builtin-tools/src/tools/WebFetchTool/utils.ts +++ b/packages/builtin-tools/src/tools/WebFetchTool/utils.ts @@ -117,10 +117,19 @@ const MAX_HTTP_CONTENT_LENGTH = 10 * 1024 * 1024 // Timeout for the main HTTP fetch request (60 seconds). // Prevents hanging indefinitely on slow/unresponsive servers. -const FETCH_TIMEOUT_MS = 60_000 +// Overridable via settings.webFetchHttpTimeoutMs (set in /web-tools panel). +const DEFAULT_FETCH_TIMEOUT_MS = 60_000 + +function getFetchTimeoutMs(): number { + const settings = getSettings_DEPRECATED() as Record & { + webFetchHttpTimeoutMs?: number + } + return settings.webFetchHttpTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS +} // Cap same-host redirect hops. Without this a malicious server can return -// a redirect loop (/a → /b → /a …) and the per-request FETCH_TIMEOUT_MS +// a redirect loop (/a → /b → /a …) and the per-request timeout +// (controlled by settings.webFetchHttpTimeoutMs) // resets on every hop, hanging the tool until user interrupt. 10 matches // common client defaults (axios=5, follow-redirects=21, Chrome=20). const MAX_REDIRECTS = 10 @@ -238,7 +247,7 @@ export async function getWithPermittedRedirects( try { return await axios.get(url, { signal, - timeout: FETCH_TIMEOUT_MS, + timeout: getFetchTimeoutMs(), maxRedirects: 0, responseType: 'arraybuffer', maxContentLength: MAX_HTTP_CONTENT_LENGTH, @@ -488,7 +497,7 @@ export async function fetchContentWithTavily( }, { signal: abortSignal, - timeout: FETCH_TIMEOUT_MS, + timeout: getFetchTimeoutMs(), headers: { 'Content-Type': 'application/json' }, }, ) diff --git a/packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts b/packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts index 54b34904d..ea8f251df 100644 --- a/packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts +++ b/packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts @@ -5,6 +5,7 @@ import axios from 'axios' import { AbortError } from 'src/utils/errors.js' +import { getSettings_DEPRECATED } from 'src/utils/settings/settings.js' import type { SearchResult, SearchOptions, WebSearchAdapter } from './types.js' const FETCH_TIMEOUT_MS = 30_000 @@ -156,6 +157,14 @@ function normalizeSnippet(snippets: string[] | undefined): string | undefined { } function getBraveApiKey(): string { + // Priority: settings.braveApiKey (from /web-tools panel) > environment variable + const settings = getSettings_DEPRECATED() as Record & { + braveApiKey?: string + } + if (settings.braveApiKey?.trim()) { + return settings.braveApiKey.trim() + } + for (const envVar of BRAVE_API_KEY_ENV_VARS) { const value = process.env[envVar]?.trim() if (value) { diff --git a/packages/builtin-tools/src/tools/WebSearchTool/adapters/tavilyAdapter.ts b/packages/builtin-tools/src/tools/WebSearchTool/adapters/tavilyAdapter.ts index 4972ab027..7644ff6fb 100644 --- a/packages/builtin-tools/src/tools/WebSearchTool/adapters/tavilyAdapter.ts +++ b/packages/builtin-tools/src/tools/WebSearchTool/adapters/tavilyAdapter.ts @@ -43,7 +43,11 @@ export class TavilySearchAdapter implements WebSearchAdapter { const settings = getSettings_DEPRECATED() as Record & { tavilyEndpointUrl?: string } - const searchUrl = settings.tavilyEndpointUrl || DEFAULT_TAVILY_SEARCH_URL + const baseUrl = settings.tavilyEndpointUrl || DEFAULT_TAVILY_SEARCH_URL + // Ensure the URL ends with /search (same pattern as fetchContentWithTavily for /extract) + const searchUrl = baseUrl.endsWith('/search') + ? baseUrl + : `${baseUrl.replace(/\/$/, '')}/search` try { const response = await axios.post<{