From cdfb2fcd4cd7bcff79795c474f41d202ab6ae3f5 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 19 Jul 2025 21:29:14 -0700 Subject: [PATCH] feat(integrations): added deeper integrations for slack, supabase, firecrawl, exa, notion (#728) * added deeper exa integrations * added firecrawl crawl tool * include (optional) indicator for fields that are not explicitly required to be filled in by the user * use aliased imports, stronger typing, added additional notion tools * added additional notion tools, tested * added additional supabase tools, updated CSP * added remaining supabase tools * finished supabase tools * fixed persistence of selector inputs on refresh, added supabase tools and slack tools * fixed failing test * remove unrelated file --- .../tools/firecrawl/crawl/[jobId]/route.ts | 39 ++++ .../channel-selector-input.tsx | 14 +- .../components/oauth-required-modal.tsx | 18 +- .../components/tool-input/tool-input.tsx | 10 +- .../components/search-modal/search-modal.tsx | 2 +- apps/sim/blocks/blocks/exa.ts | 30 ++- apps/sim/blocks/blocks/firecrawl.ts | 38 +++- apps/sim/blocks/blocks/notion.ts | 190 +++++++++++++++--- apps/sim/blocks/blocks/slack.ts | 126 +++++++++++- apps/sim/blocks/blocks/supabase.ts | 125 ++++++++++-- apps/sim/blocks/registry.ts | 3 +- apps/sim/lib/auth.ts | 7 +- apps/sim/next.config.ts | 2 +- apps/sim/tools/airtable/create_records.ts | 4 +- apps/sim/tools/airtable/get_record.ts | 4 +- apps/sim/tools/airtable/index.ts | 10 +- apps/sim/tools/airtable/list_records.ts | 4 +- apps/sim/tools/airtable/types.ts | 2 +- .../tools/airtable/update_multiple_records.ts | 7 +- apps/sim/tools/airtable/update_record.ts | 4 +- apps/sim/tools/browser_use/index.ts | 2 +- apps/sim/tools/browser_use/run_task.ts | 4 +- apps/sim/tools/browser_use/types.ts | 2 +- apps/sim/tools/clay/index.ts | 2 +- apps/sim/tools/clay/populate.ts | 4 +- apps/sim/tools/clay/types.ts | 2 +- apps/sim/tools/confluence/index.ts | 4 +- apps/sim/tools/confluence/retrieve.ts | 4 +- apps/sim/tools/confluence/types.ts | 2 +- apps/sim/tools/confluence/update.ts | 4 +- apps/sim/tools/discord/get_messages.ts | 4 +- apps/sim/tools/discord/get_server.ts | 4 +- apps/sim/tools/discord/get_user.ts | 4 +- apps/sim/tools/discord/index.ts | 8 +- apps/sim/tools/discord/send_message.ts | 4 +- apps/sim/tools/docs/search.ts | 2 +- apps/sim/tools/elevenlabs/index.ts | 2 +- apps/sim/tools/exa/answer.ts | 4 +- apps/sim/tools/exa/find_similar_links.ts | 4 +- apps/sim/tools/exa/get_contents.ts | 4 +- apps/sim/tools/exa/index.ts | 10 +- apps/sim/tools/exa/research.ts | 177 ++++++++++++++++ apps/sim/tools/exa/search.ts | 4 +- apps/sim/tools/exa/types.ts | 24 ++- apps/sim/tools/file/index.ts | 2 +- apps/sim/tools/file/parser.ts | 4 +- apps/sim/tools/file/types.ts | 2 +- apps/sim/tools/firecrawl/crawl.ts | 156 ++++++++++++++ apps/sim/tools/firecrawl/index.ts | 7 +- apps/sim/tools/firecrawl/scrape.ts | 4 +- apps/sim/tools/firecrawl/search.ts | 4 +- apps/sim/tools/firecrawl/types.ts | 30 ++- apps/sim/tools/function/execute.test.ts | 4 +- apps/sim/tools/function/execute.ts | 4 +- apps/sim/tools/function/index.ts | 2 +- apps/sim/tools/function/types.ts | 2 +- apps/sim/tools/github/comment.ts | 4 +- apps/sim/tools/github/index.ts | 8 +- apps/sim/tools/github/latest_commit.ts | 4 +- apps/sim/tools/github/pr.ts | 4 +- apps/sim/tools/github/repo_info.ts | 4 +- apps/sim/tools/github/types.ts | 2 +- apps/sim/tools/gmail/draft.ts | 4 +- apps/sim/tools/gmail/index.ts | 8 +- apps/sim/tools/gmail/read.ts | 4 +- apps/sim/tools/gmail/search.ts | 4 +- apps/sim/tools/gmail/send.ts | 4 +- apps/sim/tools/gmail/types.ts | 2 +- apps/sim/tools/google/index.ts | 2 +- apps/sim/tools/google/search.ts | 4 +- apps/sim/tools/google/types.ts | 2 +- apps/sim/tools/google_calendar/create.ts | 4 +- apps/sim/tools/google_calendar/get.ts | 4 +- apps/sim/tools/google_calendar/index.ts | 10 +- apps/sim/tools/google_calendar/invite.ts | 4 +- apps/sim/tools/google_calendar/list.ts | 4 +- apps/sim/tools/google_calendar/quick_add.ts | 4 +- apps/sim/tools/google_calendar/types.ts | 2 +- apps/sim/tools/google_calendar/update.ts | 4 +- apps/sim/tools/google_docs/create.ts | 4 +- apps/sim/tools/google_docs/index.ts | 6 +- apps/sim/tools/google_docs/read.ts | 4 +- apps/sim/tools/google_docs/types.ts | 2 +- apps/sim/tools/google_docs/write.ts | 4 +- apps/sim/tools/google_drive/create_folder.ts | 4 +- apps/sim/tools/google_drive/get_content.ts | 9 +- apps/sim/tools/google_drive/index.ts | 8 +- apps/sim/tools/google_drive/list.ts | 4 +- apps/sim/tools/google_drive/types.ts | 2 +- apps/sim/tools/google_drive/upload.ts | 6 +- apps/sim/tools/google_sheets/append.ts | 7 +- apps/sim/tools/google_sheets/index.ts | 8 +- apps/sim/tools/google_sheets/read.ts | 4 +- apps/sim/tools/google_sheets/types.ts | 2 +- apps/sim/tools/google_sheets/update.ts | 7 +- apps/sim/tools/google_sheets/write.ts | 4 +- apps/sim/tools/http/index.ts | 2 +- apps/sim/tools/http/request.test.ts | 6 +- apps/sim/tools/http/request.ts | 4 +- apps/sim/tools/http/types.ts | 2 +- apps/sim/tools/huggingface/chat.ts | 4 +- apps/sim/tools/huggingface/index.ts | 2 +- apps/sim/tools/huggingface/types.ts | 2 +- apps/sim/tools/index.test.ts | 8 +- apps/sim/tools/index.ts | 4 +- apps/sim/tools/jina/index.ts | 2 +- apps/sim/tools/jina/read_url.ts | 4 +- apps/sim/tools/jina/types.ts | 2 +- apps/sim/tools/jira/bulk_read.ts | 4 +- apps/sim/tools/jira/index.ts | 8 +- apps/sim/tools/jira/retrieve.ts | 4 +- apps/sim/tools/jira/types.ts | 2 +- apps/sim/tools/jira/update.ts | 6 +- apps/sim/tools/jira/write.ts | 6 +- apps/sim/tools/knowledge/create_document.ts | 4 +- apps/sim/tools/knowledge/index.ts | 6 +- apps/sim/tools/knowledge/search.ts | 4 +- apps/sim/tools/knowledge/upload_chunk.ts | 4 +- apps/sim/tools/linear/create_issue.ts | 4 +- apps/sim/tools/linear/index.ts | 4 +- apps/sim/tools/linear/read_issues.ts | 8 +- apps/sim/tools/linear/types.ts | 2 +- apps/sim/tools/linkup/index.ts | 2 +- apps/sim/tools/linkup/search.ts | 8 +- apps/sim/tools/linkup/types.ts | 2 +- apps/sim/tools/mem0/add_memories.ts | 2 +- apps/sim/tools/mem0/get_memories.ts | 2 +- apps/sim/tools/mem0/index.ts | 6 +- apps/sim/tools/mem0/search_memories.ts | 4 +- apps/sim/tools/mem0/types.ts | 2 +- apps/sim/tools/memory/add.ts | 4 +- apps/sim/tools/memory/delete.ts | 4 +- apps/sim/tools/memory/get.ts | 4 +- apps/sim/tools/memory/get_all.ts | 4 +- apps/sim/tools/memory/index.ts | 8 +- apps/sim/tools/memory/types.ts | 2 +- apps/sim/tools/microsoft_excel/index.ts | 6 +- apps/sim/tools/microsoft_excel/read.ts | 7 +- apps/sim/tools/microsoft_excel/table_add.ts | 7 +- apps/sim/tools/microsoft_excel/types.ts | 2 +- apps/sim/tools/microsoft_excel/write.ts | 7 +- apps/sim/tools/microsoft_teams/index.ts | 8 +- .../sim/tools/microsoft_teams/read_channel.ts | 9 +- apps/sim/tools/microsoft_teams/read_chat.ts | 9 +- apps/sim/tools/microsoft_teams/types.ts | 2 +- apps/sim/tools/microsoft_teams/utils.ts | 2 +- .../tools/microsoft_teams/write_channel.ts | 7 +- apps/sim/tools/microsoft_teams/write_chat.ts | 7 +- apps/sim/tools/mistral/index.ts | 2 +- apps/sim/tools/mistral/parser.ts | 4 +- apps/sim/tools/mistral/types.ts | 2 +- apps/sim/tools/notion/create_database.ts | 148 ++++++++++++++ apps/sim/tools/notion/create_page.ts | 38 +--- apps/sim/tools/notion/index.ts | 23 ++- apps/sim/tools/notion/query_database.ts | 186 +++++++++++++++++ apps/sim/tools/notion/read.ts | 4 +- apps/sim/tools/notion/read_database.ts | 106 ++++++++++ apps/sim/tools/notion/search.ts | 151 ++++++++++++++ apps/sim/tools/notion/types.ts | 39 +++- apps/sim/tools/notion/update_page.ts | 4 +- apps/sim/tools/notion/write.ts | 4 +- apps/sim/tools/openai/embeddings.ts | 4 +- apps/sim/tools/openai/image.ts | 4 +- apps/sim/tools/openai/index.ts | 4 +- apps/sim/tools/openai/types.ts | 2 +- apps/sim/tools/outlook/draft.ts | 4 +- apps/sim/tools/outlook/index.ts | 6 +- apps/sim/tools/outlook/read.ts | 4 +- apps/sim/tools/outlook/send.ts | 4 +- apps/sim/tools/outlook/types.ts | 2 +- apps/sim/tools/params.test.ts | 6 +- apps/sim/tools/params.ts | 4 +- apps/sim/tools/perplexity/chat.ts | 4 +- apps/sim/tools/perplexity/index.ts | 2 +- apps/sim/tools/perplexity/types.ts | 2 +- apps/sim/tools/pinecone/fetch.ts | 4 +- .../sim/tools/pinecone/generate_embeddings.ts | 4 +- apps/sim/tools/pinecone/index.ts | 10 +- apps/sim/tools/pinecone/search_text.ts | 8 +- apps/sim/tools/pinecone/search_vector.ts | 4 +- apps/sim/tools/pinecone/types.ts | 2 +- apps/sim/tools/pinecone/upsert_text.ts | 8 +- apps/sim/tools/reddit/get_comments.ts | 4 +- apps/sim/tools/reddit/get_posts.ts | 4 +- apps/sim/tools/reddit/hot_posts.ts | 4 +- apps/sim/tools/reddit/index.ts | 6 +- apps/sim/tools/reddit/types.ts | 2 +- apps/sim/tools/registry.ts | 135 ++++++++----- apps/sim/tools/s3/get_object.ts | 9 +- apps/sim/tools/s3/index.ts | 2 +- apps/sim/tools/s3/types.ts | 2 +- apps/sim/tools/serper/index.ts | 2 +- apps/sim/tools/serper/search.ts | 4 +- apps/sim/tools/serper/types.ts | 2 +- apps/sim/tools/slack/canvas.ts | 123 ++++++++++++ apps/sim/tools/slack/index.ts | 6 +- apps/sim/tools/slack/message.ts | 4 +- apps/sim/tools/slack/message_reader.ts | 120 +++++++++++ apps/sim/tools/slack/types.ts | 38 +++- apps/sim/tools/stagehand/agent.ts | 4 +- apps/sim/tools/stagehand/extract.ts | 4 +- apps/sim/tools/stagehand/index.ts | 4 +- apps/sim/tools/stagehand/types.ts | 2 +- apps/sim/tools/supabase/delete.ts | 147 ++++++++++++++ apps/sim/tools/supabase/get_row.ts | 116 +++++++++++ apps/sim/tools/supabase/index.ts | 10 +- apps/sim/tools/supabase/insert.ts | 13 +- apps/sim/tools/supabase/query.ts | 52 +++-- apps/sim/tools/supabase/types.ts | 33 ++- apps/sim/tools/supabase/update.ts | 152 ++++++++++++++ apps/sim/tools/tavily/extract.ts | 4 +- apps/sim/tools/tavily/index.ts | 4 +- apps/sim/tools/tavily/search.ts | 4 +- apps/sim/tools/tavily/types.ts | 2 +- apps/sim/tools/telegram/index.ts | 2 +- apps/sim/tools/telegram/message.ts | 6 +- apps/sim/tools/telegram/types.ts | 2 +- apps/sim/tools/thinking/index.ts | 2 +- apps/sim/tools/thinking/tool.ts | 4 +- apps/sim/tools/thinking/types.ts | 2 +- apps/sim/tools/twilio/index.ts | 2 +- apps/sim/tools/twilio/send_sms.ts | 4 +- apps/sim/tools/twilio/types.ts | 2 +- apps/sim/tools/typeform/files.ts | 4 +- apps/sim/tools/typeform/index.ts | 6 +- apps/sim/tools/typeform/insights.ts | 4 +- apps/sim/tools/typeform/responses.ts | 4 +- apps/sim/tools/typeform/types.ts | 2 +- apps/sim/tools/types.ts | 4 +- apps/sim/tools/utils.test.ts | 6 +- apps/sim/tools/utils.ts | 14 +- apps/sim/tools/vision/index.ts | 2 +- apps/sim/tools/vision/tool.ts | 4 +- apps/sim/tools/vision/types.ts | 2 +- apps/sim/tools/wealthbox/index.ts | 12 +- apps/sim/tools/whatsapp/index.ts | 2 +- apps/sim/tools/whatsapp/send_message.ts | 4 +- apps/sim/tools/whatsapp/types.ts | 2 +- apps/sim/tools/workflow/get-yaml.ts | 2 +- apps/sim/tools/workflow/index.ts | 2 +- apps/sim/tools/x/index.ts | 8 +- apps/sim/tools/x/read.ts | 4 +- apps/sim/tools/x/search.ts | 4 +- apps/sim/tools/x/types.ts | 2 +- apps/sim/tools/x/user.ts | 4 +- apps/sim/tools/x/write.ts | 4 +- apps/sim/tools/youtube/index.ts | 2 +- apps/sim/tools/youtube/search.ts | 4 +- apps/sim/tools/youtube/types.ts | 2 +- 249 files changed, 2898 insertions(+), 620 deletions(-) create mode 100644 apps/sim/app/api/tools/firecrawl/crawl/[jobId]/route.ts create mode 100644 apps/sim/tools/exa/research.ts create mode 100644 apps/sim/tools/firecrawl/crawl.ts create mode 100644 apps/sim/tools/notion/create_database.ts create mode 100644 apps/sim/tools/notion/query_database.ts create mode 100644 apps/sim/tools/notion/read_database.ts create mode 100644 apps/sim/tools/notion/search.ts create mode 100644 apps/sim/tools/slack/canvas.ts create mode 100644 apps/sim/tools/slack/message_reader.ts create mode 100644 apps/sim/tools/supabase/delete.ts create mode 100644 apps/sim/tools/supabase/get_row.ts create mode 100644 apps/sim/tools/supabase/update.ts diff --git a/apps/sim/app/api/tools/firecrawl/crawl/[jobId]/route.ts b/apps/sim/app/api/tools/firecrawl/crawl/[jobId]/route.ts new file mode 100644 index 000000000..6ea9415b7 --- /dev/null +++ b/apps/sim/app/api/tools/firecrawl/crawl/[jobId]/route.ts @@ -0,0 +1,39 @@ +import { type NextRequest, NextResponse } from 'next/server' + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ jobId: string }> } +) { + const { jobId } = await params + const authHeader = request.headers.get('authorization') + + if (!authHeader) { + return NextResponse.json({ error: 'Authorization header is required' }, { status: 401 }) + } + + try { + const response = await fetch(`https://api.firecrawl.dev/v1/crawl/${jobId}`, { + method: 'GET', + headers: { + Authorization: authHeader, + 'Content-Type': 'application/json', + }, + }) + + const data = await response.json() + + if (!response.ok) { + return NextResponse.json( + { error: data.error || data.message || 'Failed to get crawl status' }, + { status: response.status } + ) + } + + return NextResponse.json(data) + } catch (error: any) { + return NextResponse.json( + { error: `Failed to fetch crawl status: ${error.message}` }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx index 63481121f..12754b70d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import type { SubBlockConfig } from '@/blocks/types' import { useSubBlockStore } from '@/stores/workflows/subblock/store' +import { useSubBlockValue } from '../../hooks/use-sub-block-value' import { type SlackChannelInfo, SlackChannelSelector } from './components/slack-channel-selector' interface ChannelSelectorInputProps { @@ -25,7 +26,10 @@ export function ChannelSelectorInput({ isPreview = false, previewValue, }: ChannelSelectorInputProps) { - const { getValue, setValue } = useSubBlockStore() + const { getValue } = useSubBlockStore() + + // Use the proper hook to get the current value and setter (same as file-selector) + const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id) const [selectedChannelId, setSelectedChannelId] = useState('') const [_channelInfo, setChannelInfo] = useState(null) @@ -47,9 +51,9 @@ export function ChannelSelectorInput({ } // Use preview value when in preview mode, otherwise use store value - const value = isPreview ? previewValue : getValue(blockId, subBlock.id) + const value = isPreview ? previewValue : storeValue - // Get the current value from the store or prop value if in preview mode + // Get the current value from the store or prop value if in preview mode (same pattern as file-selector) useEffect(() => { if (isPreview && previewValue !== undefined) { const value = previewValue @@ -64,12 +68,12 @@ export function ChannelSelectorInput({ } }, [blockId, subBlock.id, getValue, isPreview, previewValue]) - // Handle channel selection + // Handle channel selection (same pattern as file-selector) const handleChannelChange = (channelId: string, info?: SlackChannelInfo) => { setSelectedChannelId(channelId) setChannelInfo(info || null) if (!isPreview) { - setValue(blockId, subBlock.id, channelId) + setStoreValue(channelId) } onChannelSelect?.(channelId) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx index 89c196ae0..847151652 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx @@ -107,15 +107,15 @@ const SCOPE_DESCRIPTIONS: Record = { 'guilds.members.read': 'Read your Discord guild members', read: 'Read access to your workspace', write: 'Write access to your Linear workspace', - 'channels:read': 'Read your Slack channels', - 'groups:read': 'Read your Slack private channels', - 'chat:write': 'Write to your invited Slack channels', - 'chat:write.public': 'Write to your public Slack channels', - 'users:read': 'Read your Slack users', - 'search:read': 'Read your Slack search', - 'files:read': 'Read your Slack files', - 'links:read': 'Read your Slack links', - 'links:write': 'Write to your Slack links', + 'channels:read': 'View public channels', + 'channels:history': 'Read channel messages', + 'groups:read': 'View private channels', + 'groups:history': 'Read private messages', + 'chat:write': 'Send messages', + 'chat:write.public': 'Post to public channels', + 'users:read': 'View workspace users', + 'files:write': 'Upload files', + 'canvases:write': 'Create canvas documents', } // Convert OAuth scope to user-friendly description diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx index 53c471d9d..5331483e1 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx @@ -1006,9 +1006,9 @@ export function ToolInput({ case 'channel-selector': return ( * )} - {!param.required && ( + {(!param.required || param.visibility !== 'user-only') && ( (Optional) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/search-modal/search-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/search-modal/search-modal.tsx index 420a71e10..edaf341de 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/search-modal/search-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/search-modal/search-modal.tsx @@ -896,4 +896,4 @@ export function SearchModal({ ) -} +} \ No newline at end of file diff --git a/apps/sim/blocks/blocks/exa.ts b/apps/sim/blocks/blocks/exa.ts index 657f27a23..f49396f67 100644 --- a/apps/sim/blocks/blocks/exa.ts +++ b/apps/sim/blocks/blocks/exa.ts @@ -13,7 +13,6 @@ export const ExaBlock: BlockConfig = { bgColor: '#1F40ED', icon: ExaAIIcon, subBlocks: [ - // Operation selector { id: 'operation', title: 'Operation', @@ -24,6 +23,7 @@ export const ExaBlock: BlockConfig = { { label: 'Get Contents', id: 'exa_get_contents' }, { label: 'Find Similar Links', id: 'exa_find_similar_links' }, { label: 'Answer', id: 'exa_answer' }, + { label: 'Research', id: 'exa_research' }, ], value: () => 'exa_search', }, @@ -129,6 +129,22 @@ export const ExaBlock: BlockConfig = { layout: 'full', condition: { field: 'operation', value: 'exa_answer' }, }, + // Research operation inputs + { + id: 'query', + title: 'Research Query', + type: 'long-input', + layout: 'full', + placeholder: 'Enter your research topic or question...', + condition: { field: 'operation', value: 'exa_research' }, + }, + { + id: 'includeText', + title: 'Include Full Text', + type: 'switch', + layout: 'full', + condition: { field: 'operation', value: 'exa_research' }, + }, // API Key (common) { id: 'apiKey', @@ -140,7 +156,13 @@ export const ExaBlock: BlockConfig = { }, ], tools: { - access: ['exa_search', 'exa_get_contents', 'exa_find_similar_links', 'exa_answer'], + access: [ + 'exa_search', + 'exa_get_contents', + 'exa_find_similar_links', + 'exa_answer', + 'exa_research', + ], config: { tool: (params) => { // Convert numResults to a number for operations that use it @@ -157,6 +179,8 @@ export const ExaBlock: BlockConfig = { return 'exa_find_similar_links' case 'exa_answer': return 'exa_answer' + case 'exa_research': + return 'exa_research' default: return 'exa_search' } @@ -186,5 +210,7 @@ export const ExaBlock: BlockConfig = { // Answer output answer: 'string', citations: 'json', + // Research output + research: 'json', }, } diff --git a/apps/sim/blocks/blocks/firecrawl.ts b/apps/sim/blocks/blocks/firecrawl.ts index 51595b6d7..a730b6568 100644 --- a/apps/sim/blocks/blocks/firecrawl.ts +++ b/apps/sim/blocks/blocks/firecrawl.ts @@ -21,6 +21,7 @@ export const FirecrawlBlock: BlockConfig = { options: [ { label: 'Scrape', id: 'scrape' }, { label: 'Search', id: 'search' }, + { label: 'Crawl', id: 'crawl' }, ], value: () => 'scrape', }, @@ -29,10 +30,10 @@ export const FirecrawlBlock: BlockConfig = { title: 'Website URL', type: 'short-input', layout: 'full', - placeholder: 'Enter the webpage URL to scrape', + placeholder: 'Enter the website URL', condition: { field: 'operation', - value: 'scrape', + value: ['scrape', 'crawl'], }, }, { @@ -45,6 +46,17 @@ export const FirecrawlBlock: BlockConfig = { value: 'scrape', }, }, + { + id: 'limit', + title: 'Page Limit', + type: 'short-input', + layout: 'half', + placeholder: '100', + condition: { + field: 'operation', + value: 'crawl', + }, + }, { id: 'query', title: 'Search Query', @@ -66,7 +78,7 @@ export const FirecrawlBlock: BlockConfig = { }, ], tools: { - access: ['firecrawl_scrape', 'firecrawl_search'], + access: ['firecrawl_scrape', 'firecrawl_search', 'firecrawl_crawl'], config: { tool: (params) => { switch (params.operation) { @@ -74,16 +86,32 @@ export const FirecrawlBlock: BlockConfig = { return 'firecrawl_scrape' case 'search': return 'firecrawl_search' + case 'crawl': + return 'firecrawl_crawl' default: return 'firecrawl_scrape' } }, + params: (params) => { + const { operation, limit, ...rest } = params + + switch (operation) { + case 'crawl': + return { + ...rest, + limit: limit ? Number.parseInt(limit) : undefined, + } + default: + return rest + } + }, }, }, inputs: { apiKey: { type: 'string', required: true }, operation: { type: 'string', required: true }, url: { type: 'string', required: false }, + limit: { type: 'string', required: false }, query: { type: 'string', required: false }, scrapeOptions: { type: 'json', required: false }, }, @@ -95,5 +123,9 @@ export const FirecrawlBlock: BlockConfig = { // Search output data: 'json', warning: 'any', + // Crawl output + pages: 'json', + total: 'number', + creditsUsed: 'number', }, } diff --git a/apps/sim/blocks/blocks/notion.ts b/apps/sim/blocks/blocks/notion.ts index af9a31a3c..d0e50eb1b 100644 --- a/apps/sim/blocks/blocks/notion.ts +++ b/apps/sim/blocks/blocks/notion.ts @@ -19,9 +19,17 @@ export const NotionBlock: BlockConfig = { type: 'dropdown', layout: 'full', options: [ + // Read Operations { label: 'Read Page', id: 'notion_read' }, - { label: 'Append Content', id: 'notion_write' }, + { label: 'Read Database', id: 'notion_read_database' }, + // Create Operations { label: 'Create Page', id: 'notion_create_page' }, + { label: 'Create Database', id: 'notion_create_database' }, + // Write Operations + { label: 'Append Content', id: 'notion_write' }, + // Query & Search Operations + { label: 'Query Database', id: 'notion_query_database' }, + { label: 'Search Workspace', id: 'notion_search' }, ], }, { @@ -46,6 +54,17 @@ export const NotionBlock: BlockConfig = { value: 'notion_read', }, }, + { + id: 'databaseId', + title: 'Database ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter Notion database ID', + condition: { + field: 'operation', + value: 'notion_read_database', + }, + }, { id: 'pageId', title: 'Page ID', @@ -58,23 +77,12 @@ export const NotionBlock: BlockConfig = { }, }, // Create operation fields - { - id: 'parentType', - title: 'Parent Type', - type: 'dropdown', - layout: 'full', - options: [ - { label: 'Page', id: 'page' }, - { label: 'Database', id: 'database' }, - ], - condition: { field: 'operation', value: 'notion_create_page' }, - }, { id: 'parentId', - title: 'Parent ID', + title: 'Parent Page ID', type: 'short-input', layout: 'full', - placeholder: 'ID of parent page or database', + placeholder: 'ID of parent page', condition: { field: 'operation', value: 'notion_create_page' }, }, { @@ -83,18 +91,6 @@ export const NotionBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Title for the new page', - condition: { - field: 'operation', - value: 'notion_create_page', - and: { field: 'parentType', value: 'page' }, - }, - }, - { - id: 'properties', - title: 'Page Properties (JSON)', - type: 'long-input', - layout: 'full', - placeholder: 'Enter page properties as JSON object', condition: { field: 'operation', value: 'notion_create_page', @@ -123,28 +119,126 @@ export const NotionBlock: BlockConfig = { value: 'notion_create_page', }, }, + // Query Database Fields + { + id: 'databaseId', + title: 'Database ID', + type: 'short-input', + layout: 'full', + placeholder: 'Enter Notion database ID', + condition: { field: 'operation', value: 'notion_query_database' }, + }, + { + id: 'filter', + title: 'Filter (JSON)', + type: 'long-input', + layout: 'full', + placeholder: 'Enter filter conditions as JSON (optional)', + condition: { field: 'operation', value: 'notion_query_database' }, + }, + { + id: 'sorts', + title: 'Sort Criteria (JSON)', + type: 'long-input', + layout: 'full', + placeholder: 'Enter sort criteria as JSON array (optional)', + condition: { field: 'operation', value: 'notion_query_database' }, + }, + { + id: 'pageSize', + title: 'Page Size', + type: 'short-input', + layout: 'full', + placeholder: 'Number of results (default: 100, max: 100)', + condition: { field: 'operation', value: 'notion_query_database' }, + }, + // Search Fields + { + id: 'query', + title: 'Search Query', + type: 'short-input', + layout: 'full', + placeholder: 'Enter search terms (leave empty for all pages)', + condition: { field: 'operation', value: 'notion_search' }, + }, + { + id: 'filterType', + title: 'Filter Type', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'All', id: 'all' }, + { label: 'Pages Only', id: 'page' }, + { label: 'Databases Only', id: 'database' }, + ], + condition: { field: 'operation', value: 'notion_search' }, + }, + // Create Database Fields + { + id: 'parentId', + title: 'Parent Page ID', + type: 'short-input', + layout: 'full', + placeholder: 'ID of parent page where database will be created', + condition: { field: 'operation', value: 'notion_create_database' }, + }, + { + id: 'title', + title: 'Database Title', + type: 'short-input', + layout: 'full', + placeholder: 'Title for the new database', + condition: { field: 'operation', value: 'notion_create_database' }, + }, + { + id: 'properties', + title: 'Database Properties (JSON)', + type: 'long-input', + layout: 'full', + placeholder: 'Enter database properties as JSON object', + condition: { field: 'operation', value: 'notion_create_database' }, + }, ], tools: { - access: ['notion_read', 'notion_write', 'notion_create_page'], + access: [ + 'notion_read', + 'notion_read_database', + 'notion_write', + 'notion_create_page', + 'notion_query_database', + 'notion_search', + 'notion_create_database', + ], config: { tool: (params) => { switch (params.operation) { case 'notion_read': return 'notion_read' + case 'notion_read_database': + return 'notion_read_database' case 'notion_write': return 'notion_write' case 'notion_create_page': return 'notion_create_page' + case 'notion_query_database': + return 'notion_query_database' + case 'notion_search': + return 'notion_search' + case 'notion_create_database': + return 'notion_create_database' default: return 'notion_read' } }, params: (params) => { - const { credential, operation, properties, ...rest } = params + const { credential, operation, properties, filter, sorts, ...rest } = params // Parse properties from JSON string for create operations let parsedProperties - if (operation === 'notion_create_page' && properties) { + if ( + (operation === 'notion_create_page' || operation === 'notion_create_database') && + properties + ) { try { parsedProperties = JSON.parse(properties) } catch (error) { @@ -154,10 +248,36 @@ export const NotionBlock: BlockConfig = { } } + // Parse filter for query database operations + let parsedFilter + if (operation === 'notion_query_database' && filter) { + try { + parsedFilter = JSON.parse(filter) + } catch (error) { + throw new Error( + `Invalid JSON for filter: ${error instanceof Error ? error.message : String(error)}` + ) + } + } + + // Parse sorts for query database operations + let parsedSorts + if (operation === 'notion_query_database' && sorts) { + try { + parsedSorts = JSON.parse(sorts) + } catch (error) { + throw new Error( + `Invalid JSON for sorts: ${error instanceof Error ? error.message : String(error)}` + ) + } + } + return { ...rest, accessToken: credential, ...(parsedProperties ? { properties: parsedProperties } : {}), + ...(parsedFilter ? { filter: JSON.stringify(parsedFilter) } : {}), + ...(parsedSorts ? { sorts: JSON.stringify(parsedSorts) } : {}), } }, }, @@ -168,10 +288,16 @@ export const NotionBlock: BlockConfig = { pageId: { type: 'string', required: false }, content: { type: 'string', required: false }, // Create page inputs - parentType: { type: 'string', required: true }, - parentId: { type: 'string', required: true }, + parentId: { type: 'string', required: false }, title: { type: 'string', required: false }, - properties: { type: 'string', required: false }, + // Query database inputs + databaseId: { type: 'string', required: false }, + filter: { type: 'string', required: false }, + sorts: { type: 'string', required: false }, + pageSize: { type: 'number', required: false }, + // Search inputs + query: { type: 'string', required: false }, + filterType: { type: 'string', required: false }, }, outputs: { content: 'string', diff --git a/apps/sim/blocks/blocks/slack.ts b/apps/sim/blocks/blocks/slack.ts index b839462ff..70d2964c0 100644 --- a/apps/sim/blocks/blocks/slack.ts +++ b/apps/sim/blocks/blocks/slack.ts @@ -7,7 +7,7 @@ export const SlackBlock: BlockConfig = { name: 'Slack', description: 'Send messages to Slack', longDescription: - "Comprehensive Slack integration with OAuth authentication. Send formatted messages using Slack's mrkdwn syntax or Block Kit.", + "Comprehensive Slack integration with OAuth authentication. Send formatted messages using Slack's mrkdwn syntax.", docsLink: 'https://docs.simstudio.ai/tools/slack', category: 'tools', bgColor: '#611f69', @@ -18,7 +18,11 @@ export const SlackBlock: BlockConfig = { title: 'Operation', type: 'dropdown', layout: 'full', - options: [{ label: 'Send Message', id: 'send' }], + options: [ + { label: 'Send Message', id: 'send' }, + { label: 'Create Canvas', id: 'canvas' }, + { label: 'Read Messages', id: 'read' }, + ], value: () => 'send', }, { @@ -41,13 +45,14 @@ export const SlackBlock: BlockConfig = { serviceId: 'slack', requiredScopes: [ 'channels:read', + 'channels:history', 'groups:read', + 'groups:history', 'chat:write', 'chat:write.public', 'users:read', - 'files:read', - 'links:read', - 'links:write', + 'files:write', + 'canvases:write', ], placeholder: 'Select Slack workspace', condition: { @@ -91,22 +96,87 @@ export const SlackBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Enter your message (supports Slack mrkdwn)', + condition: { + field: 'operation', + value: 'send', + }, + }, + // Canvas specific fields + { + id: 'title', + title: 'Canvas Title', + type: 'short-input', + layout: 'full', + placeholder: 'Enter canvas title', + condition: { + field: 'operation', + value: 'canvas', + }, + }, + { + id: 'content', + title: 'Canvas Content', + type: 'long-input', + layout: 'full', + placeholder: 'Enter canvas content (markdown supported)', + condition: { + field: 'operation', + value: 'canvas', + }, + }, + // Message Reader specific fields + { + id: 'limit', + title: 'Message Limit', + type: 'short-input', + layout: 'half', + placeholder: '50', + condition: { + field: 'operation', + value: 'read', + }, + }, + { + id: 'oldest', + title: 'Oldest Timestamp', + type: 'short-input', + layout: 'half', + placeholder: 'ISO 8601 timestamp', + condition: { + field: 'operation', + value: 'read', + }, }, ], tools: { - access: ['slack_message'], + access: ['slack_message', 'slack_canvas', 'slack_message_reader'], config: { tool: (params) => { switch (params.operation) { case 'send': return 'slack_message' + case 'canvas': + return 'slack_canvas' + case 'read': + return 'slack_message_reader' default: throw new Error(`Invalid Slack operation: ${params.operation}`) } }, params: (params) => { - const { credential, authMethod, botToken, operation, channel, manualChannel, ...rest } = - params + const { + credential, + authMethod, + botToken, + operation, + channel, + manualChannel, + title, + content, + limit, + oldest, + ...rest + } = params // Handle channel input (selector or manual) const effectiveChannel = (channel || manualChannel || '').trim() @@ -118,7 +188,6 @@ export const SlackBlock: BlockConfig = { } const baseParams: Record = { - ...rest, channel: effectiveChannel, } @@ -136,6 +205,36 @@ export const SlackBlock: BlockConfig = { baseParams.credential = credential } + // Handle operation-specific params + switch (operation) { + case 'send': + if (!rest.text) { + throw new Error('Message text is required for send operation') + } + baseParams.text = rest.text + break + + case 'canvas': + if (!title || !content) { + throw new Error('Title and content are required for canvas operation') + } + baseParams.title = title + baseParams.content = content + break + + case 'read': + if (limit) { + const parsedLimit = Number.parseInt(limit, 10) + baseParams.limit = !Number.isNaN(parsedLimit) ? parsedLimit : 10 + } else { + baseParams.limit = 10 + } + if (oldest) { + baseParams.oldest = oldest + } + break + } + return baseParams }, }, @@ -147,10 +246,17 @@ export const SlackBlock: BlockConfig = { botToken: { type: 'string', required: false }, channel: { type: 'string', required: false }, manualChannel: { type: 'string', required: false }, - text: { type: 'string', required: true }, + text: { type: 'string', required: false }, + title: { type: 'string', required: false }, + content: { type: 'string', required: false }, + limit: { type: 'string', required: false }, + oldest: { type: 'string', required: false }, }, outputs: { ts: 'string', channel: 'string', + canvas_id: 'string', + title: 'string', + messages: 'json', }, } diff --git a/apps/sim/blocks/blocks/supabase.ts b/apps/sim/blocks/blocks/supabase.ts index cf21a7456..dd7c1a1dd 100644 --- a/apps/sim/blocks/blocks/supabase.ts +++ b/apps/sim/blocks/blocks/supabase.ts @@ -1,7 +1,10 @@ import { SupabaseIcon } from '@/components/icons' +import { createLogger } from '@/lib/logs/console-logger' import type { BlockConfig } from '@/blocks/types' import type { SupabaseResponse } from '@/tools/supabase/types' +const logger = createLogger('SupabaseBlock') + export const SupabaseBlock: BlockConfig = { type: 'supabase', name: 'Supabase', @@ -19,8 +22,11 @@ export const SupabaseBlock: BlockConfig = { type: 'dropdown', layout: 'full', options: [ - { label: 'Read All Rows', id: 'query' }, - { label: 'Insert Rows', id: 'insert' }, + { label: 'Get Many Rows', id: 'query' }, + { label: 'Get a Row', id: 'get_row' }, + { label: 'Create a Row', id: 'insert' }, + { label: 'Update a Row', id: 'update' }, + { label: 'Delete a Row', id: 'delete' }, ], }, { @@ -28,6 +34,7 @@ export const SupabaseBlock: BlockConfig = { title: 'Project ID', type: 'short-input', layout: 'full', + password: true, placeholder: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)', }, { @@ -39,12 +46,13 @@ export const SupabaseBlock: BlockConfig = { }, { id: 'apiKey', - title: 'Client Anon Key', + title: 'Service Role Secret', type: 'short-input', layout: 'full', - placeholder: 'Your Supabase client anon key', + placeholder: 'Your Supabase service role secret key', password: true, }, + // Data input for create/update operations { id: 'data', title: 'Data', @@ -53,9 +61,75 @@ export const SupabaseBlock: BlockConfig = { placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}', condition: { field: 'operation', value: 'insert' }, }, + { + id: 'data', + title: 'Data', + type: 'code', + layout: 'full', + placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}', + condition: { field: 'operation', value: 'update' }, + }, + // Filter for get_row, update, delete operations (required) + { + id: 'filter', + title: 'Filter (PostgREST syntax)', + type: 'short-input', + layout: 'full', + placeholder: 'id=eq.123', + condition: { field: 'operation', value: 'get_row' }, + }, + { + id: 'filter', + title: 'Filter (PostgREST syntax)', + type: 'short-input', + layout: 'full', + placeholder: 'id=eq.123', + condition: { field: 'operation', value: 'update' }, + }, + { + id: 'filter', + title: 'Filter (PostgREST syntax)', + type: 'short-input', + layout: 'full', + placeholder: 'id=eq.123', + condition: { field: 'operation', value: 'delete' }, + }, + // Optional filter for query operation + { + id: 'filter', + title: 'Filter (PostgREST syntax)', + type: 'short-input', + layout: 'full', + placeholder: 'status=eq.active', + condition: { field: 'operation', value: 'query' }, + }, + // Optional order by for query operation + { + id: 'orderBy', + title: 'Order By', + type: 'short-input', + layout: 'full', + placeholder: 'column_name (add DESC for descending)', + condition: { field: 'operation', value: 'query' }, + }, + // Optional limit for query operation + { + id: 'limit', + title: 'Limit', + type: 'short-input', + layout: 'full', + placeholder: '100', + condition: { field: 'operation', value: 'query' }, + }, ], tools: { - access: ['supabase_query', 'supabase_insert'], + access: [ + 'supabase_query', + 'supabase_insert', + 'supabase_get_row', + 'supabase_update', + 'supabase_delete', + ], config: { tool: (params) => { switch (params.operation) { @@ -63,29 +137,49 @@ export const SupabaseBlock: BlockConfig = { return 'supabase_query' case 'insert': return 'supabase_insert' + case 'get_row': + return 'supabase_get_row' + case 'update': + return 'supabase_update' + case 'delete': + return 'supabase_delete' default: throw new Error(`Invalid Supabase operation: ${params.operation}`) } }, params: (params) => { - const { data, ...rest } = params + const { operation, data, filter, ...rest } = params // Parse JSON data if it's a string let parsedData - if (data && typeof data === 'string') { + if (data && typeof data === 'string' && data.trim()) { try { parsedData = JSON.parse(data) } catch (_e) { throw new Error('Invalid JSON data format') } - } else { + } else if (data && typeof data === 'object') { parsedData = data } - return { - ...rest, - data: parsedData, + // Handle filter - just pass through PostgREST syntax + let parsedFilter + if (filter && typeof filter === 'string' && filter.trim()) { + parsedFilter = filter.trim() } + + // Build params object, only including defined values + const result = { ...rest } + + if (parsedData !== undefined) { + result.data = parsedData + } + + if (parsedFilter !== undefined && parsedFilter !== '') { + result.filter = parsedFilter + } + + return result }, }, }, @@ -94,8 +188,13 @@ export const SupabaseBlock: BlockConfig = { projectId: { type: 'string', required: true }, table: { type: 'string', required: true }, apiKey: { type: 'string', required: true }, - // Insert operation inputs - data: { type: 'string', required: false }, + // Data for insert/update operations + data: { type: 'json', required: false }, + // Filter for operations + filter: { type: 'string', required: false }, + // Query operation inputs + orderBy: { type: 'string', required: false }, + limit: { type: 'number', required: false }, }, outputs: { message: 'string', diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index eabee9419..37c9287e0 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -2,7 +2,7 @@ * Blocks Registry * */ -// Import all blocks directly here + import { AgentBlock } from '@/blocks/blocks/agent' import { AirtableBlock } from '@/blocks/blocks/airtable' import { ApiBlock } from '@/blocks/blocks/api' @@ -133,7 +133,6 @@ export const registry: Record = { youtube: YouTubeBlock, } -// Helper functions to access the registry export const getBlock = (type: string): BlockConfig | undefined => registry[type] export const getBlocksByCategory = (category: 'blocks' | 'tools' | 'triggers'): BlockConfig[] => diff --git a/apps/sim/lib/auth.ts b/apps/sim/lib/auth.ts index 7f1e682dc..08b23d70c 100644 --- a/apps/sim/lib/auth.ts +++ b/apps/sim/lib/auth.ts @@ -993,13 +993,14 @@ export const auth = betterAuth({ scopes: [ // Bot token scopes only - app acts as a bot user 'channels:read', + 'channels:history', 'groups:read', + 'groups:history', 'chat:write', 'chat:write.public', - 'files:read', - 'links:read', - 'links:write', 'users:read', + 'files:write', + 'canvases:write', ], responseType: 'code', accessType: 'offline', diff --git a/apps/sim/next.config.ts b/apps/sim/next.config.ts index 0e6cc1afa..34db4f543 100644 --- a/apps/sim/next.config.ts +++ b/apps/sim/next.config.ts @@ -140,7 +140,7 @@ const nextConfig: NextConfig = { }, { key: 'Content-Security-Policy', - value: `default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.google.com https://apis.google.com https://*.vercel-scripts.com https://*.vercel-insights.com https://vercel.live https://*.vercel.live https://vercel.com https://*.vercel.app https://vitals.vercel-insights.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https://*.googleusercontent.com https://*.google.com https://*.atlassian.com https://cdn.discordapp.com https://*.githubusercontent.com; media-src 'self' blob:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ${env.NEXT_PUBLIC_APP_URL || ''} ${env.OLLAMA_URL || 'http://localhost:11434'} ${env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3002'} ${env.NEXT_PUBLIC_SOCKET_URL?.replace('http://', 'ws://').replace('https://', 'wss://') || 'ws://localhost:3002'} https://*.up.railway.app wss://*.up.railway.app https://api.browser-use.com https://*.googleapis.com https://*.amazonaws.com https://*.s3.amazonaws.com https://*.blob.core.windows.net https://*.vercel-insights.com https://vitals.vercel-insights.com https://*.atlassian.com https://vercel.live https://*.vercel.live https://vercel.com https://*.vercel.app wss://*.vercel.app; frame-src https://drive.google.com https://*.google.com; frame-ancestors 'self'; form-action 'self'; base-uri 'self'; object-src 'none'`, + value: `default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.google.com https://apis.google.com https://*.vercel-scripts.com https://*.vercel-insights.com https://vercel.live https://*.vercel.live https://vercel.com https://*.vercel.app https://vitals.vercel-insights.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https://*.googleusercontent.com https://*.google.com https://*.atlassian.com https://cdn.discordapp.com https://*.githubusercontent.com; media-src 'self' blob:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' ${env.NEXT_PUBLIC_APP_URL || ''} ${env.OLLAMA_URL || 'http://localhost:11434'} ${env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3002'} ${env.NEXT_PUBLIC_SOCKET_URL?.replace('http://', 'ws://').replace('https://', 'wss://') || 'ws://localhost:3002'} https://*.up.railway.app wss://*.up.railway.app https://api.browser-use.com https://api.exa.ai https://api.firecrawl.dev https://*.googleapis.com https://*.amazonaws.com https://*.s3.amazonaws.com https://*.blob.core.windows.net https://*.vercel-insights.com https://vitals.vercel-insights.com https://*.atlassian.com https://*.supabase.co https://vercel.live https://*.vercel.live https://vercel.com https://*.vercel.app wss://*.vercel.app; frame-src https://drive.google.com https://*.google.com; frame-ancestors 'self'; form-action 'self'; base-uri 'self'; object-src 'none'`, }, ], }, diff --git a/apps/sim/tools/airtable/create_records.ts b/apps/sim/tools/airtable/create_records.ts index 163f8e247..7f7e8bee3 100644 --- a/apps/sim/tools/airtable/create_records.ts +++ b/apps/sim/tools/airtable/create_records.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { AirtableCreateParams, AirtableCreateResponse } from './types' +import type { AirtableCreateParams, AirtableCreateResponse } from '@/tools/airtable/types' +import type { ToolConfig } from '@/tools/types' export const airtableCreateRecordsTool: ToolConfig = { id: 'airtable_create_records', diff --git a/apps/sim/tools/airtable/get_record.ts b/apps/sim/tools/airtable/get_record.ts index 2323fc5da..a789575e8 100644 --- a/apps/sim/tools/airtable/get_record.ts +++ b/apps/sim/tools/airtable/get_record.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { AirtableGetParams, AirtableGetResponse } from './types' +import type { AirtableGetParams, AirtableGetResponse } from '@/tools/airtable/types' +import type { ToolConfig } from '@/tools/types' // import { logger } from '@/utils/logger' // Removed logger due to import issues diff --git a/apps/sim/tools/airtable/index.ts b/apps/sim/tools/airtable/index.ts index 9fe70e464..d2f022c03 100644 --- a/apps/sim/tools/airtable/index.ts +++ b/apps/sim/tools/airtable/index.ts @@ -1,8 +1,8 @@ -import { airtableCreateRecordsTool } from './create_records' -import { airtableGetRecordTool } from './get_record' -import { airtableListRecordsTool } from './list_records' -import { airtableUpdateMultipleRecordsTool } from './update_multiple_records' -import { airtableUpdateRecordTool } from './update_record' +import { airtableCreateRecordsTool } from '@/tools/airtable/create_records' +import { airtableGetRecordTool } from '@/tools/airtable/get_record' +import { airtableListRecordsTool } from '@/tools/airtable/list_records' +import { airtableUpdateMultipleRecordsTool } from '@/tools/airtable/update_multiple_records' +import { airtableUpdateRecordTool } from '@/tools/airtable/update_record' export { airtableCreateRecordsTool, diff --git a/apps/sim/tools/airtable/list_records.ts b/apps/sim/tools/airtable/list_records.ts index 15736fefd..6fd04a94a 100644 --- a/apps/sim/tools/airtable/list_records.ts +++ b/apps/sim/tools/airtable/list_records.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { AirtableListParams, AirtableListResponse } from './types' +import type { AirtableListParams, AirtableListResponse } from '@/tools/airtable/types' +import type { ToolConfig } from '@/tools/types' export const airtableListRecordsTool: ToolConfig = { id: 'airtable_list_records', diff --git a/apps/sim/tools/airtable/types.ts b/apps/sim/tools/airtable/types.ts index 3408fdee0..5c1265855 100644 --- a/apps/sim/tools/airtable/types.ts +++ b/apps/sim/tools/airtable/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' // Common types export interface AirtableRecord { diff --git a/apps/sim/tools/airtable/update_multiple_records.ts b/apps/sim/tools/airtable/update_multiple_records.ts index 9e754f6c3..3c00190c0 100644 --- a/apps/sim/tools/airtable/update_multiple_records.ts +++ b/apps/sim/tools/airtable/update_multiple_records.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { AirtableUpdateMultipleParams, AirtableUpdateMultipleResponse } from './types' +import type { + AirtableUpdateMultipleParams, + AirtableUpdateMultipleResponse, +} from '@/tools/airtable/types' +import type { ToolConfig } from '@/tools/types' // import { logger } from '@/utils/logger' // Removed logger due to import issues diff --git a/apps/sim/tools/airtable/update_record.ts b/apps/sim/tools/airtable/update_record.ts index 9cf443aa0..3f17cbb75 100644 --- a/apps/sim/tools/airtable/update_record.ts +++ b/apps/sim/tools/airtable/update_record.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { AirtableUpdateParams, AirtableUpdateResponse } from './types' +import type { AirtableUpdateParams, AirtableUpdateResponse } from '@/tools/airtable/types' +import type { ToolConfig } from '@/tools/types' // import { logger } from '@/utils/logger' // Removed logger due to import issues diff --git a/apps/sim/tools/browser_use/index.ts b/apps/sim/tools/browser_use/index.ts index dd87c2fbf..26f38d8b4 100644 --- a/apps/sim/tools/browser_use/index.ts +++ b/apps/sim/tools/browser_use/index.ts @@ -1,3 +1,3 @@ -import { runTaskTool } from './run_task' +import { runTaskTool } from '@/tools/browser_use/run_task' export const browserUseRunTaskTool = runTaskTool diff --git a/apps/sim/tools/browser_use/run_task.ts b/apps/sim/tools/browser_use/run_task.ts index fc8aacf88..fe13d215d 100644 --- a/apps/sim/tools/browser_use/run_task.ts +++ b/apps/sim/tools/browser_use/run_task.ts @@ -1,6 +1,6 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { BrowserUseRunTaskParams, BrowserUseRunTaskResponse } from './types' +import type { BrowserUseRunTaskParams, BrowserUseRunTaskResponse } from '@/tools/browser_use/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('BrowserUseTool') diff --git a/apps/sim/tools/browser_use/types.ts b/apps/sim/tools/browser_use/types.ts index 9c74108d3..293bcbfa7 100644 --- a/apps/sim/tools/browser_use/types.ts +++ b/apps/sim/tools/browser_use/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface BrowserUseRunTaskParams { task: string diff --git a/apps/sim/tools/clay/index.ts b/apps/sim/tools/clay/index.ts index 6a3796cd1..0d4d1b78c 100644 --- a/apps/sim/tools/clay/index.ts +++ b/apps/sim/tools/clay/index.ts @@ -1,3 +1,3 @@ -import { clayPopulateTool } from './populate' +import { clayPopulateTool } from '@/tools/clay/populate' export { clayPopulateTool } diff --git a/apps/sim/tools/clay/populate.ts b/apps/sim/tools/clay/populate.ts index e15ab64d2..4c5fe9372 100644 --- a/apps/sim/tools/clay/populate.ts +++ b/apps/sim/tools/clay/populate.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ClayPopulateParams, ClayPopulateResponse } from './types' +import type { ClayPopulateParams, ClayPopulateResponse } from '@/tools/clay/types' +import type { ToolConfig } from '@/tools/types' export const clayPopulateTool: ToolConfig = { id: 'clay_populate', diff --git a/apps/sim/tools/clay/types.ts b/apps/sim/tools/clay/types.ts index af38c9720..f5aeb2d55 100644 --- a/apps/sim/tools/clay/types.ts +++ b/apps/sim/tools/clay/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface ClayPopulateParams { webhookURL: string diff --git a/apps/sim/tools/confluence/index.ts b/apps/sim/tools/confluence/index.ts index 4d6173a30..97f01e249 100644 --- a/apps/sim/tools/confluence/index.ts +++ b/apps/sim/tools/confluence/index.ts @@ -1,5 +1,5 @@ -import { confluenceRetrieveTool } from './retrieve' -import { confluenceUpdateTool } from './update' +import { confluenceRetrieveTool } from '@/tools/confluence/retrieve' +import { confluenceUpdateTool } from '@/tools/confluence/update' export { confluenceRetrieveTool } export { confluenceUpdateTool } diff --git a/apps/sim/tools/confluence/retrieve.ts b/apps/sim/tools/confluence/retrieve.ts index 8a7ff6939..192aeaacc 100644 --- a/apps/sim/tools/confluence/retrieve.ts +++ b/apps/sim/tools/confluence/retrieve.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ConfluenceRetrieveParams, ConfluenceRetrieveResponse } from './types' +import type { ConfluenceRetrieveParams, ConfluenceRetrieveResponse } from '@/tools/confluence/types' +import type { ToolConfig } from '@/tools/types' export const confluenceRetrieveTool: ToolConfig< ConfluenceRetrieveParams, diff --git a/apps/sim/tools/confluence/types.ts b/apps/sim/tools/confluence/types.ts index 9c71b1e8b..617e31a47 100644 --- a/apps/sim/tools/confluence/types.ts +++ b/apps/sim/tools/confluence/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface ConfluenceRetrieveParams { accessToken: string diff --git a/apps/sim/tools/confluence/update.ts b/apps/sim/tools/confluence/update.ts index 14ba0feb1..4e5e3f95a 100644 --- a/apps/sim/tools/confluence/update.ts +++ b/apps/sim/tools/confluence/update.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ConfluenceUpdateParams, ConfluenceUpdateResponse } from './types' +import type { ConfluenceUpdateParams, ConfluenceUpdateResponse } from '@/tools/confluence/types' +import type { ToolConfig } from '@/tools/types' export const confluenceUpdateTool: ToolConfig = { id: 'confluence_update', diff --git a/apps/sim/tools/discord/get_messages.ts b/apps/sim/tools/discord/get_messages.ts index 9b2ba0d11..51e81b0bb 100644 --- a/apps/sim/tools/discord/get_messages.ts +++ b/apps/sim/tools/discord/get_messages.ts @@ -1,11 +1,11 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' import type { DiscordAPIError, DiscordGetMessagesParams, DiscordGetMessagesResponse, DiscordMessage, -} from './types' +} from '@/tools/discord/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('DiscordGetMessages') diff --git a/apps/sim/tools/discord/get_server.ts b/apps/sim/tools/discord/get_server.ts index 096850ff4..274478ca9 100644 --- a/apps/sim/tools/discord/get_server.ts +++ b/apps/sim/tools/discord/get_server.ts @@ -1,11 +1,11 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' import type { DiscordAPIError, DiscordGetServerParams, DiscordGetServerResponse, DiscordGuild, -} from './types' +} from '@/tools/discord/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('DiscordGetServer') diff --git a/apps/sim/tools/discord/get_user.ts b/apps/sim/tools/discord/get_user.ts index b8d9fa144..917bc6ebb 100644 --- a/apps/sim/tools/discord/get_user.ts +++ b/apps/sim/tools/discord/get_user.ts @@ -1,11 +1,11 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' import type { DiscordAPIError, DiscordGetUserParams, DiscordGetUserResponse, DiscordUser, -} from './types' +} from '@/tools/discord/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('DiscordGetUser') diff --git a/apps/sim/tools/discord/index.ts b/apps/sim/tools/discord/index.ts index f0816e2aa..ec9e99234 100644 --- a/apps/sim/tools/discord/index.ts +++ b/apps/sim/tools/discord/index.ts @@ -1,6 +1,6 @@ -import { discordGetMessagesTool } from './get_messages' -import { discordGetServerTool } from './get_server' -import { discordGetUserTool } from './get_user' -import { discordSendMessageTool } from './send_message' +import { discordGetMessagesTool } from '@/tools/discord/get_messages' +import { discordGetServerTool } from '@/tools/discord/get_server' +import { discordGetUserTool } from '@/tools/discord/get_user' +import { discordSendMessageTool } from '@/tools/discord/send_message' export { discordSendMessageTool, discordGetMessagesTool, discordGetServerTool, discordGetUserTool } diff --git a/apps/sim/tools/discord/send_message.ts b/apps/sim/tools/discord/send_message.ts index 5d2750143..756260f35 100644 --- a/apps/sim/tools/discord/send_message.ts +++ b/apps/sim/tools/discord/send_message.ts @@ -1,11 +1,11 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' import type { DiscordAPIError, DiscordMessage, DiscordSendMessageParams, DiscordSendMessageResponse, -} from './types' +} from '@/tools/discord/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('DiscordSendMessage') diff --git a/apps/sim/tools/docs/search.ts b/apps/sim/tools/docs/search.ts index 1ccbaf95c..fc94d08ba 100644 --- a/apps/sim/tools/docs/search.ts +++ b/apps/sim/tools/docs/search.ts @@ -1,4 +1,4 @@ -import type { ToolConfig, ToolResponse } from '../types' +import type { ToolConfig, ToolResponse } from '@/tools/types' interface DocsSearchParams { query: string diff --git a/apps/sim/tools/elevenlabs/index.ts b/apps/sim/tools/elevenlabs/index.ts index 4469deb7e..fe92c04d7 100644 --- a/apps/sim/tools/elevenlabs/index.ts +++ b/apps/sim/tools/elevenlabs/index.ts @@ -1,3 +1,3 @@ -import { elevenLabsTtsTool } from './tts' +import { elevenLabsTtsTool } from '@/tools/elevenlabs/tts' export { elevenLabsTtsTool } diff --git a/apps/sim/tools/exa/answer.ts b/apps/sim/tools/exa/answer.ts index c3da9678a..78876ed28 100644 --- a/apps/sim/tools/exa/answer.ts +++ b/apps/sim/tools/exa/answer.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ExaAnswerParams, ExaAnswerResponse } from './types' +import type { ExaAnswerParams, ExaAnswerResponse } from '@/tools/exa/types' +import type { ToolConfig } from '@/tools/types' export const answerTool: ToolConfig = { id: 'exa_answer', diff --git a/apps/sim/tools/exa/find_similar_links.ts b/apps/sim/tools/exa/find_similar_links.ts index e153b6408..45aafe877 100644 --- a/apps/sim/tools/exa/find_similar_links.ts +++ b/apps/sim/tools/exa/find_similar_links.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ExaFindSimilarLinksParams, ExaFindSimilarLinksResponse } from './types' +import type { ExaFindSimilarLinksParams, ExaFindSimilarLinksResponse } from '@/tools/exa/types' +import type { ToolConfig } from '@/tools/types' export const findSimilarLinksTool: ToolConfig< ExaFindSimilarLinksParams, diff --git a/apps/sim/tools/exa/get_contents.ts b/apps/sim/tools/exa/get_contents.ts index 84d900218..67c552dbb 100644 --- a/apps/sim/tools/exa/get_contents.ts +++ b/apps/sim/tools/exa/get_contents.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ExaGetContentsParams, ExaGetContentsResponse } from './types' +import type { ExaGetContentsParams, ExaGetContentsResponse } from '@/tools/exa/types' +import type { ToolConfig } from '@/tools/types' export const getContentsTool: ToolConfig = { id: 'exa_get_contents', diff --git a/apps/sim/tools/exa/index.ts b/apps/sim/tools/exa/index.ts index 94b0107d7..b373dd13e 100644 --- a/apps/sim/tools/exa/index.ts +++ b/apps/sim/tools/exa/index.ts @@ -1,9 +1,11 @@ -import { answerTool } from './answer' -import { findSimilarLinksTool } from './find_similar_links' -import { getContentsTool } from './get_contents' -import { searchTool } from './search' +import { answerTool } from '@/tools/exa/answer' +import { findSimilarLinksTool } from '@/tools/exa/find_similar_links' +import { getContentsTool } from '@/tools/exa/get_contents' +import { researchTool } from '@/tools/exa/research' +import { searchTool } from '@/tools/exa/search' export const exaAnswerTool = answerTool export const exaFindSimilarLinksTool = findSimilarLinksTool export const exaGetContentsTool = getContentsTool export const exaSearchTool = searchTool +export const exaResearchTool = researchTool diff --git a/apps/sim/tools/exa/research.ts b/apps/sim/tools/exa/research.ts new file mode 100644 index 000000000..0ac57b2d2 --- /dev/null +++ b/apps/sim/tools/exa/research.ts @@ -0,0 +1,177 @@ +import { createLogger } from '@/lib/logs/console-logger' +import type { ExaResearchParams, ExaResearchResponse } from '@/tools/exa/types' +import type { ToolConfig } from '@/tools/types' + +const logger = createLogger('ExaResearchTool') + +const POLL_INTERVAL_MS = 5000 // 5 seconds between polls +const MAX_POLL_TIME_MS = 300000 // 5 minutes maximum polling time + +export const researchTool: ToolConfig = { + id: 'exa_research', + name: 'Exa Research', + description: + 'Perform comprehensive research using AI to generate detailed reports with citations', + version: '1.0.0', + params: { + query: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Research query or topic', + }, + includeText: { + type: 'boolean', + required: false, + visibility: 'user-only', + description: 'Include full text content in results', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Exa AI API Key', + }, + }, + request: { + url: 'https://api.exa.ai/research/v0/tasks', + method: 'POST', + isInternalRoute: false, + headers: (params) => ({ + 'Content-Type': 'application/json', + 'x-api-key': params.apiKey, + }), + body: (params) => { + const body: any = { + instructions: params.query, + model: 'exa-research', + output: { + schema: { + type: 'object', + properties: { + results: { + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + url: { type: 'string' }, + summary: { type: 'string' }, + text: { type: 'string' }, + publishedDate: { type: 'string' }, + author: { type: 'string' }, + score: { type: 'number' }, + }, + }, + }, + }, + required: ['results'], + }, + }, + } + + return body + }, + }, + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.message || data.error || 'Failed to create research task') + } + + return { + success: true, + output: { + taskId: data.id, + research: [], + }, + } + }, + postProcess: async (result, params) => { + if (!result.success) { + return result + } + + const taskId = result.output.taskId + logger.info(`Exa research task ${taskId} created, polling for completion...`) + + let elapsedTime = 0 + + while (elapsedTime < MAX_POLL_TIME_MS) { + try { + const statusResponse = await fetch(`https://api.exa.ai/research/v0/tasks/${taskId}`, { + method: 'GET', + headers: { + 'x-api-key': params.apiKey, + }, + }) + + if (!statusResponse.ok) { + throw new Error(`Failed to get task status: ${statusResponse.statusText}`) + } + + const taskData = await statusResponse.json() + logger.info(`Exa research task ${taskId} status: ${taskData.status}`) + + if (taskData.status === 'completed') { + result.output = { + research: taskData.data?.results || [ + { + title: 'Research Complete', + url: '', + summary: taskData.data || 'Research completed successfully', + text: undefined, + publishedDate: undefined, + author: undefined, + score: 1.0, + }, + ], + } + return result + } + + if (taskData.status === 'failed') { + return { + ...result, + success: false, + error: `Research task failed: ${taskData.error || 'Unknown error'}`, + } + } + + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)) + elapsedTime += POLL_INTERVAL_MS + } catch (error: any) { + logger.error('Error polling for research task status:', { + message: error.message || 'Unknown error', + taskId, + }) + + return { + ...result, + success: false, + error: `Error polling for research task status: ${error.message || 'Unknown error'}`, + } + } + } + + logger.warn( + `Research task ${taskId} did not complete within the maximum polling time (${MAX_POLL_TIME_MS / 1000}s)` + ) + return { + ...result, + success: false, + error: `Research task did not complete within the maximum polling time (${MAX_POLL_TIME_MS / 1000}s)`, + } + }, + transformError: (error) => { + const errorMessage = error?.message || '' + if (errorMessage.includes('401')) { + return new Error('Invalid API key. Please check your Exa AI API key.') + } + if (errorMessage.includes('429')) { + return new Error('Rate limit exceeded. Please try again later.') + } + return error + }, +} diff --git a/apps/sim/tools/exa/search.ts b/apps/sim/tools/exa/search.ts index d152ef642..76fa3f5e1 100644 --- a/apps/sim/tools/exa/search.ts +++ b/apps/sim/tools/exa/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ExaSearchParams, ExaSearchResponse } from './types' +import type { ExaSearchParams, ExaSearchResponse } from '@/tools/exa/types' +import type { ToolConfig } from '@/tools/types' export const searchTool: ToolConfig = { id: 'exa_search', diff --git a/apps/sim/tools/exa/types.ts b/apps/sim/tools/exa/types.ts index 8a675ef55..09106457e 100644 --- a/apps/sim/tools/exa/types.ts +++ b/apps/sim/tools/exa/types.ts @@ -1,5 +1,5 @@ // Common types for Exa AI tools -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' // Common parameters for all Exa AI tools export interface ExaBaseParams { @@ -89,8 +89,30 @@ export interface ExaAnswerResponse extends ToolResponse { } } +// Research tool types +export interface ExaResearchParams extends ExaBaseParams { + query: string + includeText?: boolean +} + +export interface ExaResearchResponse extends ToolResponse { + output: { + taskId?: string + research: { + title: string + url: string + summary: string + text?: string + publishedDate?: string + author?: string + score: number + }[] + } +} + export type ExaResponse = | ExaSearchResponse | ExaGetContentsResponse | ExaFindSimilarLinksResponse | ExaAnswerResponse + | ExaResearchResponse diff --git a/apps/sim/tools/file/index.ts b/apps/sim/tools/file/index.ts index 855c19b50..d6b6372e0 100644 --- a/apps/sim/tools/file/index.ts +++ b/apps/sim/tools/file/index.ts @@ -1,3 +1,3 @@ -import { fileParserTool } from './parser' +import { fileParserTool } from '@/tools/file/parser' export const fileParseTool = fileParserTool diff --git a/apps/sim/tools/file/parser.ts b/apps/sim/tools/file/parser.ts index a673f0c80..e3be52e3c 100644 --- a/apps/sim/tools/file/parser.ts +++ b/apps/sim/tools/file/parser.ts @@ -1,11 +1,11 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' import type { FileParseResult, FileParserInput, FileParserOutput, FileParserOutputData, -} from './types' +} from '@/tools/file/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('FileParserTool') diff --git a/apps/sim/tools/file/types.ts b/apps/sim/tools/file/types.ts index 3f060379f..e411d5611 100644 --- a/apps/sim/tools/file/types.ts +++ b/apps/sim/tools/file/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface FileParserInput { filePath: string | string[] diff --git a/apps/sim/tools/firecrawl/crawl.ts b/apps/sim/tools/firecrawl/crawl.ts new file mode 100644 index 000000000..c0d4a684e --- /dev/null +++ b/apps/sim/tools/firecrawl/crawl.ts @@ -0,0 +1,156 @@ +import { createLogger } from '@/lib/logs/console-logger' +import type { FirecrawlCrawlParams, FirecrawlCrawlResponse } from '@/tools/firecrawl/types' +import type { ToolConfig } from '@/tools/types' + +const logger = createLogger('FirecrawlCrawlTool') + +const POLL_INTERVAL_MS = 5000 // 5 seconds between polls +const MAX_POLL_TIME_MS = 300000 // 5 minutes maximum polling time + +export const crawlTool: ToolConfig = { + id: 'firecrawl_crawl', + name: 'Firecrawl Crawl', + description: 'Crawl entire websites and extract structured content from all accessible pages', + version: '1.0.0', + params: { + url: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The website URL to crawl', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Maximum number of pages to crawl (default: 100)', + }, + onlyMainContent: { + type: 'boolean', + required: false, + visibility: 'user-only', + description: 'Extract only main content from pages', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Firecrawl API Key', + }, + }, + request: { + url: 'https://api.firecrawl.dev/v1/crawl', + method: 'POST', + isInternalRoute: false, + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => ({ + url: params.url, + limit: Number(params.limit) || 100, + scrapeOptions: { + formats: ['markdown'], + onlyMainContent: params.onlyMainContent || false, + }, + }), + }, + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error || data.message || 'Failed to create crawl job') + } + + return { + success: true, + output: { + jobId: data.jobId || data.id, + pages: [], + total: 0, + creditsUsed: 0, + }, + } + }, + postProcess: async (result, params) => { + if (!result.success) { + return result + } + + const jobId = result.output.jobId + logger.info(`Firecrawl crawl job ${jobId} created, polling for completion...`) + + let elapsedTime = 0 + + while (elapsedTime < MAX_POLL_TIME_MS) { + try { + const statusResponse = await fetch(`/api/tools/firecrawl/crawl/${jobId}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${params.apiKey}`, + }, + }) + + if (!statusResponse.ok) { + throw new Error(`Failed to get crawl status: ${statusResponse.statusText}`) + } + + const crawlData = await statusResponse.json() + logger.info(`Firecrawl crawl job ${jobId} status: ${crawlData.status}`) + + if (crawlData.status === 'completed') { + result.output = { + pages: crawlData.data || [], + total: crawlData.total || 0, + creditsUsed: crawlData.creditsUsed || 0, + } + return result + } + + if (crawlData.status === 'failed') { + return { + ...result, + success: false, + error: `Crawl job failed: ${crawlData.error || 'Unknown error'}`, + } + } + + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)) + elapsedTime += POLL_INTERVAL_MS + } catch (error: any) { + logger.error('Error polling for crawl job status:', { + message: error.message || 'Unknown error', + jobId, + }) + + return { + ...result, + success: false, + error: `Error polling for crawl job status: ${error.message || 'Unknown error'}`, + } + } + } + + logger.warn( + `Crawl job ${jobId} did not complete within the maximum polling time (${MAX_POLL_TIME_MS / 1000}s)` + ) + return { + ...result, + success: false, + error: `Crawl job did not complete within the maximum polling time (${MAX_POLL_TIME_MS / 1000}s)`, + } + }, + transformError: (error) => { + const errorMessage = error?.message || '' + if (errorMessage.includes('401')) { + return new Error('Invalid API key. Please check your Firecrawl API key.') + } + if (errorMessage.includes('429')) { + return new Error('Rate limit exceeded. Please try again later.') + } + if (errorMessage.includes('402')) { + return new Error('Insufficient credits. Please check your Firecrawl account.') + } + return error + }, +} diff --git a/apps/sim/tools/firecrawl/index.ts b/apps/sim/tools/firecrawl/index.ts index a8a60b7ff..c7c173f34 100644 --- a/apps/sim/tools/firecrawl/index.ts +++ b/apps/sim/tools/firecrawl/index.ts @@ -1,4 +1,5 @@ -import { scrapeTool } from './scrape' -import { searchTool } from './search' +import { crawlTool } from '@/tools/firecrawl/crawl' +import { scrapeTool } from '@/tools/firecrawl/scrape' +import { searchTool } from '@/tools/firecrawl/search' -export { scrapeTool, searchTool } +export { scrapeTool, searchTool, crawlTool } diff --git a/apps/sim/tools/firecrawl/scrape.ts b/apps/sim/tools/firecrawl/scrape.ts index 04392b5a0..7d8496ba6 100644 --- a/apps/sim/tools/firecrawl/scrape.ts +++ b/apps/sim/tools/firecrawl/scrape.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ScrapeParams, ScrapeResponse } from './types' +import type { ScrapeParams, ScrapeResponse } from '@/tools/firecrawl/types' +import type { ToolConfig } from '@/tools/types' export const scrapeTool: ToolConfig = { id: 'firecrawl_scrape', diff --git a/apps/sim/tools/firecrawl/search.ts b/apps/sim/tools/firecrawl/search.ts index a00aeefb3..cb5e47b0b 100644 --- a/apps/sim/tools/firecrawl/search.ts +++ b/apps/sim/tools/firecrawl/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { SearchParams, SearchResponse } from './types' +import type { SearchParams, SearchResponse } from '@/tools/firecrawl/types' +import type { ToolConfig } from '@/tools/types' export const searchTool: ToolConfig = { id: 'firecrawl_search', diff --git a/apps/sim/tools/firecrawl/types.ts b/apps/sim/tools/firecrawl/types.ts index 910f463ec..d61a26877 100644 --- a/apps/sim/tools/firecrawl/types.ts +++ b/apps/sim/tools/firecrawl/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface ScrapeParams { apiKey: string @@ -14,6 +14,13 @@ export interface SearchParams { query: string } +export interface FirecrawlCrawlParams { + apiKey: string + url: string + limit?: number + onlyMainContent?: boolean +} + export interface ScrapeResponse extends ToolResponse { output: { markdown: string @@ -59,4 +66,23 @@ export interface SearchResponse extends ToolResponse { } } -export type FirecrawlResponse = ScrapeResponse | SearchResponse +export interface FirecrawlCrawlResponse extends ToolResponse { + output: { + jobId?: string + pages: Array<{ + markdown: string + html?: string + metadata: { + title: string + description: string + language: string + sourceURL: string + statusCode: number + } + }> + total: number + creditsUsed: number + } +} + +export type FirecrawlResponse = ScrapeResponse | SearchResponse | FirecrawlCrawlResponse diff --git a/apps/sim/tools/function/execute.test.ts b/apps/sim/tools/function/execute.test.ts index 7318e7481..333086a37 100644 --- a/apps/sim/tools/function/execute.test.ts +++ b/apps/sim/tools/function/execute.test.ts @@ -7,8 +7,8 @@ * which runs JavaScript code in a secure sandbox. */ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' -import { ToolTester } from '../__test-utils__/test-tools' -import { functionExecuteTool } from './execute' +import { ToolTester } from '@/tools/__test-utils__/test-tools' +import { functionExecuteTool } from '@/tools/function/execute' describe('Function Execute Tool', () => { let tester: ToolTester diff --git a/apps/sim/tools/function/execute.ts b/apps/sim/tools/function/execute.ts index 8b405e0b8..b80282e61 100644 --- a/apps/sim/tools/function/execute.ts +++ b/apps/sim/tools/function/execute.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { CodeExecutionInput, CodeExecutionOutput } from './types' +import type { CodeExecutionInput, CodeExecutionOutput } from '@/tools/function/types' +import type { ToolConfig } from '@/tools/types' const DEFAULT_TIMEOUT = 10000 // 10 seconds diff --git a/apps/sim/tools/function/index.ts b/apps/sim/tools/function/index.ts index 9dc7bcbd9..3ba0a04c7 100644 --- a/apps/sim/tools/function/index.ts +++ b/apps/sim/tools/function/index.ts @@ -1,3 +1,3 @@ -import { functionExecuteTool } from './execute' +import { functionExecuteTool } from '@/tools/function/execute' export { functionExecuteTool } diff --git a/apps/sim/tools/function/types.ts b/apps/sim/tools/function/types.ts index 06e673d5e..70d3ef625 100644 --- a/apps/sim/tools/function/types.ts +++ b/apps/sim/tools/function/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface CodeExecutionInput { code: Array<{ content: string; id: string }> | string diff --git a/apps/sim/tools/github/comment.ts b/apps/sim/tools/github/comment.ts index 9128ddd2c..2dbe2e369 100644 --- a/apps/sim/tools/github/comment.ts +++ b/apps/sim/tools/github/comment.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { CreateCommentParams, CreateCommentResponse } from './types' +import type { CreateCommentParams, CreateCommentResponse } from '@/tools/github/types' +import type { ToolConfig } from '@/tools/types' export const commentTool: ToolConfig = { id: 'github_comment', diff --git a/apps/sim/tools/github/index.ts b/apps/sim/tools/github/index.ts index 9cfe81dfb..703c8ec6d 100644 --- a/apps/sim/tools/github/index.ts +++ b/apps/sim/tools/github/index.ts @@ -1,7 +1,7 @@ -import { commentTool } from './comment' -import { latestCommitTool } from './latest_commit' -import { prTool } from './pr' -import { repoInfoTool } from './repo_info' +import { commentTool } from '@/tools/github/comment' +import { latestCommitTool } from '@/tools/github/latest_commit' +import { prTool } from '@/tools/github/pr' +import { repoInfoTool } from '@/tools/github/repo_info' export const githubCommentTool = commentTool export const githubLatestCommitTool = latestCommitTool diff --git a/apps/sim/tools/github/latest_commit.ts b/apps/sim/tools/github/latest_commit.ts index 356f39405..bec7b0ded 100644 --- a/apps/sim/tools/github/latest_commit.ts +++ b/apps/sim/tools/github/latest_commit.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { LatestCommitParams, LatestCommitResponse } from './types' +import type { LatestCommitParams, LatestCommitResponse } from '@/tools/github/types' +import type { ToolConfig } from '@/tools/types' export const latestCommitTool: ToolConfig = { id: 'github_latest_commit', diff --git a/apps/sim/tools/github/pr.ts b/apps/sim/tools/github/pr.ts index 62b23f800..8bd884b1b 100644 --- a/apps/sim/tools/github/pr.ts +++ b/apps/sim/tools/github/pr.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { PROperationParams, PullRequestResponse } from './types' +import type { PROperationParams, PullRequestResponse } from '@/tools/github/types' +import type { ToolConfig } from '@/tools/types' export const prTool: ToolConfig = { id: 'github_pr', diff --git a/apps/sim/tools/github/repo_info.ts b/apps/sim/tools/github/repo_info.ts index af8e5c4cd..b52dd8017 100644 --- a/apps/sim/tools/github/repo_info.ts +++ b/apps/sim/tools/github/repo_info.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { BaseGitHubParams, RepoInfoResponse } from './types' +import type { BaseGitHubParams, RepoInfoResponse } from '@/tools/github/types' +import type { ToolConfig } from '@/tools/types' export const repoInfoTool: ToolConfig = { id: 'github_repo_info', diff --git a/apps/sim/tools/github/types.ts b/apps/sim/tools/github/types.ts index e70c1d8b2..7ed2bcb4b 100644 --- a/apps/sim/tools/github/types.ts +++ b/apps/sim/tools/github/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' // Base parameters shared by all GitHub operations export interface BaseGitHubParams { diff --git a/apps/sim/tools/gmail/draft.ts b/apps/sim/tools/gmail/draft.ts index e16117fb3..70c8bd053 100644 --- a/apps/sim/tools/gmail/draft.ts +++ b/apps/sim/tools/gmail/draft.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GmailSendParams, GmailToolResponse } from './types' +import type { GmailSendParams, GmailToolResponse } from '@/tools/gmail/types' +import type { ToolConfig } from '@/tools/types' const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me' diff --git a/apps/sim/tools/gmail/index.ts b/apps/sim/tools/gmail/index.ts index 3558d482c..110147cad 100644 --- a/apps/sim/tools/gmail/index.ts +++ b/apps/sim/tools/gmail/index.ts @@ -1,6 +1,6 @@ -import { gmailDraftTool } from './draft' -import { gmailReadTool } from './read' -import { gmailSearchTool } from './search' -import { gmailSendTool } from './send' +import { gmailDraftTool } from '@/tools/gmail/draft' +import { gmailReadTool } from '@/tools/gmail/read' +import { gmailSearchTool } from '@/tools/gmail/search' +import { gmailSendTool } from '@/tools/gmail/send' export { gmailSendTool, gmailReadTool, gmailSearchTool, gmailDraftTool } diff --git a/apps/sim/tools/gmail/read.ts b/apps/sim/tools/gmail/read.ts index 063361062..c2494a631 100644 --- a/apps/sim/tools/gmail/read.ts +++ b/apps/sim/tools/gmail/read.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GmailMessage, GmailReadParams, GmailToolResponse } from './types' +import type { GmailMessage, GmailReadParams, GmailToolResponse } from '@/tools/gmail/types' +import type { ToolConfig } from '@/tools/types' const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me' diff --git a/apps/sim/tools/gmail/search.ts b/apps/sim/tools/gmail/search.ts index d80dee57d..78c0130b9 100644 --- a/apps/sim/tools/gmail/search.ts +++ b/apps/sim/tools/gmail/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GmailSearchParams, GmailToolResponse } from './types' +import type { GmailSearchParams, GmailToolResponse } from '@/tools/gmail/types' +import type { ToolConfig } from '@/tools/types' const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me' diff --git a/apps/sim/tools/gmail/send.ts b/apps/sim/tools/gmail/send.ts index 498f82d62..ed18f9ecb 100644 --- a/apps/sim/tools/gmail/send.ts +++ b/apps/sim/tools/gmail/send.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GmailSendParams, GmailToolResponse } from './types' +import type { GmailSendParams, GmailToolResponse } from '@/tools/gmail/types' +import type { ToolConfig } from '@/tools/types' const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me' diff --git a/apps/sim/tools/gmail/types.ts b/apps/sim/tools/gmail/types.ts index f811d5a4d..944970107 100644 --- a/apps/sim/tools/gmail/types.ts +++ b/apps/sim/tools/gmail/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' // Base parameters shared by all operations interface BaseGmailParams { diff --git a/apps/sim/tools/google/index.ts b/apps/sim/tools/google/index.ts index 93b47653a..baa2a16f5 100644 --- a/apps/sim/tools/google/index.ts +++ b/apps/sim/tools/google/index.ts @@ -1,3 +1,3 @@ -import { searchTool } from './search' +import { searchTool } from '@/tools/google/search' export { searchTool } diff --git a/apps/sim/tools/google/search.ts b/apps/sim/tools/google/search.ts index 772cf25e6..7a870df84 100644 --- a/apps/sim/tools/google/search.ts +++ b/apps/sim/tools/google/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GoogleSearchParams, GoogleSearchResponse } from './types' +import type { GoogleSearchParams, GoogleSearchResponse } from '@/tools/google/types' +import type { ToolConfig } from '@/tools/types' export const searchTool: ToolConfig = { id: 'google_search', diff --git a/apps/sim/tools/google/types.ts b/apps/sim/tools/google/types.ts index 624b5c499..1f44f82e4 100644 --- a/apps/sim/tools/google/types.ts +++ b/apps/sim/tools/google/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface GoogleSearchParams { query: string diff --git a/apps/sim/tools/google_calendar/create.ts b/apps/sim/tools/google_calendar/create.ts index b36eff211..fe1f7d09d 100644 --- a/apps/sim/tools/google_calendar/create.ts +++ b/apps/sim/tools/google_calendar/create.ts @@ -1,11 +1,11 @@ -import type { ToolConfig } from '../types' import { CALENDAR_API_BASE, type GoogleCalendarApiEventResponse, type GoogleCalendarCreateParams, type GoogleCalendarCreateResponse, type GoogleCalendarEventRequestBody, -} from './types' +} from '@/tools/google_calendar/types' +import type { ToolConfig } from '@/tools/types' export const createTool: ToolConfig = { id: 'google_calendar_create', diff --git a/apps/sim/tools/google_calendar/get.ts b/apps/sim/tools/google_calendar/get.ts index 62070bf1e..da0223434 100644 --- a/apps/sim/tools/google_calendar/get.ts +++ b/apps/sim/tools/google_calendar/get.ts @@ -1,10 +1,10 @@ -import type { ToolConfig } from '../types' import { CALENDAR_API_BASE, type GoogleCalendarApiEventResponse, type GoogleCalendarGetParams, type GoogleCalendarGetResponse, -} from './types' +} from '@/tools/google_calendar/types' +import type { ToolConfig } from '@/tools/types' export const getTool: ToolConfig = { id: 'google_calendar_get', diff --git a/apps/sim/tools/google_calendar/index.ts b/apps/sim/tools/google_calendar/index.ts index b6564bff0..2cc7ab2a8 100644 --- a/apps/sim/tools/google_calendar/index.ts +++ b/apps/sim/tools/google_calendar/index.ts @@ -1,8 +1,8 @@ -import { createTool } from './create' -import { getTool } from './get' -import { inviteTool } from './invite' -import { listTool } from './list' -import { quickAddTool } from './quick_add' +import { createTool } from '@/tools/google_calendar/create' +import { getTool } from '@/tools/google_calendar/get' +import { inviteTool } from '@/tools/google_calendar/invite' +import { listTool } from '@/tools/google_calendar/list' +import { quickAddTool } from '@/tools/google_calendar/quick_add' export const googleCalendarCreateTool = createTool export const googleCalendarGetTool = getTool diff --git a/apps/sim/tools/google_calendar/invite.ts b/apps/sim/tools/google_calendar/invite.ts index baaec7bb9..08891c531 100644 --- a/apps/sim/tools/google_calendar/invite.ts +++ b/apps/sim/tools/google_calendar/invite.ts @@ -1,9 +1,9 @@ -import type { ToolConfig } from '../types' import { CALENDAR_API_BASE, type GoogleCalendarInviteParams, type GoogleCalendarInviteResponse, -} from './types' +} from '@/tools/google_calendar/types' +import type { ToolConfig } from '@/tools/types' export const inviteTool: ToolConfig = { id: 'google_calendar_invite', diff --git a/apps/sim/tools/google_calendar/list.ts b/apps/sim/tools/google_calendar/list.ts index d454098f8..c4093efba 100644 --- a/apps/sim/tools/google_calendar/list.ts +++ b/apps/sim/tools/google_calendar/list.ts @@ -1,11 +1,11 @@ -import type { ToolConfig } from '../types' import { CALENDAR_API_BASE, type GoogleCalendarApiEventResponse, type GoogleCalendarApiListResponse, type GoogleCalendarListParams, type GoogleCalendarListResponse, -} from './types' +} from '@/tools/google_calendar/types' +import type { ToolConfig } from '@/tools/types' export const listTool: ToolConfig = { id: 'google_calendar_list', diff --git a/apps/sim/tools/google_calendar/quick_add.ts b/apps/sim/tools/google_calendar/quick_add.ts index 46ec3de2b..fa2c62bed 100644 --- a/apps/sim/tools/google_calendar/quick_add.ts +++ b/apps/sim/tools/google_calendar/quick_add.ts @@ -1,9 +1,9 @@ -import type { ToolConfig } from '../types' import { CALENDAR_API_BASE, type GoogleCalendarQuickAddParams, type GoogleCalendarQuickAddResponse, -} from './types' +} from '@/tools/google_calendar/types' +import type { ToolConfig } from '@/tools/types' export const quickAddTool: ToolConfig< GoogleCalendarQuickAddParams, diff --git a/apps/sim/tools/google_calendar/types.ts b/apps/sim/tools/google_calendar/types.ts index 6d43bdecd..c6e535259 100644 --- a/apps/sim/tools/google_calendar/types.ts +++ b/apps/sim/tools/google_calendar/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export const CALENDAR_API_BASE = 'https://www.googleapis.com/calendar/v3' diff --git a/apps/sim/tools/google_calendar/update.ts b/apps/sim/tools/google_calendar/update.ts index 4acf35ded..ea2a8bd3b 100644 --- a/apps/sim/tools/google_calendar/update.ts +++ b/apps/sim/tools/google_calendar/update.ts @@ -1,9 +1,9 @@ -import type { ToolConfig } from '../types' import { CALENDAR_API_BASE, type GoogleCalendarToolResponse, type GoogleCalendarUpdateParams, -} from './types' +} from '@/tools/google_calendar/types' +import type { ToolConfig } from '@/tools/types' export const updateTool: ToolConfig = { id: 'google_calendar_update', diff --git a/apps/sim/tools/google_docs/create.ts b/apps/sim/tools/google_docs/create.ts index 4d2eef2c4..fb6c4f3b2 100644 --- a/apps/sim/tools/google_docs/create.ts +++ b/apps/sim/tools/google_docs/create.ts @@ -1,6 +1,6 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { GoogleDocsCreateResponse, GoogleDocsToolParams } from './types' +import type { GoogleDocsCreateResponse, GoogleDocsToolParams } from '@/tools/google_docs/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('GoogleDocsCreateTool') diff --git a/apps/sim/tools/google_docs/index.ts b/apps/sim/tools/google_docs/index.ts index 9368fe720..4b13d9b27 100644 --- a/apps/sim/tools/google_docs/index.ts +++ b/apps/sim/tools/google_docs/index.ts @@ -1,6 +1,6 @@ -import { createTool } from './create' -import { readTool } from './read' -import { writeTool } from './write' +import { createTool } from '@/tools/google_docs/create' +import { readTool } from '@/tools/google_docs/read' +import { writeTool } from '@/tools/google_docs/write' export const googleDocsReadTool = readTool export const googleDocsWriteTool = writeTool diff --git a/apps/sim/tools/google_docs/read.ts b/apps/sim/tools/google_docs/read.ts index 3bb76d037..02bfbc3d9 100644 --- a/apps/sim/tools/google_docs/read.ts +++ b/apps/sim/tools/google_docs/read.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GoogleDocsReadResponse, GoogleDocsToolParams } from './types' +import type { GoogleDocsReadResponse, GoogleDocsToolParams } from '@/tools/google_docs/types' +import type { ToolConfig } from '@/tools/types' export const readTool: ToolConfig = { id: 'google_docs_read', diff --git a/apps/sim/tools/google_docs/types.ts b/apps/sim/tools/google_docs/types.ts index 44f484b68..9d0aa201a 100644 --- a/apps/sim/tools/google_docs/types.ts +++ b/apps/sim/tools/google_docs/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface GoogleDocsMetadata { documentId: string diff --git a/apps/sim/tools/google_docs/write.ts b/apps/sim/tools/google_docs/write.ts index 0adfca4e1..33fa654b2 100644 --- a/apps/sim/tools/google_docs/write.ts +++ b/apps/sim/tools/google_docs/write.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GoogleDocsToolParams, GoogleDocsWriteResponse } from './types' +import type { GoogleDocsToolParams, GoogleDocsWriteResponse } from '@/tools/google_docs/types' +import type { ToolConfig } from '@/tools/types' export const writeTool: ToolConfig = { id: 'google_docs_write', diff --git a/apps/sim/tools/google_drive/create_folder.ts b/apps/sim/tools/google_drive/create_folder.ts index da001436c..fca7624ce 100644 --- a/apps/sim/tools/google_drive/create_folder.ts +++ b/apps/sim/tools/google_drive/create_folder.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GoogleDriveToolParams, GoogleDriveUploadResponse } from './types' +import type { GoogleDriveToolParams, GoogleDriveUploadResponse } from '@/tools/google_drive/types' +import type { ToolConfig } from '@/tools/types' export const createFolderTool: ToolConfig = { id: 'google_drive_create_folder', diff --git a/apps/sim/tools/google_drive/get_content.ts b/apps/sim/tools/google_drive/get_content.ts index 21d5cbec3..50af9a182 100644 --- a/apps/sim/tools/google_drive/get_content.ts +++ b/apps/sim/tools/google_drive/get_content.ts @@ -1,7 +1,10 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { GoogleDriveGetContentResponse, GoogleDriveToolParams } from './types' -import { DEFAULT_EXPORT_FORMATS, GOOGLE_WORKSPACE_MIME_TYPES } from './utils' +import type { + GoogleDriveGetContentResponse, + GoogleDriveToolParams, +} from '@/tools/google_drive/types' +import { DEFAULT_EXPORT_FORMATS, GOOGLE_WORKSPACE_MIME_TYPES } from '@/tools/google_drive/utils' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('GoogleDriveGetContentTool') diff --git a/apps/sim/tools/google_drive/index.ts b/apps/sim/tools/google_drive/index.ts index e6a365224..634223ccd 100644 --- a/apps/sim/tools/google_drive/index.ts +++ b/apps/sim/tools/google_drive/index.ts @@ -1,7 +1,7 @@ -import { createFolderTool } from './create_folder' -import { getContentTool } from './get_content' -import { listTool } from './list' -import { uploadTool } from './upload' +import { createFolderTool } from '@/tools/google_drive/create_folder' +import { getContentTool } from '@/tools/google_drive/get_content' +import { listTool } from '@/tools/google_drive/list' +import { uploadTool } from '@/tools/google_drive/upload' export const googleDriveCreateFolderTool = createFolderTool export const googleDriveGetContentTool = getContentTool diff --git a/apps/sim/tools/google_drive/list.ts b/apps/sim/tools/google_drive/list.ts index d2377ced9..70a138a97 100644 --- a/apps/sim/tools/google_drive/list.ts +++ b/apps/sim/tools/google_drive/list.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GoogleDriveListResponse, GoogleDriveToolParams } from './types' +import type { GoogleDriveListResponse, GoogleDriveToolParams } from '@/tools/google_drive/types' +import type { ToolConfig } from '@/tools/types' export const listTool: ToolConfig = { id: 'google_drive_list', diff --git a/apps/sim/tools/google_drive/types.ts b/apps/sim/tools/google_drive/types.ts index cdbea5c04..e497781a0 100644 --- a/apps/sim/tools/google_drive/types.ts +++ b/apps/sim/tools/google_drive/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface GoogleDriveFile { id: string diff --git a/apps/sim/tools/google_drive/upload.ts b/apps/sim/tools/google_drive/upload.ts index b69b9f00a..2a5e5562e 100644 --- a/apps/sim/tools/google_drive/upload.ts +++ b/apps/sim/tools/google_drive/upload.ts @@ -1,7 +1,7 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { GoogleDriveToolParams, GoogleDriveUploadResponse } from './types' -import { GOOGLE_WORKSPACE_MIME_TYPES, SOURCE_MIME_TYPES } from './utils' +import type { GoogleDriveToolParams, GoogleDriveUploadResponse } from '@/tools/google_drive/types' +import { GOOGLE_WORKSPACE_MIME_TYPES, SOURCE_MIME_TYPES } from '@/tools/google_drive/utils' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('GoogleDriveUploadTool') diff --git a/apps/sim/tools/google_sheets/append.ts b/apps/sim/tools/google_sheets/append.ts index 532f3d376..3a962d340 100644 --- a/apps/sim/tools/google_sheets/append.ts +++ b/apps/sim/tools/google_sheets/append.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { GoogleSheetsAppendResponse, GoogleSheetsToolParams } from './types' +import type { + GoogleSheetsAppendResponse, + GoogleSheetsToolParams, +} from '@/tools/google_sheets/types' +import type { ToolConfig } from '@/tools/types' export const appendTool: ToolConfig = { id: 'google_sheets_append', diff --git a/apps/sim/tools/google_sheets/index.ts b/apps/sim/tools/google_sheets/index.ts index e751103f5..a47ebe4c9 100644 --- a/apps/sim/tools/google_sheets/index.ts +++ b/apps/sim/tools/google_sheets/index.ts @@ -1,7 +1,7 @@ -import { appendTool } from './append' -import { readTool } from './read' -import { updateTool } from './update' -import { writeTool } from './write' +import { appendTool } from '@/tools/google_sheets/append' +import { readTool } from '@/tools/google_sheets/read' +import { updateTool } from '@/tools/google_sheets/update' +import { writeTool } from '@/tools/google_sheets/write' export const googleSheetsReadTool = readTool export const googleSheetsWriteTool = writeTool diff --git a/apps/sim/tools/google_sheets/read.ts b/apps/sim/tools/google_sheets/read.ts index 52d834ee2..4858391e5 100644 --- a/apps/sim/tools/google_sheets/read.ts +++ b/apps/sim/tools/google_sheets/read.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GoogleSheetsReadResponse, GoogleSheetsToolParams } from './types' +import type { GoogleSheetsReadResponse, GoogleSheetsToolParams } from '@/tools/google_sheets/types' +import type { ToolConfig } from '@/tools/types' export const readTool: ToolConfig = { id: 'google_sheets_read', diff --git a/apps/sim/tools/google_sheets/types.ts b/apps/sim/tools/google_sheets/types.ts index e7401a872..2efc612de 100644 --- a/apps/sim/tools/google_sheets/types.ts +++ b/apps/sim/tools/google_sheets/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface GoogleSheetsRange { sheetId?: number diff --git a/apps/sim/tools/google_sheets/update.ts b/apps/sim/tools/google_sheets/update.ts index 798c600fd..bc706f0b8 100644 --- a/apps/sim/tools/google_sheets/update.ts +++ b/apps/sim/tools/google_sheets/update.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { GoogleSheetsToolParams, GoogleSheetsUpdateResponse } from './types' +import type { + GoogleSheetsToolParams, + GoogleSheetsUpdateResponse, +} from '@/tools/google_sheets/types' +import type { ToolConfig } from '@/tools/types' export const updateTool: ToolConfig = { id: 'google_sheets_update', diff --git a/apps/sim/tools/google_sheets/write.ts b/apps/sim/tools/google_sheets/write.ts index 3c6e02c56..ec07aeb5b 100644 --- a/apps/sim/tools/google_sheets/write.ts +++ b/apps/sim/tools/google_sheets/write.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { GoogleSheetsToolParams, GoogleSheetsWriteResponse } from './types' +import type { GoogleSheetsToolParams, GoogleSheetsWriteResponse } from '@/tools/google_sheets/types' +import type { ToolConfig } from '@/tools/types' export const writeTool: ToolConfig = { id: 'google_sheets_write', diff --git a/apps/sim/tools/http/index.ts b/apps/sim/tools/http/index.ts index c3c680334..aa6045137 100644 --- a/apps/sim/tools/http/index.ts +++ b/apps/sim/tools/http/index.ts @@ -1,3 +1,3 @@ -import { requestTool } from './request' +import { requestTool } from '@/tools/http/request' export { requestTool } diff --git a/apps/sim/tools/http/request.test.ts b/apps/sim/tools/http/request.test.ts index ef9ce00f5..7cf77704d 100644 --- a/apps/sim/tools/http/request.test.ts +++ b/apps/sim/tools/http/request.test.ts @@ -7,9 +7,9 @@ * to make HTTP requests to external APIs and services. */ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' -import { mockHttpResponses } from '../__test-utils__/mock-data' -import { ToolTester } from '../__test-utils__/test-tools' -import { requestTool } from './request' +import { mockHttpResponses } from '@/tools/__test-utils__/mock-data' +import { ToolTester } from '@/tools/__test-utils__/test-tools' +import { requestTool } from '@/tools/http/request' process.env.VITEST = 'true' diff --git a/apps/sim/tools/http/request.ts b/apps/sim/tools/http/request.ts index 7d6e20e48..f132237a6 100644 --- a/apps/sim/tools/http/request.ts +++ b/apps/sim/tools/http/request.ts @@ -2,8 +2,8 @@ import { env } from '@/lib/env' import { isTest } from '@/lib/environment' import { createLogger } from '@/lib/logs/console-logger' import { getBaseUrl } from '@/lib/urls/utils' -import type { HttpMethod, TableRow, ToolConfig } from '../types' -import type { RequestParams, RequestResponse } from './types' +import type { RequestParams, RequestResponse } from '@/tools/http/types' +import type { HttpMethod, TableRow, ToolConfig } from '@/tools/types' const logger = createLogger('HTTPRequestTool') diff --git a/apps/sim/tools/http/types.ts b/apps/sim/tools/http/types.ts index 9bda757d4..68b455c6f 100644 --- a/apps/sim/tools/http/types.ts +++ b/apps/sim/tools/http/types.ts @@ -1,4 +1,4 @@ -import type { HttpMethod, TableRow, ToolResponse } from '../types' +import type { HttpMethod, TableRow, ToolResponse } from '@/tools/types' export interface RequestParams { url: string diff --git a/apps/sim/tools/huggingface/chat.ts b/apps/sim/tools/huggingface/chat.ts index dd6a01d92..82933c7ef 100644 --- a/apps/sim/tools/huggingface/chat.ts +++ b/apps/sim/tools/huggingface/chat.ts @@ -1,10 +1,10 @@ -import type { ToolConfig } from '../types' import type { HuggingFaceChatParams, HuggingFaceChatResponse, HuggingFaceMessage, HuggingFaceRequestBody, -} from './types' +} from '@/tools/huggingface/types' +import type { ToolConfig } from '@/tools/types' export const chatTool: ToolConfig = { id: 'huggingface_chat', diff --git a/apps/sim/tools/huggingface/index.ts b/apps/sim/tools/huggingface/index.ts index fdef0ced6..ea6f953ae 100644 --- a/apps/sim/tools/huggingface/index.ts +++ b/apps/sim/tools/huggingface/index.ts @@ -1,3 +1,3 @@ -import { chatTool } from './chat' +import { chatTool } from '@/tools/huggingface/chat' export const huggingfaceChatTool = chatTool diff --git a/apps/sim/tools/huggingface/types.ts b/apps/sim/tools/huggingface/types.ts index a3025bca8..96f8cee5f 100644 --- a/apps/sim/tools/huggingface/types.ts +++ b/apps/sim/tools/huggingface/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface HuggingFaceUsage { prompt_tokens: number diff --git a/apps/sim/tools/index.test.ts b/apps/sim/tools/index.test.ts index 32830425d..adb3c4afe 100644 --- a/apps/sim/tools/index.test.ts +++ b/apps/sim/tools/index.test.ts @@ -7,10 +7,10 @@ * which are the central pieces of infrastructure for executing tools. */ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' -import { mockEnvironmentVariables } from './__test-utils__/test-tools' -import { executeTool } from './index' -import { tools } from './registry' -import { getTool } from './utils' +import { mockEnvironmentVariables } from '@/tools/__test-utils__/test-tools' +import { executeTool } from '@/tools/index' +import { tools } from '@/tools/registry' +import { getTool } from '@/tools/utils' describe('Tools Registry', () => { test('should include all expected built-in tools', () => { diff --git a/apps/sim/tools/index.ts b/apps/sim/tools/index.ts index 3e4560553..d232526bc 100644 --- a/apps/sim/tools/index.ts +++ b/apps/sim/tools/index.ts @@ -1,7 +1,7 @@ import { createLogger } from '@/lib/logs/console-logger' import { getBaseUrl } from '@/lib/urls/utils' -import type { OAuthTokenPayload, ToolConfig, ToolResponse } from './types' -import { formatRequestParams, getTool, getToolAsync, validateToolRequest } from './utils' +import type { OAuthTokenPayload, ToolConfig, ToolResponse } from '@/tools/types' +import { formatRequestParams, getTool, getToolAsync, validateToolRequest } from '@/tools/utils' const logger = createLogger('Tools') diff --git a/apps/sim/tools/jina/index.ts b/apps/sim/tools/jina/index.ts index 1e2e34fa8..3ea91425d 100644 --- a/apps/sim/tools/jina/index.ts +++ b/apps/sim/tools/jina/index.ts @@ -1,3 +1,3 @@ -import { readUrlTool } from './read_url' +import { readUrlTool } from '@/tools/jina/read_url' export { readUrlTool } diff --git a/apps/sim/tools/jina/read_url.ts b/apps/sim/tools/jina/read_url.ts index c4dc6a6d8..8b2169961 100644 --- a/apps/sim/tools/jina/read_url.ts +++ b/apps/sim/tools/jina/read_url.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ReadUrlParams, ReadUrlResponse } from './types' +import type { ReadUrlParams, ReadUrlResponse } from '@/tools/jina/types' +import type { ToolConfig } from '@/tools/types' export const readUrlTool: ToolConfig = { id: 'jina_read_url', diff --git a/apps/sim/tools/jina/types.ts b/apps/sim/tools/jina/types.ts index 08efdbbdd..a76330e17 100644 --- a/apps/sim/tools/jina/types.ts +++ b/apps/sim/tools/jina/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface ReadUrlParams { url: string diff --git a/apps/sim/tools/jira/bulk_read.ts b/apps/sim/tools/jira/bulk_read.ts index 5ee41cf17..09622a531 100644 --- a/apps/sim/tools/jira/bulk_read.ts +++ b/apps/sim/tools/jira/bulk_read.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { JiraRetrieveBulkParams, JiraRetrieveResponseBulk } from './types' +import type { JiraRetrieveBulkParams, JiraRetrieveResponseBulk } from '@/tools/jira/types' +import type { ToolConfig } from '@/tools/types' export const jiraBulkRetrieveTool: ToolConfig = { id: 'jira_bulk_read', diff --git a/apps/sim/tools/jira/index.ts b/apps/sim/tools/jira/index.ts index 6c7c88866..918f6be59 100644 --- a/apps/sim/tools/jira/index.ts +++ b/apps/sim/tools/jira/index.ts @@ -1,7 +1,7 @@ -import { jiraBulkRetrieveTool } from './bulk_read' -import { jiraRetrieveTool } from './retrieve' -import { jiraUpdateTool } from './update' -import { jiraWriteTool } from './write' +import { jiraBulkRetrieveTool } from '@/tools/jira/bulk_read' +import { jiraRetrieveTool } from '@/tools/jira/retrieve' +import { jiraUpdateTool } from '@/tools/jira/update' +import { jiraWriteTool } from '@/tools/jira/write' export { jiraRetrieveTool } export { jiraUpdateTool } diff --git a/apps/sim/tools/jira/retrieve.ts b/apps/sim/tools/jira/retrieve.ts index 6713cb65f..cb18626cb 100644 --- a/apps/sim/tools/jira/retrieve.ts +++ b/apps/sim/tools/jira/retrieve.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { JiraRetrieveParams, JiraRetrieveResponse } from './types' +import type { JiraRetrieveParams, JiraRetrieveResponse } from '@/tools/jira/types' +import type { ToolConfig } from '@/tools/types' export const jiraRetrieveTool: ToolConfig = { id: 'jira_retrieve', diff --git a/apps/sim/tools/jira/types.ts b/apps/sim/tools/jira/types.ts index 76d024b28..4f140ec6b 100644 --- a/apps/sim/tools/jira/types.ts +++ b/apps/sim/tools/jira/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface JiraRetrieveParams { accessToken: string diff --git a/apps/sim/tools/jira/update.ts b/apps/sim/tools/jira/update.ts index 75b09a5b2..607ee26a4 100644 --- a/apps/sim/tools/jira/update.ts +++ b/apps/sim/tools/jira/update.ts @@ -1,6 +1,6 @@ -import type { ToolConfig } from '../types' -import type { JiraUpdateParams, JiraUpdateResponse } from './types' -import { getJiraCloudId } from './utils' +import type { JiraUpdateParams, JiraUpdateResponse } from '@/tools/jira/types' +import { getJiraCloudId } from '@/tools/jira/utils' +import type { ToolConfig } from '@/tools/types' export const jiraUpdateTool: ToolConfig = { id: 'jira_update', diff --git a/apps/sim/tools/jira/write.ts b/apps/sim/tools/jira/write.ts index 137864933..563b39f5e 100644 --- a/apps/sim/tools/jira/write.ts +++ b/apps/sim/tools/jira/write.ts @@ -1,6 +1,6 @@ -import type { ToolConfig } from '../types' -import type { JiraWriteParams, JiraWriteResponse } from './types' -import { getJiraCloudId } from './utils' +import type { JiraWriteParams, JiraWriteResponse } from '@/tools/jira/types' +import { getJiraCloudId } from '@/tools/jira/utils' +import type { ToolConfig } from '@/tools/types' export const jiraWriteTool: ToolConfig = { id: 'jira_write', diff --git a/apps/sim/tools/knowledge/create_document.ts b/apps/sim/tools/knowledge/create_document.ts index cb1c6ad7d..f57235061 100644 --- a/apps/sim/tools/knowledge/create_document.ts +++ b/apps/sim/tools/knowledge/create_document.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { KnowledgeCreateDocumentResponse } from './types' +import type { KnowledgeCreateDocumentResponse } from '@/tools/knowledge/types' +import type { ToolConfig } from '@/tools/types' export const knowledgeCreateDocumentTool: ToolConfig = { id: 'knowledge_create_document', diff --git a/apps/sim/tools/knowledge/index.ts b/apps/sim/tools/knowledge/index.ts index 45514650f..6954b71d9 100644 --- a/apps/sim/tools/knowledge/index.ts +++ b/apps/sim/tools/knowledge/index.ts @@ -1,5 +1,5 @@ -import { knowledgeCreateDocumentTool } from './create_document' -import { knowledgeSearchTool } from './search' -import { knowledgeUploadChunkTool } from './upload_chunk' +import { knowledgeCreateDocumentTool } from '@/tools/knowledge/create_document' +import { knowledgeSearchTool } from '@/tools/knowledge/search' +import { knowledgeUploadChunkTool } from '@/tools/knowledge/upload_chunk' export { knowledgeSearchTool, knowledgeUploadChunkTool, knowledgeCreateDocumentTool } diff --git a/apps/sim/tools/knowledge/search.ts b/apps/sim/tools/knowledge/search.ts index f2d885e4f..73b0ca786 100644 --- a/apps/sim/tools/knowledge/search.ts +++ b/apps/sim/tools/knowledge/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { KnowledgeSearchResponse } from './types' +import type { KnowledgeSearchResponse } from '@/tools/knowledge/types' +import type { ToolConfig } from '@/tools/types' export const knowledgeSearchTool: ToolConfig = { id: 'knowledge_search', diff --git a/apps/sim/tools/knowledge/upload_chunk.ts b/apps/sim/tools/knowledge/upload_chunk.ts index 27d3c2e49..9a1f880a2 100644 --- a/apps/sim/tools/knowledge/upload_chunk.ts +++ b/apps/sim/tools/knowledge/upload_chunk.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { KnowledgeUploadChunkResponse } from './types' +import type { KnowledgeUploadChunkResponse } from '@/tools/knowledge/types' +import type { ToolConfig } from '@/tools/types' export const knowledgeUploadChunkTool: ToolConfig = { id: 'knowledge_upload_chunk', diff --git a/apps/sim/tools/linear/create_issue.ts b/apps/sim/tools/linear/create_issue.ts index 722efc831..8c5eb793f 100644 --- a/apps/sim/tools/linear/create_issue.ts +++ b/apps/sim/tools/linear/create_issue.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { LinearCreateIssueParams, LinearCreateIssueResponse } from './types' +import type { LinearCreateIssueParams, LinearCreateIssueResponse } from '@/tools/linear/types' +import type { ToolConfig } from '@/tools/types' export const linearCreateIssueTool: ToolConfig = { diff --git a/apps/sim/tools/linear/index.ts b/apps/sim/tools/linear/index.ts index 5726982ed..83ceb95ea 100644 --- a/apps/sim/tools/linear/index.ts +++ b/apps/sim/tools/linear/index.ts @@ -1,4 +1,4 @@ -import { linearCreateIssueTool } from './create_issue' -import { linearReadIssuesTool } from './read_issues' +import { linearCreateIssueTool } from '@/tools/linear/create_issue' +import { linearReadIssuesTool } from '@/tools/linear/read_issues' export { linearReadIssuesTool, linearCreateIssueTool } diff --git a/apps/sim/tools/linear/read_issues.ts b/apps/sim/tools/linear/read_issues.ts index a394f41f9..44de82ee3 100644 --- a/apps/sim/tools/linear/read_issues.ts +++ b/apps/sim/tools/linear/read_issues.ts @@ -1,5 +1,9 @@ -import type { ToolConfig } from '../types' -import type { LinearIssue, LinearReadIssuesParams, LinearReadIssuesResponse } from './types' +import type { + LinearIssue, + LinearReadIssuesParams, + LinearReadIssuesResponse, +} from '@/tools/linear/types' +import type { ToolConfig } from '@/tools/types' export const linearReadIssuesTool: ToolConfig = { id: 'linear_read_issues', diff --git a/apps/sim/tools/linear/types.ts b/apps/sim/tools/linear/types.ts index 71beefad4..b56e78d1f 100644 --- a/apps/sim/tools/linear/types.ts +++ b/apps/sim/tools/linear/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface LinearIssue { id: string diff --git a/apps/sim/tools/linkup/index.ts b/apps/sim/tools/linkup/index.ts index 694a53047..5203faaae 100644 --- a/apps/sim/tools/linkup/index.ts +++ b/apps/sim/tools/linkup/index.ts @@ -1,3 +1,3 @@ -import { searchTool } from './search' +import { searchTool } from '@/tools/linkup/search' export const linkupSearchTool = searchTool diff --git a/apps/sim/tools/linkup/search.ts b/apps/sim/tools/linkup/search.ts index 13cbfdf8b..ba79a381c 100644 --- a/apps/sim/tools/linkup/search.ts +++ b/apps/sim/tools/linkup/search.ts @@ -1,5 +1,9 @@ -import type { ToolConfig } from '../types' -import type { LinkupSearchParams, LinkupSearchResponse, LinkupSearchToolResponse } from './types' +import type { + LinkupSearchParams, + LinkupSearchResponse, + LinkupSearchToolResponse, +} from '@/tools/linkup/types' +import type { ToolConfig } from '@/tools/types' export const searchTool: ToolConfig = { id: 'linkup_search', diff --git a/apps/sim/tools/linkup/types.ts b/apps/sim/tools/linkup/types.ts index 6d78d0a8b..dff731ebf 100644 --- a/apps/sim/tools/linkup/types.ts +++ b/apps/sim/tools/linkup/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface LinkupSource { name: string diff --git a/apps/sim/tools/mem0/add_memories.ts b/apps/sim/tools/mem0/add_memories.ts index 39e018b03..13e23fa8d 100644 --- a/apps/sim/tools/mem0/add_memories.ts +++ b/apps/sim/tools/mem0/add_memories.ts @@ -1,4 +1,4 @@ -import type { ToolConfig } from '../types' +import type { ToolConfig } from '@/tools/types' // Add Memories Tool export const mem0AddMemoriesTool: ToolConfig = { diff --git a/apps/sim/tools/mem0/get_memories.ts b/apps/sim/tools/mem0/get_memories.ts index f2a0fa7a1..876686bc0 100644 --- a/apps/sim/tools/mem0/get_memories.ts +++ b/apps/sim/tools/mem0/get_memories.ts @@ -1,4 +1,4 @@ -import type { ToolConfig } from '../types' +import type { ToolConfig } from '@/tools/types' // Get Memories Tool export const mem0GetMemoriesTool: ToolConfig = { diff --git a/apps/sim/tools/mem0/index.ts b/apps/sim/tools/mem0/index.ts index 26b236862..01b331708 100644 --- a/apps/sim/tools/mem0/index.ts +++ b/apps/sim/tools/mem0/index.ts @@ -1,5 +1,5 @@ -import { mem0AddMemoriesTool } from './add_memories' -import { mem0GetMemoriesTool } from './get_memories' -import { mem0SearchMemoriesTool } from './search_memories' +import { mem0AddMemoriesTool } from '@/tools/mem0/add_memories' +import { mem0GetMemoriesTool } from '@/tools/mem0/get_memories' +import { mem0SearchMemoriesTool } from '@/tools/mem0/search_memories' export { mem0AddMemoriesTool, mem0SearchMemoriesTool, mem0GetMemoriesTool } diff --git a/apps/sim/tools/mem0/search_memories.ts b/apps/sim/tools/mem0/search_memories.ts index 40e0005d5..b91ba9e6b 100644 --- a/apps/sim/tools/mem0/search_memories.ts +++ b/apps/sim/tools/mem0/search_memories.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { Mem0Response } from './types' +import type { Mem0Response } from '@/tools/mem0/types' +import type { ToolConfig } from '@/tools/types' // Search Memories Tool export const mem0SearchMemoriesTool: ToolConfig = { diff --git a/apps/sim/tools/mem0/types.ts b/apps/sim/tools/mem0/types.ts index 2ed89b1d4..32bcc108d 100644 --- a/apps/sim/tools/mem0/types.ts +++ b/apps/sim/tools/mem0/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface Mem0Response extends ToolResponse { output: { diff --git a/apps/sim/tools/memory/add.ts b/apps/sim/tools/memory/add.ts index fb60cc310..140786c0c 100644 --- a/apps/sim/tools/memory/add.ts +++ b/apps/sim/tools/memory/add.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { MemoryResponse } from './types' +import type { MemoryResponse } from '@/tools/memory/types' +import type { ToolConfig } from '@/tools/types' export const memoryAddTool: ToolConfig = { id: 'memory_add', diff --git a/apps/sim/tools/memory/delete.ts b/apps/sim/tools/memory/delete.ts index 6e09ff14f..449b2afa5 100644 --- a/apps/sim/tools/memory/delete.ts +++ b/apps/sim/tools/memory/delete.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { MemoryResponse } from './types' +import type { MemoryResponse } from '@/tools/memory/types' +import type { ToolConfig } from '@/tools/types' export const memoryDeleteTool: ToolConfig = { id: 'memory_delete', diff --git a/apps/sim/tools/memory/get.ts b/apps/sim/tools/memory/get.ts index f354d153d..bc7887ea3 100644 --- a/apps/sim/tools/memory/get.ts +++ b/apps/sim/tools/memory/get.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { MemoryResponse } from './types' +import type { MemoryResponse } from '@/tools/memory/types' +import type { ToolConfig } from '@/tools/types' export const memoryGetTool: ToolConfig = { id: 'memory_get', diff --git a/apps/sim/tools/memory/get_all.ts b/apps/sim/tools/memory/get_all.ts index 456e6396d..74243ec93 100644 --- a/apps/sim/tools/memory/get_all.ts +++ b/apps/sim/tools/memory/get_all.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { MemoryResponse } from './types' +import type { MemoryResponse } from '@/tools/memory/types' +import type { ToolConfig } from '@/tools/types' export const memoryGetAllTool: ToolConfig = { id: 'memory_get_all', diff --git a/apps/sim/tools/memory/index.ts b/apps/sim/tools/memory/index.ts index 897e12027..36e0c1661 100644 --- a/apps/sim/tools/memory/index.ts +++ b/apps/sim/tools/memory/index.ts @@ -1,6 +1,6 @@ -import { memoryAddTool } from './add' -import { memoryDeleteTool } from './delete' -import { memoryGetTool } from './get' -import { memoryGetAllTool } from './get_all' +import { memoryAddTool } from '@/tools/memory/add' +import { memoryDeleteTool } from '@/tools/memory/delete' +import { memoryGetTool } from '@/tools/memory/get' +import { memoryGetAllTool } from '@/tools/memory/get_all' export { memoryAddTool, memoryGetTool, memoryGetAllTool, memoryDeleteTool } diff --git a/apps/sim/tools/memory/types.ts b/apps/sim/tools/memory/types.ts index 45d9368e3..77f1daaf1 100644 --- a/apps/sim/tools/memory/types.ts +++ b/apps/sim/tools/memory/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface MemoryResponse extends ToolResponse { output: { diff --git a/apps/sim/tools/microsoft_excel/index.ts b/apps/sim/tools/microsoft_excel/index.ts index 032f340b9..019f0beac 100644 --- a/apps/sim/tools/microsoft_excel/index.ts +++ b/apps/sim/tools/microsoft_excel/index.ts @@ -1,6 +1,6 @@ -import { readTool } from './read' -import { tableAddTool } from './table_add' -import { writeTool } from './write' +import { readTool } from '@/tools/microsoft_excel/read' +import { tableAddTool } from '@/tools/microsoft_excel/table_add' +import { writeTool } from '@/tools/microsoft_excel/write' export const microsoftExcelReadTool = readTool export const microsoftExcelTableAddTool = tableAddTool diff --git a/apps/sim/tools/microsoft_excel/read.ts b/apps/sim/tools/microsoft_excel/read.ts index 7e4843e37..394499cab 100644 --- a/apps/sim/tools/microsoft_excel/read.ts +++ b/apps/sim/tools/microsoft_excel/read.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { MicrosoftExcelReadResponse, MicrosoftExcelToolParams } from './types' +import type { + MicrosoftExcelReadResponse, + MicrosoftExcelToolParams, +} from '@/tools/microsoft_excel/types' +import type { ToolConfig } from '@/tools/types' export const readTool: ToolConfig = { id: 'microsoft_excel_read', diff --git a/apps/sim/tools/microsoft_excel/table_add.ts b/apps/sim/tools/microsoft_excel/table_add.ts index 0ff062601..3059a4ecd 100644 --- a/apps/sim/tools/microsoft_excel/table_add.ts +++ b/apps/sim/tools/microsoft_excel/table_add.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { MicrosoftExcelTableAddResponse, MicrosoftExcelTableToolParams } from './types' +import type { + MicrosoftExcelTableAddResponse, + MicrosoftExcelTableToolParams, +} from '@/tools/microsoft_excel/types' +import type { ToolConfig } from '@/tools/types' export const tableAddTool: ToolConfig< MicrosoftExcelTableToolParams, diff --git a/apps/sim/tools/microsoft_excel/types.ts b/apps/sim/tools/microsoft_excel/types.ts index f95bd52c5..a3ed2d803 100644 --- a/apps/sim/tools/microsoft_excel/types.ts +++ b/apps/sim/tools/microsoft_excel/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' // Type for Excel cell values - covers all valid data types that Excel supports export type ExcelCellValue = string | number | boolean | null diff --git a/apps/sim/tools/microsoft_excel/write.ts b/apps/sim/tools/microsoft_excel/write.ts index 8a6e2046a..abc54cc2a 100644 --- a/apps/sim/tools/microsoft_excel/write.ts +++ b/apps/sim/tools/microsoft_excel/write.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { MicrosoftExcelToolParams, MicrosoftExcelWriteResponse } from './types' +import type { + MicrosoftExcelToolParams, + MicrosoftExcelWriteResponse, +} from '@/tools/microsoft_excel/types' +import type { ToolConfig } from '@/tools/types' export const writeTool: ToolConfig = { id: 'microsoft_excel_write', diff --git a/apps/sim/tools/microsoft_teams/index.ts b/apps/sim/tools/microsoft_teams/index.ts index 15525eb82..b06a2e912 100644 --- a/apps/sim/tools/microsoft_teams/index.ts +++ b/apps/sim/tools/microsoft_teams/index.ts @@ -1,7 +1,7 @@ -import { readChannelTool } from './read_channel' -import { readChatTool } from './read_chat' -import { writeChannelTool } from './write_channel' -import { writeChatTool } from './write_chat' +import { readChannelTool } from '@/tools/microsoft_teams/read_channel' +import { readChatTool } from '@/tools/microsoft_teams/read_chat' +import { writeChannelTool } from '@/tools/microsoft_teams/write_channel' +import { writeChatTool } from '@/tools/microsoft_teams/write_chat' export const microsoftTeamsReadChannelTool = readChannelTool export const microsoftTeamsWriteChannelTool = writeChannelTool diff --git a/apps/sim/tools/microsoft_teams/read_channel.ts b/apps/sim/tools/microsoft_teams/read_channel.ts index a9b138c5f..b5c1267d8 100644 --- a/apps/sim/tools/microsoft_teams/read_channel.ts +++ b/apps/sim/tools/microsoft_teams/read_channel.ts @@ -1,7 +1,10 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { MicrosoftTeamsReadResponse, MicrosoftTeamsToolParams } from './types' -import { extractMessageAttachments } from './utils' +import type { + MicrosoftTeamsReadResponse, + MicrosoftTeamsToolParams, +} from '@/tools/microsoft_teams/types' +import { extractMessageAttachments } from '@/tools/microsoft_teams/utils' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('MicrosoftTeamsReadChannel') diff --git a/apps/sim/tools/microsoft_teams/read_chat.ts b/apps/sim/tools/microsoft_teams/read_chat.ts index bbd5d058f..fa13c83ad 100644 --- a/apps/sim/tools/microsoft_teams/read_chat.ts +++ b/apps/sim/tools/microsoft_teams/read_chat.ts @@ -1,6 +1,9 @@ -import type { ToolConfig } from '../types' -import type { MicrosoftTeamsReadResponse, MicrosoftTeamsToolParams } from './types' -import { extractMessageAttachments } from './utils' +import type { + MicrosoftTeamsReadResponse, + MicrosoftTeamsToolParams, +} from '@/tools/microsoft_teams/types' +import { extractMessageAttachments } from '@/tools/microsoft_teams/utils' +import type { ToolConfig } from '@/tools/types' export const readChatTool: ToolConfig = { id: 'microsoft_teams_read_chat', diff --git a/apps/sim/tools/microsoft_teams/types.ts b/apps/sim/tools/microsoft_teams/types.ts index 72a59d4f9..e880c6830 100644 --- a/apps/sim/tools/microsoft_teams/types.ts +++ b/apps/sim/tools/microsoft_teams/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface MicrosoftTeamsAttachment { id: string diff --git a/apps/sim/tools/microsoft_teams/utils.ts b/apps/sim/tools/microsoft_teams/utils.ts index b8f259a7e..10bc3227e 100644 --- a/apps/sim/tools/microsoft_teams/utils.ts +++ b/apps/sim/tools/microsoft_teams/utils.ts @@ -1,4 +1,4 @@ -import type { MicrosoftTeamsAttachment } from './types' +import type { MicrosoftTeamsAttachment } from '@/tools/microsoft_teams/types' /** * Transform raw attachment data from Microsoft Graph API diff --git a/apps/sim/tools/microsoft_teams/write_channel.ts b/apps/sim/tools/microsoft_teams/write_channel.ts index 2c90bf88f..e4792aa58 100644 --- a/apps/sim/tools/microsoft_teams/write_channel.ts +++ b/apps/sim/tools/microsoft_teams/write_channel.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { MicrosoftTeamsToolParams, MicrosoftTeamsWriteResponse } from './types' +import type { + MicrosoftTeamsToolParams, + MicrosoftTeamsWriteResponse, +} from '@/tools/microsoft_teams/types' +import type { ToolConfig } from '@/tools/types' export const writeChannelTool: ToolConfig = { id: 'microsoft_teams_write_channel', diff --git a/apps/sim/tools/microsoft_teams/write_chat.ts b/apps/sim/tools/microsoft_teams/write_chat.ts index fa8e10ab1..f71e9be2c 100644 --- a/apps/sim/tools/microsoft_teams/write_chat.ts +++ b/apps/sim/tools/microsoft_teams/write_chat.ts @@ -1,5 +1,8 @@ -import type { ToolConfig } from '../types' -import type { MicrosoftTeamsToolParams, MicrosoftTeamsWriteResponse } from './types' +import type { + MicrosoftTeamsToolParams, + MicrosoftTeamsWriteResponse, +} from '@/tools/microsoft_teams/types' +import type { ToolConfig } from '@/tools/types' export const writeChatTool: ToolConfig = { id: 'microsoft_teams_write_chat', diff --git a/apps/sim/tools/mistral/index.ts b/apps/sim/tools/mistral/index.ts index e8749a13d..53103913b 100644 --- a/apps/sim/tools/mistral/index.ts +++ b/apps/sim/tools/mistral/index.ts @@ -1,3 +1,3 @@ -import { mistralParserTool } from './parser' +import { mistralParserTool } from '@/tools/mistral/parser' export { mistralParserTool } diff --git a/apps/sim/tools/mistral/parser.ts b/apps/sim/tools/mistral/parser.ts index 05ea945b9..09b11e3e7 100644 --- a/apps/sim/tools/mistral/parser.ts +++ b/apps/sim/tools/mistral/parser.ts @@ -1,7 +1,7 @@ import { createLogger } from '@/lib/logs/console-logger' import { getBaseUrl } from '@/lib/urls/utils' -import type { ToolConfig } from '../types' -import type { MistralParserInput, MistralParserOutput } from './types' +import type { MistralParserInput, MistralParserOutput } from '@/tools/mistral/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('MistralParserTool') diff --git a/apps/sim/tools/mistral/types.ts b/apps/sim/tools/mistral/types.ts index f265ec324..b07601e3a 100644 --- a/apps/sim/tools/mistral/types.ts +++ b/apps/sim/tools/mistral/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' /** * Input parameters for the Mistral OCR parser tool diff --git a/apps/sim/tools/notion/create_database.ts b/apps/sim/tools/notion/create_database.ts new file mode 100644 index 000000000..e6df2d04b --- /dev/null +++ b/apps/sim/tools/notion/create_database.ts @@ -0,0 +1,148 @@ +import type { NotionCreateDatabaseParams, NotionResponse } from '@/tools/notion/types' +import type { ToolConfig } from '@/tools/types' + +export const notionCreateDatabaseTool: ToolConfig = { + id: 'notion_create_database', + name: 'Create Notion Database', + description: 'Create a new database in Notion with custom properties', + version: '1.0.0', + oauth: { + required: true, + provider: 'notion', + additionalScopes: ['workspace.content', 'page.write'], + }, + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Notion OAuth access token', + }, + parentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'ID of the parent page where the database will be created', + }, + title: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Title for the new database', + }, + properties: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: + 'Database properties as JSON object (optional, will create a default "Name" property if empty)', + }, + }, + + request: { + url: () => 'https://api.notion.com/v1/databases', + method: 'POST', + headers: (params: NotionCreateDatabaseParams) => { + if (!params.accessToken) { + throw new Error('Access token is required') + } + + return { + Authorization: `Bearer ${params.accessToken}`, + 'Notion-Version': '2022-06-28', + 'Content-Type': 'application/json', + } + }, + body: (params: NotionCreateDatabaseParams) => { + let parsedProperties + + // Handle properties - use provided JSON or default to Name property + if (params.properties?.trim()) { + try { + parsedProperties = JSON.parse(params.properties) + } catch (error) { + throw new Error( + `Invalid properties JSON: ${error instanceof Error ? error.message : String(error)}` + ) + } + } else { + // Default properties with a Name column + parsedProperties = { + Name: { + title: {}, + }, + } + } + + // Format parent ID + const formattedParentId = params.parentId.replace( + /(.{8})(.{4})(.{4})(.{4})(.{12})/, + '$1-$2-$3-$4-$5' + ) + + const body = { + parent: { + type: 'page_id', + page_id: formattedParentId, + }, + title: [ + { + type: 'text', + text: { + content: params.title, + }, + }, + ], + properties: parsedProperties, + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json() + throw new Error(`Notion API error: ${errorData.message || 'Unknown error'}`) + } + + const data = await response.json() + + // Extract database title + const title = data.title?.map((t: any) => t.plain_text || '').join('') || 'Untitled Database' + + // Extract properties for display + const properties = data.properties || {} + const propertyList = Object.entries(properties) + .map(([name, prop]: [string, any]) => ` ${name}: ${prop.type}`) + .join('\n') + + const content = [ + `Database "${title}" created successfully!`, + '', + 'Properties:', + propertyList, + '', + `Database ID: ${data.id}`, + `URL: ${data.url}`, + ].join('\n') + + return { + success: true, + output: { + content, + metadata: { + id: data.id, + title, + url: data.url, + createdTime: data.created_time, + properties: data.properties, + }, + }, + } + }, + + transformError: (error) => { + return error instanceof Error ? error.message : 'Failed to create Notion database' + }, +} diff --git a/apps/sim/tools/notion/create_page.ts b/apps/sim/tools/notion/create_page.ts index 5651ceb74..7a8c6aa75 100644 --- a/apps/sim/tools/notion/create_page.ts +++ b/apps/sim/tools/notion/create_page.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { NotionCreatePageParams, NotionResponse } from './types' +import type { NotionCreatePageParams, NotionResponse } from '@/tools/notion/types' +import type { ToolConfig } from '@/tools/types' export const notionCreatePageTool: ToolConfig = { id: 'notion_create_page', @@ -18,29 +18,17 @@ export const notionCreatePageTool: ToolConfig = { + id: 'notion_query_database', + name: 'Query Notion Database', + description: 'Query and filter Notion database entries with advanced filtering', + version: '1.0.0', + oauth: { + required: true, + provider: 'notion', + additionalScopes: ['workspace.content', 'database.read'], + }, + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Notion OAuth access token', + }, + databaseId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the database to query', + }, + filter: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter conditions as JSON (optional)', + }, + sorts: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Sort criteria as JSON array (optional)', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Number of results to return (default: 100, max: 100)', + }, + }, + + request: { + url: (params: NotionQueryDatabaseParams) => { + const formattedId = params.databaseId.replace( + /(.{8})(.{4})(.{4})(.{4})(.{12})/, + '$1-$2-$3-$4-$5' + ) + return `https://api.notion.com/v1/databases/${formattedId}/query` + }, + method: 'POST', + headers: (params: NotionQueryDatabaseParams) => { + if (!params.accessToken) { + throw new Error('Access token is required') + } + + return { + Authorization: `Bearer ${params.accessToken}`, + 'Notion-Version': '2022-06-28', + 'Content-Type': 'application/json', + } + }, + body: (params: NotionQueryDatabaseParams) => { + const body: any = {} + + // Add filter if provided + if (params.filter) { + try { + body.filter = JSON.parse(params.filter) + } catch (error) { + throw new Error( + `Invalid filter JSON: ${error instanceof Error ? error.message : String(error)}` + ) + } + } + + // Add sorts if provided + if (params.sorts) { + try { + body.sorts = JSON.parse(params.sorts) + } catch (error) { + throw new Error( + `Invalid sorts JSON: ${error instanceof Error ? error.message : String(error)}` + ) + } + } + + // Add page size if provided + if (params.pageSize) { + body.page_size = Math.min(params.pageSize, 100) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json() + throw new Error(`Notion API error: ${errorData.message || 'Unknown error'}`) + } + + const data = await response.json() + const results = data.results || [] + + // Format the results into readable content + const content = results + .map((page: any, index: number) => { + const properties = page.properties || {} + const title = extractTitle(properties) + const propertyValues = Object.entries(properties) + .map(([key, value]: [string, any]) => { + const formattedValue = formatPropertyValue(value) + return ` ${key}: ${formattedValue}` + }) + .join('\n') + + return `Entry ${index + 1}${title ? ` - ${title}` : ''}:\n${propertyValues}` + }) + .join('\n\n') + + return { + success: true, + output: { + content: content || 'No results found', + metadata: { + totalResults: results.length, + hasMore: data.has_more || false, + nextCursor: data.next_cursor || null, + results: results, + }, + }, + } + }, + + transformError: (error) => { + return error instanceof Error ? error.message : 'Failed to query Notion database' + }, +} + +// Helper function to extract title from properties +function extractTitle(properties: Record): string { + for (const [, value] of Object.entries(properties)) { + if ( + value.type === 'title' && + value.title && + Array.isArray(value.title) && + value.title.length > 0 + ) { + return value.title.map((t: any) => t.plain_text || '').join('') + } + } + return '' +} + +// Helper function to format property values +function formatPropertyValue(property: any): string { + switch (property.type) { + case 'title': + return property.title?.map((t: any) => t.plain_text || '').join('') || '' + case 'rich_text': + return property.rich_text?.map((t: any) => t.plain_text || '').join('') || '' + case 'number': + return String(property.number || '') + case 'select': + return property.select?.name || '' + case 'multi_select': + return property.multi_select?.map((s: any) => s.name).join(', ') || '' + case 'date': + return property.date?.start || '' + case 'checkbox': + return property.checkbox ? 'Yes' : 'No' + case 'url': + return property.url || '' + case 'email': + return property.email || '' + case 'phone_number': + return property.phone_number || '' + default: + return JSON.stringify(property) + } +} diff --git a/apps/sim/tools/notion/read.ts b/apps/sim/tools/notion/read.ts index 472cab541..2be63d7ff 100644 --- a/apps/sim/tools/notion/read.ts +++ b/apps/sim/tools/notion/read.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { NotionReadParams, NotionResponse } from './types' +import type { NotionReadParams, NotionResponse } from '@/tools/notion/types' +import type { ToolConfig } from '@/tools/types' export const notionReadTool: ToolConfig = { id: 'notion_read', diff --git a/apps/sim/tools/notion/read_database.ts b/apps/sim/tools/notion/read_database.ts new file mode 100644 index 000000000..9dfc74890 --- /dev/null +++ b/apps/sim/tools/notion/read_database.ts @@ -0,0 +1,106 @@ +import type { NotionResponse } from '@/tools/notion/types' +import type { ToolConfig } from '@/tools/types' + +export interface NotionReadDatabaseParams { + databaseId: string + accessToken: string +} + +export const notionReadDatabaseTool: ToolConfig = { + id: 'notion_read_database', + name: 'Read Notion Database', + description: 'Read database information and structure from Notion', + version: '1.0.0', + oauth: { + required: true, + provider: 'notion', + additionalScopes: ['workspace.content', 'database.read'], + }, + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Notion OAuth access token', + }, + databaseId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The ID of the Notion database to read', + }, + }, + + request: { + url: (params: NotionReadDatabaseParams) => { + // Format database ID with hyphens if needed + const formattedId = params.databaseId.replace( + /(.{8})(.{4})(.{4})(.{4})(.{12})/, + '$1-$2-$3-$4-$5' + ) + + return `https://api.notion.com/v1/databases/${formattedId}` + }, + method: 'GET', + headers: (params: NotionReadDatabaseParams) => { + if (!params.accessToken) { + throw new Error('Access token is required') + } + + return { + Authorization: `Bearer ${params.accessToken}`, + 'Notion-Version': '2022-06-28', + 'Content-Type': 'application/json', + } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json() + throw new Error(`Notion API error: ${errorData.message || 'Unknown error'}`) + } + + const data = await response.json() + + // Extract database title + const title = data.title?.map((t: any) => t.plain_text || '').join('') || 'Untitled Database' + + // Extract properties for display + const properties = data.properties || {} + const propertyList = Object.entries(properties) + .map(([name, prop]: [string, any]) => ` ${name}: ${prop.type}`) + .join('\\n') + + const content = [ + `Database: ${title}`, + '', + 'Properties:', + propertyList, + '', + `Database ID: ${data.id}`, + `URL: ${data.url}`, + `Created: ${data.created_time ? new Date(data.created_time).toLocaleDateString() : 'Unknown'}`, + `Last edited: ${data.last_edited_time ? new Date(data.last_edited_time).toLocaleDateString() : 'Unknown'}`, + ].join('\\n') + + return { + success: true, + output: { + content, + metadata: { + title, + url: data.url, + id: data.id, + createdTime: data.created_time, + lastEditedTime: data.last_edited_time, + properties: data.properties, + }, + }, + } + }, + + transformError: (error) => { + return error instanceof Error ? error.message : 'Failed to read Notion database' + }, +} diff --git a/apps/sim/tools/notion/search.ts b/apps/sim/tools/notion/search.ts new file mode 100644 index 000000000..2903d03d4 --- /dev/null +++ b/apps/sim/tools/notion/search.ts @@ -0,0 +1,151 @@ +import type { NotionResponse, NotionSearchParams } from '@/tools/notion/types' +import type { ToolConfig } from '@/tools/types' + +export const notionSearchTool: ToolConfig = { + id: 'notion_search', + name: 'Search Notion Workspace', + description: 'Search across all pages and databases in Notion workspace', + version: '1.0.0', + oauth: { + required: true, + provider: 'notion', + additionalScopes: ['workspace.content', 'page.read'], + }, + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Notion OAuth access token', + }, + query: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Search terms (leave empty to get all pages)', + }, + filterType: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Filter by object type: page, database, or leave empty for all', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-only', + description: 'Number of results to return (default: 100, max: 100)', + }, + }, + + request: { + url: () => 'https://api.notion.com/v1/search', + method: 'POST', + headers: (params: NotionSearchParams) => { + if (!params.accessToken) { + throw new Error('Access token is required') + } + + return { + Authorization: `Bearer ${params.accessToken}`, + 'Notion-Version': '2022-06-28', + 'Content-Type': 'application/json', + } + }, + body: (params: NotionSearchParams) => { + const body: any = {} + + // Add query if provided + if (params.query?.trim()) { + body.query = params.query.trim() + } + + // Add filter if provided (skip 'all' as it means no filter) + if ( + params.filterType && + params.filterType !== 'all' && + ['page', 'database'].includes(params.filterType) + ) { + body.filter = { + value: params.filterType, + property: 'object', + } + } + + // Add page size if provided + if (params.pageSize) { + body.page_size = Math.min(params.pageSize, 100) + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json() + throw new Error(`Notion API error: ${errorData.message || 'Unknown error'}`) + } + + const data = await response.json() + const results = data.results || [] + + // Format the results into readable content + const content = results + .map((item: any, index: number) => { + const objectType = item.object === 'page' ? 'Page' : 'Database' + const title = extractTitle(item) + const url = item.url || '' + const lastEdited = item.last_edited_time + ? new Date(item.last_edited_time).toLocaleDateString() + : '' + + return [ + `${index + 1}. ${objectType}: ${title}`, + ` URL: ${url}`, + lastEdited ? ` Last edited: ${lastEdited}` : '', + ] + .filter(Boolean) + .join('\n') + }) + .join('\n\n') + + return { + success: true, + output: { + content: content || 'No results found', + metadata: { + totalResults: results.length, + hasMore: data.has_more || false, + nextCursor: data.next_cursor || null, + results: results, + }, + }, + } + }, + + transformError: (error) => { + return error instanceof Error ? error.message : 'Failed to search Notion workspace' + }, +} + +// Helper function to extract title from page or database +function extractTitle(item: any): string { + if (item.object === 'page') { + // For pages, check properties first + if (item.properties?.title?.title && Array.isArray(item.properties.title.title)) { + const title = item.properties.title.title.map((t: any) => t.plain_text || '').join('') + if (title) return title + } + // Fallback to page title + return item.title || 'Untitled Page' + } + if (item.object === 'database') { + // For databases, get title from title array + if (item.title && Array.isArray(item.title)) { + return item.title.map((t: any) => t.plain_text || '').join('') || 'Untitled Database' + } + return 'Untitled Database' + } + return 'Untitled' +} diff --git a/apps/sim/tools/notion/types.ts b/apps/sim/tools/notion/types.ts index 1df5331f0..c3bb0f30f 100644 --- a/apps/sim/tools/notion/types.ts +++ b/apps/sim/tools/notion/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface NotionReadParams { pageId: string @@ -13,6 +13,14 @@ export interface NotionResponse extends ToolResponse { lastEditedTime?: string createdTime?: string url?: string + // Additional metadata for query/search operations + totalResults?: number + hasMore?: boolean + nextCursor?: string | null + results?: any[] + // Additional metadata for create operations + id?: string + properties?: Record } } } @@ -24,10 +32,8 @@ export interface NotionWriteParams { } export interface NotionCreatePageParams { - parentType: 'page' | 'database' parentId: string title?: string - properties?: Record content?: string accessToken: string } @@ -37,3 +43,30 @@ export interface NotionUpdatePageParams { properties: Record accessToken: string } + +export interface NotionQueryDatabaseParams { + databaseId: string + filter?: string + sorts?: string + pageSize?: number + accessToken: string +} + +export interface NotionSearchParams { + query?: string + filterType?: string + pageSize?: number + accessToken: string +} + +export interface NotionCreateDatabaseParams { + parentId: string + title: string + properties?: string + accessToken: string +} + +export interface NotionReadDatabaseParams { + databaseId: string + accessToken: string +} diff --git a/apps/sim/tools/notion/update_page.ts b/apps/sim/tools/notion/update_page.ts index c0f004914..688917391 100644 --- a/apps/sim/tools/notion/update_page.ts +++ b/apps/sim/tools/notion/update_page.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { NotionResponse, NotionUpdatePageParams } from './types' +import type { NotionResponse, NotionUpdatePageParams } from '@/tools/notion/types' +import type { ToolConfig } from '@/tools/types' export const notionUpdatePageTool: ToolConfig = { id: 'notion_update_page', diff --git a/apps/sim/tools/notion/write.ts b/apps/sim/tools/notion/write.ts index 6d54d43cd..49b55971f 100644 --- a/apps/sim/tools/notion/write.ts +++ b/apps/sim/tools/notion/write.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { NotionResponse, NotionWriteParams } from './types' +import type { NotionResponse, NotionWriteParams } from '@/tools/notion/types' +import type { ToolConfig } from '@/tools/types' export const notionWriteTool: ToolConfig = { id: 'notion_write', diff --git a/apps/sim/tools/openai/embeddings.ts b/apps/sim/tools/openai/embeddings.ts index ea4ee079b..69e854d62 100644 --- a/apps/sim/tools/openai/embeddings.ts +++ b/apps/sim/tools/openai/embeddings.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { OpenAIEmbeddingsParams } from './types' +import type { OpenAIEmbeddingsParams } from '@/tools/openai/types' +import type { ToolConfig } from '@/tools/types' export const embeddingsTool: ToolConfig = { id: 'openai_embeddings', diff --git a/apps/sim/tools/openai/image.ts b/apps/sim/tools/openai/image.ts index 424e64ea4..9a12a5023 100644 --- a/apps/sim/tools/openai/image.ts +++ b/apps/sim/tools/openai/image.ts @@ -1,7 +1,7 @@ import { createLogger } from '@/lib/logs/console-logger' import { getBaseUrl } from '@/lib/urls/utils' -import type { ToolConfig } from '../types' -import type { BaseImageRequestBody } from './types' +import type { BaseImageRequestBody } from '@/tools/openai/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('ImageTool') diff --git a/apps/sim/tools/openai/index.ts b/apps/sim/tools/openai/index.ts index f5e7acc93..63d875c1f 100644 --- a/apps/sim/tools/openai/index.ts +++ b/apps/sim/tools/openai/index.ts @@ -1,4 +1,4 @@ -import { embeddingsTool } from './embeddings' -import { imageTool } from './image' +import { embeddingsTool } from '@/tools/openai/embeddings' +import { imageTool } from '@/tools/openai/image' export { embeddingsTool, imageTool } diff --git a/apps/sim/tools/openai/types.ts b/apps/sim/tools/openai/types.ts index db8f4b987..532666e20 100644 --- a/apps/sim/tools/openai/types.ts +++ b/apps/sim/tools/openai/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface BaseImageRequestBody { model: string diff --git a/apps/sim/tools/outlook/draft.ts b/apps/sim/tools/outlook/draft.ts index fc4253d01..e406cc5e2 100644 --- a/apps/sim/tools/outlook/draft.ts +++ b/apps/sim/tools/outlook/draft.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { OutlookDraftParams, OutlookDraftResponse } from './types' +import type { OutlookDraftParams, OutlookDraftResponse } from '@/tools/outlook/types' +import type { ToolConfig } from '@/tools/types' export const outlookDraftTool: ToolConfig = { id: 'outlook_draft', diff --git a/apps/sim/tools/outlook/index.ts b/apps/sim/tools/outlook/index.ts index 6cd4ed142..a218db1ee 100644 --- a/apps/sim/tools/outlook/index.ts +++ b/apps/sim/tools/outlook/index.ts @@ -1,5 +1,5 @@ -import { outlookDraftTool } from './draft' -import { outlookReadTool } from './read' -import { outlookSendTool } from './send' +import { outlookDraftTool } from '@/tools/outlook/draft' +import { outlookReadTool } from '@/tools/outlook/read' +import { outlookSendTool } from '@/tools/outlook/send' export { outlookDraftTool, outlookReadTool, outlookSendTool } diff --git a/apps/sim/tools/outlook/read.ts b/apps/sim/tools/outlook/read.ts index 52e29226b..14ca192f8 100644 --- a/apps/sim/tools/outlook/read.ts +++ b/apps/sim/tools/outlook/read.ts @@ -1,11 +1,11 @@ -import type { ToolConfig } from '../types' import type { CleanedOutlookMessage, OutlookMessage, OutlookMessagesResponse, OutlookReadParams, OutlookReadResponse, -} from './types' +} from '@/tools/outlook/types' +import type { ToolConfig } from '@/tools/types' export const outlookReadTool: ToolConfig = { id: 'outlook_read', diff --git a/apps/sim/tools/outlook/send.ts b/apps/sim/tools/outlook/send.ts index 6c1234863..10d88d073 100644 --- a/apps/sim/tools/outlook/send.ts +++ b/apps/sim/tools/outlook/send.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { OutlookSendParams, OutlookSendResponse } from './types' +import type { OutlookSendParams, OutlookSendResponse } from '@/tools/outlook/types' +import type { ToolConfig } from '@/tools/types' export const outlookSendTool: ToolConfig = { id: 'outlook_send', diff --git a/apps/sim/tools/outlook/types.ts b/apps/sim/tools/outlook/types.ts index 4f71948a1..b2660d2ba 100644 --- a/apps/sim/tools/outlook/types.ts +++ b/apps/sim/tools/outlook/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface OutlookSendParams { accessToken: string diff --git a/apps/sim/tools/params.test.ts b/apps/sim/tools/params.test.ts index 9e3839746..161170da3 100644 --- a/apps/sim/tools/params.test.ts +++ b/apps/sim/tools/params.test.ts @@ -11,8 +11,8 @@ import { type ToolSchema, type ValidationResult, validateToolParameters, -} from './params' -import type { ParameterVisibility } from './types' +} from '@/tools/params' +import type { HttpMethod, ParameterVisibility } from '@/tools/types' const mockToolConfig = { id: 'test_tool', @@ -48,7 +48,7 @@ const mockToolConfig = { }, request: { url: 'https://api.example.com/test', - method: 'POST', + method: 'POST' as HttpMethod, headers: () => ({}), }, } diff --git a/apps/sim/tools/params.ts b/apps/sim/tools/params.ts index 4d2be6cb4..d5b2fd678 100644 --- a/apps/sim/tools/params.ts +++ b/apps/sim/tools/params.ts @@ -1,5 +1,5 @@ -import type { ParameterVisibility, ToolConfig } from './types' -import { getTool } from './utils' +import type { ParameterVisibility, ToolConfig } from '@/tools/types' +import { getTool } from '@/tools/utils' export interface Option { label: string diff --git a/apps/sim/tools/perplexity/chat.ts b/apps/sim/tools/perplexity/chat.ts index 5528780ca..a984d9adf 100644 --- a/apps/sim/tools/perplexity/chat.ts +++ b/apps/sim/tools/perplexity/chat.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { PerplexityChatParams, PerplexityChatResponse } from './types' +import type { PerplexityChatParams, PerplexityChatResponse } from '@/tools/perplexity/types' +import type { ToolConfig } from '@/tools/types' export const chatTool: ToolConfig = { id: 'perplexity_chat', diff --git a/apps/sim/tools/perplexity/index.ts b/apps/sim/tools/perplexity/index.ts index d4776ce50..b85e655c9 100644 --- a/apps/sim/tools/perplexity/index.ts +++ b/apps/sim/tools/perplexity/index.ts @@ -1,3 +1,3 @@ -import { chatTool } from './chat' +import { chatTool } from '@/tools/perplexity/chat' export const perplexityChatTool = chatTool diff --git a/apps/sim/tools/perplexity/types.ts b/apps/sim/tools/perplexity/types.ts index 2bcdec6e8..f09a0d240 100644 --- a/apps/sim/tools/perplexity/types.ts +++ b/apps/sim/tools/perplexity/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface PerplexityMessage { role: string diff --git a/apps/sim/tools/pinecone/fetch.ts b/apps/sim/tools/pinecone/fetch.ts index 2a32a7a84..76975561d 100644 --- a/apps/sim/tools/pinecone/fetch.ts +++ b/apps/sim/tools/pinecone/fetch.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { PineconeFetchParams, PineconeResponse, PineconeVector } from './types' +import type { PineconeFetchParams, PineconeResponse, PineconeVector } from '@/tools/pinecone/types' +import type { ToolConfig } from '@/tools/types' export const fetchTool: ToolConfig = { id: 'pinecone_fetch', diff --git a/apps/sim/tools/pinecone/generate_embeddings.ts b/apps/sim/tools/pinecone/generate_embeddings.ts index 913581c5c..7490cae77 100644 --- a/apps/sim/tools/pinecone/generate_embeddings.ts +++ b/apps/sim/tools/pinecone/generate_embeddings.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { PineconeGenerateEmbeddingsParams, PineconeResponse } from './types' +import type { PineconeGenerateEmbeddingsParams, PineconeResponse } from '@/tools/pinecone/types' +import type { ToolConfig } from '@/tools/types' export const generateEmbeddingsTool: ToolConfig< PineconeGenerateEmbeddingsParams, diff --git a/apps/sim/tools/pinecone/index.ts b/apps/sim/tools/pinecone/index.ts index 2ad8586b8..ae1dccdfc 100644 --- a/apps/sim/tools/pinecone/index.ts +++ b/apps/sim/tools/pinecone/index.ts @@ -1,8 +1,8 @@ -import { fetchTool } from './fetch' -import { generateEmbeddingsTool } from './generate_embeddings' -import { searchTextTool } from './search_text' -import { searchVectorTool } from './search_vector' -import { upsertTextTool } from './upsert_text' +import { fetchTool } from '@/tools/pinecone/fetch' +import { generateEmbeddingsTool } from '@/tools/pinecone/generate_embeddings' +import { searchTextTool } from '@/tools/pinecone/search_text' +import { searchVectorTool } from '@/tools/pinecone/search_vector' +import { upsertTextTool } from '@/tools/pinecone/upsert_text' export const pineconeFetchTool = fetchTool export const pineconeGenerateEmbeddingsTool = generateEmbeddingsTool diff --git a/apps/sim/tools/pinecone/search_text.ts b/apps/sim/tools/pinecone/search_text.ts index a8144d222..d910896a3 100644 --- a/apps/sim/tools/pinecone/search_text.ts +++ b/apps/sim/tools/pinecone/search_text.ts @@ -1,5 +1,9 @@ -import type { ToolConfig } from '../types' -import type { PineconeResponse, PineconeSearchHit, PineconeSearchTextParams } from './types' +import type { + PineconeResponse, + PineconeSearchHit, + PineconeSearchTextParams, +} from '@/tools/pinecone/types' +import type { ToolConfig } from '@/tools/types' export const searchTextTool: ToolConfig = { id: 'pinecone_search_text', diff --git a/apps/sim/tools/pinecone/search_vector.ts b/apps/sim/tools/pinecone/search_vector.ts index 6e3d860ea..c750af99d 100644 --- a/apps/sim/tools/pinecone/search_vector.ts +++ b/apps/sim/tools/pinecone/search_vector.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { PineconeResponse, PineconeSearchVectorParams } from './types' +import type { PineconeResponse, PineconeSearchVectorParams } from '@/tools/pinecone/types' +import type { ToolConfig } from '@/tools/types' export const searchVectorTool: ToolConfig = { id: 'pinecone_search_vector', diff --git a/apps/sim/tools/pinecone/types.ts b/apps/sim/tools/pinecone/types.ts index 7e189eba0..383e2b466 100644 --- a/apps/sim/tools/pinecone/types.ts +++ b/apps/sim/tools/pinecone/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' // Base Pinecone params shared across all operations export interface PineconeBaseParams { diff --git a/apps/sim/tools/pinecone/upsert_text.ts b/apps/sim/tools/pinecone/upsert_text.ts index 7f62584b4..ebd28fa4a 100644 --- a/apps/sim/tools/pinecone/upsert_text.ts +++ b/apps/sim/tools/pinecone/upsert_text.ts @@ -1,5 +1,9 @@ -import type { ToolConfig } from '../types' -import type { PineconeResponse, PineconeUpsertTextParams, PineconeUpsertTextRecord } from './types' +import type { + PineconeResponse, + PineconeUpsertTextParams, + PineconeUpsertTextRecord, +} from '@/tools/pinecone/types' +import type { ToolConfig } from '@/tools/types' export const upsertTextTool: ToolConfig = { id: 'pinecone_upsert_text', diff --git a/apps/sim/tools/reddit/get_comments.ts b/apps/sim/tools/reddit/get_comments.ts index efd64dd1f..841e7105d 100644 --- a/apps/sim/tools/reddit/get_comments.ts +++ b/apps/sim/tools/reddit/get_comments.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { RedditCommentsParams, RedditCommentsResponse } from './types' +import type { RedditCommentsParams, RedditCommentsResponse } from '@/tools/reddit/types' +import type { ToolConfig } from '@/tools/types' export const getCommentsTool: ToolConfig = { id: 'reddit_get_comments', diff --git a/apps/sim/tools/reddit/get_posts.ts b/apps/sim/tools/reddit/get_posts.ts index 274d05d1a..38a9118a2 100644 --- a/apps/sim/tools/reddit/get_posts.ts +++ b/apps/sim/tools/reddit/get_posts.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { RedditPostsParams, RedditPostsResponse } from './types' +import type { RedditPostsParams, RedditPostsResponse } from '@/tools/reddit/types' +import type { ToolConfig } from '@/tools/types' export const getPostsTool: ToolConfig = { id: 'reddit_get_posts', diff --git a/apps/sim/tools/reddit/hot_posts.ts b/apps/sim/tools/reddit/hot_posts.ts index 0453bb4eb..daf7adf69 100644 --- a/apps/sim/tools/reddit/hot_posts.ts +++ b/apps/sim/tools/reddit/hot_posts.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { RedditHotPostsResponse, RedditPost } from './types' +import type { RedditHotPostsResponse, RedditPost } from '@/tools/reddit/types' +import type { ToolConfig } from '@/tools/types' interface HotPostsParams { subreddit: string diff --git a/apps/sim/tools/reddit/index.ts b/apps/sim/tools/reddit/index.ts index e371fe017..e3facb252 100644 --- a/apps/sim/tools/reddit/index.ts +++ b/apps/sim/tools/reddit/index.ts @@ -1,6 +1,6 @@ -import { getCommentsTool } from './get_comments' -import { getPostsTool } from './get_posts' -import { hotPostsTool } from './hot_posts' +import { getCommentsTool } from '@/tools/reddit/get_comments' +import { getPostsTool } from '@/tools/reddit/get_posts' +import { hotPostsTool } from '@/tools/reddit/hot_posts' export const redditHotPostsTool = hotPostsTool export const redditGetPostsTool = getPostsTool diff --git a/apps/sim/tools/reddit/types.ts b/apps/sim/tools/reddit/types.ts index 2b120fca0..d57620b9d 100644 --- a/apps/sim/tools/reddit/types.ts +++ b/apps/sim/tools/reddit/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface RedditPost { id: string diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 3309a1eb3..bfb67b4a2 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -3,98 +3,116 @@ import { airtableGetRecordTool, airtableListRecordsTool, airtableUpdateRecordTool, -} from './airtable' -import { browserUseRunTaskTool } from './browser_use' -import { clayPopulateTool } from './clay' -import { confluenceRetrieveTool, confluenceUpdateTool } from './confluence' +} from '@/tools/airtable' +import { browserUseRunTaskTool } from '@/tools/browser_use' +import { clayPopulateTool } from '@/tools/clay' +import { confluenceRetrieveTool, confluenceUpdateTool } from '@/tools/confluence' import { discordGetMessagesTool, discordGetServerTool, discordGetUserTool, discordSendMessageTool, -} from './discord' -import { elevenLabsTtsTool } from './elevenlabs' -import { exaAnswerTool, exaFindSimilarLinksTool, exaGetContentsTool, exaSearchTool } from './exa' -import { fileParseTool } from './file' -import { scrapeTool, searchTool } from './firecrawl' -import { functionExecuteTool } from './function' +} from '@/tools/discord' +import { elevenLabsTtsTool } from '@/tools/elevenlabs' +import { + exaAnswerTool, + exaFindSimilarLinksTool, + exaGetContentsTool, + exaResearchTool, + exaSearchTool, +} from '@/tools/exa' +import { fileParseTool } from '@/tools/file' +import { crawlTool, scrapeTool, searchTool } from '@/tools/firecrawl' +import { functionExecuteTool } from '@/tools/function' import { githubCommentTool, githubLatestCommitTool, githubPrTool, githubRepoInfoTool, -} from './github' -import { gmailDraftTool, gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail' -import { searchTool as googleSearchTool } from './google' +} from '@/tools/github' import { googleCalendarCreateTool, googleCalendarGetTool, googleCalendarInviteTool, googleCalendarListTool, googleCalendarQuickAddTool, -} from './google_calendar' -import { googleDocsCreateTool, googleDocsReadTool, googleDocsWriteTool } from './google_docs' +} from '@/tools/google_calendar' +import { googleDocsCreateTool, googleDocsReadTool, googleDocsWriteTool } from '@/tools/google_docs' import { googleDriveCreateFolderTool, googleDriveGetContentTool, googleDriveListTool, googleDriveUploadTool, -} from './google_drive' +} from '@/tools/google_drive' import { googleSheetsAppendTool, googleSheetsReadTool, googleSheetsUpdateTool, googleSheetsWriteTool, -} from './google_sheets' -import { requestTool as httpRequest } from './http' -import { huggingfaceChatTool } from './huggingface' -import { readUrlTool } from './jina' -import { jiraBulkRetrieveTool, jiraRetrieveTool, jiraUpdateTool, jiraWriteTool } from './jira' +} from '@/tools/google_sheets' +import { requestTool as httpRequest } from '@/tools/http' +import { huggingfaceChatTool } from '@/tools/huggingface' +import { readUrlTool } from '@/tools/jina' +import { jiraBulkRetrieveTool, jiraRetrieveTool, jiraUpdateTool, jiraWriteTool } from '@/tools/jira' import { knowledgeCreateDocumentTool, knowledgeSearchTool, knowledgeUploadChunkTool, -} from './knowledge' -import { linearCreateIssueTool, linearReadIssuesTool } from './linear' -import { linkupSearchTool } from './linkup' -import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from './mem0' -import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from './memory' +} from '@/tools/knowledge' +import { linearCreateIssueTool, linearReadIssuesTool } from '@/tools/linear' +import { linkupSearchTool } from '@/tools/linkup' +import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from '@/tools/mem0' +import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from '@/tools/memory' import { microsoftExcelReadTool, microsoftExcelTableAddTool, microsoftExcelWriteTool, -} from './microsoft_excel' +} from '@/tools/microsoft_excel' import { microsoftTeamsReadChannelTool, microsoftTeamsReadChatTool, microsoftTeamsWriteChannelTool, microsoftTeamsWriteChatTool, -} from './microsoft_teams' -import { mistralParserTool } from './mistral' -import { notionCreatePageTool, notionReadTool, notionWriteTool } from './notion' -import { imageTool, embeddingsTool as openAIEmbeddings } from './openai' -import { outlookDraftTool, outlookReadTool, outlookSendTool } from './outlook' -import { perplexityChatTool } from './perplexity' +} from '@/tools/microsoft_teams' +import { mistralParserTool } from '@/tools/mistral' +import { + notionCreateDatabaseTool, + notionCreatePageTool, + notionQueryDatabaseTool, + notionReadDatabaseTool, + notionReadTool, + notionSearchTool, + notionWriteTool, +} from '@/tools/notion' +import { imageTool, embeddingsTool as openAIEmbeddings } from '@/tools/openai' +import { outlookDraftTool, outlookReadTool, outlookSendTool } from '@/tools/outlook' +import { perplexityChatTool } from '@/tools/perplexity' import { pineconeFetchTool, pineconeGenerateEmbeddingsTool, pineconeSearchTextTool, pineconeSearchVectorTool, pineconeUpsertTextTool, -} from './pinecone' -import { redditGetCommentsTool, redditGetPostsTool, redditHotPostsTool } from './reddit' -import { s3GetObjectTool } from './s3' -import { searchTool as serperSearch } from './serper' -import { slackMessageTool } from './slack' -import { stagehandAgentTool, stagehandExtractTool } from './stagehand' -import { supabaseInsertTool, supabaseQueryTool } from './supabase' -import { tavilyExtractTool, tavilySearchTool } from './tavily' -import { telegramMessageTool } from './telegram' -import { thinkingTool } from './thinking' -import { sendSMSTool } from './twilio' -import { typeformFilesTool, typeformInsightsTool, typeformResponsesTool } from './typeform' -import type { ToolConfig } from './types' -import { visionTool } from './vision' +} from '@/tools/pinecone' +import { redditGetCommentsTool, redditGetPostsTool, redditHotPostsTool } from '@/tools/reddit' +import { s3GetObjectTool } from '@/tools/s3' +import { searchTool as serperSearch } from '@/tools/serper' +import { slackCanvasTool, slackMessageReaderTool, slackMessageTool } from '@/tools/slack' +import { stagehandAgentTool, stagehandExtractTool } from '@/tools/stagehand' +import { + supabaseDeleteTool, + supabaseGetRowTool, + supabaseInsertTool, + supabaseQueryTool, + supabaseUpdateTool, +} from '@/tools/supabase' +import { tavilyExtractTool, tavilySearchTool } from '@/tools/tavily' +import { telegramMessageTool } from '@/tools/telegram' +import { thinkingTool } from '@/tools/thinking' +import { sendSMSTool } from '@/tools/twilio' +import { typeformFilesTool, typeformInsightsTool, typeformResponsesTool } from '@/tools/typeform' +import type { ToolConfig } from '@/tools/types' +import { visionTool } from '@/tools/vision' import { wealthboxReadContactTool, wealthboxReadNoteTool, @@ -102,11 +120,13 @@ import { wealthboxWriteContactTool, wealthboxWriteNoteTool, wealthboxWriteTaskTool, -} from './wealthbox' -import { whatsappSendMessageTool } from './whatsapp' -import { workflowExecutorTool } from './workflow' -import { xReadTool, xSearchTool, xUserTool, xWriteTool } from './x' -import { youtubeSearchTool } from './youtube' +} from '@/tools/wealthbox' +import { whatsappSendMessageTool } from '@/tools/whatsapp' +import { workflowExecutorTool } from '@/tools/workflow' +import { xReadTool, xSearchTool, xUserTool, xWriteTool } from '@/tools/x' +import { youtubeSearchTool } from '@/tools/youtube' +import { gmailDraftTool, gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail' +import { searchTool as googleSearchTool } from './google' // Registry of all available tools export const tools: Record = { @@ -119,6 +139,7 @@ export const tools: Record = { file_parser: fileParseTool, firecrawl_scrape: scrapeTool, firecrawl_search: searchTool, + firecrawl_crawl: crawlTool, google_search: googleSearchTool, jina_read_url: readUrlTool, linkup_search: linkupSearchTool, @@ -127,6 +148,8 @@ export const tools: Record = { jira_write: jiraWriteTool, jira_bulk_read: jiraBulkRetrieveTool, slack_message: slackMessageTool, + slack_message_reader: slackMessageReaderTool, + slack_canvas: slackCanvasTool, github_repo_info: githubRepoInfoTool, github_latest_commit: githubLatestCommitTool, serper_search: serperSearch, @@ -134,13 +157,20 @@ export const tools: Record = { tavily_extract: tavilyExtractTool, supabase_query: supabaseQueryTool, supabase_insert: supabaseInsertTool, + supabase_get_row: supabaseGetRowTool, + supabase_update: supabaseUpdateTool, + supabase_delete: supabaseDeleteTool, typeform_responses: typeformResponsesTool, typeform_files: typeformFilesTool, typeform_insights: typeformInsightsTool, youtube_search: youtubeSearchTool, notion_read: notionReadTool, + notion_read_database: notionReadDatabaseTool, notion_write: notionWriteTool, notion_create_page: notionCreatePageTool, + notion_query_database: notionQueryDatabaseTool, + notion_search: notionSearchTool, + notion_create_database: notionCreateDatabaseTool, gmail_send: gmailSendTool, gmail_read: gmailReadTool, gmail_search: gmailSearchTool, @@ -161,6 +191,7 @@ export const tools: Record = { exa_get_contents: exaGetContentsTool, exa_find_similar_links: exaFindSimilarLinksTool, exa_answer: exaAnswerTool, + exa_research: exaResearchTool, reddit_hot_posts: redditHotPostsTool, reddit_get_posts: redditGetPostsTool, reddit_get_comments: redditGetCommentsTool, diff --git a/apps/sim/tools/s3/get_object.ts b/apps/sim/tools/s3/get_object.ts index 57113cc7f..6a2b6ceb2 100644 --- a/apps/sim/tools/s3/get_object.ts +++ b/apps/sim/tools/s3/get_object.ts @@ -1,6 +1,11 @@ import crypto from 'crypto' -import type { ToolConfig } from '../types' -import { encodeS3PathComponent, generatePresignedUrl, getSignatureKey, parseS3Uri } from './utils' +import { + encodeS3PathComponent, + generatePresignedUrl, + getSignatureKey, + parseS3Uri, +} from '@/tools/s3/utils' +import type { ToolConfig } from '@/tools/types' export const s3GetObjectTool: ToolConfig = { id: 's3_get_object', diff --git a/apps/sim/tools/s3/index.ts b/apps/sim/tools/s3/index.ts index 8226e43c5..ad49b7cfd 100644 --- a/apps/sim/tools/s3/index.ts +++ b/apps/sim/tools/s3/index.ts @@ -1,3 +1,3 @@ -import { s3GetObjectTool } from './get_object' +import { s3GetObjectTool } from '@/tools/s3/get_object' export { s3GetObjectTool } diff --git a/apps/sim/tools/s3/types.ts b/apps/sim/tools/s3/types.ts index b2d61832e..bdccb226f 100644 --- a/apps/sim/tools/s3/types.ts +++ b/apps/sim/tools/s3/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface S3Response extends ToolResponse { output: { diff --git a/apps/sim/tools/serper/index.ts b/apps/sim/tools/serper/index.ts index 93b47653a..e8a347307 100644 --- a/apps/sim/tools/serper/index.ts +++ b/apps/sim/tools/serper/index.ts @@ -1,3 +1,3 @@ -import { searchTool } from './search' +import { searchTool } from '@/tools/serper/search' export { searchTool } diff --git a/apps/sim/tools/serper/search.ts b/apps/sim/tools/serper/search.ts index ee168369e..e9ec6c56b 100644 --- a/apps/sim/tools/serper/search.ts +++ b/apps/sim/tools/serper/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { SearchParams, SearchResponse, SearchResult } from './types' +import type { SearchParams, SearchResponse, SearchResult } from '@/tools/serper/types' +import type { ToolConfig } from '@/tools/types' export const searchTool: ToolConfig = { id: 'serper_search', diff --git a/apps/sim/tools/serper/types.ts b/apps/sim/tools/serper/types.ts index d2b77033c..c32e227a0 100644 --- a/apps/sim/tools/serper/types.ts +++ b/apps/sim/tools/serper/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface SearchParams { query: string diff --git a/apps/sim/tools/slack/canvas.ts b/apps/sim/tools/slack/canvas.ts new file mode 100644 index 000000000..1f1a40b7d --- /dev/null +++ b/apps/sim/tools/slack/canvas.ts @@ -0,0 +1,123 @@ +import type { SlackCanvasParams, SlackCanvasResponse } from '@/tools/slack/types' +import type { ToolConfig } from '@/tools/types' + +export const slackCanvasTool: ToolConfig = { + id: 'slack_canvas', + name: 'Slack Canvas Writer', + description: + 'Create and share Slack canvases in channels. Canvases are collaborative documents within Slack.', + version: '1.0.0', + + oauth: { + required: true, + provider: 'slack', + additionalScopes: [ + 'channels:read', + 'groups:read', + 'chat:write', + 'chat:write.public', + 'canvases:write', + 'files:write', + 'users:read', + ], + }, + + params: { + authMethod: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Authentication method: oauth or bot_token', + }, + botToken: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Bot token for Custom Bot', + }, + accessToken: { + type: 'string', + required: false, + visibility: 'hidden', + description: 'OAuth access token or bot token for Slack API', + }, + channel: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Target Slack channel (e.g., #general)', + }, + title: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Title of the canvas', + }, + content: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Canvas content in markdown format', + }, + document_content: { + type: 'object', + required: false, + visibility: 'hidden', + description: 'Structured canvas document content', + }, + }, + + request: { + url: 'https://slack.com/api/canvases.create', + method: 'POST', + headers: (params: SlackCanvasParams) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken || params.botToken}`, + }), + body: (params: SlackCanvasParams) => { + // Use structured document content if provided, otherwise use markdown format + if (params.document_content) { + return { + title: params.title, + channel_id: params.channel, + document_content: params.document_content, + } + } + // Use the correct Canvas API format with markdown + return { + title: params.title, + channel_id: params.channel, + document_content: { + type: 'markdown', + markdown: params.content, + }, + } + }, + }, + + transformResponse: async (response: Response, params?: SlackCanvasParams) => { + if (!params) { + throw new Error('Parameters are required for canvas creation') + } + const data = await response.json() + if (!data.ok) { + throw new Error(data.error || 'Slack Canvas API error') + } + + // The canvas is created in the channel, so we just need to return the result + // No need to post a separate message since the canvas appears in the channel + return { + success: true, + output: { + canvas_id: data.canvas_id || data.id, + channel: params.channel, + title: params.title, + }, + } + }, + + transformError: (error: any) => { + const message = error.message || 'Slack Canvas creation failed' + return message + }, +} diff --git a/apps/sim/tools/slack/index.ts b/apps/sim/tools/slack/index.ts index 642218ac9..0355b1478 100644 --- a/apps/sim/tools/slack/index.ts +++ b/apps/sim/tools/slack/index.ts @@ -1,3 +1,5 @@ -import { slackMessageTool } from './message' +import { slackCanvasTool } from '@/tools/slack/canvas' +import { slackMessageTool } from '@/tools/slack/message' +import { slackMessageReaderTool } from '@/tools/slack/message_reader' -export { slackMessageTool } +export { slackMessageTool, slackCanvasTool, slackMessageReaderTool } diff --git a/apps/sim/tools/slack/message.ts b/apps/sim/tools/slack/message.ts index a772e68b7..ab5035de7 100644 --- a/apps/sim/tools/slack/message.ts +++ b/apps/sim/tools/slack/message.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { SlackMessageParams, SlackMessageResponse } from './types' +import type { SlackMessageParams, SlackMessageResponse } from '@/tools/slack/types' +import type { ToolConfig } from '@/tools/types' export const slackMessageTool: ToolConfig = { id: 'slack_message', diff --git a/apps/sim/tools/slack/message_reader.ts b/apps/sim/tools/slack/message_reader.ts new file mode 100644 index 000000000..47822f359 --- /dev/null +++ b/apps/sim/tools/slack/message_reader.ts @@ -0,0 +1,120 @@ +import type { SlackMessageReaderParams, SlackMessageReaderResponse } from '@/tools/slack/types' +import type { ToolConfig } from '@/tools/types' + +export const slackMessageReaderTool: ToolConfig< + SlackMessageReaderParams, + SlackMessageReaderResponse +> = { + id: 'slack_message_reader', + name: 'Slack Message Reader', + description: + 'Read the latest messages from Slack channels. Retrieve conversation history with filtering options.', + version: '1.0.0', + + oauth: { + required: true, + provider: 'slack', + additionalScopes: [ + 'channels:read', + 'channels:history', + 'groups:read', + 'groups:history', + 'users:read', + ], + }, + + params: { + authMethod: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Authentication method: oauth or bot_token', + }, + botToken: { + type: 'string', + required: false, + visibility: 'user-only', + description: 'Bot token for Custom Bot', + }, + accessToken: { + type: 'string', + required: false, + visibility: 'hidden', + description: 'OAuth access token or bot token for Slack API', + }, + channel: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Slack channel to read messages from (e.g., #general)', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of messages to retrieve (default: 10, max: 100)', + }, + oldest: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Start of time range (timestamp)', + }, + latest: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'End of time range (timestamp)', + }, + }, + + request: { + url: (params: SlackMessageReaderParams) => { + const url = new URL('https://slack.com/api/conversations.history') + url.searchParams.append('channel', params.channel) + // Cap limit at 15 due to Slack API restrictions for non-Marketplace apps + url.searchParams.append('limit', String(Math.min(params.limit || 10, 15))) + + if (params.oldest) { + url.searchParams.append('oldest', params.oldest) + } + if (params.latest) { + url.searchParams.append('latest', params.latest) + } + + return url.toString() + }, + method: 'GET', + headers: (params: SlackMessageReaderParams) => ({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken || params.botToken}`, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + if (!data.ok) { + throw new Error(data.error || 'Slack Message Reader API error') + } + + const messages = (data.messages || []).map((message: any) => ({ + ts: message.ts, + text: message.text || '', + user: message.user || message.bot_id || 'unknown', + type: message.type || 'message', + subtype: message.subtype, + })) + + return { + success: true, + output: { + messages, + }, + } + }, + + transformError: (error: any) => { + const message = error.message || 'Slack message reading failed' + return message + }, +} diff --git a/apps/sim/tools/slack/types.ts b/apps/sim/tools/slack/types.ts index ce37dcac5..85778dbf2 100644 --- a/apps/sim/tools/slack/types.ts +++ b/apps/sim/tools/slack/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface SlackBaseParams { authMethod: 'oauth' | 'bot_token' @@ -12,6 +12,20 @@ export interface SlackMessageParams extends SlackBaseParams { thread_ts?: string } +export interface SlackCanvasParams extends SlackBaseParams { + channel: string + title: string + content: string + document_content?: object +} + +export interface SlackMessageReaderParams extends SlackBaseParams { + channel: string + limit?: number + oldest?: string + latest?: string +} + export interface SlackMessageResponse extends ToolResponse { output: { ts: string @@ -19,4 +33,24 @@ export interface SlackMessageResponse extends ToolResponse { } } -export type SlackResponse = SlackMessageResponse +export interface SlackCanvasResponse extends ToolResponse { + output: { + canvas_id: string + channel: string + title: string + } +} + +export interface SlackMessageReaderResponse extends ToolResponse { + output: { + messages: Array<{ + ts: string + text: string + user: string + type: string + subtype?: string + }> + } +} + +export type SlackResponse = SlackCanvasResponse | SlackMessageReaderResponse | SlackMessageResponse diff --git a/apps/sim/tools/stagehand/agent.ts b/apps/sim/tools/stagehand/agent.ts index dcee8eeef..c4b188332 100644 --- a/apps/sim/tools/stagehand/agent.ts +++ b/apps/sim/tools/stagehand/agent.ts @@ -1,6 +1,6 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { StagehandAgentParams, StagehandAgentResponse } from './types' +import type { StagehandAgentParams, StagehandAgentResponse } from '@/tools/stagehand/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('StagehandAgentTool') diff --git a/apps/sim/tools/stagehand/extract.ts b/apps/sim/tools/stagehand/extract.ts index bb4a290f2..1338e101a 100644 --- a/apps/sim/tools/stagehand/extract.ts +++ b/apps/sim/tools/stagehand/extract.ts @@ -1,6 +1,6 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { StagehandExtractParams, StagehandExtractResponse } from './types' +import type { StagehandExtractParams, StagehandExtractResponse } from '@/tools/stagehand/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('StagehandExtractTool') diff --git a/apps/sim/tools/stagehand/index.ts b/apps/sim/tools/stagehand/index.ts index 7d09d490f..9242617b7 100644 --- a/apps/sim/tools/stagehand/index.ts +++ b/apps/sim/tools/stagehand/index.ts @@ -1,5 +1,5 @@ -import { agentTool } from './agent' -import { extractTool } from './extract' +import { agentTool } from '@/tools/stagehand/agent' +import { extractTool } from '@/tools/stagehand/extract' export const stagehandExtractTool = extractTool export const stagehandAgentTool = agentTool diff --git a/apps/sim/tools/stagehand/types.ts b/apps/sim/tools/stagehand/types.ts index 3497fab42..dd224ad2f 100644 --- a/apps/sim/tools/stagehand/types.ts +++ b/apps/sim/tools/stagehand/types.ts @@ -1,5 +1,5 @@ import { z } from 'zod' -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface StagehandExtractParams { instruction: string diff --git a/apps/sim/tools/supabase/delete.ts b/apps/sim/tools/supabase/delete.ts new file mode 100644 index 000000000..dec512b7c --- /dev/null +++ b/apps/sim/tools/supabase/delete.ts @@ -0,0 +1,147 @@ +import type { SupabaseDeleteParams, SupabaseDeleteResponse } from '@/tools/supabase/types' +import type { ToolConfig } from '@/tools/types' + +export const deleteTool: ToolConfig = { + id: 'supabase_delete', + name: 'Supabase Delete Row', + description: 'Delete rows from a Supabase table based on filter criteria', + version: '1.0', + params: { + projectId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)', + }, + table: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the Supabase table to delete from', + }, + filter: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'PostgREST filter to identify rows to delete (e.g., "id=eq.123", "status=eq.active", "name=not.is.null")', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Your Supabase service role secret key', + }, + }, + request: { + url: (params) => `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*`, + method: 'DELETE', + headers: (params) => ({ + apikey: params.apiKey, + Authorization: `Bearer ${params.apiKey}`, + Prefer: 'return=representation', + }), + }, + directExecution: async (params: SupabaseDeleteParams) => { + try { + // Construct the URL for the Supabase REST API with select to return deleted data + let url = `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*` + + // Add filters (required for delete) - using PostgREST syntax + if (params.filter?.trim()) { + url += `&${params.filter.trim()}` + } else { + throw new Error( + 'Filter is required for delete operations to prevent accidental deletion of all rows' + ) + } + + // Fetch the data + const response = await fetch(url, { + method: 'DELETE', + headers: { + apikey: params.apiKey, + Authorization: `Bearer ${params.apiKey}`, + Prefer: 'return=representation', + }, + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Error from Supabase: ${response.status} ${errorText}`) + } + + // Handle empty response from delete operations + const text = await response.text() + let data + + if (text?.trim()) { + try { + data = JSON.parse(text) + } catch (e) { + // If we can't parse it, just use the text + data = text + } + } else { + // Empty response means successful deletion + data = [] + } + + const deletedCount = Array.isArray(data) ? data.length : text ? 1 : 0 + + return { + success: true, + output: { + message: `Successfully deleted ${deletedCount === 0 ? 'row(s)' : `${deletedCount} row(s)`} from ${params.table}`, + results: data, + }, + error: undefined, + } + } catch (error) { + return { + success: false, + output: { + message: `Error deleting rows from Supabase: ${error instanceof Error ? error.message : String(error)}`, + results: null, + }, + error: error instanceof Error ? error.message : String(error), + } + } + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json() + throw new Error(error.message || 'Failed to delete rows from Supabase') + } + + // Handle empty response from delete operations + const text = await response.text() + let data + + if (text?.trim()) { + try { + data = JSON.parse(text) + } catch (e) { + // If we can't parse it, just use the text + data = text + } + } else { + // Empty response means successful deletion + data = [] + } + + const deletedCount = Array.isArray(data) ? data.length : text ? 1 : 0 + + return { + success: true, + output: { + message: `Successfully deleted ${deletedCount === 0 ? 'row(s)' : `${deletedCount} row(s)`}`, + results: data, + }, + error: undefined, + } + }, + transformError: (error: any) => { + return error.message || 'An error occurred while deleting rows from Supabase' + }, +} diff --git a/apps/sim/tools/supabase/get_row.ts b/apps/sim/tools/supabase/get_row.ts new file mode 100644 index 000000000..4779b19c2 --- /dev/null +++ b/apps/sim/tools/supabase/get_row.ts @@ -0,0 +1,116 @@ +import type { SupabaseGetRowParams, SupabaseGetRowResponse } from '@/tools/supabase/types' +import type { ToolConfig } from '@/tools/types' + +export const getRowTool: ToolConfig = { + id: 'supabase_get_row', + name: 'Supabase Get Row', + description: 'Get a single row from a Supabase table based on filter criteria', + version: '1.0', + params: { + projectId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)', + }, + table: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the Supabase table to query', + }, + filter: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'PostgREST filter to find the specific row (e.g., "id=eq.123", "status=eq.active", "name=not.is.null")', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Your Supabase service role secret key', + }, + }, + request: { + url: (params) => `https://${params.projectId}.supabase.co/rest/v1/${params.table}`, + method: 'GET', + headers: (params) => ({ + apikey: params.apiKey, + Authorization: `Bearer ${params.apiKey}`, + }), + }, + directExecution: async (params: SupabaseGetRowParams) => { + try { + // Construct the URL for the Supabase REST API + let url = `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*` + + // Add filters (required for get_row) - using PostgREST syntax + if (params.filter?.trim()) { + url += `&${params.filter.trim()}` + } + + // Limit to 1 row since we want a single row + url += `&limit=1` + + // Fetch the data + const response = await fetch(url, { + method: 'GET', + headers: { + apikey: params.apiKey, + Authorization: `Bearer ${params.apiKey}`, + }, + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Error from Supabase: ${response.status} ${errorText}`) + } + + const data = await response.json() + const row = data.length > 0 ? data[0] : null + + return { + success: true, + output: { + message: row + ? `Successfully found row in ${params.table}` + : `No row found in ${params.table} matching the criteria`, + results: row, + }, + error: undefined, + } + } catch (error) { + return { + success: false, + output: { + message: `Error getting row from Supabase: ${error instanceof Error ? error.message : String(error)}`, + results: null, + }, + error: error instanceof Error ? error.message : String(error), + } + } + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json() + throw new Error(error.message || 'Failed to get row from Supabase') + } + + const data = await response.json() + const row = data.length > 0 ? data[0] : null + + return { + success: true, + output: { + message: row ? 'Successfully found row' : 'No row found matching the criteria', + results: row, + }, + error: undefined, + } + }, + transformError: (error: any) => { + return error.message || 'An error occurred while getting row from Supabase' + }, +} diff --git a/apps/sim/tools/supabase/index.ts b/apps/sim/tools/supabase/index.ts index 73c6c51bf..bc1a488ce 100644 --- a/apps/sim/tools/supabase/index.ts +++ b/apps/sim/tools/supabase/index.ts @@ -1,5 +1,11 @@ -import { insertTool } from './insert' -import { queryTool } from './query' +import { deleteTool } from '@/tools/supabase/delete' +import { getRowTool } from '@/tools/supabase/get_row' +import { insertTool } from '@/tools/supabase/insert' +import { queryTool } from '@/tools/supabase/query' +import { updateTool } from '@/tools/supabase/update' export const supabaseQueryTool = queryTool export const supabaseInsertTool = insertTool +export const supabaseGetRowTool = getRowTool +export const supabaseUpdateTool = updateTool +export const supabaseDeleteTool = deleteTool diff --git a/apps/sim/tools/supabase/insert.ts b/apps/sim/tools/supabase/insert.ts index e7cb92ebc..a1ddb9a32 100644 --- a/apps/sim/tools/supabase/insert.ts +++ b/apps/sim/tools/supabase/insert.ts @@ -1,16 +1,11 @@ -import type { ToolConfig } from '../types' -import type { SupabaseInsertParams, SupabaseInsertResponse } from './types' +import type { SupabaseInsertParams, SupabaseInsertResponse } from '@/tools/supabase/types' +import type { ToolConfig } from '@/tools/types' export const insertTool: ToolConfig = { id: 'supabase_insert', name: 'Supabase Insert', description: 'Insert data into a Supabase table', version: '1.0', - oauth: { - required: false, - provider: 'supabase', - additionalScopes: ['database.write', 'projects.read'], - }, params: { projectId: { type: 'string', @@ -33,8 +28,8 @@ export const insertTool: ToolConfig = { id: 'supabase_query', name: 'Supabase Query', description: 'Query data from a Supabase table', version: '1.0', - oauth: { - required: false, - provider: 'supabase', - additionalScopes: ['database.read', 'projects.read'], - }, params: { projectId: { type: 'string', @@ -25,16 +20,29 @@ export const queryTool: ToolConfig = description: 'The name of the Supabase table to query', }, filter: { - type: 'object', + type: 'string', required: false, visibility: 'user-or-llm', - description: 'Filter to apply to the query', + description: + 'PostgREST filter (e.g., "id=eq.2", "name=not.is.null", "age=gt.18&status=eq.active")', + }, + orderBy: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Column to order by (add DESC for descending)', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of rows to return', }, apiKey: { type: 'string', required: true, - visibility: 'user-only', - description: 'Your Supabase client anon key', + visibility: 'hidden', + description: 'Your Supabase service role secret key', }, }, request: { @@ -48,7 +56,25 @@ export const queryTool: ToolConfig = directExecution: async (params: SupabaseQueryParams) => { try { // Construct the URL for the Supabase REST API - const url = `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*` + let url = `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*` + + // Add filters if provided - using PostgREST syntax + if (params.filter?.trim()) { + url += `&${params.filter.trim()}` + } + + // Add order by if provided + if (params.orderBy) { + const orderParam = params.orderBy.includes('DESC') + ? `${params.orderBy.replace(' DESC', '').replace('DESC', '')}.desc` + : `${params.orderBy}.asc` + url += `&order=${orderParam}` + } + + // Add limit if provided + if (params.limit) { + url += `&limit=${params.limit}` + } // Fetch the data const response = await fetch(url, { @@ -69,7 +95,7 @@ export const queryTool: ToolConfig = return { success: true, output: { - message: `Successfully queried data from ${params.table}`, + message: `Successfully queried ${data.length} row(s) from ${params.table}`, results: data, }, error: undefined, diff --git a/apps/sim/tools/supabase/types.ts b/apps/sim/tools/supabase/types.ts index 9a62416df..da33bcd7a 100644 --- a/apps/sim/tools/supabase/types.ts +++ b/apps/sim/tools/supabase/types.ts @@ -1,9 +1,12 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface SupabaseQueryParams { apiKey: string projectId: string table: string + filter?: string + orderBy?: string + limit?: number } export interface SupabaseInsertParams { @@ -13,6 +16,28 @@ export interface SupabaseInsertParams { data: any } +export interface SupabaseGetRowParams { + apiKey: string + projectId: string + table: string + filter: string +} + +export interface SupabaseUpdateParams { + apiKey: string + projectId: string + table: string + filter: string + data: any +} + +export interface SupabaseDeleteParams { + apiKey: string + projectId: string + table: string + filter: string +} + export interface SupabaseBaseResponse extends ToolResponse { output: { message: string @@ -25,4 +50,10 @@ export interface SupabaseQueryResponse extends SupabaseBaseResponse {} export interface SupabaseInsertResponse extends SupabaseBaseResponse {} +export interface SupabaseGetRowResponse extends SupabaseBaseResponse {} + +export interface SupabaseUpdateResponse extends SupabaseBaseResponse {} + +export interface SupabaseDeleteResponse extends SupabaseBaseResponse {} + export interface SupabaseResponse extends SupabaseBaseResponse {} diff --git a/apps/sim/tools/supabase/update.ts b/apps/sim/tools/supabase/update.ts new file mode 100644 index 000000000..5a140dc95 --- /dev/null +++ b/apps/sim/tools/supabase/update.ts @@ -0,0 +1,152 @@ +import type { SupabaseUpdateParams, SupabaseUpdateResponse } from '@/tools/supabase/types' +import type { ToolConfig } from '@/tools/types' + +export const updateTool: ToolConfig = { + id: 'supabase_update', + name: 'Supabase Update Row', + description: 'Update rows in a Supabase table based on filter criteria', + version: '1.0', + params: { + projectId: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)', + }, + table: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'The name of the Supabase table to update', + }, + filter: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: + 'PostgREST filter to identify rows to update (e.g., "id=eq.123", "status=eq.active", "name=not.is.null")', + }, + data: { + type: 'object', + required: true, + visibility: 'user-or-llm', + description: 'Data to update in the matching rows', + }, + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Your Supabase service role secret key', + }, + }, + request: { + url: (params) => `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*`, + method: 'PATCH', + headers: (params) => ({ + apikey: params.apiKey, + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Prefer: 'return=representation', + }), + }, + directExecution: async (params: SupabaseUpdateParams) => { + try { + // Construct the URL for the Supabase REST API with select to return updated data + let url = `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*` + + // Add filters (required for update) - using PostgREST syntax + if (params.filter?.trim()) { + url += `&${params.filter.trim()}` + } + + // Fetch the data + const response = await fetch(url, { + method: 'PATCH', + headers: { + apikey: params.apiKey, + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + Prefer: 'return=representation', + }, + body: JSON.stringify(params.data), + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Error from Supabase: ${response.status} ${errorText}`) + } + + // Handle potentially empty response from update operations + const text = await response.text() + let data + + if (text?.trim()) { + try { + data = JSON.parse(text) + } catch (e) { + // If we can't parse it, just use the text + data = text + } + } else { + // Empty response means successful update + data = [] + } + + const updatedCount = Array.isArray(data) ? data.length : text ? 1 : 0 + + return { + success: true, + output: { + message: `Successfully updated ${updatedCount === 0 ? 'row(s)' : `${updatedCount} row(s)`} in ${params.table}`, + results: data, + }, + error: undefined, + } + } catch (error) { + return { + success: false, + output: { + message: `Error updating rows in Supabase: ${error instanceof Error ? error.message : String(error)}`, + results: null, + }, + error: error instanceof Error ? error.message : String(error), + } + } + }, + transformResponse: async (response: Response) => { + if (!response.ok) { + const error = await response.json() + throw new Error(error.message || 'Failed to update rows in Supabase') + } + + // Handle potentially empty response from update operations + const text = await response.text() + let data + + if (text?.trim()) { + try { + data = JSON.parse(text) + } catch (e) { + // If we can't parse it, just use the text + data = text + } + } else { + // Empty response means successful update + data = [] + } + + const updatedCount = Array.isArray(data) ? data.length : text ? 1 : 0 + + return { + success: true, + output: { + message: `Successfully updated ${updatedCount === 0 ? 'row(s)' : `${updatedCount} row(s)`}`, + results: data, + }, + error: undefined, + } + }, + transformError: (error: any) => { + return error.message || 'An error occurred while updating rows in Supabase' + }, +} diff --git a/apps/sim/tools/tavily/extract.ts b/apps/sim/tools/tavily/extract.ts index 5476eaff2..3403def96 100644 --- a/apps/sim/tools/tavily/extract.ts +++ b/apps/sim/tools/tavily/extract.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { TavilyExtractParams, TavilyExtractResponse } from './types' +import type { TavilyExtractParams, TavilyExtractResponse } from '@/tools/tavily/types' +import type { ToolConfig } from '@/tools/types' export const extractTool: ToolConfig = { id: 'tavily_extract', diff --git a/apps/sim/tools/tavily/index.ts b/apps/sim/tools/tavily/index.ts index 872caa5ee..f301820c8 100644 --- a/apps/sim/tools/tavily/index.ts +++ b/apps/sim/tools/tavily/index.ts @@ -1,5 +1,5 @@ -import { extractTool } from './extract' -import { searchTool } from './search' +import { extractTool } from '@/tools/tavily/extract' +import { searchTool } from '@/tools/tavily/search' export const tavilyExtractTool = extractTool export const tavilySearchTool = searchTool diff --git a/apps/sim/tools/tavily/search.ts b/apps/sim/tools/tavily/search.ts index 241c40205..c900973d8 100644 --- a/apps/sim/tools/tavily/search.ts +++ b/apps/sim/tools/tavily/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { TavilySearchParams, TavilySearchResponse } from './types' +import type { TavilySearchParams, TavilySearchResponse } from '@/tools/tavily/types' +import type { ToolConfig } from '@/tools/types' export const searchTool: ToolConfig = { id: 'tavily_search', diff --git a/apps/sim/tools/tavily/types.ts b/apps/sim/tools/tavily/types.ts index 4e0018068..658be45e8 100644 --- a/apps/sim/tools/tavily/types.ts +++ b/apps/sim/tools/tavily/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface TavilySearchResult { title: string diff --git a/apps/sim/tools/telegram/index.ts b/apps/sim/tools/telegram/index.ts index 25648e34f..8b8e96974 100644 --- a/apps/sim/tools/telegram/index.ts +++ b/apps/sim/tools/telegram/index.ts @@ -1,3 +1,3 @@ -import { telegramMessageTool } from './message' +import { telegramMessageTool } from '@/tools/telegram/message' export { telegramMessageTool } diff --git a/apps/sim/tools/telegram/message.ts b/apps/sim/tools/telegram/message.ts index a58f0ee04..0235365cc 100644 --- a/apps/sim/tools/telegram/message.ts +++ b/apps/sim/tools/telegram/message.ts @@ -1,6 +1,6 @@ -import type { ToolConfig } from '../types' -import type { TelegramMessageParams, TelegramMessageResponse } from './types' -import { convertMarkdownToHTML } from './utils' +import type { TelegramMessageParams, TelegramMessageResponse } from '@/tools/telegram/types' +import { convertMarkdownToHTML } from '@/tools/telegram/utils' +import type { ToolConfig } from '@/tools/types' export const telegramMessageTool: ToolConfig = { id: 'telegram_message', diff --git a/apps/sim/tools/telegram/types.ts b/apps/sim/tools/telegram/types.ts index fd5819ec9..965f61c72 100644 --- a/apps/sim/tools/telegram/types.ts +++ b/apps/sim/tools/telegram/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface TelegramMessageParams { botToken: string diff --git a/apps/sim/tools/thinking/index.ts b/apps/sim/tools/thinking/index.ts index 539cc2f3f..c543040c9 100644 --- a/apps/sim/tools/thinking/index.ts +++ b/apps/sim/tools/thinking/index.ts @@ -1,3 +1,3 @@ -import { thinkingTool } from './tool' +import { thinkingTool } from '@/tools/thinking/tool' export { thinkingTool } diff --git a/apps/sim/tools/thinking/tool.ts b/apps/sim/tools/thinking/tool.ts index c4d54d2e0..939648e60 100644 --- a/apps/sim/tools/thinking/tool.ts +++ b/apps/sim/tools/thinking/tool.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { ThinkingToolParams, ThinkingToolResponse } from './types' +import type { ThinkingToolParams, ThinkingToolResponse } from '@/tools/thinking/types' +import type { ToolConfig } from '@/tools/types' export const thinkingTool: ToolConfig = { id: 'thinking_tool', diff --git a/apps/sim/tools/thinking/types.ts b/apps/sim/tools/thinking/types.ts index e3d7350c0..a1eade9da 100644 --- a/apps/sim/tools/thinking/types.ts +++ b/apps/sim/tools/thinking/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface ThinkingToolParams { thought: string diff --git a/apps/sim/tools/twilio/index.ts b/apps/sim/tools/twilio/index.ts index 98cc0b097..4aeef464a 100644 --- a/apps/sim/tools/twilio/index.ts +++ b/apps/sim/tools/twilio/index.ts @@ -1,3 +1,3 @@ -import { sendSMSTool } from './send_sms' +import { sendSMSTool } from '@/tools/twilio/send_sms' export { sendSMSTool } diff --git a/apps/sim/tools/twilio/send_sms.ts b/apps/sim/tools/twilio/send_sms.ts index 600f9c7df..a3de5f198 100644 --- a/apps/sim/tools/twilio/send_sms.ts +++ b/apps/sim/tools/twilio/send_sms.ts @@ -1,6 +1,6 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { TwilioSendSMSParams, TwilioSMSBlockOutput } from './types' +import type { TwilioSendSMSParams, TwilioSMSBlockOutput } from '@/tools/twilio/types' +import type { ToolConfig } from '@/tools/types' const logger = createLogger('Twilio Send SMS Tool') diff --git a/apps/sim/tools/twilio/types.ts b/apps/sim/tools/twilio/types.ts index d7fd41565..f9afc5570 100644 --- a/apps/sim/tools/twilio/types.ts +++ b/apps/sim/tools/twilio/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface TwilioSendSMSParams { phoneNumbers: string diff --git a/apps/sim/tools/typeform/files.ts b/apps/sim/tools/typeform/files.ts index 839ea033d..dd0ea89b8 100644 --- a/apps/sim/tools/typeform/files.ts +++ b/apps/sim/tools/typeform/files.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { TypeformFilesParams, TypeformFilesResponse } from './types' +import type { TypeformFilesParams, TypeformFilesResponse } from '@/tools/typeform/types' +import type { ToolConfig } from '@/tools/types' export const filesTool: ToolConfig = { id: 'typeform_files', diff --git a/apps/sim/tools/typeform/index.ts b/apps/sim/tools/typeform/index.ts index 9034fb468..850d626a5 100644 --- a/apps/sim/tools/typeform/index.ts +++ b/apps/sim/tools/typeform/index.ts @@ -1,6 +1,6 @@ -import { filesTool } from './files' -import { insightsTool } from './insights' -import { responsesTool } from './responses' +import { filesTool } from '@/tools/typeform/files' +import { insightsTool } from '@/tools/typeform/insights' +import { responsesTool } from '@/tools/typeform/responses' export const typeformResponsesTool = responsesTool export const typeformFilesTool = filesTool diff --git a/apps/sim/tools/typeform/insights.ts b/apps/sim/tools/typeform/insights.ts index d68626faa..d75b28966 100644 --- a/apps/sim/tools/typeform/insights.ts +++ b/apps/sim/tools/typeform/insights.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { TypeformInsightsParams, TypeformInsightsResponse } from './types' +import type { TypeformInsightsParams, TypeformInsightsResponse } from '@/tools/typeform/types' +import type { ToolConfig } from '@/tools/types' export const insightsTool: ToolConfig = { id: 'typeform_insights', diff --git a/apps/sim/tools/typeform/responses.ts b/apps/sim/tools/typeform/responses.ts index 65fd25ad3..96b9e0ba6 100644 --- a/apps/sim/tools/typeform/responses.ts +++ b/apps/sim/tools/typeform/responses.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { TypeformResponsesParams, TypeformResponsesResponse } from './types' +import type { TypeformResponsesParams, TypeformResponsesResponse } from '@/tools/typeform/types' +import type { ToolConfig } from '@/tools/types' export const responsesTool: ToolConfig = { id: 'typeform_responses', diff --git a/apps/sim/tools/typeform/types.ts b/apps/sim/tools/typeform/types.ts index d7562efbf..b8d876130 100644 --- a/apps/sim/tools/typeform/types.ts +++ b/apps/sim/tools/typeform/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface TypeformFilesParams { formId: string diff --git a/apps/sim/tools/types.ts b/apps/sim/tools/types.ts index 363faf852..83777f5b5 100644 --- a/apps/sim/tools/types.ts +++ b/apps/sim/tools/types.ts @@ -1,6 +1,6 @@ import type { OAuthService } from '@/lib/oauth/oauth' -export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' +export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' export type ParameterVisibility = | 'user-or-llm' // User can provide OR LLM must generate @@ -50,7 +50,7 @@ export interface ToolConfig

{ // Request configuration request: { url: string | ((params: P) => string) - method: string + method: HttpMethod headers: (params: P) => Record body?: (params: P) => Record isInternalRoute?: boolean // Whether this is an internal API route diff --git a/apps/sim/tools/utils.test.ts b/apps/sim/tools/utils.test.ts index 2fe41bec8..10a452758 100644 --- a/apps/sim/tools/utils.test.ts +++ b/apps/sim/tools/utils.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { ToolConfig } from './types' +import type { ToolConfig } from '@/tools/types' import { createCustomToolRequestBody, createParamSchema, @@ -8,7 +8,7 @@ import { getClientEnvVars, transformTable, validateToolRequest, -} from './utils' +} from '@/tools/utils' vi.mock('@/lib/logs/console-logger', () => ({ createLogger: vi.fn().mockReturnValue({ @@ -202,10 +202,12 @@ describe('validateToolRequest', () => { params: { required1: { type: 'string', + required: true, visibility: 'user-or-llm', }, required2: { type: 'number', + required: true, visibility: 'user-or-llm', }, optional: { diff --git a/apps/sim/tools/utils.ts b/apps/sim/tools/utils.ts index 3c0209c24..58c9f5fd8 100644 --- a/apps/sim/tools/utils.ts +++ b/apps/sim/tools/utils.ts @@ -2,10 +2,10 @@ import { createLogger } from '@/lib/logs/console-logger' import { getBaseUrl } from '@/lib/urls/utils' import { useCustomToolsStore } from '@/stores/custom-tools/store' import { useEnvironmentStore } from '@/stores/settings/environment/store' -import { docsSearchTool } from './docs/search' -import { tools } from './registry' -import type { TableRow, ToolConfig, ToolResponse } from './types' -import { getUserWorkflowTool } from './workflow/get-yaml' +import { docsSearchTool } from '@/tools/docs/search' +import { tools } from '@/tools/registry' +import type { TableRow, ToolConfig, ToolResponse } from '@/tools/types' +import { getUserWorkflowTool } from '@/tools/workflow/get-yaml' const logger = createLogger('ToolsUtils') @@ -166,7 +166,11 @@ export function validateToolRequest( // Ensure all required parameters for tool call are provided // Note: user-only parameters are not checked here as they're optional for (const [paramName, paramConfig] of Object.entries(tool.params)) { - if (paramConfig.visibility === 'user-or-llm' && !(paramName in params)) { + if ( + paramConfig.visibility === 'user-or-llm' && + paramConfig.required && + !(paramName in params) + ) { throw new Error(`Parameter "${paramName}" is required for ${toolId} but was not provided`) } } diff --git a/apps/sim/tools/vision/index.ts b/apps/sim/tools/vision/index.ts index 33ebc1c11..8b4f0ad59 100644 --- a/apps/sim/tools/vision/index.ts +++ b/apps/sim/tools/vision/index.ts @@ -1,3 +1,3 @@ -import { visionTool } from './tool' +import { visionTool } from '@/tools/vision/tool' export { visionTool } diff --git a/apps/sim/tools/vision/tool.ts b/apps/sim/tools/vision/tool.ts index 604934dea..6bf3e43b7 100644 --- a/apps/sim/tools/vision/tool.ts +++ b/apps/sim/tools/vision/tool.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { VisionParams, VisionResponse } from './types' +import type { ToolConfig } from '@/tools/types' +import type { VisionParams, VisionResponse } from '@/tools/vision/types' export const visionTool: ToolConfig = { id: 'vision_tool', diff --git a/apps/sim/tools/vision/types.ts b/apps/sim/tools/vision/types.ts index 4aef9a848..d4401e69b 100644 --- a/apps/sim/tools/vision/types.ts +++ b/apps/sim/tools/vision/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface VisionParams { apiKey: string diff --git a/apps/sim/tools/wealthbox/index.ts b/apps/sim/tools/wealthbox/index.ts index 25845a666..11068b999 100644 --- a/apps/sim/tools/wealthbox/index.ts +++ b/apps/sim/tools/wealthbox/index.ts @@ -1,9 +1,9 @@ -import { wealthboxReadContactTool } from './read_contact' -import { wealthboxReadNoteTool } from './read_note' -import { wealthboxReadTaskTool } from './read_task' -import { wealthboxWriteContactTool } from './write_contact' -import { wealthboxWriteNoteTool } from './write_note' -import { wealthboxWriteTaskTool } from './write_task' +import { wealthboxReadContactTool } from '@/tools/wealthbox/read_contact' +import { wealthboxReadNoteTool } from '@/tools/wealthbox/read_note' +import { wealthboxReadTaskTool } from '@/tools/wealthbox/read_task' +import { wealthboxWriteContactTool } from '@/tools/wealthbox/write_contact' +import { wealthboxWriteNoteTool } from '@/tools/wealthbox/write_note' +import { wealthboxWriteTaskTool } from '@/tools/wealthbox/write_task' export { wealthboxReadNoteTool } export { wealthboxWriteNoteTool } diff --git a/apps/sim/tools/whatsapp/index.ts b/apps/sim/tools/whatsapp/index.ts index 1aba69467..cde6dc689 100644 --- a/apps/sim/tools/whatsapp/index.ts +++ b/apps/sim/tools/whatsapp/index.ts @@ -1,3 +1,3 @@ -import { sendMessageTool } from './send_message' +import { sendMessageTool } from '@/tools/whatsapp/send_message' export const whatsappSendMessageTool = sendMessageTool diff --git a/apps/sim/tools/whatsapp/send_message.ts b/apps/sim/tools/whatsapp/send_message.ts index 0df7cc942..4bac6b422 100644 --- a/apps/sim/tools/whatsapp/send_message.ts +++ b/apps/sim/tools/whatsapp/send_message.ts @@ -1,6 +1,6 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { WhatsAppResponse, WhatsAppSendMessageParams } from './types' +import type { ToolConfig } from '@/tools/types' +import type { WhatsAppResponse, WhatsAppSendMessageParams } from '@/tools/whatsapp/types' const logger = createLogger('WhatsAppSendMessageTool') diff --git a/apps/sim/tools/whatsapp/types.ts b/apps/sim/tools/whatsapp/types.ts index 5db258fa0..3c5699748 100644 --- a/apps/sim/tools/whatsapp/types.ts +++ b/apps/sim/tools/whatsapp/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface WhatsAppSendMessageParams { phoneNumber: string diff --git a/apps/sim/tools/workflow/get-yaml.ts b/apps/sim/tools/workflow/get-yaml.ts index 87f58aaf8..40af7713c 100644 --- a/apps/sim/tools/workflow/get-yaml.ts +++ b/apps/sim/tools/workflow/get-yaml.ts @@ -1,4 +1,4 @@ -import type { ToolConfig, ToolResponse } from '../types' +import type { ToolConfig, ToolResponse } from '@/tools/types' interface GetWorkflowParams { includeMetadata?: boolean diff --git a/apps/sim/tools/workflow/index.ts b/apps/sim/tools/workflow/index.ts index 785a1d5cf..6330fc9c4 100644 --- a/apps/sim/tools/workflow/index.ts +++ b/apps/sim/tools/workflow/index.ts @@ -1 +1 @@ -export { workflowExecutorTool } from './executor' +export { workflowExecutorTool } from '@/tools/workflow/executor' diff --git a/apps/sim/tools/x/index.ts b/apps/sim/tools/x/index.ts index 9dfba17ad..3c29709a5 100644 --- a/apps/sim/tools/x/index.ts +++ b/apps/sim/tools/x/index.ts @@ -1,7 +1,7 @@ -import { xReadTool } from './read' -import { xSearchTool } from './search' -import { xUserTool } from './user' -import { xWriteTool } from './write' +import { xReadTool } from '@/tools/x/read' +import { xSearchTool } from '@/tools/x/search' +import { xUserTool } from '@/tools/x/user' +import { xWriteTool } from '@/tools/x/write' export { xReadTool } export { xWriteTool } diff --git a/apps/sim/tools/x/read.ts b/apps/sim/tools/x/read.ts index ecb3f2a93..66a30a883 100644 --- a/apps/sim/tools/x/read.ts +++ b/apps/sim/tools/x/read.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { XReadParams, XReadResponse, XTweet } from './types' +import type { ToolConfig } from '@/tools/types' +import type { XReadParams, XReadResponse, XTweet } from '@/tools/x/types' export const xReadTool: ToolConfig = { id: 'x_read', diff --git a/apps/sim/tools/x/search.ts b/apps/sim/tools/x/search.ts index de2c54ab9..94f0afbfa 100644 --- a/apps/sim/tools/x/search.ts +++ b/apps/sim/tools/x/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { XSearchParams, XSearchResponse, XTweet, XUser } from './types' +import type { ToolConfig } from '@/tools/types' +import type { XSearchParams, XSearchResponse, XTweet, XUser } from '@/tools/x/types' export const xSearchTool: ToolConfig = { id: 'x_search', diff --git a/apps/sim/tools/x/types.ts b/apps/sim/tools/x/types.ts index 62c52209e..407e0c11c 100644 --- a/apps/sim/tools/x/types.ts +++ b/apps/sim/tools/x/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' // Common Types export interface XTweet { diff --git a/apps/sim/tools/x/user.ts b/apps/sim/tools/x/user.ts index 7474c09dd..10cb91cc7 100644 --- a/apps/sim/tools/x/user.ts +++ b/apps/sim/tools/x/user.ts @@ -1,6 +1,6 @@ import { createLogger } from '@/lib/logs/console-logger' -import type { ToolConfig } from '../types' -import type { XUser, XUserParams, XUserResponse } from './types' +import type { ToolConfig } from '@/tools/types' +import type { XUser, XUserParams, XUserResponse } from '@/tools/x/types' const logger = createLogger('XUserTool') diff --git a/apps/sim/tools/x/write.ts b/apps/sim/tools/x/write.ts index 51e09a68c..c509c33fc 100644 --- a/apps/sim/tools/x/write.ts +++ b/apps/sim/tools/x/write.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { XWriteParams, XWriteResponse } from './types' +import type { ToolConfig } from '@/tools/types' +import type { XWriteParams, XWriteResponse } from '@/tools/x/types' export const xWriteTool: ToolConfig = { id: 'x_write', diff --git a/apps/sim/tools/youtube/index.ts b/apps/sim/tools/youtube/index.ts index e17092b54..f0001ac06 100644 --- a/apps/sim/tools/youtube/index.ts +++ b/apps/sim/tools/youtube/index.ts @@ -1,3 +1,3 @@ -import { youtubeSearchTool } from './search' +import { youtubeSearchTool } from '@/tools/youtube/search' export { youtubeSearchTool } diff --git a/apps/sim/tools/youtube/search.ts b/apps/sim/tools/youtube/search.ts index 2c3bf19a0..bd910278c 100644 --- a/apps/sim/tools/youtube/search.ts +++ b/apps/sim/tools/youtube/search.ts @@ -1,5 +1,5 @@ -import type { ToolConfig } from '../types' -import type { YouTubeSearchParams, YouTubeSearchResponse } from './types' +import type { ToolConfig } from '@/tools/types' +import type { YouTubeSearchParams, YouTubeSearchResponse } from '@/tools/youtube/types' export const youtubeSearchTool: ToolConfig = { id: 'youtube_search', diff --git a/apps/sim/tools/youtube/types.ts b/apps/sim/tools/youtube/types.ts index 8b3bb9924..c06676801 100644 --- a/apps/sim/tools/youtube/types.ts +++ b/apps/sim/tools/youtube/types.ts @@ -1,4 +1,4 @@ -import type { ToolResponse } from '../types' +import type { ToolResponse } from '@/tools/types' export interface YouTubeSearchParams { apiKey: string