( toolId: string, tool: ToolConfig, requestParams: RequestParams )
| 54 | * Server-only: uses DNS validation and IP-pinned fetch. |
| 55 | */ |
| 56 | export async function executeRequest( |
| 57 | toolId: string, |
| 58 | tool: ToolConfig, |
| 59 | requestParams: RequestParams |
| 60 | ): Promise<ToolResponse> { |
| 61 | try { |
| 62 | const { url, method, headers, body } = requestParams |
| 63 | const isExternalUrl = url.startsWith('http://') || url.startsWith('https://') |
| 64 | const externalResponse = isExternalUrl |
| 65 | ? (() => { |
| 66 | return validateUrlWithDNS(url, 'url').then((urlValidation) => { |
| 67 | if (!urlValidation.isValid) { |
| 68 | throw new Error(urlValidation.error) |
| 69 | } |
| 70 | return secureFetchWithPinnedIP(url, urlValidation.resolvedIP!, { |
| 71 | method, |
| 72 | headers, |
| 73 | body, |
| 74 | }) |
| 75 | }) |
| 76 | })() |
| 77 | : fetch(url, { method, headers, body }) |
| 78 | |
| 79 | const resolvedResponse = await externalResponse |
| 80 | |
| 81 | if (!resolvedResponse.ok) { |
| 82 | let errorData: any |
| 83 | try { |
| 84 | errorData = await resolvedResponse.json() |
| 85 | } catch (_e) { |
| 86 | try { |
| 87 | errorData = await resolvedResponse.text() |
| 88 | } catch (_e2) { |
| 89 | errorData = null |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | const error = extractErrorMessage({ |
| 94 | status: resolvedResponse.status, |
| 95 | statusText: resolvedResponse.statusText, |
| 96 | data: errorData, |
| 97 | }) |
| 98 | logger.error(`${toolId} error:`, { error }) |
| 99 | throw new Error(error) |
| 100 | } |
| 101 | |
| 102 | const transformResponse = |
| 103 | tool.transformResponse || |
| 104 | (async (resp: Response) => ({ |
| 105 | success: true, |
| 106 | output: await resp.json(), |
| 107 | })) |
| 108 | |
| 109 | return await transformResponse(resolvedResponse as Response) |
| 110 | } catch (error: any) { |
| 111 | return { |
| 112 | success: false, |
| 113 | output: {}, |
no test coverage detected