diff --git a/packages/tools/package.json b/packages/tools/package.json index e15829b..4393207 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -45,6 +45,14 @@ "./textfile-search": { "import": "./dist/textfile-search/index.esm.js", "require": "./dist/textfile-search/index.cjs.js" + }, + "./zapier-webhook": { + "import": "./dist/zapier-webhook/index.esm.js", + "require": "./dist/zapier-webhook/index.cjs.js" + }, + "./make-webhook": { + "import": "./dist/make-webhook/index.esm.js", + "require": "./dist/make-webhook/index.cjs.js" } }, "files": [ diff --git a/packages/tools/rollup.config.mjs b/packages/tools/rollup.config.mjs index 264dcb6..ee8d01f 100644 --- a/packages/tools/rollup.config.mjs +++ b/packages/tools/rollup.config.mjs @@ -18,6 +18,8 @@ const toolFolders = [ 'website-search', 'pdf-search', 'textfile-search', + 'zapier-webhook', + 'make-webhook', ]; // Add more folder names as needed const toolConfigs = toolFolders.map((tool) => { diff --git a/packages/tools/src/index.js b/packages/tools/src/index.js index f77c74e..e876e17 100644 --- a/packages/tools/src/index.js +++ b/packages/tools/src/index.js @@ -8,3 +8,5 @@ export * from './simple-rag/index.js'; export * from './website-search/index.js'; export * from './pdf-search/index.js'; export * from './textfile-search/index.js'; +export * from './zapier-webhook/index.js'; +export * from './make-webhook/index.js'; diff --git a/packages/tools/src/make-webhook/README.md b/packages/tools/src/make-webhook/README.md new file mode 100644 index 0000000..938d09c --- /dev/null +++ b/packages/tools/src/make-webhook/README.md @@ -0,0 +1,48 @@ +# Make Webhook Tool + +The Make Webhook Tool allows AI agents to interact with Make's webhook service, enabling seamless integration with thousands of apps and services supported by Make. + +## Purpose + +The Make Webhook Tool is designed to extend the capabilities of AI agents by allowing them to trigger workflows and automate tasks across various applications using Make's webhook functionality. This tool is ideal for scenarios where agents need to interact with multiple services and automate complex workflows. + +## Features + +- Easy integration with Make's webhook service +- Trigger workflows and automate tasks across thousands of apps +- Configurable options for webhook events and payloads + +## Usage + +To use the Make Webhook Tool, follow these steps: + +**Configure the Tool**: Create an instance of the `MakeWebhook` tool with the required configuration. + +```javascript +import { z } from 'zod'; + +const MakeTool = new MakeWebhook({ + url: 'https://hooks.Make.com/hooks/catch/4716958/2sdvyu2', // Set your Make webhook URL here + schema: z.object({ + emailSubject: z.string().describe('The subject of the email.'), + issuesSummary: z.string().describe('The summary of the issues.'), + }), +}); +``` + +**Use the Tool**: Integrate the tool into your workflow or agent. + +```javascript +const response = await MakeTool._call({ + emailSubject: 'Weekly GitHub Issues Report', + issuesSummary: 'Summary of the issues found in the repository.', +}); + +console.log(response); +``` + +For questions or discussions, join our [Discord](https://kaibanjs.com/discord). + +## License + +MIT License diff --git a/packages/tools/src/make-webhook/index.js b/packages/tools/src/make-webhook/index.js new file mode 100644 index 0000000..7d0c9c9 --- /dev/null +++ b/packages/tools/src/make-webhook/index.js @@ -0,0 +1,90 @@ +/** + * Make Webhook Tool + * + * This tool allows integration with Make's webhook service, enabling seamless + * interaction with thousands of apps and services supported by Make. It is + * designed to trigger workflows and automate tasks across various applications + * using Make's webhook functionality. + * + * Key features of Make Webhook Tool: + * - Easy integration with Make's webhook service + * - Trigger workflows and automate tasks across thousands of apps + * - Configurable options for webhook events and payloads + * + * Usage: + * const MakeTool = new MakeWebhook({ + * url: 'https://hooks.Make.com/hooks/catch/4716958/2sdvyu2', // Set your Make webhook URL here + * schema: z.object({ + * emailSubject: z.string().describe('The subject of the email.'), + * issuesSummary: z.string().describe('The summary of the issues.'), + * }), + * }); + * const response = await MakeTool._call({ + * emailSubject: 'Weekly GitHub Issues Report', + * issuesSummary: 'Summary of the issues found in the repository.', + * }); + * + * For more information about Make, visit: https://Make.com/ + */ + +import { Tool } from '@langchain/core/tools'; +import ky from 'ky'; +import { HTTPError } from 'ky'; + +/** + * Class representing a Make Webhook tool. + * @extends Tool + */ +export class MakeWebhook extends Tool { + /** + * Create a MakeWebhook tool. + * @param {Object} fields - The configuration fields for the tool. + * @param {string} fields.url - The Make webhook URL. + * @param {Object} fields.schema - The schema for the input data using Zod. + */ + constructor(fields) { + super(fields); + this.url = fields.url; + this.name = 'make_webhook'; + this.description = + 'A tool for triggering Make webhooks to integrate with various services. Input should be a JSON object with the necessary data for the webhook.'; + + this.httpClient = ky; + this.schema = fields.schema; + } + + /** + * Call the Make webhook with the provided input data. + * @param {Object} input - The input data for the webhook. + * @returns {Promise} The response from the webhook as a JSON string. + */ + async _call(input) { + try { + const response = await this.httpClient.post(this.url, { + json: input, + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + return 'Could not parse Make webhook response. Please try again.'; + } + + return 'Webhook response success'; + } catch (error) { + if (error instanceof HTTPError) { + const statusCode = error.response.status; + let errorType = 'Unknown'; + if (statusCode >= 400 && statusCode < 500) { + errorType = 'Client Error'; + } else if (statusCode >= 500) { + errorType = 'Server Error'; + } + return `API request failed: ${errorType} (${statusCode})`; + } else { + return `An unexpected error occurred: ${error.message}`; + } + } + } +} diff --git a/packages/tools/src/make-webhook/tool.stories.jsx b/packages/tools/src/make-webhook/tool.stories.jsx new file mode 100644 index 0000000..41a5d9b --- /dev/null +++ b/packages/tools/src/make-webhook/tool.stories.jsx @@ -0,0 +1,93 @@ +import { z } from 'zod'; +import { ToolPreviewer } from '../_utils/ToolPreviewer.jsx'; +import { AgentWithToolPreviewer } from '../_utils/AgentWithToolPreviewer.jsx'; +import { GithubIssues } from '../github-issues/index.js'; +import { MakeWebhook } from './index.js'; +import { Agent, Task, Team } from '../../../../src/index'; +import React from 'react'; + +export default { + title: 'Tools/Make Webhook', + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: {}, +}; + +const githubTool = new GithubIssues({ + token: import.meta.env.VITE_GITHUB_TOKEN, + limit: 5, +}); + +const makeTool = new MakeWebhook({ + url: 'https://hook.us2.make.com/fwxq61cn8k5f6143rsomlk3plcupsypp', // Set your Make webhook URL here + schema: z.object({ + emailSubject: z.string().describe('The subject of the email.'), + issuesSummary: z.string().describe('The summary of the issues.'), + }), +}); + +export const Default = { + render: (args) => , + args: { + toolInstance: makeTool, + callParams: { + emailSubject: 'Weekly GitHub Issues Report', + issuesSummary: 'Summary of the issues found in the repository.', + }, + }, +}; + +// Create an agent with the GitHub tool +const issueAnalyzer = new Agent({ + name: 'Issue Analyzer', + role: 'GitHub Repository Inspector', + goal: 'Analyze and summarize GitHub repository issues', + tools: [githubTool], +}); + +// Create an agent with the Make webhook tool +const emailSender = new Agent({ + name: 'Email Sender', + role: 'Email Reporter', + goal: 'Send summarized issues via email using Make webhook', + tools: [makeTool], +}); + +// Create an analysis task +const issueAnalysisTask = new Task({ + description: + 'Fetch and analyze issues from the following repository: {repoUrl}', + agent: issueAnalyzer, + expectedOutput: 'A structured summary of repository issues', +}); + +// Create a task to send the summary via Make webhook +const sendEmailTask = new Task({ + description: 'Send the summarized issues via Make webhook', + agent: emailSender, + expectedOutput: 'A confirmation that the email was sent successfully', +}); + +// Create the team +const team = new Team({ + name: 'Issue Reporting Team', + description: + 'Team to fetch GitHub issues and send them via email using Make webhook', + agents: [issueAnalyzer, emailSender], + tasks: [issueAnalysisTask, sendEmailTask], + inputs: { + repoUrl: 'https://github.com/facebook/react', + }, + env: { + OPENAI_API_KEY: import.meta.env.VITE_OPENAI_API_KEY, + }, +}); + +export const withAgent = { + render: (args) => , + args: { + team: team, + }, +}; diff --git a/packages/tools/src/make-webhook/tool.test.js b/packages/tools/src/make-webhook/tool.test.js new file mode 100644 index 0000000..4e3b2b0 --- /dev/null +++ b/packages/tools/src/make-webhook/tool.test.js @@ -0,0 +1,242 @@ +const { MakeWebhook } = require('../../dist/make-webhook/index.cjs.js'); + +describe('MakeWebhook', () => { + test('MakeWebhook sends correct parameters and receives response', async () => { + const tool = new MakeWebhook({ + url: 'https://hook.us2.make.com/fwxq61cn8k5f6143rsomlk3plcupsypp', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + let capturedRequest; + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + (request) => { + capturedRequest = request; + return new Response( + JSON.stringify({ + status: 'success', + message: 'Webhook received', + }), + { + status: 200, + headers: { 'Content-Type': 'application/json' }, + } + ); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + // Check request + expect(capturedRequest.url).toBe( + 'https://hook.us2.make.com/fwxq61cn8k5f6143rsomlk3plcupsypp' + ); + expect(capturedRequest.method).toBe('POST'); + expect(capturedRequest.headers.get('Content-Type')).toBe( + 'application/json' + ); + const body = await capturedRequest.json(); + expect(body).toEqual({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + // Check response + expect(result).toBe('Webhook response success'); + }); + + test('MakeWebhook handles client error (4xx)', async () => { + const tool = new MakeWebhook({ + url: 'https://hook.us2.make.com/fwxq61cn8k5f6143rsomlk3plcupsypp', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + () => { + return new Response( + JSON.stringify({ + status: 'error', + message: 'Invalid request', + }), + { + status: 400, + headers: { 'Content-Type': 'application/json' }, + } + ); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + expect(result).toBe('API request failed: Client Error (400)'); + }); + + test('MakeWebhook handles server error (5xx)', async () => { + const tool = new MakeWebhook({ + url: 'https://hook.us2.make.com/fwxq61cn8k5f6143rsomlk3plcupsypp', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + () => { + return new Response( + JSON.stringify({ + status: 'error', + message: 'Internal Server Error', + }), + { + status: 500, + headers: { 'Content-Type': 'application/json' }, + } + ); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + expect(result).toBe('API request failed: Server Error (500)'); + }); + + test('MakeWebhook handles unexpected errors', async () => { + const tool = new MakeWebhook({ + url: 'https://hook.us2.make.com/fwxq61cn8k5f6143rsomlk3plcupsypp', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + () => { + throw new Error('Network Error'); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + expect(result).toBe('An unexpected error occurred: Network Error'); + }); + + test('MakeWebhook is exported correctly in both paths', () => { + const { MakeWebhook } = require('../../dist/make-webhook/index.cjs.js'); + const { MakeWebhook: MakeWebhookMain } = require('../../dist/index.cjs.js'); + + // Check that both imports are constructor functions + expect(typeof MakeWebhook).toBe('function'); + expect(typeof MakeWebhookMain).toBe('function'); + + // Check they have the same name and properties + expect(MakeWebhook.name).toBe(MakeWebhookMain.name); + expect(Object.keys(MakeWebhook.prototype)).toEqual( + Object.keys(MakeWebhookMain.prototype) + ); + }); +}); diff --git a/packages/tools/src/zapier-webhook/README.md b/packages/tools/src/zapier-webhook/README.md new file mode 100644 index 0000000..6bd42d6 --- /dev/null +++ b/packages/tools/src/zapier-webhook/README.md @@ -0,0 +1,48 @@ +# Zapier Webhook Tool + +The Zapier Webhook Tool allows AI agents to interact with Zapier's webhook service, enabling seamless integration with thousands of apps and services supported by Zapier. + +## Purpose + +The Zapier Webhook Tool is designed to extend the capabilities of AI agents by allowing them to trigger workflows and automate tasks across various applications using Zapier's webhook functionality. This tool is ideal for scenarios where agents need to interact with multiple services and automate complex workflows. + +## Features + +- Easy integration with Zapier's webhook service +- Trigger workflows and automate tasks across thousands of apps +- Configurable options for webhook events and payloads + +## Usage + +To use the Zapier Webhook Tool, follow these steps: + +**Configure the Tool**: Create an instance of the `ZapierWebhook` tool with the required configuration. + +```javascript +import { z } from 'zod'; + +const zapierTool = new ZapierWebhook({ + url: 'https://hooks.zapier.com/hooks/catch/4716958/2sdvyu2', // Set your Zapier webhook URL here + schema: z.object({ + emailSubject: z.string().describe('The subject of the email.'), + issuesSummary: z.string().describe('The summary of the issues.'), + }), +}); +``` + +**Use the Tool**: Integrate the tool into your workflow or agent. + +```javascript +const response = await zapierTool._call({ + emailSubject: 'Weekly GitHub Issues Report', + issuesSummary: 'Summary of the issues found in the repository.', +}); + +console.log(response); +``` + +For questions or discussions, join our [Discord](https://kaibanjs.com/discord). + +## License + +MIT License diff --git a/packages/tools/src/zapier-webhook/index.js b/packages/tools/src/zapier-webhook/index.js new file mode 100644 index 0000000..744a7ff --- /dev/null +++ b/packages/tools/src/zapier-webhook/index.js @@ -0,0 +1,89 @@ +/** + * Zapier Webhook Tool + * + * This tool allows integration with Zapier's webhook service, enabling seamless + * interaction with thousands of apps and services supported by Zapier. It is + * designed to trigger workflows and automate tasks across various applications + * using Zapier's webhook functionality. + * + * Key features of Zapier Webhook Tool: + * - Easy integration with Zapier's webhook service + * - Trigger workflows and automate tasks across thousands of apps + * - Configurable options for webhook events and payloads + * + * Usage: + * const zapierTool = new ZapierWebhook({ + * url: 'https://hooks.zapier.com/hooks/catch/4716958/2sdvyu2', // Set your Zapier webhook URL here + * schema: z.object({ + * emailSubject: z.string().describe('The subject of the email.'), + * issuesSummary: z.string().describe('The summary of the issues.'), + * }), + * }); + * const response = await zapierTool._call({ + * emailSubject: 'Weekly GitHub Issues Report', + * issuesSummary: 'Summary of the issues found in the repository.', + * }); + * + * For more information about Zapier, visit: https://zapier.com/ + */ + +import { Tool } from '@langchain/core/tools'; +import ky from 'ky'; +import { HTTPError } from 'ky'; + +/** + * Class representing a Zapier Webhook tool. + * @extends Tool + */ +export class ZapierWebhook extends Tool { + /** + * Create a ZapierWebhook tool. + * @param {Object} fields - The configuration fields for the tool. + * @param {string} fields.url - The Zapier webhook URL. + * @param {Object} fields.schema - The schema for the input data using Zod. + */ + constructor(fields) { + super(fields); + this.url = fields.url; + this.name = 'zapier_webhook'; + this.description = + 'A tool for triggering Zapier webhooks to integrate with various services. Input should be a JSON object with the necessary data for the webhook.'; + + this.httpClient = ky; + this.schema = fields.schema; + } + + /** + * Call the Zapier webhook with the provided input data. + * @param {Object} input - The input data for the webhook. + * @returns {Promise} The response from the webhook as a JSON string. + */ + async _call(input) { + try { + const jsonData = await this.httpClient + .post(this.url, { + body: JSON.stringify(input), + }) + .json(); + + if (!jsonData || typeof jsonData !== 'object') { + return 'Could not parse Zapier webhook response. Please try again.'; + } + + return JSON.stringify(jsonData); + } catch (error) { + if (error instanceof HTTPError) { + const statusCode = error.response.status; + let errorType = 'Unknown'; + if (statusCode >= 400 && statusCode < 500) { + errorType = 'Client Error'; + } else if (statusCode >= 500) { + errorType = 'Server Error'; + } + return `API request failed: ${errorType} (${statusCode})`; + } else { + return `An unexpected error occurred: ${error.message}`; + } + } + } +} diff --git a/packages/tools/src/zapier-webhook/tool.stories.jsx b/packages/tools/src/zapier-webhook/tool.stories.jsx new file mode 100644 index 0000000..0792a65 --- /dev/null +++ b/packages/tools/src/zapier-webhook/tool.stories.jsx @@ -0,0 +1,93 @@ +import { z } from 'zod'; +import { ToolPreviewer } from '../_utils/ToolPreviewer.jsx'; +import { AgentWithToolPreviewer } from '../_utils/AgentWithToolPreviewer.jsx'; +import { GithubIssues } from '../github-issues/index.js'; +import { ZapierWebhook } from './index.js'; +import { Agent, Task, Team } from '../../../../src/index'; +import React from 'react'; + +export default { + title: 'Tools/Zapier Webhook', + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], + argTypes: {}, +}; + +const githubTool = new GithubIssues({ + token: import.meta.env.VITE_GITHUB_TOKEN, + limit: 5, +}); + +const zapierTool = new ZapierWebhook({ + url: 'https://hooks.zapier.com/hooks/catch/4716958/2sdvyu2', // Set your Zapier webhook URL here + schema: z.object({ + emailSubject: z.string().describe('The subject of the email.'), + issuesSummary: z.string().describe('The summary of the issues.'), + }), +}); + +export const Default = { + render: (args) => , + args: { + toolInstance: zapierTool, + callParams: { + emailSubject: 'Weekly GitHub Issues Report', + issuesSummary: 'Summary of the issues found in the repository.', + }, + }, +}; + +// Create an agent with the GitHub tool +const issueAnalyzer = new Agent({ + name: 'Issue Analyzer', + role: 'GitHub Repository Inspector', + goal: 'Analyze and summarize GitHub repository issues', + tools: [githubTool], +}); + +// Create an agent with the Zapier webhook tool +const emailSender = new Agent({ + name: 'Email Sender', + role: 'Email Reporter', + goal: 'Send summarized issues via email using Zapier webhook', + tools: [zapierTool], +}); + +// Create an analysis task +const issueAnalysisTask = new Task({ + description: + 'Fetch and analyze issues from the following repository: {repoUrl}', + agent: issueAnalyzer, + expectedOutput: 'A structured summary of repository issues', +}); + +// Create a task to send the summary via Zapier webhook +const sendEmailTask = new Task({ + description: 'Send the summarized issues via Zapier webhook', + agent: emailSender, + expectedOutput: 'A confirmation that the email was sent successfully', +}); + +// Create the team +const team = new Team({ + name: 'Issue Reporting Team', + description: + 'Team to fetch GitHub issues and send them via email using Zapier webhook', + agents: [issueAnalyzer, emailSender], + tasks: [issueAnalysisTask, sendEmailTask], + inputs: { + repoUrl: 'https://github.com/facebook/react', + }, + env: { + OPENAI_API_KEY: import.meta.env.VITE_OPENAI_API_KEY, + }, +}); + +export const withAgent = { + render: (args) => , + args: { + team: team, + }, +}; diff --git a/packages/tools/src/zapier-webhook/tool.test.js b/packages/tools/src/zapier-webhook/tool.test.js new file mode 100644 index 0000000..40c34ad --- /dev/null +++ b/packages/tools/src/zapier-webhook/tool.test.js @@ -0,0 +1,243 @@ +const { ZapierWebhook } = require('../../dist/zapier-webhook/index.cjs.js'); + +describe('ZapierWebhook', () => { + test('ZapierWebhook sends correct parameters and receives response', async () => { + const tool = new ZapierWebhook({ + url: 'https://hooks.zapier.com/hooks/catch/123456/abcdef', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + let capturedRequest; + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + (request) => { + capturedRequest = request; + return new Response( + JSON.stringify({ + status: 'success', + message: 'Webhook received', + }), + { + status: 200, + headers: { 'Content-Type': 'application/json' }, + } + ); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + // Check request + expect(capturedRequest.url).toBe( + 'https://hooks.zapier.com/hooks/catch/123456/abcdef' + ); + expect(capturedRequest.method).toBe('POST'); + const body = await capturedRequest.json(); + expect(body).toEqual({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + // Check response + expect(result).toBe( + JSON.stringify({ status: 'success', message: 'Webhook received' }) + ); + }); + + test('ZapierWebhook handles client error (4xx)', async () => { + const tool = new ZapierWebhook({ + url: 'https://hooks.zapier.com/hooks/catch/123456/abcdef', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + () => { + return new Response( + JSON.stringify({ + status: 'error', + message: 'Invalid request', + }), + { + status: 400, + headers: { 'Content-Type': 'application/json' }, + } + ); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + expect(result).toBe('API request failed: Client Error (400)'); + }); + + test('ZapierWebhook handles server error (5xx)', async () => { + const tool = new ZapierWebhook({ + url: 'https://hooks.zapier.com/hooks/catch/123456/abcdef', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + () => { + return new Response( + JSON.stringify({ + status: 'error', + message: 'Internal Server Error', + }), + { + status: 500, + headers: { 'Content-Type': 'application/json' }, + } + ); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + expect(result).toBe('API request failed: Server Error (500)'); + }); + + test('ZapierWebhook handles unexpected errors', async () => { + const tool = new ZapierWebhook({ + url: 'https://hooks.zapier.com/hooks/catch/123456/abcdef', + schema: { + data: { + type: 'object', + properties: { + issues: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }); + + tool.httpClient = tool.httpClient.extend({ + hooks: { + beforeRequest: [ + () => { + throw new Error('Network Error'); + }, + ], + }, + }); + + const result = await tool._call({ + data: { + issues: [ + { title: 'Issue 1', url: 'https://github.com/owner/repo/issues/1' }, + { title: 'Issue 2', url: 'https://github.com/owner/repo/issues/2' }, + ], + }, + }); + + expect(result).toBe('An unexpected error occurred: Network Error'); + }); + + test('ZapierWebhook is exported correctly in both paths', () => { + const { ZapierWebhook } = require('../../dist/zapier-webhook/index.cjs.js'); + const { + ZapierWebhook: ZapierWebhookMain, + } = require('../../dist/index.cjs.js'); + + // Check that both imports are constructor functions + expect(typeof ZapierWebhook).toBe('function'); + expect(typeof ZapierWebhookMain).toBe('function'); + + // Check they have the same name and properties + expect(ZapierWebhook.name).toBe(ZapierWebhookMain.name); + expect(Object.keys(ZapierWebhook.prototype)).toEqual( + Object.keys(ZapierWebhookMain.prototype) + ); + }); +});