diff --git a/apps/docs/content/docs/de/tools/apify.mdx b/apps/docs/content/docs/de/tools/apify.mdx index c8c82105c7..cb8385055c 100644 --- a/apps/docs/content/docs/de/tools/apify.mdx +++ b/apps/docs/content/docs/de/tools/apify.mdx @@ -54,7 +54,6 @@ Führe einen APIFY-Aktor synchron aus und erhalte Ergebnisse (maximal 5 Minuten) | `success` | boolean | Ob die Aktor-Ausführung erfolgreich war | | `runId` | string | APIFY-Ausführungs-ID | | `status` | string | Ausführungsstatus \(SUCCEEDED, FAILED, usw.\) | -| `datasetId` | string | Dataset-ID mit Ergebnissen | | `items` | array | Dataset-Elemente \(falls abgeschlossen\) | ### `apify_run_actor_async` diff --git a/apps/docs/content/docs/en/tools/apify.mdx b/apps/docs/content/docs/en/tools/apify.mdx index d26a1eb70a..2ba9276b1c 100644 --- a/apps/docs/content/docs/en/tools/apify.mdx +++ b/apps/docs/content/docs/en/tools/apify.mdx @@ -57,7 +57,6 @@ Run an APIFY actor synchronously and get results (max 5 minutes) | `success` | boolean | Whether the actor run succeeded | | `runId` | string | APIFY run ID | | `status` | string | Run status \(SUCCEEDED, FAILED, etc.\) | -| `datasetId` | string | Dataset ID containing results | | `items` | array | Dataset items \(if completed\) | ### `apify_run_actor_async` diff --git a/apps/docs/content/docs/es/tools/apify.mdx b/apps/docs/content/docs/es/tools/apify.mdx index 5a712eee12..0e925d41c4 100644 --- a/apps/docs/content/docs/es/tools/apify.mdx +++ b/apps/docs/content/docs/es/tools/apify.mdx @@ -54,7 +54,6 @@ Ejecuta un actor de APIFY de forma sincrónica y obtén resultados (máximo 5 mi | `success` | boolean | Si la ejecución del actor tuvo éxito | | `runId` | string | ID de ejecución de APIFY | | `status` | string | Estado de la ejecución \(SUCCEEDED, FAILED, etc.\) | -| `datasetId` | string | ID del conjunto de datos que contiene los resultados | | `items` | array | Elementos del conjunto de datos \(si se completó\) | ### `apify_run_actor_async` diff --git a/apps/docs/content/docs/fr/tools/apify.mdx b/apps/docs/content/docs/fr/tools/apify.mdx index 150c967ef7..8010f2cad3 100644 --- a/apps/docs/content/docs/fr/tools/apify.mdx +++ b/apps/docs/content/docs/fr/tools/apify.mdx @@ -54,7 +54,6 @@ Exécuter un acteur APIFY de manière synchrone et obtenir les résultats (maxim | `success` | booléen | Indique si l'exécution de l'acteur a réussi | | `runId` | chaîne | ID d'exécution APIFY | | `status` | chaîne | Statut d'exécution \(SUCCEEDED, FAILED, etc.\) | -| `datasetId` | chaîne | ID du jeu de données contenant les résultats | | `items` | tableau | Éléments du jeu de données \(si terminé\) | ### `apify_run_actor_async` diff --git a/apps/docs/content/docs/ja/tools/apify.mdx b/apps/docs/content/docs/ja/tools/apify.mdx index ff5ef18f65..04b4ae19d2 100644 --- a/apps/docs/content/docs/ja/tools/apify.mdx +++ b/apps/docs/content/docs/ja/tools/apify.mdx @@ -54,7 +54,6 @@ APIPYアクターを同期的に実行して結果を取得(最大5分) | `success` | boolean | アクター実行が成功したかどうか | | `runId` | string | APIFY実行ID | | `status` | string | 実行ステータス(SUCCEEDED、FAILEDなど) | -| `datasetId` | string | 結果を含むデータセットID | | `items` | array | データセット項目(完了した場合) | ### `apify_run_actor_async` diff --git a/apps/docs/content/docs/zh/tools/apify.mdx b/apps/docs/content/docs/zh/tools/apify.mdx index c384c060b8..66dfb5b403 100644 --- a/apps/docs/content/docs/zh/tools/apify.mdx +++ b/apps/docs/content/docs/zh/tools/apify.mdx @@ -54,7 +54,6 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | `success` | boolean | actor 运行是否成功 | | `runId` | string | APIFY 运行 ID | | `status` | string | 运行状态 \(SUCCEEDED, FAILED 等\) | -| `datasetId` | string | 包含结果的数据集 ID | | `items` | array | 数据集条目 \(如果已完成\) | ### `apify_run_actor_async` diff --git a/apps/sim/app/api/files/parse/route.ts b/apps/sim/app/api/files/parse/route.ts index a81560ea41..fe91b2348f 100644 --- a/apps/sim/app/api/files/parse/route.ts +++ b/apps/sim/app/api/files/parse/route.ts @@ -15,6 +15,7 @@ import { extractCleanFilename, extractStorageKey, extractWorkspaceIdFromExecutionKey, + getMimeTypeFromExtension, getViewerUrl, inferContextFromKey, } from '@/lib/uploads/utils/file-utils' @@ -44,36 +45,6 @@ interface ParseResult { } } -const fileTypeMap: Record = { - // Text formats - txt: 'text/plain', - csv: 'text/csv', - json: 'application/json', - xml: 'application/xml', - md: 'text/markdown', - html: 'text/html', - css: 'text/css', - js: 'application/javascript', - ts: 'application/typescript', - // Document formats - pdf: 'application/pdf', - doc: 'application/msword', - docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - // Spreadsheet formats - xls: 'application/vnd.ms-excel', - xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - // Presentation formats - ppt: 'application/vnd.ms-powerpoint', - pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - // Image formats - png: 'image/png', - jpg: 'image/jpeg', - jpeg: 'image/jpeg', - gif: 'image/gif', - // Archive formats - zip: 'application/zip', -} - /** * Main API route handler */ @@ -378,7 +349,8 @@ async function handleExternalUrl( }) } else { const { uploadWorkspaceFile } = await import('@/lib/uploads/contexts/workspace') - const mimeType = response.headers.get('content-type') || getMimeType(extension) + const mimeType = + response.headers.get('content-type') || getMimeTypeFromExtension(extension) await uploadWorkspaceFile(workspaceId, userId, buffer, filename, mimeType) logger.info(`Saved URL file to workspace storage: ${filename}`) } @@ -570,7 +542,7 @@ async function handleLocalFile( content: result.content, filePath, metadata: { - fileType: fileType || getMimeType(extension), + fileType: fileType || getMimeTypeFromExtension(extension), size: stats.size, hash, processingTime: 0, @@ -705,7 +677,7 @@ async function handleGenericTextBuffer( content: result.content, filePath: originalPath || filename, metadata: { - fileType: fileType || getMimeType(extension), + fileType: fileType || getMimeTypeFromExtension(extension), size: fileBuffer.length, hash: createHash('md5').update(fileBuffer).digest('hex'), processingTime: 0, @@ -723,7 +695,7 @@ async function handleGenericTextBuffer( content, filePath: originalPath || filename, metadata: { - fileType: fileType || getMimeType(extension), + fileType: fileType || getMimeTypeFromExtension(extension), size: fileBuffer.length, hash: createHash('md5').update(fileBuffer).digest('hex'), processingTime: 0, @@ -764,7 +736,7 @@ function handleGenericBuffer( content, filePath: filename, metadata: { - fileType: fileType || getMimeType(extension), + fileType: fileType || getMimeTypeFromExtension(extension), size: fileBuffer.length, hash: createHash('md5').update(fileBuffer).digest('hex'), processingTime: 0, @@ -787,13 +759,6 @@ async function parseBufferAsPdf(buffer: Buffer) { } } -/** - * Get MIME type from file extension - */ -function getMimeType(extension: string): string { - return fileTypeMap[extension] || 'application/octet-stream' -} - /** * Format bytes to human readable size */ diff --git a/apps/sim/app/api/tools/asana/add-comment/route.ts b/apps/sim/app/api/tools/asana/add-comment/route.ts index fa9bf681f2..bd00e151c0 100644 --- a/apps/sim/app/api/tools/asana/add-comment/route.ts +++ b/apps/sim/app/api/tools/asana/add-comment/route.ts @@ -86,18 +86,16 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, - output: { - ts: new Date().toISOString(), - gid: story.gid, - text: story.text || '', - created_at: story.created_at, - created_by: story.created_by - ? { - gid: story.created_by.gid, - name: story.created_by.name, - } - : undefined, - }, + ts: new Date().toISOString(), + gid: story.gid, + text: story.text || '', + created_at: story.created_at, + created_by: story.created_by + ? { + gid: story.created_by.gid, + name: story.created_by.name, + } + : undefined, }) } catch (error) { logger.error('Error processing request:', error) diff --git a/apps/sim/app/api/tools/asana/create-task/route.ts b/apps/sim/app/api/tools/asana/create-task/route.ts index 70bd00f1b4..69200e6d90 100644 --- a/apps/sim/app/api/tools/asana/create-task/route.ts +++ b/apps/sim/app/api/tools/asana/create-task/route.ts @@ -99,15 +99,13 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, - output: { - ts: new Date().toISOString(), - gid: task.gid, - name: task.name, - notes: task.notes || '', - completed: task.completed || false, - created_at: task.created_at, - permalink_url: task.permalink_url, - }, + ts: new Date().toISOString(), + gid: task.gid, + name: task.name, + notes: task.notes || '', + completed: task.completed || false, + created_at: task.created_at, + permalink_url: task.permalink_url, }) } catch (error: any) { logger.error('Error creating Asana task:', { diff --git a/apps/sim/app/api/tools/asana/get-projects/route.ts b/apps/sim/app/api/tools/asana/get-projects/route.ts index 5e93e93c4b..f26da3fd9a 100644 --- a/apps/sim/app/api/tools/asana/get-projects/route.ts +++ b/apps/sim/app/api/tools/asana/get-projects/route.ts @@ -73,14 +73,12 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, - output: { - ts: new Date().toISOString(), - projects: projects.map((project: any) => ({ - gid: project.gid, - name: project.name, - resource_type: project.resource_type, - })), - }, + ts: new Date().toISOString(), + projects: projects.map((project: any) => ({ + gid: project.gid, + name: project.name, + resource_type: project.resource_type, + })), }) } catch (error) { logger.error('Error processing request:', error) diff --git a/apps/sim/app/api/tools/asana/get-task/route.ts b/apps/sim/app/api/tools/asana/get-task/route.ts index 8122986e9e..bcc459e4c5 100644 --- a/apps/sim/app/api/tools/asana/get-task/route.ts +++ b/apps/sim/app/api/tools/asana/get-task/route.ts @@ -69,31 +69,29 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, - output: { - ts: new Date().toISOString(), - gid: task.gid, - resource_type: task.resource_type, - resource_subtype: task.resource_subtype, - name: task.name, - notes: task.notes || '', - completed: task.completed || false, - assignee: task.assignee - ? { - gid: task.assignee.gid, - name: task.assignee.name, - } - : undefined, - created_by: task.created_by - ? { - gid: task.created_by.gid, - resource_type: task.created_by.resource_type, - name: task.created_by.name, - } - : undefined, - due_on: task.due_on || undefined, - created_at: task.created_at, - modified_at: task.modified_at, - }, + ts: new Date().toISOString(), + gid: task.gid, + resource_type: task.resource_type, + resource_subtype: task.resource_subtype, + name: task.name, + notes: task.notes || '', + completed: task.completed || false, + assignee: task.assignee + ? { + gid: task.assignee.gid, + name: task.assignee.name, + } + : undefined, + created_by: task.created_by + ? { + gid: task.created_by.gid, + resource_type: task.created_by.resource_type, + name: task.created_by.name, + } + : undefined, + due_on: task.due_on || undefined, + created_at: task.created_at, + modified_at: task.modified_at, }) } @@ -180,34 +178,32 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, - output: { - ts: new Date().toISOString(), - tasks: tasks.map((task: any) => ({ - gid: task.gid, - resource_type: task.resource_type, - resource_subtype: task.resource_subtype, - name: task.name, - notes: task.notes || '', - completed: task.completed || false, - assignee: task.assignee - ? { - gid: task.assignee.gid, - name: task.assignee.name, - } - : undefined, - created_by: task.created_by - ? { - gid: task.created_by.gid, - resource_type: task.created_by.resource_type, - name: task.created_by.name, - } - : undefined, - due_on: task.due_on || undefined, - created_at: task.created_at, - modified_at: task.modified_at, - })), - next_page: result.next_page, - }, + ts: new Date().toISOString(), + tasks: tasks.map((task: any) => ({ + gid: task.gid, + resource_type: task.resource_type, + resource_subtype: task.resource_subtype, + name: task.name, + notes: task.notes || '', + completed: task.completed || false, + assignee: task.assignee + ? { + gid: task.assignee.gid, + name: task.assignee.name, + } + : undefined, + created_by: task.created_by + ? { + gid: task.created_by.gid, + resource_type: task.created_by.resource_type, + name: task.created_by.name, + } + : undefined, + due_on: task.due_on || undefined, + created_at: task.created_at, + modified_at: task.modified_at, + })), + next_page: result.next_page, }) } catch (error) { logger.error('Error processing request:', error) diff --git a/apps/sim/app/api/tools/asana/search-tasks/route.ts b/apps/sim/app/api/tools/asana/search-tasks/route.ts index c6e7d8cb62..397b9b07ce 100644 --- a/apps/sim/app/api/tools/asana/search-tasks/route.ts +++ b/apps/sim/app/api/tools/asana/search-tasks/route.ts @@ -96,34 +96,32 @@ export async function POST(request: Request) { return NextResponse.json({ success: true, - output: { - ts: new Date().toISOString(), - tasks: tasks.map((task: any) => ({ - gid: task.gid, - resource_type: task.resource_type, - resource_subtype: task.resource_subtype, - name: task.name, - notes: task.notes || '', - completed: task.completed || false, - assignee: task.assignee - ? { - gid: task.assignee.gid, - name: task.assignee.name, - } - : undefined, - created_by: task.created_by - ? { - gid: task.created_by.gid, - resource_type: task.created_by.resource_type, - name: task.created_by.name, - } - : undefined, - due_on: task.due_on || undefined, - created_at: task.created_at, - modified_at: task.modified_at, - })), - next_page: result.next_page, - }, + ts: new Date().toISOString(), + tasks: tasks.map((task: any) => ({ + gid: task.gid, + resource_type: task.resource_type, + resource_subtype: task.resource_subtype, + name: task.name, + notes: task.notes || '', + completed: task.completed || false, + assignee: task.assignee + ? { + gid: task.assignee.gid, + name: task.assignee.name, + } + : undefined, + created_by: task.created_by + ? { + gid: task.created_by.gid, + resource_type: task.created_by.resource_type, + name: task.created_by.name, + } + : undefined, + due_on: task.due_on || undefined, + created_at: task.created_at, + modified_at: task.modified_at, + })), + next_page: result.next_page, }) } catch (error) { logger.error('Error processing request:', error) diff --git a/apps/sim/app/api/tools/asana/update-task/route.ts b/apps/sim/app/api/tools/asana/update-task/route.ts index 2eca4d7c78..e83cc5ef9b 100644 --- a/apps/sim/app/api/tools/asana/update-task/route.ts +++ b/apps/sim/app/api/tools/asana/update-task/route.ts @@ -99,14 +99,12 @@ export async function PUT(request: Request) { return NextResponse.json({ success: true, - output: { - ts: new Date().toISOString(), - gid: task.gid, - name: task.name, - notes: task.notes || '', - completed: task.completed || false, - modified_at: task.modified_at, - }, + ts: new Date().toISOString(), + gid: task.gid, + name: task.name, + notes: task.notes || '', + completed: task.completed || false, + modified_at: task.modified_at, }) } catch (error: any) { logger.error('Error updating Asana task:', { diff --git a/apps/sim/app/api/tools/confluence/create-page/route.ts b/apps/sim/app/api/tools/confluence/create-page/route.ts index f049b86722..c50acf93e8 100644 --- a/apps/sim/app/api/tools/confluence/create-page/route.ts +++ b/apps/sim/app/api/tools/confluence/create-page/route.ts @@ -31,6 +31,16 @@ export async function POST(request: Request) { return NextResponse.json({ error: 'Space ID is required' }, { status: 400 }) } + if (!/^\d+$/.test(String(spaceId))) { + return NextResponse.json( + { + error: + 'Invalid Space ID. The Space ID must be a numeric value, not the space key from the URL. Use the "list" operation to get all spaces with their numeric IDs.', + }, + { status: 400 } + ) + } + if (!title) { return NextResponse.json({ error: 'Title is required' }, { status: 400 }) } @@ -91,10 +101,24 @@ export async function POST(request: Request) { statusText: response.statusText, error: JSON.stringify(errorData, null, 2), }) - const errorMessage = - errorData?.message || - (errorData?.errors && JSON.stringify(errorData.errors)) || - `Failed to create Confluence page (${response.status})` + + let errorMessage = `Failed to create Confluence page (${response.status})` + if (errorData?.message) { + errorMessage = errorData.message + } else if (errorData?.errors && Array.isArray(errorData.errors)) { + const firstError = errorData.errors[0] + if (firstError?.title) { + if (firstError.title.includes("'spaceId'") && firstError.title.includes('Long')) { + errorMessage = + 'Invalid Space ID. Use the list spaces operation to find valid space IDs.' + } else { + errorMessage = firstError.title + } + } else { + errorMessage = JSON.stringify(errorData.errors) + } + } + return NextResponse.json({ error: errorMessage }, { status: response.status }) } diff --git a/apps/sim/app/api/tools/confluence/upload-attachment/route.ts b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts new file mode 100644 index 0000000000..21e9f75ef8 --- /dev/null +++ b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts @@ -0,0 +1,135 @@ +import { type NextRequest, NextResponse } from 'next/server' +import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' +import { createLogger } from '@/lib/logs/console/logger' +import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' +import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' +import { getConfluenceCloudId } from '@/tools/confluence/utils' + +const logger = createLogger('ConfluenceUploadAttachmentAPI') + +export const dynamic = 'force-dynamic' + +export async function POST(request: NextRequest) { + try { + const body = await request.json() + const { domain, accessToken, cloudId: providedCloudId, pageId, file, fileName, comment } = body + + if (!domain) { + return NextResponse.json({ error: 'Domain is required' }, { status: 400 }) + } + + if (!accessToken) { + return NextResponse.json({ error: 'Access token is required' }, { status: 400 }) + } + + if (!pageId) { + return NextResponse.json({ error: 'Page ID is required' }, { status: 400 }) + } + + if (!file) { + return NextResponse.json({ error: 'File is required' }, { status: 400 }) + } + + const pageIdValidation = validateAlphanumericId(pageId, 'pageId', 255) + if (!pageIdValidation.isValid) { + return NextResponse.json({ error: pageIdValidation.error }, { status: 400 }) + } + + const cloudId = providedCloudId || (await getConfluenceCloudId(domain, accessToken)) + + const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId') + if (!cloudIdValidation.isValid) { + return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 }) + } + + let fileToProcess = file + if (Array.isArray(file)) { + if (file.length === 0) { + return NextResponse.json({ error: 'No file provided' }, { status: 400 }) + } + fileToProcess = file[0] + } + + let userFile + try { + userFile = processSingleFileToUserFile(fileToProcess, 'confluence-upload', logger) + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to process file' }, + { status: 400 } + ) + } + + let fileBuffer: Buffer + try { + fileBuffer = await downloadFileFromStorage(userFile, 'confluence-upload', logger) + } catch (error) { + logger.error('Failed to download file from storage:', error) + return NextResponse.json( + { + error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`, + }, + { status: 500 } + ) + } + + const uploadFileName = fileName || userFile.name || 'attachment' + const mimeType = userFile.type || 'application/octet-stream' + + const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/rest/api/content/${pageId}/child/attachment` + + const formData = new FormData() + const blob = new Blob([new Uint8Array(fileBuffer)], { type: mimeType }) + formData.append('file', blob, uploadFileName) + + if (comment) { + formData.append('comment', comment) + } + + const response = await fetch(url, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'X-Atlassian-Token': 'nocheck', + }, + body: formData, + }) + + if (!response.ok) { + const errorData = await response.json().catch(() => null) + logger.error('Confluence API error response:', { + status: response.status, + statusText: response.statusText, + error: JSON.stringify(errorData, null, 2), + }) + + let errorMessage = `Failed to upload attachment to Confluence (${response.status})` + if (errorData?.message) { + errorMessage = errorData.message + } else if (errorData?.errorMessage) { + errorMessage = errorData.errorMessage + } + + return NextResponse.json({ error: errorMessage }, { status: response.status }) + } + + const data = await response.json() + + const attachment = data.results?.[0] || data + + return NextResponse.json({ + attachmentId: attachment.id, + title: attachment.title, + fileSize: attachment.extensions?.fileSize || 0, + mediaType: attachment.extensions?.mediaType || mimeType, + downloadUrl: attachment._links?.download || '', + pageId: pageId, + }) + } catch (error) { + logger.error('Error uploading Confluence attachment:', error) + return NextResponse.json( + { error: (error as Error).message || 'Internal server error' }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/tools/onedrive/upload/route.ts b/apps/sim/app/api/tools/onedrive/upload/route.ts index 43875ce2a5..db79361204 100644 --- a/apps/sim/app/api/tools/onedrive/upload/route.ts +++ b/apps/sim/app/api/tools/onedrive/upload/route.ts @@ -4,7 +4,10 @@ import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { createLogger } from '@/lib/logs/console/logger' -import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' +import { + getExtensionFromMimeType, + processSingleFileToUserFile, +} from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { normalizeExcelValues } from '@/tools/onedrive/utils' @@ -27,9 +30,8 @@ const OneDriveUploadSchema = z.object({ fileName: z.string().min(1, 'File name is required'), file: z.any().optional(), // UserFile object (optional for blank Excel creation) folderId: z.string().optional().nullable(), - mimeType: z.string().optional(), - // Optional Excel write-after-create inputs - values: ExcelValuesSchema.optional(), + mimeType: z.string().nullish(), // Accept string, null, or undefined + values: ExcelValuesSchema.optional().nullable(), }) export async function POST(request: NextRequest) { @@ -149,9 +151,17 @@ export async function POST(request: NextRequest) { ) } - // Ensure file name has correct extension for Excel files + // Ensure file name has an appropriate extension let fileName = validatedData.fileName - if (isExcelCreation && !fileName.endsWith('.xlsx')) { + const hasExtension = fileName.includes('.') && fileName.lastIndexOf('.') > 0 + + if (!hasExtension) { + const extension = getExtensionFromMimeType(mimeType) + if (extension) { + fileName = `${fileName}.${extension}` + logger.info(`[${requestId}] Added extension to filename: ${fileName}`) + } + } else if (isExcelCreation && !fileName.endsWith('.xlsx')) { fileName = `${fileName.replace(/\.[^.]*$/, '')}.xlsx` } diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/components/icons/document-icons.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/components/icons/document-icons.tsx index 161dff2192..2eb04511a4 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/components/icons/document-icons.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/components/icons/document-icons.tsx @@ -1,4 +1,8 @@ import type React from 'react' +import { + SUPPORTED_AUDIO_EXTENSIONS, + SUPPORTED_VIDEO_EXTENSIONS, +} from '@/lib/uploads/utils/validation' interface IconProps { className?: string @@ -223,13 +227,19 @@ export const DefaultFileIcon: React.FC = ({ className = 'w-6 h-6' }) export function getDocumentIcon(mimeType: string, filename: string): React.FC { const extension = filename.split('.').pop()?.toLowerCase() - const audioExtensions = ['mp3', 'm4a', 'wav', 'webm', 'ogg', 'flac', 'aac', 'opus'] - if (mimeType.startsWith('audio/') || (extension && audioExtensions.includes(extension))) { + if ( + mimeType.startsWith('audio/') || + (extension && + SUPPORTED_AUDIO_EXTENSIONS.includes(extension as (typeof SUPPORTED_AUDIO_EXTENSIONS)[number])) + ) { return AudioIcon } - const videoExtensions = ['mp4', 'mov', 'avi', 'mkv'] - if (mimeType.startsWith('video/') || (extension && videoExtensions.includes(extension))) { + if ( + mimeType.startsWith('video/') || + (extension && + SUPPORTED_VIDEO_EXTENSIONS.includes(extension as (typeof SUPPORTED_VIDEO_EXTENSIONS)[number])) + ) { return VideoIcon } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx index b72c66d1f6..23e6130f26 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx @@ -205,6 +205,18 @@ const generateOutputPaths = (outputs: Record, prefix = ''): string[ } else if (typeof value === 'object' && value !== null) { if ('type' in value && typeof value.type === 'string') { paths.push(currentPath) + if (value.type === 'object' && value.properties) { + paths.push(...generateOutputPaths(value.properties, currentPath)) + } else if (value.type === 'array' && value.items?.properties) { + paths.push(...generateOutputPaths(value.items.properties, currentPath)) + } else if ( + value.type === 'array' && + value.items && + typeof value.items === 'object' && + !('type' in value.items) + ) { + paths.push(...generateOutputPaths(value.items, currentPath)) + } } else { const subPaths = generateOutputPaths(value, currentPath) paths.push(...subPaths) diff --git a/apps/sim/blocks/blocks/asana.ts b/apps/sim/blocks/blocks/asana.ts index f4fa584f64..7f27e48862 100644 --- a/apps/sim/blocks/blocks/asana.ts +++ b/apps/sim/blocks/blocks/asana.ts @@ -287,6 +287,19 @@ export const AsanaBlock: BlockConfig = { }, outputs: { success: { type: 'boolean', description: 'Operation success status' }, - output: { type: 'string', description: 'Operation result (JSON)' }, + ts: { type: 'string', description: 'Timestamp of the response' }, + gid: { type: 'string', description: 'Resource globally unique identifier' }, + name: { type: 'string', description: 'Resource name' }, + notes: { type: 'string', description: 'Task notes or description' }, + completed: { type: 'boolean', description: 'Whether the task is completed' }, + text: { type: 'string', description: 'Comment text content' }, + assignee: { type: 'json', description: 'Assignee details (gid, name)' }, + created_by: { type: 'json', description: 'Creator details (gid, name)' }, + due_on: { type: 'string', description: 'Due date (YYYY-MM-DD)' }, + created_at: { type: 'string', description: 'Creation timestamp' }, + modified_at: { type: 'string', description: 'Last modified timestamp' }, + permalink_url: { type: 'string', description: 'URL to the resource in Asana' }, + tasks: { type: 'json', description: 'Array of tasks' }, + projects: { type: 'json', description: 'Array of projects' }, }, } diff --git a/apps/sim/blocks/blocks/confluence.ts b/apps/sim/blocks/blocks/confluence.ts index 67c10a0424..6823bb617a 100644 --- a/apps/sim/blocks/blocks/confluence.ts +++ b/apps/sim/blocks/blocks/confluence.ts @@ -29,6 +29,7 @@ export const ConfluenceBlock: BlockConfig = { { label: 'List Comments', id: 'list_comments' }, { label: 'Update Comment', id: 'update_comment' }, { label: 'Delete Comment', id: 'delete_comment' }, + { label: 'Upload Attachment', id: 'upload_attachment' }, { label: 'List Attachments', id: 'list_attachments' }, { label: 'Delete Attachment', id: 'delete_attachment' }, { label: 'List Labels', id: 'list_labels' }, @@ -155,6 +156,28 @@ export const ConfluenceBlock: BlockConfig = { required: true, condition: { field: 'operation', value: 'delete_attachment' }, }, + { + id: 'attachmentFile', + title: 'File', + type: 'file-upload', + placeholder: 'Select file to upload', + required: true, + condition: { field: 'operation', value: 'upload_attachment' }, + }, + { + id: 'attachmentFileName', + title: 'File Name', + type: 'short-input', + placeholder: 'Optional custom file name', + condition: { field: 'operation', value: 'upload_attachment' }, + }, + { + id: 'attachmentComment', + title: 'Comment', + type: 'short-input', + placeholder: 'Optional comment for the attachment', + condition: { field: 'operation', value: 'upload_attachment' }, + }, { id: 'labelName', title: 'Label Name', @@ -185,6 +208,7 @@ export const ConfluenceBlock: BlockConfig = { 'confluence_list_comments', 'confluence_update_comment', 'confluence_delete_comment', + 'confluence_upload_attachment', 'confluence_list_attachments', 'confluence_delete_attachment', 'confluence_list_labels', @@ -212,6 +236,8 @@ export const ConfluenceBlock: BlockConfig = { return 'confluence_update_comment' case 'delete_comment': return 'confluence_delete_comment' + case 'upload_attachment': + return 'confluence_upload_attachment' case 'list_attachments': return 'confluence_list_attachments' case 'delete_attachment': @@ -227,11 +253,19 @@ export const ConfluenceBlock: BlockConfig = { } }, params: (params) => { - const { credential, pageId, manualPageId, operation, ...rest } = params + const { + credential, + pageId, + manualPageId, + operation, + attachmentFile, + attachmentFileName, + attachmentComment, + ...rest + } = params const effectivePageId = (pageId || manualPageId || '').trim() - // Operations that require pageId const requiresPageId = [ 'read', 'update', @@ -240,9 +274,9 @@ export const ConfluenceBlock: BlockConfig = { 'list_comments', 'list_attachments', 'list_labels', + 'upload_attachment', ] - // Operations that require spaceId const requiresSpaceId = ['create', 'get_space'] if (requiresPageId.includes(operation) && !effectivePageId) { @@ -253,6 +287,18 @@ export const ConfluenceBlock: BlockConfig = { throw new Error('Space ID is required for this operation.') } + if (operation === 'upload_attachment') { + return { + credential, + pageId: effectivePageId, + operation, + file: attachmentFile, + fileName: attachmentFileName, + comment: attachmentComment, + ...rest, + } + } + return { credential, pageId: effectivePageId || undefined, @@ -276,6 +322,9 @@ export const ConfluenceBlock: BlockConfig = { comment: { type: 'string', description: 'Comment text' }, commentId: { type: 'string', description: 'Comment identifier' }, attachmentId: { type: 'string', description: 'Attachment identifier' }, + attachmentFile: { type: 'json', description: 'File to upload as attachment' }, + attachmentFileName: { type: 'string', description: 'Custom file name for attachment' }, + attachmentComment: { type: 'string', description: 'Comment for the attachment' }, labelName: { type: 'string', description: 'Label name' }, limit: { type: 'number', description: 'Maximum number of results' }, }, @@ -297,6 +346,9 @@ export const ConfluenceBlock: BlockConfig = { spaces: { type: 'array', description: 'List of spaces' }, commentId: { type: 'string', description: 'Comment identifier' }, attachmentId: { type: 'string', description: 'Attachment identifier' }, + fileSize: { type: 'number', description: 'Attachment file size in bytes' }, + mediaType: { type: 'string', description: 'Attachment MIME type' }, + downloadUrl: { type: 'string', description: 'Attachment download URL' }, labelName: { type: 'string', description: 'Label name' }, spaceId: { type: 'string', description: 'Space identifier' }, name: { type: 'string', description: 'Space name' }, diff --git a/apps/sim/blocks/blocks/google_groups.ts b/apps/sim/blocks/blocks/google_groups.ts index 658d66be74..99c3b5672b 100644 --- a/apps/sim/blocks/blocks/google_groups.ts +++ b/apps/sim/blocks/blocks/google_groups.ts @@ -312,6 +312,12 @@ export const GoogleGroupsBlock: BlockConfig = { roles: { type: 'string', description: 'Filter by roles for list members' }, }, outputs: { - output: { type: 'json', description: 'Google Groups API response data' }, + groups: { type: 'json', description: 'Array of group objects (for list_groups)' }, + group: { type: 'json', description: 'Single group object (for get/create/update_group)' }, + members: { type: 'json', description: 'Array of member objects (for list_members)' }, + member: { type: 'json', description: 'Single member object (for get/add/update_member)' }, + isMember: { type: 'boolean', description: 'Membership check result (for has_member)' }, + message: { type: 'string', description: 'Success message (for delete/remove operations)' }, + nextPageToken: { type: 'string', description: 'Token for fetching next page of results' }, }, } diff --git a/apps/sim/blocks/blocks/google_vault.ts b/apps/sim/blocks/blocks/google_vault.ts index 5f3513e053..92806faf99 100644 --- a/apps/sim/blocks/blocks/google_vault.ts +++ b/apps/sim/blocks/blocks/google_vault.ts @@ -257,9 +257,16 @@ export const GoogleVaultBlock: BlockConfig = { description: { type: 'string', description: 'Matter description' }, }, outputs: { - // Common outputs - output: { type: 'json', description: 'Vault API response data' }, - // Download export file output + matters: { type: 'json', description: 'Array of matter objects (for list_matters)' }, + exports: { type: 'json', description: 'Array of export objects (for list_matters_export)' }, + holds: { type: 'json', description: 'Array of hold objects (for list_matters_holds)' }, + matter: { type: 'json', description: 'Created matter object (for create_matters)' }, + export: { type: 'json', description: 'Created export object (for create_matters_export)' }, + hold: { type: 'json', description: 'Created hold object (for create_matters_holds)' }, file: { type: 'json', description: 'Downloaded export file (UserFile) from execution files' }, + nextPageToken: { + type: 'string', + description: 'Token for fetching next page of results (for list operations)', + }, }, } diff --git a/apps/sim/blocks/blocks/hubspot.ts b/apps/sim/blocks/blocks/hubspot.ts index 218284bca1..fcd3e6d65b 100644 --- a/apps/sim/blocks/blocks/hubspot.ts +++ b/apps/sim/blocks/blocks/hubspot.ts @@ -72,21 +72,37 @@ export const HubSpotBlock: BlockConfig = { id: 'contactId', title: 'Contact ID or Email', type: 'short-input', - placeholder: 'Optional - Leave empty to list all contacts', - condition: { field: 'operation', value: ['get_contacts', 'update_contact'] }, + placeholder: 'Leave empty to list all contacts', + condition: { field: 'operation', value: 'get_contacts' }, + }, + { + id: 'contactId', + title: 'Contact ID or Email', + type: 'short-input', + placeholder: 'Numeric ID, or email (requires ID Property below)', + condition: { field: 'operation', value: 'update_contact' }, + required: true, }, { id: 'companyId', title: 'Company ID or Domain', type: 'short-input', - placeholder: 'Optional - Leave empty to list all companies', - condition: { field: 'operation', value: ['get_companies', 'update_company'] }, + placeholder: 'Leave empty to list all companies', + condition: { field: 'operation', value: 'get_companies' }, + }, + { + id: 'companyId', + title: 'Company ID or Domain', + type: 'short-input', + placeholder: 'Numeric ID, or domain (requires ID Property below)', + condition: { field: 'operation', value: 'update_company' }, + required: true, }, { id: 'idProperty', title: 'ID Property', type: 'short-input', - placeholder: 'Optional - e.g., "email" for contacts, "domain" for companies', + placeholder: 'Required if using email/domain (e.g., "email" or "domain")', condition: { field: 'operation', value: ['get_contacts', 'update_contact', 'get_companies', 'update_company'], @@ -822,33 +838,48 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no credential, } - if (propertiesToSet) { + const createUpdateOps = [ + 'create_contact', + 'update_contact', + 'create_company', + 'update_company', + ] + if (propertiesToSet && createUpdateOps.includes(operation as string)) { cleanParams.properties = propertiesToSet } - if (properties && !searchProperties) { + const getListOps = ['get_contacts', 'get_companies', 'get_deals'] + if (properties && !searchProperties && getListOps.includes(operation as string)) { cleanParams.properties = properties } - if (searchProperties) { + const searchOps = ['search_contacts', 'search_companies'] + if (searchProperties && searchOps.includes(operation as string)) { cleanParams.properties = searchProperties } - if (filterGroups) { + if (filterGroups && searchOps.includes(operation as string)) { cleanParams.filterGroups = filterGroups } - if (sorts) { + if (sorts && searchOps.includes(operation as string)) { cleanParams.sorts = sorts } - if (associations) { + if (associations && ['create_contact', 'create_company'].includes(operation as string)) { cleanParams.associations = associations } - // Add other params + const excludeKeys = [ + 'propertiesToSet', + 'properties', + 'searchProperties', + 'filterGroups', + 'sorts', + 'associations', + ] Object.entries(rest).forEach(([key, value]) => { - if (value !== undefined && value !== null && value !== '') { + if (value !== undefined && value !== null && value !== '' && !excludeKeys.includes(key)) { cleanParams[key] = value } }) diff --git a/apps/sim/lib/uploads/utils/file-utils.ts b/apps/sim/lib/uploads/utils/file-utils.ts index 946ed02f36..cd311bdf5e 100644 --- a/apps/sim/lib/uploads/utils/file-utils.ts +++ b/apps/sim/lib/uploads/utils/file-utils.ts @@ -214,6 +214,75 @@ export function getMimeTypeFromExtension(extension: string): string { return extensionMimeMap[extension.toLowerCase()] || 'application/octet-stream' } +/** + * Get file extension from MIME type + * @param mimeType - MIME type string + * @returns File extension without dot, or null if not found + */ +export function getExtensionFromMimeType(mimeType: string): string | null { + const mimeToExtension: Record = { + // Images + 'image/jpeg': 'jpg', + 'image/jpg': 'jpg', + 'image/png': 'png', + 'image/gif': 'gif', + 'image/webp': 'webp', + 'image/svg+xml': 'svg', + + // Documents + 'application/pdf': 'pdf', + 'text/plain': 'txt', + 'text/csv': 'csv', + 'application/json': 'json', + 'application/xml': 'xml', + 'text/xml': 'xml', + 'text/html': 'html', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx', + 'application/msword': 'doc', + 'application/vnd.ms-excel': 'xls', + 'application/vnd.ms-powerpoint': 'ppt', + 'text/markdown': 'md', + 'application/rtf': 'rtf', + + // Audio + 'audio/mpeg': 'mp3', + 'audio/mp3': 'mp3', + 'audio/mp4': 'm4a', + 'audio/x-m4a': 'm4a', + 'audio/m4a': 'm4a', + 'audio/wav': 'wav', + 'audio/wave': 'wav', + 'audio/x-wav': 'wav', + 'audio/webm': 'webm', + 'audio/ogg': 'ogg', + 'audio/vorbis': 'ogg', + 'audio/flac': 'flac', + 'audio/x-flac': 'flac', + 'audio/aac': 'aac', + 'audio/x-aac': 'aac', + 'audio/opus': 'opus', + + // Video + 'video/mp4': 'mp4', + 'video/mpeg': 'mpg', + 'video/quicktime': 'mov', + 'video/x-quicktime': 'mov', + 'video/x-msvideo': 'avi', + 'video/avi': 'avi', + 'video/x-matroska': 'mkv', + 'video/webm': 'webm', + + // Archives + 'application/zip': 'zip', + 'application/x-zip-compressed': 'zip', + 'application/gzip': 'gz', + } + + return mimeToExtension[mimeType.toLowerCase()] || null +} + /** * Format bytes to human-readable file size * @param bytes - File size in bytes diff --git a/apps/sim/tools/apify/run_actor_sync.ts b/apps/sim/tools/apify/run_actor_sync.ts index 8ef5533a04..f7991cfd0f 100644 --- a/apps/sim/tools/apify/run_actor_sync.ts +++ b/apps/sim/tools/apify/run_actor_sync.ts @@ -101,7 +101,6 @@ export const apifyRunActorSyncTool: ToolConfig = success: { type: 'boolean', description: 'Whether the actor run succeeded' }, runId: { type: 'string', description: 'APIFY run ID' }, status: { type: 'string', description: 'Run status (SUCCEEDED, FAILED, etc.)' }, - datasetId: { type: 'string', description: 'Dataset ID containing results' }, items: { type: 'array', description: 'Dataset items (if completed)' }, }, } diff --git a/apps/sim/tools/asana/add_comment.ts b/apps/sim/tools/asana/add_comment.ts index 0c2b67468f..413b1d2859 100644 --- a/apps/sim/tools/asana/add_comment.ts +++ b/apps/sim/tools/asana/add_comment.ts @@ -52,31 +52,33 @@ export const asanaAddCommentTool: ToolConfig - next_page?: { - offset: string - path: string - uri: string - } + created_by?: { + gid: string + resource_type: string + name: string } + due_on?: string + created_at: string + modified_at: string + }> + next_page?: { + offset: string + path: string + uri: string + } + } } export interface AsanaCreateTaskParams { diff --git a/apps/sim/tools/asana/update_task.ts b/apps/sim/tools/asana/update_task.ts index 6aa7aaa993..a015667063 100644 --- a/apps/sim/tools/asana/update_task.ts +++ b/apps/sim/tools/asana/update_task.ts @@ -92,33 +92,21 @@ export const asanaUpdateTaskTool: ToolConfig = { + id: 'confluence_upload_attachment', + name: 'Confluence Upload Attachment', + description: 'Upload a file as an attachment to a Confluence page.', + version: '1.0.0', + + oauth: { + required: true, + provider: 'confluence', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'OAuth access token for Confluence', + }, + domain: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Your Confluence domain (e.g., yourcompany.atlassian.net)', + }, + pageId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Confluence page ID to attach the file to', + }, + file: { + type: 'file', + required: true, + visibility: 'user-or-llm', + description: 'The file to upload as an attachment', + }, + fileName: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional custom file name for the attachment', + }, + comment: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional comment to add to the attachment', + }, + cloudId: { + type: 'string', + required: false, + visibility: 'user-only', + description: + 'Confluence Cloud ID for the instance. If not provided, it will be fetched using the domain.', + }, + }, + + request: { + url: () => '/api/tools/confluence/upload-attachment', + method: 'POST', + headers: (params: ConfluenceUploadAttachmentParams) => { + return { + Accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.accessToken}`, + } + }, + body: (params: ConfluenceUploadAttachmentParams) => { + return { + domain: params.domain, + accessToken: params.accessToken, + cloudId: params.cloudId, + pageId: params.pageId, + file: params.file, + fileName: params.fileName, + comment: params.comment, + } + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + return { + success: true, + output: { + ts: new Date().toISOString(), + attachmentId: data.attachmentId || '', + title: data.title || '', + fileSize: data.fileSize || 0, + mediaType: data.mediaType || '', + downloadUrl: data.downloadUrl || '', + pageId: data.pageId || '', + }, + } + }, + + outputs: { + ts: { type: 'string', description: 'Timestamp of upload' }, + attachmentId: { type: 'string', description: 'Uploaded attachment ID' }, + title: { type: 'string', description: 'Attachment file name' }, + fileSize: { type: 'number', description: 'File size in bytes' }, + mediaType: { type: 'string', description: 'MIME type of the attachment' }, + downloadUrl: { type: 'string', description: 'Download URL for the attachment' }, + pageId: { type: 'string', description: 'Page ID the attachment was added to' }, + }, +} diff --git a/apps/sim/tools/google_groups/add_member.ts b/apps/sim/tools/google_groups/add_member.ts index 0a306efb61..e2d1e967e6 100644 --- a/apps/sim/tools/google_groups/add_member.ts +++ b/apps/sim/tools/google_groups/add_member.ts @@ -66,7 +66,11 @@ export const addMemberTool: ToolConfig = { if (!response.ok) { throw new Error(data.error?.message || 'Failed to create matter') } - return { success: true, output: data } + return { success: true, output: { matter: data } } + }, + + outputs: { + matter: { type: 'json', description: 'Created matter object' }, }, } diff --git a/apps/sim/tools/google_vault/create_matters_export.ts b/apps/sim/tools/google_vault/create_matters_export.ts index a65a31232a..f468fc7ab7 100644 --- a/apps/sim/tools/google_vault/create_matters_export.ts +++ b/apps/sim/tools/google_vault/create_matters_export.ts @@ -91,6 +91,10 @@ export const createMattersExportTool: ToolConfig = { return `https://vault.googleapis.com/v1/matters/${params.matterId}` } const url = new URL('https://vault.googleapis.com/v1/matters') - // Handle pageSize - convert to number if needed if (params.pageSize !== undefined && params.pageSize !== null) { const pageSize = Number(params.pageSize) if (Number.isFinite(pageSize) && pageSize > 0) { @@ -39,18 +38,26 @@ export const listMattersTool: ToolConfig = { } } if (params.pageToken) url.searchParams.set('pageToken', params.pageToken) - // Default BASIC view implicitly by omitting 'view' and 'state' params return url.toString() }, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}` }), }, - transformResponse: async (response: Response) => { + transformResponse: async (response: Response, params?: GoogleVaultListMattersParams) => { const data = await response.json() if (!response.ok) { throw new Error(data.error?.message || 'Failed to list matters') } + if (params?.matterId) { + return { success: true, output: { matter: data } } + } return { success: true, output: data } }, + + outputs: { + matters: { type: 'json', description: 'Array of matter objects' }, + matter: { type: 'json', description: 'Single matter object (when matterId is provided)' }, + nextPageToken: { type: 'string', description: 'Token for fetching next page of results' }, + }, } diff --git a/apps/sim/tools/google_vault/list_matters_export.ts b/apps/sim/tools/google_vault/list_matters_export.ts index 3be55c01ab..539ccd11d2 100644 --- a/apps/sim/tools/google_vault/list_matters_export.ts +++ b/apps/sim/tools/google_vault/list_matters_export.ts @@ -1,8 +1,6 @@ import type { GoogleVaultListMattersExportParams } from '@/tools/google_vault/types' import type { ToolConfig } from '@/tools/types' -// matters.exports.list -// GET https://vault.googleapis.com/v1/matters/{matterId}/exports export const listMattersExportTool: ToolConfig = { id: 'list_matters_export', name: 'Vault List Exports (by Matter)', @@ -28,7 +26,6 @@ export const listMattersExportTool: ToolConfig 0) { @@ -42,13 +39,20 @@ export const listMattersExportTool: ToolConfig ({ Authorization: `Bearer ${params.accessToken}` }), }, - transformResponse: async (response: Response) => { + transformResponse: async (response: Response, params?: GoogleVaultListMattersExportParams) => { const data = await response.json() if (!response.ok) { throw new Error(data.error?.message || 'Failed to list exports') } - - // Return the raw API response without modifications + if (params?.exportId) { + return { success: true, output: { export: data } } + } return { success: true, output: data } }, + + outputs: { + exports: { type: 'json', description: 'Array of export objects' }, + export: { type: 'json', description: 'Single export object (when exportId is provided)' }, + nextPageToken: { type: 'string', description: 'Token for fetching next page of results' }, + }, } diff --git a/apps/sim/tools/google_vault/list_matters_holds.ts b/apps/sim/tools/google_vault/list_matters_holds.ts index 0e8e35f017..6f7a90cdce 100644 --- a/apps/sim/tools/google_vault/list_matters_holds.ts +++ b/apps/sim/tools/google_vault/list_matters_holds.ts @@ -26,7 +26,6 @@ export const listMattersHoldsTool: ToolConfig return `https://vault.googleapis.com/v1/matters/${params.matterId}/holds/${params.holdId}` } const url = new URL(`https://vault.googleapis.com/v1/matters/${params.matterId}/holds`) - // Handle pageSize - convert to number if needed if (params.pageSize !== undefined && params.pageSize !== null) { const pageSize = Number(params.pageSize) if (Number.isFinite(pageSize) && pageSize > 0) { @@ -34,18 +33,26 @@ export const listMattersHoldsTool: ToolConfig } } if (params.pageToken) url.searchParams.set('pageToken', params.pageToken) - // Default BASIC_HOLD implicitly by omitting 'view' return url.toString() }, method: 'GET', headers: (params) => ({ Authorization: `Bearer ${params.accessToken}` }), }, - transformResponse: async (response: Response) => { + transformResponse: async (response: Response, params?: GoogleVaultListMattersHoldsParams) => { const data = await response.json() if (!response.ok) { throw new Error(data.error?.message || 'Failed to list holds') } + if (params?.holdId) { + return { success: true, output: { hold: data } } + } return { success: true, output: data } }, + + outputs: { + holds: { type: 'json', description: 'Array of hold objects' }, + hold: { type: 'json', description: 'Single hold object (when holdId is provided)' }, + nextPageToken: { type: 'string', description: 'Token for fetching next page of results' }, + }, } diff --git a/apps/sim/tools/hubspot/create_company.ts b/apps/sim/tools/hubspot/create_company.ts index 589fc59989..9ead3fb5d8 100644 --- a/apps/sim/tools/hubspot/create_company.ts +++ b/apps/sim/tools/hubspot/create_company.ts @@ -56,8 +56,17 @@ export const hubspotCreateCompanyTool: ToolConfig< } }, body: (params) => { + let properties = params.properties + if (typeof properties === 'string') { + try { + properties = JSON.parse(properties) + } catch (e) { + throw new Error('Invalid JSON format for properties. Please provide a valid JSON object.') + } + } + const body: any = { - properties: params.properties, + properties, } if (params.associations && params.associations.length > 0) { @@ -90,21 +99,8 @@ export const hubspotCreateCompanyTool: ToolConfig< }, outputs: { + company: { type: 'object', description: 'Created HubSpot company object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Created company data', - properties: { - company: { - type: 'object', - description: 'Created company object with properties and ID', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/hubspot/create_contact.ts b/apps/sim/tools/hubspot/create_contact.ts index 1acc4f3452..11ec2894c3 100644 --- a/apps/sim/tools/hubspot/create_contact.ts +++ b/apps/sim/tools/hubspot/create_contact.ts @@ -59,8 +59,17 @@ export const hubspotCreateContactTool: ToolConfig< } }, body: (params) => { + let properties = params.properties + if (typeof properties === 'string') { + try { + properties = JSON.parse(properties) + } catch (e) { + throw new Error('Invalid JSON format for properties. Please provide a valid JSON object.') + } + } + const body: any = { - properties: params.properties, + properties, } if (params.associations && params.associations.length > 0) { @@ -93,21 +102,8 @@ export const hubspotCreateContactTool: ToolConfig< }, outputs: { + contact: { type: 'object', description: 'Created HubSpot contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Created contact data', - properties: { - contact: { - type: 'object', - description: 'Created contact object with properties and ID', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/hubspot/get_company.ts b/apps/sim/tools/hubspot/get_company.ts index 3ace7b1c3b..b8b684703a 100644 --- a/apps/sim/tools/hubspot/get_company.ts +++ b/apps/sim/tools/hubspot/get_company.ts @@ -103,21 +103,8 @@ export const hubspotGetCompanyTool: ToolConfig { const body: any = {} - if (params.filterGroups && params.filterGroups.length > 0) { - body.filterGroups = params.filterGroups + if (params.filterGroups) { + let parsedFilterGroups = params.filterGroups + if (typeof params.filterGroups === 'string') { + try { + parsedFilterGroups = JSON.parse(params.filterGroups) + } catch (e) { + throw new Error(`Invalid JSON for filterGroups: ${(e as Error).message}`) + } + } + if (Array.isArray(parsedFilterGroups) && parsedFilterGroups.length > 0) { + body.filterGroups = parsedFilterGroups + } } - if (params.sorts && params.sorts.length > 0) { - body.sorts = params.sorts + if (params.sorts) { + let parsedSorts = params.sorts + if (typeof params.sorts === 'string') { + try { + parsedSorts = JSON.parse(params.sorts) + } catch (e) { + throw new Error(`Invalid JSON for sorts: ${(e as Error).message}`) + } + } + if (Array.isArray(parsedSorts) && parsedSorts.length > 0) { + body.sorts = parsedSorts + } } if (params.query) { body.query = params.query } - if (params.properties && params.properties.length > 0) { - body.properties = params.properties + if (params.properties) { + let parsedProperties = params.properties + if (typeof params.properties === 'string') { + try { + parsedProperties = JSON.parse(params.properties) + } catch (e) { + throw new Error(`Invalid JSON for properties: ${(e as Error).message}`) + } + } + if (Array.isArray(parsedProperties) && parsedProperties.length > 0) { + body.properties = parsedProperties + } } if (params.limit) { body.limit = params.limit @@ -115,46 +145,29 @@ export const hubspotSearchCompaniesTool: ToolConfig< throw new Error(data.message || 'Failed to search companies in HubSpot') } - return { - success: true, - output: { - companies: data.results || [], + const result = { + companies: data.results || [], + total: data.total, + paging: data.paging, + metadata: { + operation: 'search_companies' as const, + totalReturned: data.results?.length || 0, total: data.total, - paging: data.paging, - metadata: { - operation: 'search_companies' as const, - totalReturned: data.results?.length || 0, - total: data.total, - }, - success: true, }, } + + return { + success: true, + output: result, + ...result, + } }, outputs: { + companies: { type: 'array', description: 'Array of matching HubSpot company objects' }, + total: { type: 'number', description: 'Total number of matching companies' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Search results', - properties: { - companies: { - type: 'array', - description: 'Array of matching company objects', - }, - total: { - type: 'number', - description: 'Total number of matching companies', - }, - paging: { - type: 'object', - description: 'Pagination information', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/hubspot/search_contacts.ts b/apps/sim/tools/hubspot/search_contacts.ts index 4bed79b0d2..710b279337 100644 --- a/apps/sim/tools/hubspot/search_contacts.ts +++ b/apps/sim/tools/hubspot/search_contacts.ts @@ -84,17 +84,47 @@ export const hubspotSearchContactsTool: ToolConfig< body: (params) => { const body: any = {} - if (params.filterGroups && params.filterGroups.length > 0) { - body.filterGroups = params.filterGroups + if (params.filterGroups) { + let parsedFilterGroups = params.filterGroups + if (typeof params.filterGroups === 'string') { + try { + parsedFilterGroups = JSON.parse(params.filterGroups) + } catch (e) { + throw new Error(`Invalid JSON for filterGroups: ${(e as Error).message}`) + } + } + if (Array.isArray(parsedFilterGroups) && parsedFilterGroups.length > 0) { + body.filterGroups = parsedFilterGroups + } } - if (params.sorts && params.sorts.length > 0) { - body.sorts = params.sorts + if (params.sorts) { + let parsedSorts = params.sorts + if (typeof params.sorts === 'string') { + try { + parsedSorts = JSON.parse(params.sorts) + } catch (e) { + throw new Error(`Invalid JSON for sorts: ${(e as Error).message}`) + } + } + if (Array.isArray(parsedSorts) && parsedSorts.length > 0) { + body.sorts = parsedSorts + } } if (params.query) { body.query = params.query } - if (params.properties && params.properties.length > 0) { - body.properties = params.properties + if (params.properties) { + let parsedProperties = params.properties + if (typeof params.properties === 'string') { + try { + parsedProperties = JSON.parse(params.properties) + } catch (e) { + throw new Error(`Invalid JSON for properties: ${(e as Error).message}`) + } + } + if (Array.isArray(parsedProperties) && parsedProperties.length > 0) { + body.properties = parsedProperties + } } if (params.limit) { body.limit = params.limit @@ -115,46 +145,29 @@ export const hubspotSearchContactsTool: ToolConfig< throw new Error(data.message || 'Failed to search contacts in HubSpot') } - return { - success: true, - output: { - contacts: data.results || [], + const result = { + contacts: data.results || [], + total: data.total, + paging: data.paging, + metadata: { + operation: 'search_contacts' as const, + totalReturned: data.results?.length || 0, total: data.total, - paging: data.paging, - metadata: { - operation: 'search_contacts' as const, - totalReturned: data.results?.length || 0, - total: data.total, - }, - success: true, }, } + + return { + success: true, + output: result, + ...result, + } }, outputs: { + contacts: { type: 'array', description: 'Array of matching HubSpot contact objects' }, + total: { type: 'number', description: 'Total number of matching contacts' }, + paging: { type: 'object', description: 'Pagination information' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Search results', - properties: { - contacts: { - type: 'array', - description: 'Array of matching contact objects', - }, - total: { - type: 'number', - description: 'Total number of matching contacts', - }, - paging: { - type: 'object', - description: 'Pagination information', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/hubspot/types.ts b/apps/sim/tools/hubspot/types.ts index 920d6d2a62..23e3084997 100644 --- a/apps/sim/tools/hubspot/types.ts +++ b/apps/sim/tools/hubspot/types.ts @@ -131,16 +131,13 @@ export interface HubSpotUpdateContactParams { // Search Contacts export interface HubSpotSearchContactsResponse extends ToolResponse { - output: { - contacts: HubSpotContact[] + contacts: HubSpotContact[] + total: number + paging?: HubSpotPaging + metadata: { + operation: 'search_contacts' + totalReturned: number total: number - paging?: HubSpotPaging - metadata: { - operation: 'search_contacts' - totalReturned: number - total: number - } - success: boolean } } @@ -212,17 +209,14 @@ export type HubSpotUpdateCompanyResponse = Omit & { - output: { - companies: HubSpotContact[] +export interface HubSpotSearchCompaniesResponse extends ToolResponse { + companies: HubSpotContact[] + total: number + paging?: HubSpotPaging + metadata: { + operation: 'search_companies' + totalReturned: number total: number - paging?: HubSpotPaging - metadata: { - operation: 'search_companies' - totalReturned: number - total: number - } - success: boolean } } diff --git a/apps/sim/tools/hubspot/update_company.ts b/apps/sim/tools/hubspot/update_company.ts index cf8c9ce8f1..2812a39e4d 100644 --- a/apps/sim/tools/hubspot/update_company.ts +++ b/apps/sim/tools/hubspot/update_company.ts @@ -69,8 +69,17 @@ export const hubspotUpdateCompanyTool: ToolConfig< } }, body: (params) => { + let properties = params.properties + if (typeof properties === 'string') { + try { + properties = JSON.parse(properties) + } catch (e) { + throw new Error('Invalid JSON format for properties. Please provide a valid JSON object.') + } + } + return { - properties: params.properties, + properties, } }, }, @@ -97,21 +106,8 @@ export const hubspotUpdateCompanyTool: ToolConfig< }, outputs: { + company: { type: 'object', description: 'Updated HubSpot company object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Updated company data', - properties: { - company: { - type: 'object', - description: 'Updated company object with properties', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/hubspot/update_contact.ts b/apps/sim/tools/hubspot/update_contact.ts index 755a192576..59fbae4f5e 100644 --- a/apps/sim/tools/hubspot/update_contact.ts +++ b/apps/sim/tools/hubspot/update_contact.ts @@ -69,8 +69,17 @@ export const hubspotUpdateContactTool: ToolConfig< } }, body: (params) => { + let properties = params.properties + if (typeof properties === 'string') { + try { + properties = JSON.parse(properties) + } catch (e) { + throw new Error('Invalid JSON format for properties. Please provide a valid JSON object.') + } + } + return { - properties: params.properties, + properties, } }, }, @@ -97,21 +106,8 @@ export const hubspotUpdateContactTool: ToolConfig< }, outputs: { + contact: { type: 'object', description: 'Updated HubSpot contact object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Updated contact data', - properties: { - contact: { - type: 'object', - description: 'Updated contact object with properties', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/onedrive/upload.ts b/apps/sim/tools/onedrive/upload.ts index 6d4cc366a7..b523613f78 100644 --- a/apps/sim/tools/onedrive/upload.ts +++ b/apps/sim/tools/onedrive/upload.ts @@ -63,32 +63,25 @@ export const uploadTool: ToolConfig request: { url: (params) => { - // If file is provided OR Excel file is being created, use custom API route const isExcelFile = params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' if (params.file || isExcelFile) { return '/api/tools/onedrive/upload' } - // Direct upload for text files - use Microsoft Graph API let fileName = params.fileName || 'untitled' - // For text files, ensure .txt extension if (!fileName.endsWith('.txt')) { - // Remove any existing extensions and add .txt fileName = `${fileName.replace(/\.[^.]*$/, '')}.txt` } - // Build the proper URL based on parent folder const parentFolderId = params.manualFolderId || params.folderSelector if (parentFolderId && parentFolderId.trim() !== '') { return `https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(parentFolderId)}:/${fileName}:/content` } - // Default to root folder return `https://graph.microsoft.com/v1.0/me/drive/root:/${fileName}:/content` }, method: (params) => { - // Use POST for custom API route (file uploads or Excel creation), PUT for direct text upload const isExcelFile = params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' return params.file || isExcelFile ? 'POST' : 'PUT' @@ -98,11 +91,9 @@ export const uploadTool: ToolConfig const isExcelFile = params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - // For file uploads or Excel creation via custom API, send JSON if (params.file || isExcelFile) { headers['Content-Type'] = 'application/json' } else { - // For direct text uploads, use direct PUT with access token headers.Authorization = `Bearer ${params.accessToken}` headers['Content-Type'] = 'text/plain' } @@ -112,20 +103,17 @@ export const uploadTool: ToolConfig const isExcelFile = params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - // For file uploads or Excel creation, send all params as JSON to custom API route if (params.file || isExcelFile) { return { accessToken: params.accessToken, fileName: params.fileName, file: params.file, folderId: params.manualFolderId || params.folderSelector, - mimeType: params.mimeType, - // Optional Excel content write-after-create - values: params.values, + ...(params.mimeType && { mimeType: params.mimeType }), + ...(params.values && { values: params.values }), } } - // For text files, send content directly return (params.content || '') as unknown as Record }, }, @@ -136,7 +124,6 @@ export const uploadTool: ToolConfig const isExcelFile = params?.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - // Handle response from custom API route (for file uploads or Excel creation) if ((params?.file || isExcelFile) && data.success !== undefined) { if (!data.success) { throw new Error(data.error || 'Failed to upload file') @@ -153,7 +140,6 @@ export const uploadTool: ToolConfig } } - // Handle response from direct Microsoft Graph API (for text-only uploads) const fileData = data logger.info('Successfully uploaded file to OneDrive', { diff --git a/apps/sim/tools/pipedrive/create_activity.ts b/apps/sim/tools/pipedrive/create_activity.ts index e1d2d57bec..906d1697b0 100644 --- a/apps/sim/tools/pipedrive/create_activity.ts +++ b/apps/sim/tools/pipedrive/create_activity.ts @@ -132,21 +132,8 @@ export const pipedriveCreateActivityTool: ToolConfig< }, outputs: { + activity: { type: 'object', description: 'The created activity object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Created activity details', - properties: { - activity: { - type: 'object', - description: 'The created activity object', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/pipedrive/create_deal.ts b/apps/sim/tools/pipedrive/create_deal.ts index fd0ce4cf33..91c7798c0f 100644 --- a/apps/sim/tools/pipedrive/create_deal.ts +++ b/apps/sim/tools/pipedrive/create_deal.ts @@ -132,21 +132,8 @@ export const pipedriveCreateDealTool: ToolConfig< }, outputs: { + deal: { type: 'object', description: 'The created deal object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Created deal details', - properties: { - deal: { - type: 'object', - description: 'The created deal object', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/pipedrive/create_lead.ts b/apps/sim/tools/pipedrive/create_lead.ts index 9cb6fc59ab..f7f1223d19 100644 --- a/apps/sim/tools/pipedrive/create_lead.ts +++ b/apps/sim/tools/pipedrive/create_lead.ts @@ -141,21 +141,8 @@ export const pipedriveCreateLeadTool: ToolConfig< }, outputs: { + lead: { type: 'object', description: 'The created lead object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Created lead details', - properties: { - lead: { - type: 'object', - description: 'The created lead object', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/pipedrive/create_project.ts b/apps/sim/tools/pipedrive/create_project.ts index a3be010663..6673c4e404 100644 --- a/apps/sim/tools/pipedrive/create_project.ts +++ b/apps/sim/tools/pipedrive/create_project.ts @@ -97,21 +97,8 @@ export const pipedriveCreateProjectTool: ToolConfig< }, outputs: { + project: { type: 'object', description: 'The created project object' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Created project details', - properties: { - project: { - type: 'object', - description: 'The created project object', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/pipedrive/delete_lead.ts b/apps/sim/tools/pipedrive/delete_lead.ts index 9765e3ce0a..fcaee03f72 100644 --- a/apps/sim/tools/pipedrive/delete_lead.ts +++ b/apps/sim/tools/pipedrive/delete_lead.ts @@ -72,21 +72,8 @@ export const pipedriveDeleteLeadTool: ToolConfig< }, outputs: { + data: { type: 'object', description: 'Deletion confirmation data' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Deletion result', - properties: { - data: { - type: 'object', - description: 'Deletion confirmation data', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/pipedrive/get_activities.ts b/apps/sim/tools/pipedrive/get_activities.ts index b64fb3f2c1..2d0d3070b3 100644 --- a/apps/sim/tools/pipedrive/get_activities.ts +++ b/apps/sim/tools/pipedrive/get_activities.ts @@ -113,21 +113,8 @@ export const pipedriveGetActivitiesTool: ToolConfig< }, outputs: { + activities: { type: 'array', description: 'Array of activity objects from Pipedrive' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Activities data', - properties: { - activities: { - type: 'array', - description: 'Array of activity objects from Pipedrive', - }, - metadata: { - type: 'object', - description: 'Operation metadata', - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/pipedrive/get_all_deals.ts b/apps/sim/tools/pipedrive/get_all_deals.ts index 2c789e9da0..6c645f5880 100644 --- a/apps/sim/tools/pipedrive/get_all_deals.ts +++ b/apps/sim/tools/pipedrive/get_all_deals.ts @@ -118,44 +118,8 @@ export const pipedriveGetAllDealsTool: ToolConfig< }, outputs: { + deals: { type: 'array', description: 'Array of deal objects from Pipedrive' }, + metadata: { type: 'object', description: 'Operation metadata' }, success: { type: 'boolean', description: 'Operation success status' }, - output: { - type: 'object', - description: 'Deals data and metadata', - properties: { - deals: { - type: 'array', - description: 'Array of deal objects from Pipedrive', - items: { - type: 'object', - properties: { - id: { type: 'number', description: 'Deal ID' }, - title: { type: 'string', description: 'Deal title' }, - value: { type: 'number', description: 'Deal value' }, - currency: { type: 'string', description: 'Deal currency' }, - status: { type: 'string', description: 'Deal status' }, - stage_id: { type: 'number', description: 'Stage ID' }, - pipeline_id: { type: 'number', description: 'Pipeline ID' }, - owner_id: { type: 'number', description: 'Owner user ID' }, - add_time: { type: 'string', description: 'Deal creation time' }, - update_time: { type: 'string', description: 'Deal last update time' }, - }, - }, - }, - metadata: { - type: 'object', - description: 'Operation metadata', - properties: { - operation: { type: 'string', description: 'The operation performed' }, - totalItems: { type: 'number', description: 'Total number of deals returned' }, - hasMore: { - type: 'boolean', - description: 'Whether there are more items to fetch via pagination', - }, - }, - }, - success: { type: 'boolean', description: 'Operation success status' }, - }, - }, }, } diff --git a/apps/sim/tools/pipedrive/get_deal.ts b/apps/sim/tools/pipedrive/get_deal.ts index 1d4239166b..2733cfe284 100644 --- a/apps/sim/tools/pipedrive/get_deal.ts +++ b/apps/sim/tools/pipedrive/get_deal.ts @@ -61,21 +61,8 @@ export const pipedriveGetDealTool: ToolConfig = { confluence_update_comment: confluenceUpdateCommentTool, confluence_delete_comment: confluenceDeleteCommentTool, confluence_list_attachments: confluenceListAttachmentsTool, + confluence_upload_attachment: confluenceUploadAttachmentTool, confluence_delete_attachment: confluenceDeleteAttachmentTool, confluence_list_labels: confluenceListLabelsTool, confluence_get_space: confluenceGetSpaceTool,