Skip to content

Commit cdfb2fc

Browse files
authored
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
1 parent 5ee6625 commit cdfb2fc

File tree

249 files changed

+2898
-620
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

249 files changed

+2898
-620
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { type NextRequest, NextResponse } from 'next/server'
2+
3+
export async function GET(
4+
request: NextRequest,
5+
{ params }: { params: Promise<{ jobId: string }> }
6+
) {
7+
const { jobId } = await params
8+
const authHeader = request.headers.get('authorization')
9+
10+
if (!authHeader) {
11+
return NextResponse.json({ error: 'Authorization header is required' }, { status: 401 })
12+
}
13+
14+
try {
15+
const response = await fetch(`https://api.firecrawl.dev/v1/crawl/${jobId}`, {
16+
method: 'GET',
17+
headers: {
18+
Authorization: authHeader,
19+
'Content-Type': 'application/json',
20+
},
21+
})
22+
23+
const data = await response.json()
24+
25+
if (!response.ok) {
26+
return NextResponse.json(
27+
{ error: data.error || data.message || 'Failed to get crawl status' },
28+
{ status: response.status }
29+
)
30+
}
31+
32+
return NextResponse.json(data)
33+
} catch (error: any) {
34+
return NextResponse.json(
35+
{ error: `Failed to fetch crawl status: ${error.message}` },
36+
{ status: 500 }
37+
)
38+
}
39+
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'
44
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
55
import type { SubBlockConfig } from '@/blocks/types'
66
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
7+
import { useSubBlockValue } from '../../hooks/use-sub-block-value'
78
import { type SlackChannelInfo, SlackChannelSelector } from './components/slack-channel-selector'
89

910
interface ChannelSelectorInputProps {
@@ -25,7 +26,10 @@ export function ChannelSelectorInput({
2526
isPreview = false,
2627
previewValue,
2728
}: ChannelSelectorInputProps) {
28-
const { getValue, setValue } = useSubBlockStore()
29+
const { getValue } = useSubBlockStore()
30+
31+
// Use the proper hook to get the current value and setter (same as file-selector)
32+
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id)
2933
const [selectedChannelId, setSelectedChannelId] = useState<string>('')
3034
const [_channelInfo, setChannelInfo] = useState<SlackChannelInfo | null>(null)
3135

@@ -47,9 +51,9 @@ export function ChannelSelectorInput({
4751
}
4852

4953
// Use preview value when in preview mode, otherwise use store value
50-
const value = isPreview ? previewValue : getValue(blockId, subBlock.id)
54+
const value = isPreview ? previewValue : storeValue
5155

52-
// Get the current value from the store or prop value if in preview mode
56+
// Get the current value from the store or prop value if in preview mode (same pattern as file-selector)
5357
useEffect(() => {
5458
if (isPreview && previewValue !== undefined) {
5559
const value = previewValue
@@ -64,12 +68,12 @@ export function ChannelSelectorInput({
6468
}
6569
}, [blockId, subBlock.id, getValue, isPreview, previewValue])
6670

67-
// Handle channel selection
71+
// Handle channel selection (same pattern as file-selector)
6872
const handleChannelChange = (channelId: string, info?: SlackChannelInfo) => {
6973
setSelectedChannelId(channelId)
7074
setChannelInfo(info || null)
7175
if (!isPreview) {
72-
setValue(blockId, subBlock.id, channelId)
76+
setStoreValue(channelId)
7377
}
7478
onChannelSelect?.(channelId)
7579
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,15 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
107107
'guilds.members.read': 'Read your Discord guild members',
108108
read: 'Read access to your workspace',
109109
write: 'Write access to your Linear workspace',
110-
'channels:read': 'Read your Slack channels',
111-
'groups:read': 'Read your Slack private channels',
112-
'chat:write': 'Write to your invited Slack channels',
113-
'chat:write.public': 'Write to your public Slack channels',
114-
'users:read': 'Read your Slack users',
115-
'search:read': 'Read your Slack search',
116-
'files:read': 'Read your Slack files',
117-
'links:read': 'Read your Slack links',
118-
'links:write': 'Write to your Slack links',
110+
'channels:read': 'View public channels',
111+
'channels:history': 'Read channel messages',
112+
'groups:read': 'View private channels',
113+
'groups:history': 'Read private messages',
114+
'chat:write': 'Send messages',
115+
'chat:write.public': 'Post to public channels',
116+
'users:read': 'View workspace users',
117+
'files:write': 'Upload files',
118+
'canvases:write': 'Create canvas documents',
119119
}
120120

121121
// Convert OAuth scope to user-friendly description

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,9 +1006,9 @@ export function ToolInput({
10061006
case 'channel-selector':
10071007
return (
10081008
<ChannelSelectorInput
1009-
blockId={uniqueBlockId}
1009+
blockId={blockId}
10101010
subBlock={{
1011-
id: param.id,
1011+
id: `tool-${toolIndex || 0}-${param.id}`,
10121012
type: 'channel-selector' as const,
10131013
title: param.id,
10141014
provider: uiComponent.provider || 'slack',
@@ -1023,9 +1023,9 @@ export function ToolInput({
10231023
case 'project-selector':
10241024
return (
10251025
<ProjectSelectorInput
1026-
blockId={uniqueBlockId}
1026+
blockId={blockId}
10271027
subBlock={{
1028-
id: param.id,
1028+
id: `tool-${toolIndex || 0}-${param.id}`,
10291029
type: 'project-selector' as const,
10301030
title: param.id,
10311031
provider: uiComponent.provider || 'jira',
@@ -1632,7 +1632,7 @@ export function ToolInput({
16321632
{param.required && param.visibility === 'user-only' && (
16331633
<span className='ml-1 text-red-500'>*</span>
16341634
)}
1635-
{!param.required && (
1635+
{(!param.required || param.visibility !== 'user-only') && (
16361636
<span className='ml-1 text-muted-foreground/60 text-xs'>
16371637
(Optional)
16381638
</span>

apps/sim/app/workspace/[workspaceId]/w/components/search-modal/search-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,4 +896,4 @@ export function SearchModal({
896896
</DialogPortal>
897897
</Dialog>
898898
)
899-
}
899+
}

apps/sim/blocks/blocks/exa.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
1313
bgColor: '#1F40ED',
1414
icon: ExaAIIcon,
1515
subBlocks: [
16-
// Operation selector
1716
{
1817
id: 'operation',
1918
title: 'Operation',
@@ -24,6 +23,7 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
2423
{ label: 'Get Contents', id: 'exa_get_contents' },
2524
{ label: 'Find Similar Links', id: 'exa_find_similar_links' },
2625
{ label: 'Answer', id: 'exa_answer' },
26+
{ label: 'Research', id: 'exa_research' },
2727
],
2828
value: () => 'exa_search',
2929
},
@@ -129,6 +129,22 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
129129
layout: 'full',
130130
condition: { field: 'operation', value: 'exa_answer' },
131131
},
132+
// Research operation inputs
133+
{
134+
id: 'query',
135+
title: 'Research Query',
136+
type: 'long-input',
137+
layout: 'full',
138+
placeholder: 'Enter your research topic or question...',
139+
condition: { field: 'operation', value: 'exa_research' },
140+
},
141+
{
142+
id: 'includeText',
143+
title: 'Include Full Text',
144+
type: 'switch',
145+
layout: 'full',
146+
condition: { field: 'operation', value: 'exa_research' },
147+
},
132148
// API Key (common)
133149
{
134150
id: 'apiKey',
@@ -140,7 +156,13 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
140156
},
141157
],
142158
tools: {
143-
access: ['exa_search', 'exa_get_contents', 'exa_find_similar_links', 'exa_answer'],
159+
access: [
160+
'exa_search',
161+
'exa_get_contents',
162+
'exa_find_similar_links',
163+
'exa_answer',
164+
'exa_research',
165+
],
144166
config: {
145167
tool: (params) => {
146168
// Convert numResults to a number for operations that use it
@@ -157,6 +179,8 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
157179
return 'exa_find_similar_links'
158180
case 'exa_answer':
159181
return 'exa_answer'
182+
case 'exa_research':
183+
return 'exa_research'
160184
default:
161185
return 'exa_search'
162186
}
@@ -186,5 +210,7 @@ export const ExaBlock: BlockConfig<ExaResponse> = {
186210
// Answer output
187211
answer: 'string',
188212
citations: 'json',
213+
// Research output
214+
research: 'json',
189215
},
190216
}

apps/sim/blocks/blocks/firecrawl.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
2121
options: [
2222
{ label: 'Scrape', id: 'scrape' },
2323
{ label: 'Search', id: 'search' },
24+
{ label: 'Crawl', id: 'crawl' },
2425
],
2526
value: () => 'scrape',
2627
},
@@ -29,10 +30,10 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
2930
title: 'Website URL',
3031
type: 'short-input',
3132
layout: 'full',
32-
placeholder: 'Enter the webpage URL to scrape',
33+
placeholder: 'Enter the website URL',
3334
condition: {
3435
field: 'operation',
35-
value: 'scrape',
36+
value: ['scrape', 'crawl'],
3637
},
3738
},
3839
{
@@ -45,6 +46,17 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
4546
value: 'scrape',
4647
},
4748
},
49+
{
50+
id: 'limit',
51+
title: 'Page Limit',
52+
type: 'short-input',
53+
layout: 'half',
54+
placeholder: '100',
55+
condition: {
56+
field: 'operation',
57+
value: 'crawl',
58+
},
59+
},
4860
{
4961
id: 'query',
5062
title: 'Search Query',
@@ -66,24 +78,40 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
6678
},
6779
],
6880
tools: {
69-
access: ['firecrawl_scrape', 'firecrawl_search'],
81+
access: ['firecrawl_scrape', 'firecrawl_search', 'firecrawl_crawl'],
7082
config: {
7183
tool: (params) => {
7284
switch (params.operation) {
7385
case 'scrape':
7486
return 'firecrawl_scrape'
7587
case 'search':
7688
return 'firecrawl_search'
89+
case 'crawl':
90+
return 'firecrawl_crawl'
7791
default:
7892
return 'firecrawl_scrape'
7993
}
8094
},
95+
params: (params) => {
96+
const { operation, limit, ...rest } = params
97+
98+
switch (operation) {
99+
case 'crawl':
100+
return {
101+
...rest,
102+
limit: limit ? Number.parseInt(limit) : undefined,
103+
}
104+
default:
105+
return rest
106+
}
107+
},
81108
},
82109
},
83110
inputs: {
84111
apiKey: { type: 'string', required: true },
85112
operation: { type: 'string', required: true },
86113
url: { type: 'string', required: false },
114+
limit: { type: 'string', required: false },
87115
query: { type: 'string', required: false },
88116
scrapeOptions: { type: 'json', required: false },
89117
},
@@ -95,5 +123,9 @@ export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
95123
// Search output
96124
data: 'json',
97125
warning: 'any',
126+
// Crawl output
127+
pages: 'json',
128+
total: 'number',
129+
creditsUsed: 'number',
98130
},
99131
}

0 commit comments

Comments
 (0)