improvement(oauth): added advanced mode for all tools with oauth and selectors (#721)

* fixed SVG error, added tool-level awareness for param validation

* added advanced mode for oauth block

* added wealthbox advanced mode

* fixed wealthbox

* moved types to types file

* ack pr comments
This commit is contained in:
Waleed Latif
2025-07-18 10:39:56 -07:00
committed by GitHub
parent 3d5d7474ed
commit 732df0494e
92 changed files with 1354 additions and 608 deletions

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AgentBlock } from './agent'
import { AgentBlock } from '@/blocks/blocks/agent'
vi.mock('@/blocks', () => ({
getAllBlocks: vi.fn(() => [

View File

@@ -1,6 +1,7 @@
import { AgentIcon } from '@/components/icons'
import { isHosted } from '@/lib/environment'
import { createLogger } from '@/lib/logs/console-logger'
import type { BlockConfig } from '@/blocks/types'
import {
getAllModelProviders,
getBaseModelProviders,
@@ -13,7 +14,6 @@ import {
} from '@/providers/utils'
import { useOllamaStore } from '@/stores/ollama/store'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
const logger = createLogger('AgentBlock')

View File

@@ -1,19 +1,6 @@
import { AirtableIcon } from '@/components/icons'
import type {
AirtableCreateResponse,
AirtableGetResponse,
AirtableListResponse,
AirtableUpdateMultipleResponse,
AirtableUpdateResponse,
} from '@/tools/airtable/types'
import type { BlockConfig } from '../types'
type AirtableResponse =
| AirtableListResponse
| AirtableGetResponse
| AirtableCreateResponse
| AirtableUpdateResponse
| AirtableUpdateMultipleResponse
import type { BlockConfig } from '@/blocks/types'
import type { AirtableResponse } from '@/tools/airtable/types'
export const AirtableBlock: BlockConfig<AirtableResponse> = {
type: 'airtable',

View File

@@ -1,6 +1,6 @@
import { ApiIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { RequestResponse } from '@/tools/http/types'
import type { BlockConfig } from '../types'
export const ApiBlock: BlockConfig<RequestResponse> = {
type: 'api',

View File

@@ -1,15 +1,6 @@
import { BrowserUseIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface BrowserUseResponse extends ToolResponse {
output: {
id: string
success: boolean
output: any
steps: any[]
}
}
import type { BlockConfig } from '@/blocks/types'
import type { BrowserUseResponse } from '@/tools/browser_use/types'
export const BrowserUseBlock: BlockConfig<BrowserUseResponse> = {
type: 'browser_use',

View File

@@ -1,6 +1,6 @@
import { ClayIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { ClayPopulateResponse } from '@/tools/clay/types'
import type { BlockConfig } from '../types'
export const ClayBlock: BlockConfig<ClayPopulateResponse> = {
type: 'clay',

View File

@@ -1,5 +1,5 @@
import { ConditionalIcon } from '@/components/icons'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
interface ConditionBlockOutput {
success: boolean

View File

@@ -1,8 +1,6 @@
import { ConfluenceIcon } from '@/components/icons'
import type { ConfluenceRetrieveResponse, ConfluenceUpdateResponse } from '@/tools/confluence/types'
import type { BlockConfig } from '../types'
type ConfluenceResponse = ConfluenceRetrieveResponse | ConfluenceUpdateResponse
import type { BlockConfig } from '@/blocks/types'
import type { ConfluenceResponse } from '@/tools/confluence/types'
export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
type: 'confluence',
@@ -48,7 +46,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
],
placeholder: 'Select Confluence account',
},
// Use file-selector component for page selection
// Page selector (basic mode)
{
id: 'pageId',
title: 'Select Page',
@@ -57,6 +55,16 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
provider: 'confluence',
serviceId: 'confluence',
placeholder: 'Select Confluence page',
mode: 'basic',
},
// Manual page ID input (advanced mode)
{
id: 'manualPageId',
title: 'Page ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Confluence page ID',
mode: 'advanced',
},
// Update page fields
{
@@ -90,10 +98,18 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
}
},
params: (params) => {
const { credential, ...rest } = params
const { credential, pageId, manualPageId, ...rest } = params
// Use the selected page ID or the manually entered one
const effectivePageId = (pageId || manualPageId || '').trim()
if (!effectivePageId) {
throw new Error('Page ID is required. Please select a page or enter a page ID manually.')
}
return {
accessToken: credential,
pageId: effectivePageId,
...rest,
}
},
@@ -103,7 +119,8 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
operation: { type: 'string', required: true },
domain: { type: 'string', required: true },
credential: { type: 'string', required: true },
pageId: { type: 'string', required: true },
pageId: { type: 'string', required: false },
manualPageId: { type: 'string', required: false },
// Update operation inputs
title: { type: 'string', required: false },
content: { type: 'string', required: false },

View File

@@ -1,6 +1,6 @@
import { DiscordIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { DiscordResponse } from '@/tools/discord/types'
import type { BlockConfig } from '../types'
export const DiscordBlock: BlockConfig<DiscordResponse> = {
type: 'discord',
@@ -32,6 +32,7 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
placeholder: 'Enter Discord bot token',
password: true,
},
// Server selector (basic mode)
{
id: 'serverId',
title: 'Server',
@@ -40,11 +41,26 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
provider: 'discord',
serviceId: 'discord',
placeholder: 'Select Discord server',
mode: 'basic',
condition: {
field: 'operation',
value: ['discord_send_message', 'discord_get_messages', 'discord_get_server'],
},
},
// Manual server ID input (advanced mode)
{
id: 'manualServerId',
title: 'Server ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Discord server ID',
mode: 'advanced',
condition: {
field: 'operation',
value: ['discord_send_message', 'discord_get_messages', 'discord_get_server'],
},
},
// Channel selector (basic mode)
{
id: 'channelId',
title: 'Channel',
@@ -53,6 +69,17 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
provider: 'discord',
serviceId: 'discord',
placeholder: 'Select Discord channel',
mode: 'basic',
condition: { field: 'operation', value: ['discord_send_message', 'discord_get_messages'] },
},
// Manual channel ID input (advanced mode)
{
id: 'manualChannelId',
title: 'Channel ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Discord channel ID',
mode: 'advanced',
condition: { field: 'operation', value: ['discord_send_message', 'discord_get_messages'] },
},
{
@@ -108,25 +135,56 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
if (!params.botToken) throw new Error('Bot token required for this operation')
commonParams.botToken = params.botToken
// Handle server ID (selector or manual)
const effectiveServerId = (params.serverId || params.manualServerId || '').trim()
// Handle channel ID (selector or manual)
const effectiveChannelId = (params.channelId || params.manualChannelId || '').trim()
switch (params.operation) {
case 'discord_send_message':
if (!effectiveServerId) {
throw new Error(
'Server ID is required. Please select a server or enter a server ID manually.'
)
}
if (!effectiveChannelId) {
throw new Error(
'Channel ID is required. Please select a channel or enter a channel ID manually.'
)
}
return {
...commonParams,
serverId: params.serverId,
channelId: params.channelId,
serverId: effectiveServerId,
channelId: effectiveChannelId,
content: params.content,
}
case 'discord_get_messages':
if (!effectiveServerId) {
throw new Error(
'Server ID is required. Please select a server or enter a server ID manually.'
)
}
if (!effectiveChannelId) {
throw new Error(
'Channel ID is required. Please select a channel or enter a channel ID manually.'
)
}
return {
...commonParams,
serverId: params.serverId,
channelId: params.channelId,
serverId: effectiveServerId,
channelId: effectiveChannelId,
limit: params.limit ? Math.min(Math.max(1, Number(params.limit)), 100) : 10,
}
case 'discord_get_server':
if (!effectiveServerId) {
throw new Error(
'Server ID is required. Please select a server or enter a server ID manually.'
)
}
return {
...commonParams,
serverId: params.serverId,
serverId: effectiveServerId,
}
case 'discord_get_user':
return {
@@ -143,7 +201,9 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
operation: { type: 'string', required: true },
botToken: { type: 'string', required: true },
serverId: { type: 'string', required: false },
manualServerId: { type: 'string', required: false },
channelId: { type: 'string', required: false },
manualChannelId: { type: 'string', required: false },
content: { type: 'string', required: false },
limit: { type: 'number', required: false },
userId: { type: 'string', required: false },

View File

@@ -1,12 +1,6 @@
import { ElevenLabsIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface ElevenLabsBlockResponse extends ToolResponse {
output: {
audioUrl: string
}
}
import type { BlockConfig } from '@/blocks/types'
import type { ElevenLabsBlockResponse } from '@/tools/elevenlabs/types'
export const ElevenLabsBlock: BlockConfig<ElevenLabsBlockResponse> = {
type: 'elevenlabs',

View File

@@ -1,11 +1,11 @@
import { ChartBarIcon } from '@/components/icons'
import { isHosted } from '@/lib/environment'
import { createLogger } from '@/lib/logs/console-logger'
import type { BlockConfig, ParamType } from '@/blocks/types'
import type { ProviderId } from '@/providers/types'
import { getAllModelProviders, getBaseModelProviders, getHostedModels } from '@/providers/utils'
import { useOllamaStore } from '@/stores/ollama/store'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig, ParamType } from '../types'
const logger = createLogger('EvaluatorBlock')

View File

@@ -1,17 +1,6 @@
import { ExaAIIcon } from '@/components/icons'
import type {
ExaAnswerResponse,
ExaFindSimilarLinksResponse,
ExaGetContentsResponse,
ExaSearchResponse,
} from '@/tools/exa/types'
import type { BlockConfig } from '../types'
type ExaResponse =
| ExaSearchResponse
| ExaGetContentsResponse
| ExaFindSimilarLinksResponse
| ExaAnswerResponse
import type { BlockConfig } from '@/blocks/types'
import type { ExaResponse } from '@/tools/exa/types'
export const ExaBlock: BlockConfig<ExaResponse> = {
type: 'exa',

View File

@@ -1,8 +1,8 @@
import { DocumentIcon } from '@/components/icons'
import { isProd } from '@/lib/environment'
import { createLogger } from '@/lib/logs/console-logger'
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '@/blocks/types'
import type { FileParserOutput } from '@/tools/file/types'
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '../types'
const logger = createLogger('FileBlock')

View File

@@ -1,8 +1,6 @@
import { FirecrawlIcon } from '@/components/icons'
import type { ScrapeResponse, SearchResponse } from '@/tools/firecrawl/types'
import type { BlockConfig } from '../types'
type FirecrawlResponse = ScrapeResponse | SearchResponse
import type { BlockConfig } from '@/blocks/types'
import type { FirecrawlResponse } from '@/tools/firecrawl/types'
export const FirecrawlBlock: BlockConfig<FirecrawlResponse> = {
type: 'firecrawl',

View File

@@ -1,6 +1,6 @@
import { CodeIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { CodeExecutionOutput } from '@/tools/function/types'
import type { BlockConfig } from '../types'
export const FunctionBlock: BlockConfig<CodeExecutionOutput> = {
type: 'function',

View File

@@ -1,17 +1,6 @@
import { GithubIcon } from '@/components/icons'
import type {
CreateCommentResponse,
LatestCommitResponse,
PullRequestResponse,
RepoInfoResponse,
} from '@/tools/github/types'
import type { BlockConfig } from '../types'
type GitHubResponse =
| PullRequestResponse
| CreateCommentResponse
| LatestCommitResponse
| RepoInfoResponse
import type { BlockConfig } from '@/blocks/types'
import type { GitHubResponse } from '@/tools/github/types'
export const GitHubBlock: BlockConfig<GitHubResponse> = {
type: 'github',

View File

@@ -1,6 +1,6 @@
import { GmailIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { GmailToolResponse } from '@/tools/gmail/types'
import type { BlockConfig } from '../types'
export const GmailBlock: BlockConfig<GmailToolResponse> = {
type: 'gmail',
@@ -67,7 +67,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
placeholder: 'Email content',
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
},
// Read Email Fields - Add folder selector
// Label/folder selector (basic mode)
{
id: 'folder',
title: 'Label',
@@ -80,6 +80,17 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
'https://www.googleapis.com/auth/gmail.labels',
],
placeholder: 'Select Gmail label/folder',
mode: 'basic',
condition: { field: 'operation', value: 'read_gmail' },
},
// Manual label/folder input (advanced mode)
{
id: 'manualFolder',
title: 'Label/Folder',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Gmail label name (e.g., INBOX, SENT, or custom label)',
mode: 'advanced',
condition: { field: 'operation', value: 'read_gmail' },
},
{
@@ -141,11 +152,14 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
},
params: (params) => {
// Pass the credential directly from the credential field
const { credential, ...rest } = params
const { credential, folder, manualFolder, ...rest } = params
// Handle folder input (selector or manual)
const effectiveFolder = (folder || manualFolder || '').trim()
// Ensure folder is always provided for read_gmail operation
if (rest.operation === 'read_gmail') {
rest.folder = rest.folder || 'INBOX'
rest.folder = effectiveFolder || 'INBOX'
}
return {
@@ -164,6 +178,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
body: { type: 'string', required: false },
// Read operation inputs
folder: { type: 'string', required: false },
manualFolder: { type: 'string', required: false },
messageId: { type: 'string', required: false },
unreadOnly: { type: 'boolean', required: false },
// Search operation inputs

View File

@@ -1,24 +1,6 @@
import { GoogleIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface GoogleSearchResponse extends ToolResponse {
output: {
items: Array<{
title: string
link: string
snippet: string
displayLink?: string
pagemap?: Record<string, any>
}>
searchInformation: {
totalResults: string
searchTime: number
formattedSearchTime: string
formattedTotalResults: string
}
}
}
import type { BlockConfig } from '@/blocks/types'
import type { GoogleSearchResponse } from '@/tools/google/types'
export const GoogleSearchBlock: BlockConfig<GoogleSearchResponse> = {
type: 'google_search',

View File

@@ -1,19 +1,6 @@
import { GoogleCalendarIcon } from '@/components/icons'
import type {
GoogleCalendarCreateResponse,
GoogleCalendarGetResponse,
GoogleCalendarInviteResponse,
GoogleCalendarListResponse,
GoogleCalendarQuickAddResponse,
} from '@/tools/google_calendar/types'
import type { BlockConfig } from '../types'
type GoogleCalendarResponse =
| GoogleCalendarCreateResponse
| GoogleCalendarListResponse
| GoogleCalendarGetResponse
| GoogleCalendarQuickAddResponse
| GoogleCalendarInviteResponse
import type { BlockConfig } from '@/blocks/types'
import type { GoogleCalendarResponse } from '@/tools/google_calendar/types'
export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
type: 'google_calendar',
@@ -49,6 +36,7 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
requiredScopes: ['https://www.googleapis.com/auth/calendar'],
placeholder: 'Select Google Calendar account',
},
// Calendar selector (basic mode)
{
id: 'calendarId',
title: 'Calendar',
@@ -58,6 +46,16 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
serviceId: 'google-calendar',
requiredScopes: ['https://www.googleapis.com/auth/calendar'],
placeholder: 'Select calendar',
mode: 'basic',
},
// Manual calendar ID input (advanced mode)
{
id: 'manualCalendarId',
title: 'Calendar ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter calendar ID (e.g., primary or calendar@gmail.com)',
mode: 'advanced',
},
// Create Event Fields
@@ -220,9 +218,23 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
}
},
params: (params) => {
const { credential, operation, attendees, replaceExisting, ...rest } = params
const {
credential,
operation,
attendees,
replaceExisting,
calendarId,
manualCalendarId,
...rest
} = params
const processedParams = { ...rest }
// Handle calendar ID (selector or manual)
const effectiveCalendarId = (calendarId || manualCalendarId || '').trim()
const processedParams: Record<string, any> = {
...rest,
calendarId: effectiveCalendarId || 'primary',
}
// Convert comma-separated attendees string to array, only if it has content
if (attendees && typeof attendees === 'string' && attendees.trim().length > 0) {
@@ -258,6 +270,7 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
operation: { type: 'string', required: true },
credential: { type: 'string', required: true },
calendarId: { type: 'string', required: false },
manualCalendarId: { type: 'string', required: false },
// Create operation inputs
summary: { type: 'string', required: false },

View File

@@ -1,15 +1,6 @@
import { GoogleDocsIcon } from '@/components/icons'
import type {
GoogleDocsCreateResponse,
GoogleDocsReadResponse,
GoogleDocsWriteResponse,
} from '@/tools/google_docs/types'
import type { BlockConfig } from '../types'
type GoogleDocsResponse =
| GoogleDocsReadResponse
| GoogleDocsWriteResponse
| GoogleDocsCreateResponse
import type { BlockConfig } from '@/blocks/types'
import type { GoogleDocsResponse } from '@/tools/google_docs/types'
export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
type: 'google_docs',
@@ -45,7 +36,7 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
placeholder: 'Select Google account',
},
// Document Selector for read operation
// Document selector (basic mode)
{
id: 'documentId',
title: 'Select Document',
@@ -56,38 +47,18 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
requiredScopes: [],
mimeType: 'application/vnd.google-apps.document',
placeholder: 'Select a document',
condition: { field: 'operation', value: 'read' },
mode: 'basic',
condition: { field: 'operation', value: ['read', 'write'] },
},
// Document Selector for write operation
{
id: 'documentId',
title: 'Select Document',
type: 'file-selector',
layout: 'full',
provider: 'google-drive',
serviceId: 'google-drive',
requiredScopes: [],
mimeType: 'application/vnd.google-apps.document',
placeholder: 'Select a document',
condition: { field: 'operation', value: 'write' },
},
// Manual Document ID for read operation
// Manual document ID input (advanced mode)
{
id: 'manualDocumentId',
title: 'Or Enter Document ID Manually',
title: 'Document ID',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the document',
condition: { field: 'operation', value: 'read' },
},
// Manual Document ID for write operation
{
id: 'manualDocumentId',
title: 'Or Enter Document ID Manually',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the document',
condition: { field: 'operation', value: 'write' },
placeholder: 'Enter document ID',
mode: 'advanced',
condition: { field: 'operation', value: ['read', 'write'] },
},
// Create-specific Fields
{
@@ -98,7 +69,7 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
placeholder: 'Enter title for the new document',
condition: { field: 'operation', value: 'create' },
},
// Folder Selector for create operation
// Folder selector (basic mode)
{
id: 'folderSelector',
title: 'Select Parent Folder',
@@ -109,15 +80,17 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
requiredScopes: [],
mimeType: 'application/vnd.google-apps.folder',
placeholder: 'Select a parent folder',
mode: 'basic',
condition: { field: 'operation', value: 'create' },
},
// Manual Folder ID for create operation
// Manual folder ID input (advanced mode)
{
id: 'folderId',
title: 'Or Enter Parent Folder ID Manually',
title: 'Parent Folder ID',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the parent folder (leave empty for root folder)',
placeholder: 'Enter parent folder ID (leave empty for root folder)',
mode: 'advanced',
condition: { field: 'operation', value: 'create' },
},
// Content Field for write operation

View File

@@ -1,15 +1,6 @@
import { GoogleDriveIcon } from '@/components/icons'
import type {
GoogleDriveGetContentResponse,
GoogleDriveListResponse,
GoogleDriveUploadResponse,
} from '@/tools/google_drive/types'
import type { BlockConfig } from '../types'
type GoogleDriveResponse =
| GoogleDriveUploadResponse
| GoogleDriveGetContentResponse
| GoogleDriveListResponse
import type { BlockConfig } from '@/blocks/types'
import type { GoogleDriveResponse } from '@/tools/google_drive/types'
export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
type: 'google_drive',
@@ -87,18 +78,17 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
mimeType: 'application/vnd.google-apps.folder',
placeholder: 'Select a parent folder',
mode: 'basic',
condition: { field: 'operation', value: 'upload' },
},
{
id: 'folderId',
title: 'Or Enter Parent Folder ID Manually',
id: 'manualFolderId',
title: 'Parent Folder ID',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the parent folder (leave empty for root folder)',
condition: {
field: 'operation',
value: 'upload',
},
placeholder: 'Enter parent folder ID (leave empty for root folder)',
mode: 'advanced',
condition: { field: 'operation', value: 'upload' },
},
// Get Content Fields
// {
@@ -160,21 +150,20 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
mimeType: 'application/vnd.google-apps.folder',
placeholder: 'Select a parent folder',
mode: 'basic',
condition: { field: 'operation', value: 'create_folder' },
},
// Manual Folder ID input (shown only when no folder is selected)
// Manual Folder ID input (advanced mode)
{
id: 'folderId',
title: 'Or Enter Parent Folder ID Manually',
id: 'manualFolderId',
title: 'Parent Folder ID',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the parent folder (leave empty for root folder)',
condition: {
field: 'operation',
value: 'create_folder',
},
placeholder: 'Enter parent folder ID (leave empty for root folder)',
mode: 'advanced',
condition: { field: 'operation', value: 'create_folder' },
},
// List Fields - Folder Selector
// List Fields - Folder Selector (basic mode)
{
id: 'folderSelector',
title: 'Select Folder',
@@ -185,19 +174,18 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
requiredScopes: ['https://www.googleapis.com/auth/drive.file'],
mimeType: 'application/vnd.google-apps.folder',
placeholder: 'Select a folder to list files from',
mode: 'basic',
condition: { field: 'operation', value: 'list' },
},
// Manual Folder ID input (shown only when no folder is selected)
// Manual Folder ID input (advanced mode)
{
id: 'folderId',
title: 'Or Enter Folder ID Manually',
id: 'manualFolderId',
title: 'Folder ID',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the folder to list (leave empty for root folder)',
condition: {
field: 'operation',
value: 'list',
},
placeholder: 'Enter folder ID (leave empty for root folder)',
mode: 'advanced',
condition: { field: 'operation', value: 'list' },
},
{
id: 'query',
@@ -234,14 +222,14 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
}
},
params: (params) => {
const { credential, folderId, folderSelector, mimeType, ...rest } = params
const { credential, folderSelector, manualFolderId, mimeType, ...rest } = params
// Use folderSelector if provided, otherwise use folderId
const effectiveFolderId = folderSelector || folderId || ''
// Use folderSelector if provided, otherwise use manualFolderId
const effectiveFolderId = (folderSelector || manualFolderId || '').trim()
return {
accessToken: credential,
folderId: effectiveFolderId.trim(),
folderId: effectiveFolderId,
pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined,
mimeType: mimeType,
...rest,
@@ -259,8 +247,8 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
// Get Content operation inputs
// fileId: { type: 'string', required: false },
// List operation inputs
folderId: { type: 'string', required: false },
folderSelector: { type: 'string', required: false },
manualFolderId: { type: 'string', required: false },
query: { type: 'string', required: false },
pageSize: { type: 'number', required: false },
},

View File

@@ -1,17 +1,6 @@
import { GoogleSheetsIcon } from '@/components/icons'
import type {
GoogleSheetsAppendResponse,
GoogleSheetsReadResponse,
GoogleSheetsUpdateResponse,
GoogleSheetsWriteResponse,
} from '@/tools/google_sheets/types'
import type { BlockConfig } from '../types'
type GoogleSheetsResponse =
| GoogleSheetsReadResponse
| GoogleSheetsWriteResponse
| GoogleSheetsUpdateResponse
| GoogleSheetsAppendResponse
import type { BlockConfig } from '@/blocks/types'
import type { GoogleSheetsResponse } from '@/tools/google_sheets/types'
export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
type: 'google_sheets',
@@ -59,15 +48,16 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
requiredScopes: [],
mimeType: 'application/vnd.google-apps.spreadsheet',
placeholder: 'Select a spreadsheet',
mode: 'basic',
},
// Manual Spreadsheet ID (hidden by default)
// Manual Spreadsheet ID (advanced mode)
{
id: 'manualSpreadsheetId',
title: 'Or Enter Spreadsheet ID Manually',
title: 'Spreadsheet ID',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the spreadsheet (from URL)',
condition: { field: 'spreadsheetId', value: '' },
mode: 'advanced',
},
// Range
{

View File

@@ -1,6 +1,6 @@
import { HuggingFaceIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { HuggingFaceChatResponse } from '@/tools/huggingface/types'
import type { BlockConfig } from '../types'
export const HuggingFaceBlock: BlockConfig<HuggingFaceChatResponse> = {
type: 'huggingface',

View File

@@ -1,6 +1,6 @@
import { ImageIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { DalleResponse } from '@/tools/openai/types'
import type { BlockConfig } from '../types'
export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
type: 'image_generator',

View File

@@ -1,6 +1,6 @@
import { JinaAIIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { ReadUrlResponse } from '@/tools/jina/types'
import type { BlockConfig } from '../types'
export const JinaBlock: BlockConfig<ReadUrlResponse> = {
type: 'jina',

View File

@@ -1,17 +1,6 @@
import { JiraIcon } from '@/components/icons'
import type {
JiraRetrieveResponse,
JiraRetrieveResponseBulk,
JiraUpdateResponse,
JiraWriteResponse,
} from '@/tools/jira/types'
import type { BlockConfig } from '../types'
type JiraResponse =
| JiraRetrieveResponse
| JiraUpdateResponse
| JiraWriteResponse
| JiraRetrieveResponseBulk
import type { BlockConfig } from '@/blocks/types'
import type { JiraResponse } from '@/tools/jira/types'
export const JiraBlock: BlockConfig<JiraResponse> = {
type: 'jira',
@@ -24,7 +13,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
bgColor: '#E0E0E0',
icon: JiraIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
@@ -62,7 +50,7 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
],
placeholder: 'Select Jira account',
},
// Use file-selector component for issue selection
// Project selector (basic mode)
{
id: 'projectId',
title: 'Select Project',
@@ -71,7 +59,18 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
provider: 'jira',
serviceId: 'jira',
placeholder: 'Select Jira project',
mode: 'basic',
},
// Manual project ID input (advanced mode)
{
id: 'manualProjectId',
title: 'Project ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Jira project ID',
mode: 'advanced',
},
// Issue selector (basic mode)
{
id: 'issueKey',
title: 'Select Issue',
@@ -81,6 +80,17 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
serviceId: 'jira',
placeholder: 'Select Jira issue',
condition: { field: 'operation', value: ['read', 'update'] },
mode: 'basic',
},
// Manual issue key input (advanced mode)
{
id: 'manualIssueKey',
title: 'Issue Key',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Jira issue key',
condition: { field: 'operation', value: ['read', 'update'] },
mode: 'advanced',
},
{
id: 'summary',
@@ -117,18 +127,32 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
}
},
params: (params) => {
const { credential, projectId, manualProjectId, issueKey, manualIssueKey, ...rest } = params
// Base params that are always needed
const baseParams = {
accessToken: params.credential,
accessToken: credential,
domain: params.domain,
}
// Use the selected project ID or the manually entered one
const effectiveProjectId = (projectId || manualProjectId || '').trim()
// Use the selected issue key or the manually entered one
const effectiveIssueKey = (issueKey || manualIssueKey || '').trim()
// Define allowed parameters for each operation
switch (params.operation) {
case 'write': {
if (!effectiveProjectId) {
throw new Error(
'Project ID is required. Please select a project or enter a project ID manually.'
)
}
// For write operations, only include write-specific fields
const writeParams = {
projectId: params.projectId,
projectId: effectiveProjectId,
summary: params.summary || '',
description: params.description || '',
issueType: params.issueType || 'Task',
@@ -141,10 +165,21 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
}
}
case 'update': {
if (!effectiveProjectId) {
throw new Error(
'Project ID is required. Please select a project or enter a project ID manually.'
)
}
if (!effectiveIssueKey) {
throw new Error(
'Issue Key is required. Please select an issue or enter an issue key manually.'
)
}
// For update operations, only include update-specific fields
const updateParams = {
projectId: params.projectId,
issueKey: params.issueKey,
projectId: effectiveProjectId,
issueKey: effectiveIssueKey,
summary: params.summary || '',
description: params.description || '',
}
@@ -155,17 +190,29 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
}
}
case 'read': {
if (!effectiveIssueKey) {
throw new Error(
'Issue Key is required. Please select an issue or enter an issue key manually.'
)
}
// For read operations, only include read-specific fields
return {
...baseParams,
issueKey: params.issueKey,
issueKey: effectiveIssueKey,
}
}
case 'read-bulk': {
if (!effectiveProjectId) {
throw new Error(
'Project ID is required. Please select a project or enter a project ID manually.'
)
}
// For read-bulk operations, only include read-bulk-specific fields
return {
...baseParams,
projectId: params.projectId,
projectId: effectiveProjectId,
}
}
default:
@@ -178,8 +225,10 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
operation: { type: 'string', required: true },
domain: { type: 'string', required: true },
credential: { type: 'string', required: true },
issueKey: { type: 'string', required: true },
issueKey: { type: 'string', required: false },
projectId: { type: 'string', required: false },
manualProjectId: { type: 'string', required: false },
manualIssueKey: { type: 'string', required: false },
// Update operation inputs
summary: { type: 'string', required: true },
description: { type: 'string', required: false },

View File

@@ -1,5 +1,5 @@
import { PackageSearchIcon } from '@/components/icons'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
export const KnowledgeBlock: BlockConfig = {
type: 'knowledge',
@@ -26,6 +26,25 @@ export const KnowledgeBlock: BlockConfig = {
return 'knowledge_search'
}
},
params: (params) => {
// Validate required fields for each operation
if (params.operation === 'search' && !params.knowledgeBaseIds) {
throw new Error('Knowledge base IDs are required for search operation')
}
if (
(params.operation === 'upload_chunk' || params.operation === 'create_document') &&
!params.knowledgeBaseId
) {
throw new Error(
'Knowledge base ID is required for upload_chunk and create_document operations'
)
}
if (params.operation === 'upload_chunk' && !params.documentId) {
throw new Error('Document ID is required for upload_chunk operation')
}
return params
},
},
},
inputs: {

View File

@@ -1,8 +1,6 @@
import { LinearIcon } from '@/components/icons'
import type { LinearCreateIssueResponse, LinearReadIssuesResponse } from '@/tools/linear/types'
import type { BlockConfig } from '../types'
type LinearResponse = LinearReadIssuesResponse | LinearCreateIssueResponse
import type { BlockConfig } from '@/blocks/types'
import type { LinearResponse } from '@/tools/linear/types'
export const LinearBlock: BlockConfig<LinearResponse> = {
type: 'linear',
@@ -42,6 +40,7 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
provider: 'linear',
serviceId: 'linear',
placeholder: 'Select a team',
mode: 'basic',
},
{
id: 'projectId',
@@ -51,6 +50,25 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
provider: 'linear',
serviceId: 'linear',
placeholder: 'Select a project',
mode: 'basic',
},
// Manual team ID input (advanced mode)
{
id: 'manualTeamId',
title: 'Team ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Linear team ID',
mode: 'advanced',
},
// Manual project ID input (advanced mode)
{
id: 'manualProjectId',
title: 'Project ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Linear project ID',
mode: 'advanced',
},
{
id: 'title',
@@ -73,19 +91,40 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
tool: (params) =>
params.operation === 'write' ? 'linear_create_issue' : 'linear_read_issues',
params: (params) => {
// Handle team ID (selector or manual)
const effectiveTeamId = (params.teamId || params.manualTeamId || '').trim()
// Handle project ID (selector or manual)
const effectiveProjectId = (params.projectId || params.manualProjectId || '').trim()
if (!effectiveTeamId) {
throw new Error('Team ID is required. Please select a team or enter a team ID manually.')
}
if (!effectiveProjectId) {
throw new Error(
'Project ID is required. Please select a project or enter a project ID manually.'
)
}
if (params.operation === 'write') {
if (!params.title?.trim()) {
throw new Error('Title is required for creating issues.')
}
if (!params.description?.trim()) {
throw new Error('Description is required for creating issues.')
}
return {
credential: params.credential,
teamId: params.teamId,
projectId: params.projectId,
teamId: effectiveTeamId,
projectId: effectiveProjectId,
title: params.title,
description: params.description,
}
}
return {
credential: params.credential,
teamId: params.teamId,
projectId: params.projectId,
teamId: effectiveTeamId,
projectId: effectiveProjectId,
}
},
},
@@ -93,8 +132,10 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
inputs: {
operation: { type: 'string', required: true },
credential: { type: 'string', required: true },
teamId: { type: 'string', required: true },
projectId: { type: 'string', required: true },
teamId: { type: 'string', required: false },
projectId: { type: 'string', required: false },
manualTeamId: { type: 'string', required: false },
manualProjectId: { type: 'string', required: false },
title: { type: 'string', required: false },
description: { type: 'string', required: false },
},

View File

@@ -1,6 +1,6 @@
import { LinkupIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { LinkupSearchToolResponse } from '@/tools/linkup/types'
import type { BlockConfig } from '../types'
export const LinkupBlock: BlockConfig<LinkupSearchToolResponse> = {
type: 'linkup',

View File

@@ -1,6 +1,6 @@
import { Mem0Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { Mem0Response } from '@/tools/mem0/types'
import type { BlockConfig } from '../types'
export const Mem0Block: BlockConfig<Mem0Response> = {
type: 'mem0',

View File

@@ -1,5 +1,5 @@
import { BrainIcon } from '@/components/icons'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
export const MemoryBlock: BlockConfig = {
type: 'memory',

View File

@@ -1,15 +1,6 @@
import { MicrosoftExcelIcon } from '@/components/icons'
import type {
MicrosoftExcelReadResponse,
MicrosoftExcelTableAddResponse,
MicrosoftExcelWriteResponse,
} from '@/tools/microsoft_excel/types'
import type { BlockConfig } from '../types'
type MicrosoftExcelResponse =
| MicrosoftExcelReadResponse
| MicrosoftExcelWriteResponse
| MicrosoftExcelTableAddResponse
import type { BlockConfig } from '@/blocks/types'
import type { MicrosoftExcelResponse } from '@/tools/microsoft_excel/types'
export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
type: 'microsoft_excel',
@@ -53,14 +44,15 @@ export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
requiredScopes: [],
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
placeholder: 'Select a spreadsheet',
mode: 'basic',
},
{
id: 'manualSpreadsheetId',
title: 'Or Enter Spreadsheet ID Manually',
title: 'Spreadsheet ID',
type: 'short-input',
layout: 'full',
placeholder: 'ID of the spreadsheet (from URL)',
condition: { field: 'spreadsheetId', value: '' },
placeholder: 'Enter spreadsheet ID',
mode: 'advanced',
},
{
id: 'range',

View File

@@ -1,11 +1,6 @@
import { MicrosoftTeamsIcon } from '@/components/icons'
import type {
MicrosoftTeamsReadResponse,
MicrosoftTeamsWriteResponse,
} from '@/tools/microsoft_teams/types'
import type { BlockConfig } from '../types'
type MicrosoftTeamsResponse = MicrosoftTeamsReadResponse | MicrosoftTeamsWriteResponse
import type { BlockConfig } from '@/blocks/types'
import type { MicrosoftTeamsResponse } from '@/tools/microsoft_teams/types'
export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
type: 'microsoft_teams',
@@ -64,6 +59,16 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
serviceId: 'microsoft-teams',
requiredScopes: [],
placeholder: 'Select a team',
mode: 'basic',
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
},
{
id: 'manualTeamId',
title: 'Team ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter team ID',
mode: 'advanced',
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
},
{
@@ -75,6 +80,16 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
serviceId: 'microsoft-teams',
requiredScopes: [],
placeholder: 'Select a chat',
mode: 'basic',
condition: { field: 'operation', value: ['read_chat', 'write_chat'] },
},
{
id: 'manualChatId',
title: 'Chat ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter chat ID',
mode: 'advanced',
condition: { field: 'operation', value: ['read_chat', 'write_chat'] },
},
{
@@ -86,6 +101,16 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
serviceId: 'microsoft-teams',
requiredScopes: [],
placeholder: 'Select a channel',
mode: 'basic',
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
},
{
id: 'manualChannelId',
title: 'Channel ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter channel ID',
mode: 'advanced',
condition: { field: 'operation', value: ['read_channel', 'write_channel'] },
},
// Create-specific Fields
@@ -121,7 +146,22 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
}
},
params: (params) => {
const { credential, operation, ...rest } = params
const {
credential,
operation,
teamId,
manualTeamId,
chatId,
manualChatId,
channelId,
manualChannelId,
...rest
} = params
// Use the selected IDs or the manually entered ones
const effectiveTeamId = (teamId || manualTeamId || '').trim()
const effectiveChatId = (chatId || manualChatId || '').trim()
const effectiveChannelId = (channelId || manualChannelId || '').trim()
// Build the parameters based on operation type
const baseParams = {
@@ -131,27 +171,33 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
// For chat operations, we need chatId
if (operation === 'read_chat' || operation === 'write_chat') {
if (!params.chatId) {
throw new Error('Chat ID is required for chat operations')
if (!effectiveChatId) {
throw new Error(
'Chat ID is required for chat operations. Please select a chat or enter a chat ID manually.'
)
}
return {
...baseParams,
chatId: params.chatId,
chatId: effectiveChatId,
}
}
// For channel operations, we need teamId and channelId
if (operation === 'read_channel' || operation === 'write_channel') {
if (!params.teamId) {
throw new Error('Team ID is required for channel operations')
if (!effectiveTeamId) {
throw new Error(
'Team ID is required for channel operations. Please select a team or enter a team ID manually.'
)
}
if (!params.channelId) {
throw new Error('Channel ID is required for channel operations')
if (!effectiveChannelId) {
throw new Error(
'Channel ID is required for channel operations. Please select a channel or enter a channel ID manually.'
)
}
return {
...baseParams,
teamId: params.teamId,
channelId: params.channelId,
teamId: effectiveTeamId,
channelId: effectiveChannelId,
}
}
@@ -162,11 +208,14 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
inputs: {
operation: { type: 'string', required: true },
credential: { type: 'string', required: true },
messageId: { type: 'string', required: true },
chatId: { type: 'string', required: true },
channelId: { type: 'string', required: true },
teamId: { type: 'string', required: true },
content: { type: 'string', required: true },
messageId: { type: 'string', required: false },
chatId: { type: 'string', required: false },
manualChatId: { type: 'string', required: false },
channelId: { type: 'string', required: false },
manualChannelId: { type: 'string', required: false },
teamId: { type: 'string', required: false },
manualTeamId: { type: 'string', required: false },
content: { type: 'string', required: false },
},
outputs: {
content: 'string',

View File

@@ -1,7 +1,7 @@
import { MistralIcon } from '@/components/icons'
import { isProd } from '@/lib/environment'
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '@/blocks/types'
import type { MistralParserOutput } from '@/tools/mistral/types'
import type { BlockConfig, SubBlockConfig, SubBlockLayout, SubBlockType } from '../types'
const shouldEnableFileUpload = isProd

View File

@@ -1,6 +1,6 @@
import { NotionIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { NotionResponse } from '@/tools/notion/types'
import type { BlockConfig } from '../types'
export const NotionBlock: BlockConfig<NotionResponse> = {
type: 'notion',

View File

@@ -1,5 +1,5 @@
import { OpenAIIcon } from '@/components/icons'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
export const OpenAIBlock: BlockConfig = {
type: 'openai',

View File

@@ -1,14 +1,8 @@
import { OutlookIcon } from '@/components/icons'
import type {
OutlookDraftResponse,
OutlookReadResponse,
OutlookSendResponse,
} from '@/tools/outlook/types'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
import type { OutlookResponse } from '@/tools/outlook/types'
export const OutlookBlock: BlockConfig<
OutlookReadResponse | OutlookSendResponse | OutlookDraftResponse
> = {
export const OutlookBlock: BlockConfig<OutlookResponse> = {
type: 'outlook',
name: 'Outlook',
description: 'Access Outlook',
@@ -19,7 +13,6 @@ export const OutlookBlock: BlockConfig<
bgColor: '#E0E0E0',
icon: OutlookIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
@@ -31,7 +24,6 @@ export const OutlookBlock: BlockConfig<
{ label: 'Read Email', id: 'read_outlook' },
],
},
// Gmail Credentials
{
id: 'credential',
title: 'Microsoft Account',
@@ -51,7 +43,6 @@ export const OutlookBlock: BlockConfig<
],
placeholder: 'Select Microsoft account',
},
// Send Email Fields
{
id: 'to',
title: 'To',
@@ -76,7 +67,7 @@ export const OutlookBlock: BlockConfig<
placeholder: 'Email content',
condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
},
// Read Email Fields - Add folder selector
// Read Email Fields - Add folder selector (basic mode)
{
id: 'folder',
title: 'Folder',
@@ -86,6 +77,17 @@ export const OutlookBlock: BlockConfig<
serviceId: 'outlook',
requiredScopes: ['Mail.ReadWrite', 'Mail.ReadBasic', 'Mail.Read'],
placeholder: 'Select Outlook folder',
mode: 'basic',
condition: { field: 'operation', value: 'read_outlook' },
},
// Manual folder input (advanced mode)
{
id: 'manualFolder',
title: 'Folder',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Outlook folder name (e.g., INBOX, SENT, or custom folder)',
mode: 'advanced',
condition: { field: 'operation', value: 'read_outlook' },
},
{
@@ -114,11 +116,14 @@ export const OutlookBlock: BlockConfig<
},
params: (params) => {
// Pass the credential directly from the credential field
const { credential, ...rest } = params
const { credential, folder, manualFolder, ...rest } = params
// Handle folder input (selector or manual)
const effectiveFolder = (folder || manualFolder || '').trim()
// Set default folder to INBOX if not specified
if (rest.operation === 'read_outlook' && !rest.folder) {
rest.folder = 'INBOX'
if (rest.operation === 'read_outlook') {
rest.folder = effectiveFolder || 'INBOX'
}
return {
@@ -137,6 +142,7 @@ export const OutlookBlock: BlockConfig<
body: { type: 'string', required: false },
// Read operation inputs
folder: { type: 'string', required: false },
manualFolder: { type: 'string', required: false },
maxResults: { type: 'number', required: false },
},
outputs: {

View File

@@ -1,18 +1,6 @@
import { PerplexityIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface PerplexityChatResponse extends ToolResponse {
output: {
content: string
model: string
usage: {
prompt_tokens: number
completion_tokens: number
total_tokens: number
}
}
}
import type { BlockConfig } from '@/blocks/types'
import type { PerplexityChatResponse } from '@/tools/perplexity/types'
export const PerplexityBlock: BlockConfig<PerplexityChatResponse> = {
type: 'perplexity',

View File

@@ -1,7 +1,6 @@
import { PineconeIcon } from '@/components/icons'
// You'll need to create this icon
import type { BlockConfig } from '@/blocks/types'
import type { PineconeResponse } from '@/tools/pinecone/types'
import type { BlockConfig } from '../types'
export const PineconeBlock: BlockConfig<PineconeResponse> = {
type: 'pinecone',

View File

@@ -1,14 +1,8 @@
import { RedditIcon } from '@/components/icons'
import type {
RedditCommentsResponse,
RedditHotPostsResponse,
RedditPostsResponse,
} from '@/tools/reddit/types'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
import type { RedditResponse } from '@/tools/reddit/types'
export const RedditBlock: BlockConfig<
RedditHotPostsResponse | RedditPostsResponse | RedditCommentsResponse
> = {
export const RedditBlock: BlockConfig<RedditResponse> = {
type: 'reddit',
name: 'Reddit',
description: 'Access Reddit data and content',

View File

@@ -1,6 +1,6 @@
import { ResponseIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { ResponseBlockOutput } from '@/tools/response/types'
import type { BlockConfig } from '../types'
export const ResponseBlock: BlockConfig<ResponseBlockOutput> = {
type: 'response',

View File

@@ -1,10 +1,10 @@
import { ConnectIcon } from '@/components/icons'
import { isHosted } from '@/lib/environment'
import type { BlockConfig } from '@/blocks/types'
import type { ProviderId } from '@/providers/types'
import { getAllModelProviders, getBaseModelProviders, getHostedModels } from '@/providers/utils'
import { useOllamaStore } from '@/stores/ollama/store'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface RouterResponse extends ToolResponse {
output: {

View File

@@ -1,6 +1,6 @@
import { S3Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { S3Response } from '@/tools/s3/types'
import type { BlockConfig } from '../types'
export const S3Block: BlockConfig<S3Response> = {
type: 's3',

View File

@@ -1,6 +1,6 @@
import { SerperIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { SearchResponse } from '@/tools/serper/types'
import type { BlockConfig } from '../types'
export const SerperBlock: BlockConfig<SearchResponse> = {
type: 'serper',

View File

@@ -1,8 +1,6 @@
import { SlackIcon } from '@/components/icons'
import type { SlackMessageResponse } from '@/tools/slack/types'
import type { BlockConfig } from '../types'
type SlackResponse = SlackMessageResponse
import type { BlockConfig } from '@/blocks/types'
import type { SlackResponse } from '@/tools/slack/types'
export const SlackBlock: BlockConfig<SlackResponse> = {
type: 'slack',
@@ -76,6 +74,16 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
layout: 'full',
provider: 'slack',
placeholder: 'Select Slack channel',
mode: 'basic',
},
// Manual channel ID input (advanced mode)
{
id: 'manualChannel',
title: 'Channel ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Slack channel ID (e.g., C1234567890)',
mode: 'advanced',
},
{
id: 'text',
@@ -97,10 +105,21 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
}
},
params: (params) => {
const { credential, authMethod, botToken, operation, ...rest } = params
const { credential, authMethod, botToken, operation, channel, manualChannel, ...rest } =
params
const baseParams = {
// Handle channel input (selector or manual)
const effectiveChannel = (channel || manualChannel || '').trim()
if (!effectiveChannel) {
throw new Error(
'Channel is required. Please select a channel or enter a channel ID manually.'
)
}
const baseParams: Record<string, any> = {
...rest,
channel: effectiveChannel,
}
// Handle authentication based on method
@@ -126,7 +145,8 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
authMethod: { type: 'string', required: true },
credential: { type: 'string', required: false },
botToken: { type: 'string', required: false },
channel: { type: 'string', required: true },
channel: { type: 'string', required: false },
manualChannel: { type: 'string', required: false },
text: { type: 'string', required: true },
},
outputs: {

View File

@@ -1,8 +1,8 @@
import { StagehandIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface StagehandExtractResponse extends ToolResponse {
export interface StagehandExtractResponse extends ToolResponse {
output: {
data: Record<string, any>
}

View File

@@ -1,22 +1,6 @@
import { StagehandIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface StagehandAgentResponse extends ToolResponse {
output: {
agentResult: {
success: boolean
completed: boolean
message: string
actions: Array<{
type: string
params: Record<string, any>
result: Record<string, any>
}>
}
structuredOutput?: Record<string, any>
}
}
import type { BlockConfig } from '@/blocks/types'
import type { StagehandAgentResponse } from '@/tools/stagehand/types'
export const StagehandAgentBlock: BlockConfig<StagehandAgentResponse> = {
type: 'stagehand_agent',

View File

@@ -1,5 +1,5 @@
import { StartIcon } from '@/components/icons'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
export const StarterBlock: BlockConfig = {
type: 'starter',

View File

@@ -1,14 +1,6 @@
import { SupabaseIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface SupabaseResponse extends ToolResponse {
output: {
message: string
results: any
}
error?: string
}
import type { BlockConfig } from '@/blocks/types'
import type { SupabaseResponse } from '@/tools/supabase/types'
export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
type: 'supabase',
@@ -21,7 +13,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
bgColor: '#1C1C1C',
icon: SupabaseIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
@@ -32,7 +23,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
{ label: 'Insert Rows', id: 'insert' },
],
},
// Common Fields
{
id: 'projectId',
title: 'Project ID',
@@ -55,7 +45,6 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
placeholder: 'Your Supabase client anon key',
password: true,
},
// Insert-specific Fields
{
id: 'data',
title: 'Data',

View File

@@ -1,8 +1,6 @@
import { TavilyIcon } from '@/components/icons'
import type { TavilyExtractResponse, TavilySearchResponse } from '@/tools/tavily/types'
import type { BlockConfig } from '../types'
type TavilyResponse = TavilySearchResponse | TavilyExtractResponse
import type { BlockConfig } from '@/blocks/types'
import type { TavilyResponse } from '@/tools/tavily/types'
export const TavilyBlock: BlockConfig<TavilyResponse> = {
type: 'tavily',
@@ -15,7 +13,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
bgColor: '#0066FF',
icon: TavilyIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
@@ -27,7 +24,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
],
value: () => 'tavily_search',
},
// API Key (common)
{
id: 'apiKey',
title: 'API Key',
@@ -36,7 +32,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
placeholder: 'Enter your Tavily API key',
password: true,
},
// Search operation inputs
{
id: 'query',
title: 'Search Query',
@@ -53,7 +48,6 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
placeholder: '5',
condition: { field: 'operation', value: 'tavily_search' },
},
// Extract operation inputs
{
id: 'urls',
title: 'URL',
@@ -93,10 +87,8 @@ export const TavilyBlock: BlockConfig<TavilyResponse> = {
inputs: {
operation: { type: 'string', required: true },
apiKey: { type: 'string', required: true },
// Search operation
query: { type: 'string', required: false },
maxResults: { type: 'number', required: false },
// Extract operation
urls: { type: 'string', required: false },
extract_depth: { type: 'string', required: false },
},

View File

@@ -1,6 +1,6 @@
import { TelegramIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { TelegramMessageResponse } from '@/tools/telegram/types'
import type { BlockConfig } from '../types'
export const TelegramBlock: BlockConfig<TelegramMessageResponse> = {
type: 'telegram',

View File

@@ -1,12 +1,6 @@
import { BrainIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface ThinkingToolResponse extends ToolResponse {
output: {
acknowledgedThought: string
}
}
import type { BlockConfig } from '@/blocks/types'
import type { ThinkingToolResponse } from '@/tools/thinking/types'
export const ThinkingBlock: BlockConfig<ThinkingToolResponse> = {
type: 'thinking',

View File

@@ -1,7 +1,7 @@
import { TranslateIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { ProviderId } from '@/providers/types'
import { getBaseModelProviders } from '@/providers/utils'
import type { BlockConfig } from '../types'
const getTranslationPrompt = (
targetLanguage: string

View File

@@ -1,6 +1,6 @@
import { TwilioIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { TwilioSMSBlockOutput } from '@/tools/twilio/types'
import type { BlockConfig } from '../types'
export const TwilioSMSBlock: BlockConfig<TwilioSMSBlockOutput> = {
type: 'twilio_sms',

View File

@@ -1,78 +1,6 @@
import { TypeformIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
interface TypeformResponse extends ToolResponse {
output:
| {
total_items: number
page_count: number
items: Array<{
landing_id: string
token: string
landed_at: string
submitted_at: string
metadata: {
user_agent: string
platform: string
referer: string
network_id: string
browser: string
}
answers: Array<{
field: {
id: string
type: string
ref: string
}
type: string
[key: string]: any // For different answer types (text, boolean, number, etc.)
}>
hidden: Record<string, any>
calculated: {
score: number
}
variables: Array<{
key: string
type: string
[key: string]: any // For different variable types
}>
}>
}
| {
fileUrl: string
contentType: string
filename: string
}
| {
fields: Array<{
dropoffs: number
id: string
label: string
ref: string
title: string
type: string
views: number
}>
form: {
platforms: Array<{
average_time: number
completion_rate: number
platform: string
responses_count: number
total_visits: number
unique_visits: number
}>
summary: {
average_time: number
completion_rate: number
responses_count: number
total_visits: number
unique_visits: number
}
}
}
}
import type { BlockConfig } from '@/blocks/types'
import type { TypeformResponse } from '@/tools/typeform/types'
export const TypeformBlock: BlockConfig<TypeformResponse> = {
type: 'typeform',

View File

@@ -1,6 +1,6 @@
import { EyeIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { VisionResponse } from '@/tools/vision/types'
import type { BlockConfig } from '../types'
export const VisionBlock: BlockConfig<VisionResponse> = {
type: 'vision',

View File

@@ -1,8 +1,6 @@
import { WealthboxIcon } from '@/components/icons'
import type { WealthboxReadResponse, WealthboxWriteResponse } from '@/tools/wealthbox/types'
import type { BlockConfig } from '../types'
type WealthboxResponse = WealthboxReadResponse | WealthboxWriteResponse
import type { BlockConfig } from '@/blocks/types'
import type { WealthboxResponse } from '@/tools/wealthbox/types'
export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
type: 'wealthbox',
@@ -56,17 +54,37 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
requiredScopes: ['login', 'data'],
layout: 'full',
placeholder: 'Enter Contact ID',
mode: 'basic',
condition: { field: 'operation', value: ['read_contact', 'write_task', 'write_note'] },
},
{
id: 'manualContactId',
title: 'Contact ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Contact ID',
mode: 'advanced',
condition: { field: 'operation', value: ['read_contact', 'write_task', 'write_note'] },
},
{
id: 'taskId',
title: 'Select Task',
type: 'short-input',
type: 'file-selector',
provider: 'wealthbox',
serviceId: 'wealthbox',
requiredScopes: ['login', 'data'],
layout: 'full',
placeholder: 'Enter Task ID',
mode: 'basic',
condition: { field: 'operation', value: ['read_task'] },
},
{
id: 'manualTaskId',
title: 'Task ID',
type: 'short-input',
layout: 'full',
placeholder: 'Enter Task ID',
mode: 'advanced',
condition: { field: 'operation', value: ['read_task'] },
},
{
@@ -155,7 +173,14 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
}
},
params: (params) => {
const { credential, operation, ...rest } = params
const { credential, operation, contactId, manualContactId, taskId, manualTaskId, ...rest } =
params
// Handle contact ID input (selector or manual)
const effectiveContactId = (contactId || manualContactId || '').trim()
// Handle task ID input (selector or manual)
const effectiveTaskId = (taskId || manualTaskId || '').trim()
// Build the parameters based on operation type
const baseParams = {
@@ -168,25 +193,40 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
return {
...baseParams,
noteId: params.noteId,
contactId: effectiveContactId,
}
}
// For contact operations, we need contactId
if (operation === 'read_contact') {
if (!params.contactId) {
if (!effectiveContactId) {
throw new Error('Contact ID is required for contact operations')
}
return {
...baseParams,
contactId: params.contactId,
contactId: effectiveContactId,
}
}
// For task operations, we need taskId
if (operation === 'read_task') {
if (!effectiveTaskId) {
throw new Error('Task ID is required for task operations')
}
return {
...baseParams,
taskId: params.taskId,
taskId: effectiveTaskId,
}
}
// For write_task and write_note operations, we need contactId
if (operation === 'write_task' || operation === 'write_note') {
if (!effectiveContactId) {
throw new Error('Contact ID is required for this operation')
}
return {
...baseParams,
contactId: effectiveContactId,
}
}
@@ -199,7 +239,9 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
credential: { type: 'string', required: true },
noteId: { type: 'string', required: false },
contactId: { type: 'string', required: false },
manualContactId: { type: 'string', required: false },
taskId: { type: 'string', required: false },
manualTaskId: { type: 'string', required: false },
content: { type: 'string', required: false },
firstName: { type: 'string', required: false },
lastName: { type: 'string', required: false },

View File

@@ -1,16 +1,8 @@
import { WhatsAppIcon } from '@/components/icons'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
import type { BlockConfig } from '@/blocks/types'
import type { WhatsAppResponse } from '@/tools/whatsapp/types'
interface WhatsAppBlockOutput extends ToolResponse {
output: {
success: boolean
messageId?: string
error?: string
}
}
export const WhatsAppBlock: BlockConfig<WhatsAppBlockOutput> = {
export const WhatsAppBlock: BlockConfig<WhatsAppResponse> = {
type: 'whatsapp',
name: 'WhatsApp',
description: 'Send WhatsApp messages',

View File

@@ -1,8 +1,8 @@
import { WorkflowIcon } from '@/components/icons'
import { createLogger } from '@/lib/logs/console-logger'
import type { BlockConfig } from '@/blocks/types'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import type { ToolResponse } from '@/tools/types'
import type { BlockConfig } from '../types'
const logger = createLogger('WorkflowBlock')

View File

@@ -1,8 +1,6 @@
import { xIcon } from '@/components/icons'
import type { XReadResponse, XSearchResponse, XUserResponse, XWriteResponse } from '@/tools/x/types'
import type { BlockConfig } from '../types'
type XResponse = XWriteResponse | XReadResponse | XSearchResponse | XUserResponse
import type { BlockConfig } from '@/blocks/types'
import type { XResponse } from '@/tools/x/types'
export const XBlock: BlockConfig<XResponse> = {
type: 'x',
@@ -15,7 +13,6 @@ export const XBlock: BlockConfig<XResponse> = {
bgColor: '#000000', // X's black color
icon: xIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
@@ -29,7 +26,6 @@ export const XBlock: BlockConfig<XResponse> = {
],
value: () => 'x_write',
},
// X OAuth Authentication
{
id: 'credential',
title: 'X Account',
@@ -40,7 +36,6 @@ export const XBlock: BlockConfig<XResponse> = {
requiredScopes: ['tweet.read', 'tweet.write', 'users.read'],
placeholder: 'Select X account',
},
// Write operation inputs
{
id: 'text',
title: 'Tweet Text',
@@ -65,7 +60,6 @@ export const XBlock: BlockConfig<XResponse> = {
placeholder: 'Enter comma-separated media IDs',
condition: { field: 'operation', value: 'x_write' },
},
// Read operation inputs
{
id: 'tweetId',
title: 'Tweet ID',
@@ -86,7 +80,6 @@ export const XBlock: BlockConfig<XResponse> = {
value: () => 'false',
condition: { field: 'operation', value: 'x_read' },
},
// Search operation inputs
{
id: 'query',
title: 'Search Query',
@@ -131,7 +124,6 @@ export const XBlock: BlockConfig<XResponse> = {
placeholder: 'YYYY-MM-DDTHH:mm:ssZ',
condition: { field: 'operation', value: 'x_search' },
},
// User operation inputs
{
id: 'username',
title: 'Username',
@@ -198,21 +190,17 @@ export const XBlock: BlockConfig<XResponse> = {
inputs: {
operation: { type: 'string', required: true },
credential: { type: 'string', required: true },
// Write operation
text: { type: 'string', required: false },
replyTo: { type: 'string', required: false },
mediaIds: { type: 'string', required: false },
poll: { type: 'json', required: false },
// Read operation
tweetId: { type: 'string', required: false },
includeReplies: { type: 'boolean', required: false },
// Search operation
query: { type: 'string', required: false },
maxResults: { type: 'number', required: false },
startTime: { type: 'string', required: false },
endTime: { type: 'string', required: false },
sortOrder: { type: 'string', required: false },
// User operation
username: { type: 'string', required: false },
includeRecentTweets: { type: 'boolean', required: false },
},

View File

@@ -1,6 +1,6 @@
import { YouTubeIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { YouTubeSearchResponse } from '@/tools/youtube/types'
import type { BlockConfig } from '../types'
export const YouTubeBlock: BlockConfig<YouTubeSearchResponse> = {
type: 'youtube',

View File

@@ -5,8 +5,8 @@ import {
getBlocksByCategory,
isValidBlockType,
registry,
} from './registry'
} from '@/blocks/registry'
export { registry, getBlock, getBlocksByCategory, getAllBlockTypes, isValidBlockType, getAllBlocks }
export type { BlockConfig } from './types'
export type { BlockConfig } from '@/blocks/types'

View File

@@ -3,67 +3,67 @@
*
*/
// Import all blocks directly here
import { AgentBlock } from './blocks/agent'
import { AirtableBlock } from './blocks/airtable'
import { ApiBlock } from './blocks/api'
import { BrowserUseBlock } from './blocks/browser_use'
import { ClayBlock } from './blocks/clay'
import { ConditionBlock } from './blocks/condition'
import { ConfluenceBlock } from './blocks/confluence'
import { DiscordBlock } from './blocks/discord'
import { ElevenLabsBlock } from './blocks/elevenlabs'
import { EvaluatorBlock } from './blocks/evaluator'
import { ExaBlock } from './blocks/exa'
import { FileBlock } from './blocks/file'
import { FirecrawlBlock } from './blocks/firecrawl'
import { FunctionBlock } from './blocks/function'
import { GitHubBlock } from './blocks/github'
import { GmailBlock } from './blocks/gmail'
import { GoogleSearchBlock } from './blocks/google'
import { GoogleCalendarBlock } from './blocks/google_calendar'
import { GoogleDocsBlock } from './blocks/google_docs'
import { GoogleDriveBlock } from './blocks/google_drive'
import { GoogleSheetsBlock } from './blocks/google_sheets'
import { HuggingFaceBlock } from './blocks/huggingface'
import { ImageGeneratorBlock } from './blocks/image_generator'
import { JinaBlock } from './blocks/jina'
import { JiraBlock } from './blocks/jira'
import { KnowledgeBlock } from './blocks/knowledge'
import { LinearBlock } from './blocks/linear'
import { LinkupBlock } from './blocks/linkup'
import { Mem0Block } from './blocks/mem0'
import { MemoryBlock } from './blocks/memory'
import { MicrosoftExcelBlock } from './blocks/microsoft_excel'
import { MicrosoftTeamsBlock } from './blocks/microsoft_teams'
import { MistralParseBlock } from './blocks/mistral_parse'
import { NotionBlock } from './blocks/notion'
import { OpenAIBlock } from './blocks/openai'
import { OutlookBlock } from './blocks/outlook'
import { PerplexityBlock } from './blocks/perplexity'
import { PineconeBlock } from './blocks/pinecone'
import { RedditBlock } from './blocks/reddit'
import { ResponseBlock } from './blocks/response'
import { RouterBlock } from './blocks/router'
import { S3Block } from './blocks/s3'
import { SerperBlock } from './blocks/serper'
import { SlackBlock } from './blocks/slack'
import { StagehandBlock } from './blocks/stagehand'
import { StagehandAgentBlock } from './blocks/stagehand_agent'
import { StarterBlock } from './blocks/starter'
import { SupabaseBlock } from './blocks/supabase'
import { TavilyBlock } from './blocks/tavily'
import { TelegramBlock } from './blocks/telegram'
import { ThinkingBlock } from './blocks/thinking'
import { TranslateBlock } from './blocks/translate'
import { TwilioSMSBlock } from './blocks/twilio'
import { TypeformBlock } from './blocks/typeform'
import { VisionBlock } from './blocks/vision'
import { WealthboxBlock } from './blocks/wealthbox'
import { WhatsAppBlock } from './blocks/whatsapp'
import { WorkflowBlock } from './blocks/workflow'
import { XBlock } from './blocks/x'
import { YouTubeBlock } from './blocks/youtube'
import type { BlockConfig } from './types'
import { AgentBlock } from '@/blocks/blocks/agent'
import { AirtableBlock } from '@/blocks/blocks/airtable'
import { ApiBlock } from '@/blocks/blocks/api'
import { BrowserUseBlock } from '@/blocks/blocks/browser_use'
import { ClayBlock } from '@/blocks/blocks/clay'
import { ConditionBlock } from '@/blocks/blocks/condition'
import { ConfluenceBlock } from '@/blocks/blocks/confluence'
import { DiscordBlock } from '@/blocks/blocks/discord'
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
import { EvaluatorBlock } from '@/blocks/blocks/evaluator'
import { ExaBlock } from '@/blocks/blocks/exa'
import { FileBlock } from '@/blocks/blocks/file'
import { FirecrawlBlock } from '@/blocks/blocks/firecrawl'
import { FunctionBlock } from '@/blocks/blocks/function'
import { GitHubBlock } from '@/blocks/blocks/github'
import { GmailBlock } from '@/blocks/blocks/gmail'
import { GoogleSearchBlock } from '@/blocks/blocks/google'
import { GoogleCalendarBlock } from '@/blocks/blocks/google_calendar'
import { GoogleDocsBlock } from '@/blocks/blocks/google_docs'
import { GoogleDriveBlock } from '@/blocks/blocks/google_drive'
import { GoogleSheetsBlock } from '@/blocks/blocks/google_sheets'
import { HuggingFaceBlock } from '@/blocks/blocks/huggingface'
import { ImageGeneratorBlock } from '@/blocks/blocks/image_generator'
import { JinaBlock } from '@/blocks/blocks/jina'
import { JiraBlock } from '@/blocks/blocks/jira'
import { KnowledgeBlock } from '@/blocks/blocks/knowledge'
import { LinearBlock } from '@/blocks/blocks/linear'
import { LinkupBlock } from '@/blocks/blocks/linkup'
import { Mem0Block } from '@/blocks/blocks/mem0'
import { MemoryBlock } from '@/blocks/blocks/memory'
import { MicrosoftExcelBlock } from '@/blocks/blocks/microsoft_excel'
import { MicrosoftTeamsBlock } from '@/blocks/blocks/microsoft_teams'
import { MistralParseBlock } from '@/blocks/blocks/mistral_parse'
import { NotionBlock } from '@/blocks/blocks/notion'
import { OpenAIBlock } from '@/blocks/blocks/openai'
import { OutlookBlock } from '@/blocks/blocks/outlook'
import { PerplexityBlock } from '@/blocks/blocks/perplexity'
import { PineconeBlock } from '@/blocks/blocks/pinecone'
import { RedditBlock } from '@/blocks/blocks/reddit'
import { ResponseBlock } from '@/blocks/blocks/response'
import { RouterBlock } from '@/blocks/blocks/router'
import { S3Block } from '@/blocks/blocks/s3'
import { SerperBlock } from '@/blocks/blocks/serper'
import { SlackBlock } from '@/blocks/blocks/slack'
import { StagehandBlock } from '@/blocks/blocks/stagehand'
import { StagehandAgentBlock } from '@/blocks/blocks/stagehand_agent'
import { StarterBlock } from '@/blocks/blocks/starter'
import { SupabaseBlock } from '@/blocks/blocks/supabase'
import { TavilyBlock } from '@/blocks/blocks/tavily'
import { TelegramBlock } from '@/blocks/blocks/telegram'
import { ThinkingBlock } from '@/blocks/blocks/thinking'
import { TranslateBlock } from '@/blocks/blocks/translate'
import { TwilioSMSBlock } from '@/blocks/blocks/twilio'
import { TypeformBlock } from '@/blocks/blocks/typeform'
import { VisionBlock } from '@/blocks/blocks/vision'
import { WealthboxBlock } from '@/blocks/blocks/wealthbox'
import { WhatsAppBlock } from '@/blocks/blocks/whatsapp'
import { WorkflowBlock } from '@/blocks/blocks/workflow'
import { XBlock } from '@/blocks/blocks/x'
import { YouTubeBlock } from '@/blocks/blocks/youtube'
import type { BlockConfig } from '@/blocks/types'
// Registry of all available blocks, alphabetically sorted
export const registry: Record<string, BlockConfig> = {

View File

@@ -2808,7 +2808,7 @@ export const ResponseIcon = (props: SVGProps<SVGSVGElement>) => (
>
<path
d='m1030.975 188 81.249 81.249-429.228 429.228h300.747c516.223 0 936.257 420.034 936.257 936.257v98.028h-114.92v-98.028c0-452.901-368.436-821.337-821.337-821.337H682.996l429.228 429.229-81.25 81.248-567.936-567.937L1030.975 188Zm-463.038.011 81.249 81.25-486.688 486.688 486.688 486.688-81.249 81.249L0 755.949 567.937 188.01Z'
fill-rule='evenodd'
fillRule='evenodd'
/>
</svg>
)

View File

@@ -12,6 +12,11 @@ vi.mock('@/lib/logs/console-logger', () => ({
})),
}))
// Blocks
vi.mock('@/blocks/index', () => ({
getBlock: vi.fn(),
}))
// Tools
vi.mock('@/tools/utils', () => ({
getTool: vi.fn(),

View File

@@ -1,19 +1,10 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { getBlock } from '@/blocks/index'
import { BlockType } from '@/executor/consts'
import { InputResolver } from '@/executor/resolver/resolver'
import type { ExecutionContext } from '@/executor/types'
import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types'
// Mock logger
vi.mock('@/lib/logs/console-logger', () => ({
createLogger: vi.fn().mockReturnValue({
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}),
}))
describe('InputResolver', () => {
let sampleWorkflow: SerializedWorkflow
let mockContext: any
@@ -1898,4 +1889,502 @@ describe('InputResolver', () => {
expect(() => loopResolver.resolveInputs(testBlock, loopContext)).not.toThrow()
})
})
describe('Conditional Input Filtering', () => {
const mockGetBlock = getBlock as ReturnType<typeof vi.fn>
afterEach(() => {
mockGetBlock.mockReset()
})
it('should filter inputs based on operation conditions for Knowledge block', () => {
// Mock the Knowledge block configuration
mockGetBlock.mockReturnValue({
type: 'knowledge',
subBlocks: [
{
id: 'operation',
type: 'dropdown',
options: [
{ label: 'Search', id: 'search' },
{ label: 'Upload Chunk', id: 'upload_chunk' },
],
},
{
id: 'query',
type: 'short-input',
condition: { field: 'operation', value: 'search' },
},
{
id: 'knowledgeBaseIds',
type: 'knowledge-base-selector',
condition: { field: 'operation', value: 'search' },
},
{
id: 'documentId',
type: 'document-selector',
condition: { field: 'operation', value: 'upload_chunk' },
},
{
id: 'content',
type: 'long-input',
condition: { field: 'operation', value: 'upload_chunk' },
},
],
})
// Create a Knowledge block with upload_chunk operation
const knowledgeBlock: SerializedBlock = {
id: 'knowledge-block',
metadata: { id: 'knowledge', name: 'Knowledge Block' },
position: { x: 0, y: 0 },
config: {
tool: 'knowledge',
params: {
operation: 'upload_chunk',
query: '<start.docName>', // This should be filtered out
knowledgeBaseIds: 'kb-1', // This should be filtered out
documentId: 'doc-1', // This should be included
content: 'chunk content', // This should be included
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(knowledgeBlock, mockContext)
// Should only include inputs for upload_chunk operation
expect(result).toHaveProperty('operation', 'upload_chunk')
expect(result).toHaveProperty('documentId', 'doc-1')
expect(result).toHaveProperty('content', 'chunk content')
// Should NOT include inputs for search operation
expect(result).not.toHaveProperty('query')
expect(result).not.toHaveProperty('knowledgeBaseIds')
})
it('should filter inputs based on operation conditions for Knowledge block search operation', () => {
// Mock the Knowledge block configuration
mockGetBlock.mockReturnValue({
type: 'knowledge',
subBlocks: [
{
id: 'operation',
type: 'dropdown',
options: [
{ label: 'Search', id: 'search' },
{ label: 'Upload Chunk', id: 'upload_chunk' },
],
},
{
id: 'query',
type: 'short-input',
condition: { field: 'operation', value: 'search' },
},
{
id: 'knowledgeBaseIds',
type: 'knowledge-base-selector',
condition: { field: 'operation', value: 'search' },
},
{
id: 'documentId',
type: 'document-selector',
condition: { field: 'operation', value: 'upload_chunk' },
},
{
id: 'content',
type: 'long-input',
condition: { field: 'operation', value: 'upload_chunk' },
},
],
})
// Create a Knowledge block with search operation
const knowledgeBlock: SerializedBlock = {
id: 'knowledge-block',
metadata: { id: 'knowledge', name: 'Knowledge Block' },
position: { x: 0, y: 0 },
config: {
tool: 'knowledge',
params: {
operation: 'search',
query: 'search query',
knowledgeBaseIds: 'kb-1',
documentId: 'doc-1', // This should be filtered out
content: 'chunk content', // This should be filtered out
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(knowledgeBlock, mockContext)
// Should only include inputs for search operation
expect(result).toHaveProperty('operation', 'search')
expect(result).toHaveProperty('query', 'search query')
expect(result).toHaveProperty('knowledgeBaseIds', 'kb-1')
// Should NOT include inputs for upload_chunk operation
expect(result).not.toHaveProperty('documentId')
expect(result).not.toHaveProperty('content')
})
it('should handle array conditions correctly', () => {
// Mock a block with array condition
mockGetBlock.mockReturnValue({
type: 'test-block',
subBlocks: [
{
id: 'operation',
type: 'dropdown',
options: [
{ label: 'Create', id: 'create' },
{ label: 'Update', id: 'update' },
{ label: 'Delete', id: 'delete' },
],
},
{
id: 'data',
type: 'long-input',
condition: { field: 'operation', value: ['create', 'update'] },
},
{
id: 'id',
type: 'short-input',
condition: { field: 'operation', value: ['update', 'delete'] },
},
],
})
const testBlock: SerializedBlock = {
id: 'test-block',
metadata: { id: 'test-block', name: 'Test Block' },
position: { x: 0, y: 0 },
config: {
tool: 'test-block',
params: {
operation: 'update',
data: 'some data',
id: 'item-1',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(testBlock, mockContext)
// Should include inputs for update operation (both data and id)
expect(result).toHaveProperty('operation', 'update')
expect(result).toHaveProperty('data', 'some data')
expect(result).toHaveProperty('id', 'item-1')
})
it('should include all inputs when no conditions are present', () => {
// Mock a block with no conditions
mockGetBlock.mockReturnValue({
type: 'simple-block',
subBlocks: [
{
id: 'param1',
type: 'short-input',
},
{
id: 'param2',
type: 'long-input',
},
],
})
const simpleBlock: SerializedBlock = {
id: 'simple-block',
metadata: { id: 'simple-block', name: 'Simple Block' },
position: { x: 0, y: 0 },
config: {
tool: 'simple-block',
params: {
param1: 'value1',
param2: 'value2',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(simpleBlock, mockContext)
// Should include all inputs
expect(result).toHaveProperty('param1', 'value1')
expect(result).toHaveProperty('param2', 'value2')
})
it('should return all inputs when block config is not found', () => {
// Mock getBlock to return undefined
mockGetBlock.mockReturnValue(undefined)
const unknownBlock: SerializedBlock = {
id: 'unknown-block',
metadata: { id: 'unknown-type', name: 'Unknown Block' },
position: { x: 0, y: 0 },
config: {
tool: 'unknown-type',
params: {
param1: 'value1',
param2: 'value2',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(unknownBlock, mockContext)
// Should include all inputs when block config is not found
expect(result).toHaveProperty('param1', 'value1')
expect(result).toHaveProperty('param2', 'value2')
})
it('should handle negated conditions correctly', () => {
// Mock a block with negated condition
mockGetBlock.mockReturnValue({
type: 'test-block',
subBlocks: [
{
id: 'operation',
type: 'dropdown',
options: [
{ label: 'Create', id: 'create' },
{ label: 'Delete', id: 'delete' },
],
},
{
id: 'confirmationField',
type: 'short-input',
condition: { field: 'operation', value: 'create', not: true },
},
],
})
const testBlock: SerializedBlock = {
id: 'test-block',
metadata: { id: 'test-block', name: 'Test Block' },
position: { x: 0, y: 0 },
config: {
tool: 'test-block',
params: {
operation: 'delete',
confirmationField: 'confirmed',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(testBlock, mockContext)
// Should include confirmationField because operation is NOT 'create'
expect(result).toHaveProperty('operation', 'delete')
expect(result).toHaveProperty('confirmationField', 'confirmed')
})
it('should handle compound AND conditions correctly', () => {
// Mock a block with compound AND condition
mockGetBlock.mockReturnValue({
type: 'test-block',
subBlocks: [
{
id: 'operation',
type: 'dropdown',
options: [
{ label: 'Create', id: 'create' },
{ label: 'Update', id: 'update' },
],
},
{
id: 'enabled',
type: 'switch',
},
{
id: 'specialField',
type: 'short-input',
condition: {
field: 'operation',
value: 'update',
and: { field: 'enabled', value: true },
},
},
],
})
const testBlock: SerializedBlock = {
id: 'test-block',
metadata: { id: 'test-block', name: 'Test Block' },
position: { x: 0, y: 0 },
config: {
tool: 'test-block',
params: {
operation: 'update',
enabled: true,
specialField: 'special value',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(testBlock, mockContext)
// Should include specialField because operation is 'update' AND enabled is true
expect(result).toHaveProperty('operation', 'update')
expect(result).toHaveProperty('enabled', true)
expect(result).toHaveProperty('specialField', 'special value')
})
it('should always include inputs without conditions', () => {
// Mock a block with mixed conditions
mockGetBlock.mockReturnValue({
type: 'test-block',
subBlocks: [
{
id: 'operation',
type: 'dropdown',
// No condition - should always be included
},
{
id: 'alwaysVisible',
type: 'short-input',
// No condition - should always be included
},
{
id: 'conditionalField',
type: 'short-input',
condition: { field: 'operation', value: 'search' },
},
],
})
const testBlock: SerializedBlock = {
id: 'test-block',
metadata: { id: 'test-block', name: 'Test Block' },
position: { x: 0, y: 0 },
config: {
tool: 'test-block',
params: {
operation: 'upload',
alwaysVisible: 'always here',
conditionalField: 'should be filtered out',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result = resolver.resolveInputs(testBlock, mockContext)
// Should include inputs without conditions
expect(result).toHaveProperty('operation', 'upload')
expect(result).toHaveProperty('alwaysVisible', 'always here')
// Should NOT include conditional field that doesn't match
expect(result).not.toHaveProperty('conditionalField')
})
it('should handle duplicate field names with different conditions (Knowledge block case)', () => {
// Mock Knowledge block with duplicate content fields
mockGetBlock.mockReturnValue({
type: 'knowledge',
subBlocks: [
{
id: 'operation',
type: 'dropdown',
},
{
id: 'content',
title: 'Chunk Content',
type: 'long-input',
condition: { field: 'operation', value: 'upload_chunk' },
},
{
id: 'content',
title: 'Document Content',
type: 'long-input',
condition: { field: 'operation', value: 'create_document' },
},
],
})
// Test upload_chunk operation
const uploadChunkBlock: SerializedBlock = {
id: 'knowledge-block',
metadata: { id: 'knowledge', name: 'Knowledge Block' },
position: { x: 0, y: 0 },
config: {
tool: 'knowledge',
params: {
operation: 'upload_chunk',
content: 'chunk content here',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result1 = resolver.resolveInputs(uploadChunkBlock, mockContext)
expect(result1).toHaveProperty('operation', 'upload_chunk')
expect(result1).toHaveProperty('content', 'chunk content here')
// Test create_document operation
const createDocBlock: SerializedBlock = {
id: 'knowledge-block',
metadata: { id: 'knowledge', name: 'Knowledge Block' },
position: { x: 0, y: 0 },
config: {
tool: 'knowledge',
params: {
operation: 'create_document',
content: 'document content here',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result2 = resolver.resolveInputs(createDocBlock, mockContext)
expect(result2).toHaveProperty('operation', 'create_document')
expect(result2).toHaveProperty('content', 'document content here')
// Test search operation (should NOT include content)
const searchBlock: SerializedBlock = {
id: 'knowledge-block',
metadata: { id: 'knowledge', name: 'Knowledge Block' },
position: { x: 0, y: 0 },
config: {
tool: 'knowledge',
params: {
operation: 'search',
content: 'should be filtered out',
},
},
inputs: {},
outputs: {},
enabled: true,
}
const result3 = resolver.resolveInputs(searchBlock, mockContext)
expect(result3).toHaveProperty('operation', 'search')
expect(result3).not.toHaveProperty('content')
})
})
})

View File

@@ -1,6 +1,7 @@
import { BlockPathCalculator } from '@/lib/block-path-calculator'
import { createLogger } from '@/lib/logs/console-logger'
import { VariableManager } from '@/lib/variables/variable-manager'
import { getBlock } from '@/blocks/index'
import type { LoopManager } from '@/executor/loops/loops'
import type { ExecutionContext } from '@/executor/types'
import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types'
@@ -55,6 +56,102 @@ export class InputResolver {
}
}
/**
* Evaluates if a sub-block should be active based on its condition
* @param condition - The condition to evaluate
* @param currentValues - Current values of all inputs
* @returns True if the sub-block should be active
*/
private evaluateSubBlockCondition(
condition:
| {
field: string
value: any
not?: boolean
and?: { field: string; value: any; not?: boolean }
}
| undefined,
currentValues: Record<string, any>
): boolean {
if (!condition) return true
// Get the field value
const fieldValue = currentValues[condition.field]
// Check if the condition value is an array
const isValueMatch = Array.isArray(condition.value)
? fieldValue != null &&
(condition.not
? !condition.value.includes(fieldValue)
: condition.value.includes(fieldValue))
: condition.not
? fieldValue !== condition.value
: fieldValue === condition.value
// Check both conditions if 'and' is present
const isAndValueMatch =
!condition.and ||
(() => {
const andFieldValue = currentValues[condition.and!.field]
return Array.isArray(condition.and!.value)
? andFieldValue != null &&
(condition.and!.not
? !condition.and!.value.includes(andFieldValue)
: condition.and!.value.includes(andFieldValue))
: condition.and!.not
? andFieldValue !== condition.and!.value
: andFieldValue === condition.and!.value
})()
return isValueMatch && isAndValueMatch
}
/**
* Filters inputs based on sub-block conditions
* @param block - Block to filter inputs for
* @param inputs - All input parameters
* @returns Filtered input parameters that should be processed
*/
private filterInputsByConditions(
block: SerializedBlock,
inputs: Record<string, any>
): Record<string, any> {
const blockType = block.metadata?.id
if (!blockType) return inputs
const blockConfig = getBlock(blockType)
if (!blockConfig || !blockConfig.subBlocks) return inputs
// Filter inputs based on conditions
const filteredInputs: Record<string, any> = {}
for (const [key, value] of Object.entries(inputs)) {
// Check if this input should be included based on subBlock conditions
let shouldInclude = false
// Find all subBlocks with this ID
const matchingSubBlocks = blockConfig.subBlocks.filter((sb) => sb.id === key)
if (matchingSubBlocks.length === 0) {
// No subBlock config found for this input - include it
shouldInclude = true
} else {
// Check if any of the matching subBlocks should be active
for (const subBlock of matchingSubBlocks) {
if (!subBlock.condition || this.evaluateSubBlockCondition(subBlock.condition, inputs)) {
shouldInclude = true
break
}
}
}
if (shouldInclude) {
filteredInputs[key] = value
}
}
return filteredInputs
}
/**
* Resolves all inputs for a block based on current context.
* Handles block references, environment variables, and JSON parsing.
@@ -64,7 +161,9 @@ export class InputResolver {
* @returns Resolved input parameters
*/
resolveInputs(block: SerializedBlock, context: ExecutionContext): Record<string, any> {
const inputs = { ...block.config.params }
const allInputs = { ...block.config.params }
// Filter inputs based on sub-block conditions to only process active fields
const inputs = this.filterInputsByConditions(block, allInputs)
const result: Record<string, any> = {}
// Process each input parameter
for (const [key, value] of Object.entries(inputs)) {

View File

@@ -87,3 +87,10 @@ export interface AirtableUpdateMultipleResponse extends ToolResponse {
}
}
}
export type AirtableResponse =
| AirtableListResponse
| AirtableGetResponse
| AirtableCreateResponse
| AirtableUpdateResponse
| AirtableUpdateMultipleResponse

View File

@@ -27,3 +27,12 @@ export interface BrowserUseTaskOutput {
export interface BrowserUseRunTaskResponse extends ToolResponse {
output: BrowserUseTaskOutput
}
export interface BrowserUseResponse extends ToolResponse {
output: {
id: string
success: boolean
output: any
steps: BrowserUseTaskStep[]
}
}

View File

@@ -42,3 +42,5 @@ export interface ConfluenceUpdateResponse extends ToolResponse {
success: boolean
}
}
export type ConfluenceResponse = ConfluenceRetrieveResponse | ConfluenceUpdateResponse

View File

@@ -12,3 +12,9 @@ export interface ElevenLabsTtsResponse extends ToolResponse {
audioUrl: string
}
}
export interface ElevenLabsBlockResponse extends ToolResponse {
output: {
audioUrl: string
}
}

View File

@@ -88,3 +88,9 @@ export interface ExaAnswerResponse extends ToolResponse {
}[]
}
}
export type ExaResponse =
| ExaSearchResponse
| ExaGetContentsResponse
| ExaFindSimilarLinksResponse
| ExaAnswerResponse

View File

@@ -58,3 +58,5 @@ export interface SearchResponse extends ToolResponse {
warning?: string
}
}
export type FirecrawlResponse = ScrapeResponse | SearchResponse

View File

@@ -147,3 +147,9 @@ export interface RepoInfoResponse extends ToolResponse {
metadata: RepoMetadata
}
}
export type GitHubResponse =
| PullRequestResponse
| CreateCommentResponse
| LatestCommitResponse
| RepoInfoResponse

View File

@@ -276,3 +276,11 @@ export interface GoogleCalendarApiListResponse {
nextSyncToken?: string
items: GoogleCalendarApiEventResponse[]
}
export type GoogleCalendarResponse =
| GoogleCalendarCreateResponse
| GoogleCalendarListResponse
| GoogleCalendarGetResponse
| GoogleCalendarQuickAddResponse
| GoogleCalendarInviteResponse
| GoogleCalendarUpdateResponse

View File

@@ -38,3 +38,8 @@ export interface GoogleDocsToolParams {
folderId?: string
folderSelector?: string
}
export type GoogleDocsResponse =
| GoogleDocsReadResponse
| GoogleDocsWriteResponse
| GoogleDocsCreateResponse

View File

@@ -45,3 +45,8 @@ export interface GoogleDriveToolParams {
pageToken?: string
exportMimeType?: string
}
export type GoogleDriveResponse =
| GoogleDriveUploadResponse
| GoogleDriveGetContentResponse
| GoogleDriveListResponse

View File

@@ -69,3 +69,9 @@ export interface GoogleSheetsToolParams {
responseValueRenderOption?: 'FORMATTED_VALUE' | 'UNFORMATTED_VALUE' | 'FORMULA'
majorDimension?: 'ROWS' | 'COLUMNS'
}
export type GoogleSheetsResponse =
| GoogleSheetsReadResponse
| GoogleSheetsWriteResponse
| GoogleSheetsUpdateResponse
| GoogleSheetsAppendResponse

View File

@@ -104,3 +104,9 @@ export interface JiraCloudResource {
scopes: string[]
avatarUrl: string
}
export type JiraResponse =
| JiraRetrieveResponse
| JiraUpdateResponse
| JiraWriteResponse
| JiraRetrieveResponseBulk

View File

@@ -34,3 +34,5 @@ export interface LinearCreateIssueResponse extends ToolResponse {
issue: LinearIssue
}
}
export type LinearResponse = LinearReadIssuesResponse | LinearCreateIssueResponse

View File

@@ -67,3 +67,8 @@ export interface MicrosoftExcelTableToolParams {
values: ExcelCellValue[][]
rowIndex?: number
}
export type MicrosoftExcelResponse =
| MicrosoftExcelReadResponse
| MicrosoftExcelWriteResponse
| MicrosoftExcelTableAddResponse

View File

@@ -57,3 +57,5 @@ export interface MicrosoftTeamsToolParams {
teamId?: string
content?: string
}
export type MicrosoftTeamsResponse = MicrosoftTeamsReadResponse | MicrosoftTeamsWriteResponse

View File

@@ -127,3 +127,5 @@ export interface CleanedOutlookMessage {
isRead?: boolean
importance?: string
}
export type OutlookResponse = OutlookReadResponse | OutlookSendResponse | OutlookDraftResponse

View File

@@ -74,3 +74,5 @@ export interface RedditCommentsResponse extends ToolResponse {
comments: RedditComment[]
}
}
export type RedditResponse = RedditHotPostsResponse | RedditPostsResponse | RedditCommentsResponse

View File

@@ -18,3 +18,5 @@ export interface SlackMessageResponse extends ToolResponse {
channel: string
}
}
export type SlackResponse = SlackMessageResponse

View File

@@ -13,18 +13,16 @@ export interface SupabaseInsertParams {
data: any
}
export interface SupabaseQueryResponse extends ToolResponse {
error?: string
export interface SupabaseBaseResponse extends ToolResponse {
output: {
message: string
results: any
}
error?: string
}
export interface SupabaseInsertResponse extends ToolResponse {
error?: string
output: {
message: string
results: any
}
}
export interface SupabaseQueryResponse extends SupabaseBaseResponse {}
export interface SupabaseInsertResponse extends SupabaseBaseResponse {}
export interface SupabaseResponse extends SupabaseBaseResponse {}

View File

@@ -70,3 +70,5 @@ export interface SearchResponse extends ToolResponse {
response_time: number
}
}
export type TavilyResponse = TavilySearchResponse | TavilyExtractResponse

View File

@@ -103,3 +103,10 @@ export interface TypeformResponsesResponse extends ToolResponse {
}>
}
}
export interface TypeformResponse extends ToolResponse {
output:
| TypeformResponsesResponse['output']
| TypeformFilesResponse['output']
| TypeformInsightsData
}

View File

@@ -140,3 +140,5 @@ export interface WealthboxWriteParams {
category?: number
priority?: 'Low' | 'Medium' | 'High'
}
export type WealthboxResponse = WealthboxReadResponse | WealthboxWriteResponse

View File

@@ -1,10 +1,10 @@
import { createLogger } from '@/lib/logs/console-logger'
import type { ToolConfig } from '../types'
import type { WhatsAppToolResponse } from './types'
import type { WhatsAppResponse, WhatsAppSendMessageParams } from './types'
const logger = createLogger('WhatsAppSendMessageTool')
export const sendMessageTool: ToolConfig<any, WhatsAppToolResponse> = {
export const sendMessageTool: ToolConfig<WhatsAppSendMessageParams, WhatsAppResponse> = {
id: 'whatsapp_send_message',
name: 'WhatsApp',
description: 'Send WhatsApp messages',

View File

@@ -1,6 +1,13 @@
import type { ToolResponse } from '../types'
export interface WhatsAppToolResponse extends ToolResponse {
export interface WhatsAppSendMessageParams {
phoneNumber: string
message: string
phoneNumberId: string
accessToken: string
}
export interface WhatsAppResponse extends ToolResponse {
output: {
success: boolean
messageId?: string

View File

@@ -105,3 +105,5 @@ export interface XUserResponse extends ToolResponse {
recentTweets?: XTweet[]
}
}
export type XResponse = XWriteResponse | XReadResponse | XSearchResponse | XUserResponse