diff --git a/apps/sim/app/api/auth/oauth/token/route.ts b/apps/sim/app/api/auth/oauth/token/route.ts index b89aff1aa9..a65508fdab 100644 --- a/apps/sim/app/api/auth/oauth/token/route.ts +++ b/apps/sim/app/api/auth/oauth/token/route.ts @@ -78,10 +78,20 @@ export async function POST(request: NextRequest) { try { // Refresh the token if needed const { accessToken } = await refreshTokenIfNeeded(requestId, credential, credentialId) + + let instanceUrl: string | undefined + if (credential.providerId === 'salesforce' && credential.scope) { + const instanceMatch = credential.scope.match(/__sf_instance__:([^\s]+)/) + if (instanceMatch) { + instanceUrl = instanceMatch[1] + } + } + return NextResponse.json( { accessToken, idToken: credential.idToken || undefined, + ...(instanceUrl && { instanceUrl }), }, { status: 200 } ) @@ -147,10 +157,21 @@ export async function GET(request: NextRequest) { try { const { accessToken } = await refreshTokenIfNeeded(requestId, credential, credentialId) + + // For Salesforce, extract instanceUrl from the scope field + let instanceUrl: string | undefined + if (credential.providerId === 'salesforce' && credential.scope) { + const instanceMatch = credential.scope.match(/__sf_instance__:([^\s]+)/) + if (instanceMatch) { + instanceUrl = instanceMatch[1] + } + } + return NextResponse.json( { accessToken, idToken: credential.idToken || undefined, + ...(instanceUrl && { instanceUrl }), }, { status: 200 } ) diff --git a/apps/sim/blocks/blocks/pinecone.ts b/apps/sim/blocks/blocks/pinecone.ts index b7ebee7801..7db8ed40d8 100644 --- a/apps/sim/blocks/blocks/pinecone.ts +++ b/apps/sim/blocks/blocks/pinecone.ts @@ -264,7 +264,7 @@ export const PineconeBlock: BlockConfig = { outputs: { matches: { type: 'json', description: 'Search matches' }, - upsertedCount: { type: 'number', description: 'Upserted count' }, + statusText: { type: 'string', description: 'Status of the upsert operation' }, data: { type: 'json', description: 'Response data' }, model: { type: 'string', description: 'Model information' }, vector_type: { type: 'string', description: 'Vector type' }, diff --git a/apps/sim/blocks/blocks/supabase.ts b/apps/sim/blocks/blocks/supabase.ts index b2c7add8ec..e540265d4c 100644 --- a/apps/sim/blocks/blocks/supabase.ts +++ b/apps/sim/blocks/blocks/supabase.ts @@ -596,13 +596,20 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e }, // Storage Upload fields { - id: 'path', - title: 'File Path', + id: 'fileName', + title: 'File Name', type: 'short-input', - placeholder: 'folder/file.jpg', + placeholder: 'myfile.pdf', condition: { field: 'operation', value: 'storage_upload' }, required: true, }, + { + id: 'path', + title: 'Folder Path (optional)', + type: 'short-input', + placeholder: 'folder/subfolder/', + condition: { field: 'operation', value: 'storage_upload' }, + }, { id: 'fileContent', title: 'File Content', @@ -1065,10 +1072,10 @@ Return ONLY the PostgREST filter expression - no explanations, no markdown, no e countType: { type: 'string', description: 'Count type: exact, planned, or estimated' }, // Storage operation inputs bucket: { type: 'string', description: 'Storage bucket name' }, - path: { type: 'string', description: 'File path in storage' }, + path: { type: 'string', description: 'File or folder path in storage' }, fileContent: { type: 'string', description: 'File content (base64 for binary)' }, contentType: { type: 'string', description: 'MIME type of the file' }, - fileName: { type: 'string', description: 'Optional filename override for downloaded file' }, + fileName: { type: 'string', description: 'File name for upload or download override' }, upsert: { type: 'boolean', description: 'Whether to overwrite existing file' }, download: { type: 'boolean', description: 'Whether to force download' }, paths: { type: 'array', description: 'Array of file paths' }, diff --git a/apps/sim/blocks/blocks/typeform.ts b/apps/sim/blocks/blocks/typeform.ts index e8648d0783..69042e2266 100644 --- a/apps/sim/blocks/blocks/typeform.ts +++ b/apps/sim/blocks/blocks/typeform.ts @@ -199,10 +199,76 @@ export const TypeformBlock: BlockConfig = { { id: 'operations', title: 'JSON Patch Operations', - type: 'long-input', - placeholder: 'JSON array of patch operations (RFC 6902)', + type: 'code', + language: 'json', + placeholder: '[{"op": "replace", "path": "/title", "value": "New Title"}]', condition: { field: 'operation', value: 'typeform_update_form' }, required: true, + wandConfig: { + enabled: true, + maintainHistory: true, + prompt: `You are an expert at creating JSON Patch operations (RFC 6902) for Typeform forms. +Generate ONLY the JSON array of patch operations based on the user's request. +The output MUST be a valid JSON array, starting with [ and ending with ]. + +Current operations: {context} + +### JSON PATCH OPERATIONS +Each operation is an object with: +- "op": The operation type ("add", "remove", "replace", "move", "copy", "test") +- "path": JSON pointer to the target location (e.g., "/title", "/fields/0", "/settings/language") +- "value": The new value (required for "add", "replace", "copy", "test") +- "from": Source path (required for "move" and "copy") + +### COMMON TYPEFORM PATHS +- /title - Form title +- /settings/language - Form language (e.g., "en", "es", "fr") +- /settings/is_public - Whether form is public (true/false) +- /settings/show_progress_bar - Show progress bar (true/false) +- /fields - Array of form fields +- /fields/- - Add to end of fields array +- /fields/0 - First field +- /welcome_screens - Array of welcome screens +- /thankyou_screens - Array of thank you screens +- /theme/href - Theme URL reference + +### FIELD OBJECT STRUCTURE +{ + "type": "short_text" | "long_text" | "email" | "number" | "multiple_choice" | "yes_no" | "rating" | "date" | "dropdown" | "file_upload", + "title": "Question text", + "ref": "unique_reference_id", + "properties": { ... }, + "validations": { "required": true/false } +} + +### EXAMPLES + +**Change form title:** +[{"op": "replace", "path": "/title", "value": "My Updated Form"}] + +**Add a new text field:** +[{"op": "add", "path": "/fields/-", "value": {"type": "short_text", "title": "What is your name?", "ref": "name_field", "validations": {"required": true}}}] + +**Add multiple choice field:** +[{"op": "add", "path": "/fields/-", "value": {"type": "multiple_choice", "title": "Select your favorite color", "ref": "color_field", "properties": {"choices": [{"label": "Red"}, {"label": "Blue"}, {"label": "Green"}]}}}] + +**Remove first field:** +[{"op": "remove", "path": "/fields/0"}] + +**Update form settings:** +[{"op": "replace", "path": "/settings/language", "value": "es"}, {"op": "replace", "path": "/settings/is_public", "value": false}] + +**Multiple operations:** +[ + {"op": "replace", "path": "/title", "value": "Customer Feedback Form"}, + {"op": "add", "path": "/fields/-", "value": {"type": "rating", "title": "Rate your experience", "ref": "rating_field", "properties": {"steps": 5}}}, + {"op": "replace", "path": "/settings/show_progress_bar", "value": true} +] + +Do not include any explanations, markdown formatting, or other text outside the JSON array.`, + placeholder: 'Describe how you want to update the form...', + generationType: 'json-object', + }, }, ...getTrigger('typeform_webhook').subBlocks, ], @@ -322,6 +388,9 @@ export const TypeformBlock: BlockConfig = { fields: { type: 'json', description: 'Form fields array' }, welcome_screens: { type: 'json', description: 'Welcome screens array' }, thankyou_screens: { type: 'json', description: 'Thank you screens array' }, + created_at: { type: 'string', description: 'Form creation timestamp' }, + last_updated_at: { type: 'string', description: 'Form last update timestamp' }, + published_at: { type: 'string', description: 'Form publication timestamp' }, _links: { type: 'json', description: 'Related resource links' }, // Delete form outputs deleted: { type: 'boolean', description: 'Whether the form was deleted' }, diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index 6910bbb50a..9e5afd40dc 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -120,6 +120,37 @@ export const auth = betterAuth({ }) if (existing) { + let scopeToStore = account.scope + + if (account.providerId === 'salesforce' && account.accessToken) { + try { + const response = await fetch( + 'https://login.salesforce.com/services/oauth2/userinfo', + { + headers: { + Authorization: `Bearer ${account.accessToken}`, + }, + } + ) + + if (response.ok) { + const data = await response.json() + + if (data.profile) { + const match = data.profile.match(/^(https:\/\/[^/]+)/) + if (match && match[1] !== 'https://login.salesforce.com') { + const instanceUrl = match[1] + const existingScope = + account.scope || 'api refresh_token openid offline_access' + scopeToStore = `__sf_instance__:${instanceUrl} ${existingScope}` + } + } + } + } catch (error) { + logger.error('Failed to fetch Salesforce instance URL', { error }) + } + } + await db .update(schema.account) .set({ @@ -129,7 +160,7 @@ export const auth = betterAuth({ idToken: account.idToken, accessTokenExpiresAt: account.accessTokenExpiresAt, refreshTokenExpiresAt: account.refreshTokenExpiresAt, - scope: account.scope, + scope: scopeToStore, updatedAt: new Date(), }) .where(eq(schema.account.id, existing.id)) @@ -140,24 +171,47 @@ export const auth = betterAuth({ return { data: account } }, after: async (account) => { - // Salesforce doesn't return expires_in in its token response (unlike other OAuth providers). - // We set a default 2-hour expiration so token refresh logic works correctly. - if (account.providerId === 'salesforce' && !account.accessTokenExpiresAt) { - const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000) - try { - await db - .update(schema.account) - .set({ accessTokenExpiresAt: twoHoursFromNow }) - .where(eq(schema.account.id, account.id)) - logger.info( - '[databaseHooks.account.create.after] Set default expiration for Salesforce token', - { accountId: account.id, expiresAt: twoHoursFromNow } - ) - } catch (error) { - logger.error( - '[databaseHooks.account.create.after] Failed to set Salesforce token expiration', - { accountId: account.id, error } - ) + if (account.providerId === 'salesforce') { + const updates: { + accessTokenExpiresAt?: Date + scope?: string + } = {} + + if (!account.accessTokenExpiresAt) { + updates.accessTokenExpiresAt = new Date(Date.now() + 2 * 60 * 60 * 1000) + } + + if (account.accessToken) { + try { + const response = await fetch( + 'https://login.salesforce.com/services/oauth2/userinfo', + { + headers: { + Authorization: `Bearer ${account.accessToken}`, + }, + } + ) + + if (response.ok) { + const data = await response.json() + + if (data.profile) { + const match = data.profile.match(/^(https:\/\/[^/]+)/) + if (match && match[1] !== 'https://login.salesforce.com') { + const instanceUrl = match[1] + const existingScope = + account.scope || 'api refresh_token openid offline_access' + updates.scope = `__sf_instance__:${instanceUrl} ${existingScope}` + } + } + } + } catch (error) { + logger.error('Failed to fetch Salesforce instance URL', { error }) + } + } + + if (Object.keys(updates).length > 0) { + await db.update(schema.account).set(updates).where(eq(schema.account.id, account.id)) } } }, @@ -913,8 +967,6 @@ export const auth = betterAuth({ redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/salesforce`, getUserInfo: async (tokens) => { try { - logger.info('Fetching Salesforce user profile') - const response = await fetch( 'https://login.salesforce.com/services/oauth2/userinfo', { diff --git a/apps/sim/tools/__test-utils__/mock-data.ts b/apps/sim/tools/__test-utils__/mock-data.ts index eef5b03bb3..111a8321ed 100644 --- a/apps/sim/tools/__test-utils__/mock-data.ts +++ b/apps/sim/tools/__test-utils__/mock-data.ts @@ -168,7 +168,7 @@ export const mockPineconeResponses = { // Upsert response upsertResponse: { - upsertedCount: 5, + statusText: 'Created', }, } diff --git a/apps/sim/tools/index.ts b/apps/sim/tools/index.ts index b4898a6860..c0104f465b 100644 --- a/apps/sim/tools/index.ts +++ b/apps/sim/tools/index.ts @@ -304,10 +304,11 @@ export async function executeTool( if (data.idToken) { contextParams.idToken = data.idToken } + if (data.instanceUrl) { + contextParams.instanceUrl = data.instanceUrl + } - logger.info( - `[${requestId}] Successfully got access token for ${toolId}, length: ${data.accessToken?.length || 0}` - ) + logger.info(`[${requestId}] Successfully got access token for ${toolId}`) // Preserve credential for downstream transforms while removing it from request payload // so we don't leak it to external services. @@ -746,6 +747,8 @@ async function handleInternalRequest( url: fullUrl, json: () => response.json(), text: () => response.text(), + arrayBuffer: () => response.arrayBuffer(), + blob: () => response.blob(), } as Response const data = await tool.transformResponse(mockResponse, params) diff --git a/apps/sim/tools/intercom/create_company.ts b/apps/sim/tools/intercom/create_company.ts index 0b456c597f..f696ea5163 100644 --- a/apps/sim/tools/intercom/create_company.ts +++ b/apps/sim/tools/intercom/create_company.ts @@ -20,7 +20,33 @@ export interface IntercomCreateCompanyParams { export interface IntercomCreateCompanyResponse { success: boolean output: { - company: any + company: { + id: string + type: string + app_id: string + company_id: string + name?: string + website?: string + plan: Record + size?: number + industry?: string + monthly_spend: number + session_count: number + user_count: number + created_at: number + updated_at: number + remote_created_at?: number + custom_attributes: Record + tags: { + type: string + tags: any[] + } + segments: { + type: string + segments: any[] + } + [key: string]: any + } metadata: { operation: 'create_company' companyId: string @@ -159,15 +185,55 @@ export const intercomCreateCompanyTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + company: { type: 'object', - description: 'Created or updated company data', + description: 'Created or updated company object', properties: { - company: { type: 'object', description: 'Company object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + id: { type: 'string', description: 'Unique identifier for the company' }, + type: { type: 'string', description: 'Object type (company)' }, + app_id: { type: 'string', description: 'Intercom app ID' }, + company_id: { type: 'string', description: 'Your unique identifier for the company' }, + name: { type: 'string', description: 'Name of the company' }, + website: { type: 'string', description: 'Company website URL' }, + plan: { type: 'object', description: 'Company plan information' }, + size: { type: 'number', description: 'Number of employees' }, + industry: { type: 'string', description: 'Industry the company operates in' }, + monthly_spend: { type: 'number', description: 'Monthly revenue from this company' }, + session_count: { type: 'number', description: 'Number of sessions' }, + user_count: { type: 'number', description: 'Number of users in the company' }, + created_at: { type: 'number', description: 'Unix timestamp when company was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when company was last updated' }, + remote_created_at: { + type: 'number', + description: 'Unix timestamp when company was created by you', + }, + custom_attributes: { type: 'object', description: 'Custom attributes set on the company' }, + tags: { + type: 'object', + description: 'Tags associated with the company', + properties: { + type: { type: 'string', description: 'Tag list type' }, + tags: { type: 'array', description: 'Array of tag objects' }, + }, + }, + segments: { + type: 'object', + description: 'Segments the company belongs to', + properties: { + type: { type: 'string', description: 'Segment list type' }, + segments: { type: 'array', description: 'Array of segment objects' }, + }, + }, }, }, + metadata: { + type: 'object', + description: 'Operation metadata', + properties: { + operation: { type: 'string', description: 'The operation performed (create_company)' }, + companyId: { type: 'string', description: 'ID of the created/updated company' }, + }, + }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/create_contact.ts b/apps/sim/tools/intercom/create_contact.ts index 6f2d85893b..e1443e4154 100644 --- a/apps/sim/tools/intercom/create_contact.ts +++ b/apps/sim/tools/intercom/create_contact.ts @@ -23,7 +23,58 @@ export interface IntercomCreateContactParams { export interface IntercomCreateContactResponse { success: boolean output: { - contact: any + contact: { + id: string + type: string + role: string + email: string | null + phone: string | null + name: string | null + avatar: string | null + owner_id: string | null + external_id: string | null + created_at: number + updated_at: number + signed_up_at: number | null + last_seen_at: number | null + workspace_id: string + custom_attributes: Record + tags: { + type: string + url: string + data: any[] + has_more: boolean + total_count: number + } + notes: { + type: string + url: string + data: any[] + has_more: boolean + total_count: number + } + companies: { + type: string + url: string + data: any[] + has_more: boolean + total_count: number + } + location: { + type: string + city: string | null + region: string | null + country: string | null + country_code: string | null + continent_code: string | null + } + social_profiles: { + type: string + data: any[] + } + unsubscribed_from_emails: boolean + [key: string]: any + } metadata: { operation: 'create_contact' contactId: string @@ -182,15 +233,92 @@ export const intercomCreateContactTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + contact: { type: 'object', - description: 'Created contact data', + description: 'Created contact object', properties: { - contact: { type: 'object', description: 'Created contact object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + id: { type: 'string', description: 'Unique identifier for the contact' }, + type: { type: 'string', description: 'Object type (contact)' }, + role: { type: 'string', description: 'Role of the contact (user or lead)' }, + email: { type: 'string', description: 'Email address of the contact' }, + phone: { type: 'string', description: 'Phone number of the contact' }, + name: { type: 'string', description: 'Name of the contact' }, + avatar: { type: 'string', description: 'Avatar URL of the contact' }, + owner_id: { type: 'string', description: 'ID of the admin assigned to this contact' }, + external_id: { type: 'string', description: 'External identifier for the contact' }, + created_at: { type: 'number', description: 'Unix timestamp when contact was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when contact was last updated' }, + signed_up_at: { type: 'number', description: 'Unix timestamp when user signed up' }, + last_seen_at: { type: 'number', description: 'Unix timestamp when user was last seen' }, + workspace_id: { type: 'string', description: 'Workspace ID the contact belongs to' }, + custom_attributes: { type: 'object', description: 'Custom attributes set on the contact' }, + tags: { + type: 'object', + description: 'Tags associated with the contact', + properties: { + type: { type: 'string', description: 'List type' }, + url: { type: 'string', description: 'URL to fetch tags' }, + data: { type: 'array', description: 'Array of tag objects' }, + has_more: { type: 'boolean', description: 'Whether there are more tags' }, + total_count: { type: 'number', description: 'Total number of tags' }, + }, + }, + notes: { + type: 'object', + description: 'Notes associated with the contact', + properties: { + type: { type: 'string', description: 'List type' }, + url: { type: 'string', description: 'URL to fetch notes' }, + data: { type: 'array', description: 'Array of note objects' }, + has_more: { type: 'boolean', description: 'Whether there are more notes' }, + total_count: { type: 'number', description: 'Total number of notes' }, + }, + }, + companies: { + type: 'object', + description: 'Companies associated with the contact', + properties: { + type: { type: 'string', description: 'List type' }, + url: { type: 'string', description: 'URL to fetch companies' }, + data: { type: 'array', description: 'Array of company objects' }, + has_more: { type: 'boolean', description: 'Whether there are more companies' }, + total_count: { type: 'number', description: 'Total number of companies' }, + }, + }, + location: { + type: 'object', + description: 'Location information for the contact', + properties: { + type: { type: 'string', description: 'Location type' }, + city: { type: 'string', description: 'City' }, + region: { type: 'string', description: 'Region/State' }, + country: { type: 'string', description: 'Country' }, + country_code: { type: 'string', description: 'Country code' }, + continent_code: { type: 'string', description: 'Continent code' }, + }, + }, + social_profiles: { + type: 'object', + description: 'Social profiles of the contact', + properties: { + type: { type: 'string', description: 'List type' }, + data: { type: 'array', description: 'Array of social profile objects' }, + }, + }, + unsubscribed_from_emails: { + type: 'boolean', + description: 'Whether contact is unsubscribed from emails', + }, }, }, + metadata: { + type: 'object', + description: 'Operation metadata', + properties: { + operation: { type: 'string', description: 'The operation performed (create_contact)' }, + contactId: { type: 'string', description: 'ID of the created contact' }, + }, + }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/create_message.ts b/apps/sim/tools/intercom/create_message.ts index bdcf0a56e5..3f71c359d4 100644 --- a/apps/sim/tools/intercom/create_message.ts +++ b/apps/sim/tools/intercom/create_message.ts @@ -161,15 +161,27 @@ export const intercomCreateMessageTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + message: { type: 'object', - description: 'Created message data', + description: 'Created message object', properties: { - message: { type: 'object', description: 'Created message object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + id: { type: 'string', description: 'Unique identifier for the message' }, + type: { type: 'string', description: 'Object type (message)' }, + created_at: { type: 'number', description: 'Unix timestamp when message was created' }, + body: { type: 'string', description: 'Body of the message' }, + message_type: { type: 'string', description: 'Type of the message (in_app or email)' }, + conversation_id: { type: 'string', description: 'ID of the conversation created' }, + owner: { type: 'object', description: 'Owner of the message' }, }, }, + metadata: { + type: 'object', + description: 'Operation metadata', + properties: { + operation: { type: 'string', description: 'The operation performed (create_message)' }, + messageId: { type: 'string', description: 'ID of the created message' }, + }, + }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/create_ticket.ts b/apps/sim/tools/intercom/create_ticket.ts index c5d0dd12b3..91b8b22073 100644 --- a/apps/sim/tools/intercom/create_ticket.ts +++ b/apps/sim/tools/intercom/create_ticket.ts @@ -149,15 +149,41 @@ export const intercomCreateTicketTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + ticket: { type: 'object', - description: 'Created ticket data', + description: 'Created ticket object', properties: { - ticket: { type: 'object', description: 'Created ticket object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + id: { type: 'string', description: 'Unique identifier for the ticket' }, + type: { type: 'string', description: 'Object type (ticket)' }, + ticket_id: { type: 'string', description: 'Ticket ID' }, + ticket_type: { type: 'object', description: 'Type of the ticket' }, + ticket_attributes: { type: 'object', description: 'Attributes of the ticket' }, + ticket_state: { type: 'string', description: 'State of the ticket' }, + ticket_state_internal_label: { + type: 'string', + description: 'Internal label for ticket state', + }, + ticket_state_external_label: { + type: 'string', + description: 'External label for ticket state', + }, + created_at: { type: 'number', description: 'Unix timestamp when ticket was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when ticket was last updated' }, + contacts: { type: 'object', description: 'Contacts associated with the ticket' }, + admin_assignee_id: { type: 'string', description: 'ID of assigned admin' }, + team_assignee_id: { type: 'string', description: 'ID of assigned team' }, + is_shared: { type: 'boolean', description: 'Whether the ticket is shared' }, + open: { type: 'boolean', description: 'Whether the ticket is open' }, }, }, + metadata: { + type: 'object', + description: 'Operation metadata', + properties: { + operation: { type: 'string', description: 'The operation performed (create_ticket)' }, + ticketId: { type: 'string', description: 'ID of the created ticket' }, + }, + }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/delete_contact.ts b/apps/sim/tools/intercom/delete_contact.ts index 27bdd8e6dd..85e6d229b0 100644 --- a/apps/sim/tools/intercom/delete_contact.ts +++ b/apps/sim/tools/intercom/delete_contact.ts @@ -77,16 +77,15 @@ export const intercomDeleteContactTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + id: { type: 'string', description: 'ID of deleted contact' }, + deleted: { type: 'boolean', description: 'Whether the contact was deleted' }, + metadata: { type: 'object', - description: 'Deletion result', + description: 'Operation metadata', properties: { - id: { type: 'string', description: 'ID of deleted contact' }, - deleted: { type: 'boolean', description: 'Deletion status' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + operation: { type: 'string', description: 'The operation performed (delete_contact)' }, }, }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/get_company.ts b/apps/sim/tools/intercom/get_company.ts index c23d08edcd..8cdf2693b1 100644 --- a/apps/sim/tools/intercom/get_company.ts +++ b/apps/sim/tools/intercom/get_company.ts @@ -76,15 +76,36 @@ export const intercomGetCompanyTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + company: { type: 'object', - description: 'Company data', + description: 'Company object', properties: { - company: { type: 'object', description: 'Company object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + id: { type: 'string', description: 'Unique identifier for the company' }, + type: { type: 'string', description: 'Object type (company)' }, + app_id: { type: 'string', description: 'Intercom app ID' }, + company_id: { type: 'string', description: 'Your unique identifier for the company' }, + name: { type: 'string', description: 'Name of the company' }, + website: { type: 'string', description: 'Company website URL' }, + plan: { type: 'object', description: 'Company plan information' }, + size: { type: 'number', description: 'Number of employees' }, + industry: { type: 'string', description: 'Industry the company operates in' }, + monthly_spend: { type: 'number', description: 'Monthly revenue from this company' }, + session_count: { type: 'number', description: 'Number of sessions' }, + user_count: { type: 'number', description: 'Number of users in the company' }, + created_at: { type: 'number', description: 'Unix timestamp when company was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when company was last updated' }, + custom_attributes: { type: 'object', description: 'Custom attributes set on the company' }, + tags: { type: 'object', description: 'Tags associated with the company' }, + segments: { type: 'object', description: 'Segments the company belongs to' }, }, }, + metadata: { + type: 'object', + description: 'Operation metadata', + properties: { + operation: { type: 'string', description: 'The operation performed (get_company)' }, + }, + }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/get_contact.ts b/apps/sim/tools/intercom/get_contact.ts index b76bf11d65..fb362add9c 100644 --- a/apps/sim/tools/intercom/get_contact.ts +++ b/apps/sim/tools/intercom/get_contact.ts @@ -76,15 +76,41 @@ export const intercomGetContactTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + contact: { + type: 'object', + description: 'Contact object', + properties: { + id: { type: 'string', description: 'Unique identifier for the contact' }, + type: { type: 'string', description: 'Object type (contact)' }, + role: { type: 'string', description: 'Role of the contact (user or lead)' }, + email: { type: 'string', description: 'Email address of the contact' }, + phone: { type: 'string', description: 'Phone number of the contact' }, + name: { type: 'string', description: 'Name of the contact' }, + avatar: { type: 'string', description: 'Avatar URL of the contact' }, + owner_id: { type: 'string', description: 'ID of the admin assigned to this contact' }, + external_id: { type: 'string', description: 'External identifier for the contact' }, + created_at: { type: 'number', description: 'Unix timestamp when contact was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when contact was last updated' }, + workspace_id: { type: 'string', description: 'Workspace ID the contact belongs to' }, + custom_attributes: { type: 'object', description: 'Custom attributes set on the contact' }, + tags: { type: 'object', description: 'Tags associated with the contact' }, + notes: { type: 'object', description: 'Notes associated with the contact' }, + companies: { type: 'object', description: 'Companies associated with the contact' }, + location: { type: 'object', description: 'Location information for the contact' }, + social_profiles: { type: 'object', description: 'Social profiles of the contact' }, + unsubscribed_from_emails: { + type: 'boolean', + description: 'Whether contact is unsubscribed from emails', + }, + }, + }, + metadata: { type: 'object', - description: 'Contact data', + description: 'Operation metadata', properties: { - contact: { type: 'object', description: 'Contact object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + operation: { type: 'string', description: 'The operation performed (get_contact)' }, }, }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/get_conversation.ts b/apps/sim/tools/intercom/get_conversation.ts index e7e6a858e6..37c529f842 100644 --- a/apps/sim/tools/intercom/get_conversation.ts +++ b/apps/sim/tools/intercom/get_conversation.ts @@ -101,15 +101,41 @@ export const intercomGetConversationTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + conversation: { + type: 'object', + description: 'Conversation object', + properties: { + id: { type: 'string', description: 'Unique identifier for the conversation' }, + type: { type: 'string', description: 'Object type (conversation)' }, + title: { type: 'string', description: 'Title of the conversation' }, + created_at: { type: 'number', description: 'Unix timestamp when conversation was created' }, + updated_at: { + type: 'number', + description: 'Unix timestamp when conversation was last updated', + }, + waiting_since: { type: 'number', description: 'Unix timestamp when waiting for reply' }, + snoozed_until: { type: 'number', description: 'Unix timestamp when snooze ends' }, + open: { type: 'boolean', description: 'Whether the conversation is open' }, + state: { type: 'string', description: 'State of the conversation' }, + read: { type: 'boolean', description: 'Whether the conversation has been read' }, + priority: { type: 'string', description: 'Priority of the conversation' }, + admin_assignee_id: { type: 'number', description: 'ID of assigned admin' }, + team_assignee_id: { type: 'string', description: 'ID of assigned team' }, + tags: { type: 'object', description: 'Tags on the conversation' }, + source: { type: 'object', description: 'Source of the conversation' }, + contacts: { type: 'object', description: 'Contacts in the conversation' }, + teammates: { type: 'object', description: 'Teammates in the conversation' }, + conversation_parts: { type: 'object', description: 'Parts of the conversation' }, + statistics: { type: 'object', description: 'Conversation statistics' }, + }, + }, + metadata: { type: 'object', - description: 'Conversation data', + description: 'Operation metadata', properties: { - conversation: { type: 'object', description: 'Conversation object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + operation: { type: 'string', description: 'The operation performed (get_conversation)' }, }, }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/get_ticket.ts b/apps/sim/tools/intercom/get_ticket.ts index a0c92cf561..8cdd0ceef8 100644 --- a/apps/sim/tools/intercom/get_ticket.ts +++ b/apps/sim/tools/intercom/get_ticket.ts @@ -74,15 +74,43 @@ export const intercomGetTicketTool: ToolConfig + tags: { + type: string + url: string + data: any[] + has_more: boolean + total_count: number + } + notes: { + type: string + url: string + data: any[] + has_more: boolean + total_count: number + } + companies: { + type: string + url: string + data: any[] + has_more: boolean + total_count: number + } + location: { + type: string + city: string | null + region: string | null + country: string | null + country_code: string | null + continent_code: string | null + } + social_profiles: { + type: string + data: any[] + } + unsubscribed_from_emails: boolean + [key: string]: any + }> + pages: { + type: string + page: number + per_page: number + total_pages: number + } metadata: { operation: 'search_contacts' - total_count?: number + total_count: number } success: boolean } @@ -137,16 +193,63 @@ export const intercomSearchContactsTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + contacts: { + type: 'array', + description: 'Array of matching contact objects', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique identifier for the contact' }, + type: { type: 'string', description: 'Object type (contact)' }, + role: { type: 'string', description: 'Role of the contact (user or lead)' }, + email: { type: 'string', description: 'Email address of the contact' }, + phone: { type: 'string', description: 'Phone number of the contact' }, + name: { type: 'string', description: 'Name of the contact' }, + avatar: { type: 'string', description: 'Avatar URL of the contact' }, + owner_id: { type: 'string', description: 'ID of the admin assigned to this contact' }, + external_id: { type: 'string', description: 'External identifier for the contact' }, + created_at: { type: 'number', description: 'Unix timestamp when contact was created' }, + updated_at: { + type: 'number', + description: 'Unix timestamp when contact was last updated', + }, + signed_up_at: { type: 'number', description: 'Unix timestamp when user signed up' }, + last_seen_at: { type: 'number', description: 'Unix timestamp when user was last seen' }, + workspace_id: { type: 'string', description: 'Workspace ID the contact belongs to' }, + custom_attributes: { + type: 'object', + description: 'Custom attributes set on the contact', + }, + tags: { type: 'object', description: 'Tags associated with the contact' }, + notes: { type: 'object', description: 'Notes associated with the contact' }, + companies: { type: 'object', description: 'Companies associated with the contact' }, + location: { type: 'object', description: 'Location information for the contact' }, + social_profiles: { type: 'object', description: 'Social profiles of the contact' }, + unsubscribed_from_emails: { + type: 'boolean', + description: 'Whether contact is unsubscribed from emails', + }, + }, + }, + }, + pages: { + type: 'object', + description: 'Pagination information', + properties: { + type: { type: 'string', description: 'Pages type identifier' }, + page: { type: 'number', description: 'Current page number' }, + per_page: { type: 'number', description: 'Number of results per page' }, + total_pages: { type: 'number', description: 'Total number of pages' }, + }, + }, + metadata: { type: 'object', - description: 'Search results', + description: 'Operation metadata', properties: { - contacts: { type: 'array', description: 'Array of matching contact objects' }, - pages: { type: 'object', description: 'Pagination information' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + operation: { type: 'string', description: 'The operation performed (search_contacts)' }, + total_count: { type: 'number', description: 'Total number of matching contacts' }, }, }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/search_conversations.ts b/apps/sim/tools/intercom/search_conversations.ts index 8b3ba6b758..a50fe183a8 100644 --- a/apps/sim/tools/intercom/search_conversations.ts +++ b/apps/sim/tools/intercom/search_conversations.ts @@ -136,16 +136,57 @@ export const intercomSearchConversationsTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + conversations: { + type: 'array', + description: 'Array of matching conversation objects', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique identifier for the conversation' }, + type: { type: 'string', description: 'Object type (conversation)' }, + title: { type: 'string', description: 'Title of the conversation' }, + created_at: { + type: 'number', + description: 'Unix timestamp when conversation was created', + }, + updated_at: { + type: 'number', + description: 'Unix timestamp when conversation was last updated', + }, + waiting_since: { type: 'number', description: 'Unix timestamp when waiting for reply' }, + open: { type: 'boolean', description: 'Whether the conversation is open' }, + state: { type: 'string', description: 'State of the conversation' }, + read: { type: 'boolean', description: 'Whether the conversation has been read' }, + priority: { type: 'string', description: 'Priority of the conversation' }, + admin_assignee_id: { type: 'number', description: 'ID of assigned admin' }, + team_assignee_id: { type: 'string', description: 'ID of assigned team' }, + tags: { type: 'object', description: 'Tags on the conversation' }, + source: { type: 'object', description: 'Source of the conversation' }, + contacts: { type: 'object', description: 'Contacts in the conversation' }, + }, + }, + }, + pages: { type: 'object', - description: 'Search results', + description: 'Pagination information', properties: { - conversations: { type: 'array', description: 'Array of matching conversation objects' }, - pages: { type: 'object', description: 'Pagination information' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + type: { type: 'string', description: 'Pages type identifier' }, + page: { type: 'number', description: 'Current page number' }, + per_page: { type: 'number', description: 'Number of results per page' }, + total_pages: { type: 'number', description: 'Total number of pages' }, }, }, + metadata: { + type: 'object', + description: 'Operation metadata', + properties: { + operation: { + type: 'string', + description: 'The operation performed (search_conversations)', + }, + total_count: { type: 'number', description: 'Total number of matching conversations' }, + }, + }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/intercom/update_contact.ts b/apps/sim/tools/intercom/update_contact.ts index aa6f537869..10cbc2c5d8 100644 --- a/apps/sim/tools/intercom/update_contact.ts +++ b/apps/sim/tools/intercom/update_contact.ts @@ -188,15 +188,42 @@ export const intercomUpdateContactTool: ToolConfig< }, outputs: { - success: { type: 'boolean', description: 'Operation success status' }, - output: { + contact: { + type: 'object', + description: 'Updated contact object', + properties: { + id: { type: 'string', description: 'Unique identifier for the contact' }, + type: { type: 'string', description: 'Object type (contact)' }, + role: { type: 'string', description: 'Role of the contact (user or lead)' }, + email: { type: 'string', description: 'Email address of the contact' }, + phone: { type: 'string', description: 'Phone number of the contact' }, + name: { type: 'string', description: 'Name of the contact' }, + avatar: { type: 'string', description: 'Avatar URL of the contact' }, + owner_id: { type: 'string', description: 'ID of the admin assigned to this contact' }, + external_id: { type: 'string', description: 'External identifier for the contact' }, + created_at: { type: 'number', description: 'Unix timestamp when contact was created' }, + updated_at: { type: 'number', description: 'Unix timestamp when contact was last updated' }, + workspace_id: { type: 'string', description: 'Workspace ID the contact belongs to' }, + custom_attributes: { type: 'object', description: 'Custom attributes set on the contact' }, + tags: { type: 'object', description: 'Tags associated with the contact' }, + notes: { type: 'object', description: 'Notes associated with the contact' }, + companies: { type: 'object', description: 'Companies associated with the contact' }, + location: { type: 'object', description: 'Location information for the contact' }, + social_profiles: { type: 'object', description: 'Social profiles of the contact' }, + unsubscribed_from_emails: { + type: 'boolean', + description: 'Whether contact is unsubscribed from emails', + }, + }, + }, + metadata: { type: 'object', - description: 'Updated contact data', + description: 'Operation metadata', properties: { - contact: { type: 'object', description: 'Updated contact object' }, - metadata: { type: 'object', description: 'Operation metadata' }, - success: { type: 'boolean', description: 'Operation success' }, + operation: { type: 'string', description: 'The operation performed (update_contact)' }, + contactId: { type: 'string', description: 'ID of the updated contact' }, }, }, + success: { type: 'boolean', description: 'Operation success status' }, }, } diff --git a/apps/sim/tools/pinecone/types.ts b/apps/sim/tools/pinecone/types.ts index 383e2b4666..e6b9025713 100644 --- a/apps/sim/tools/pinecone/types.ts +++ b/apps/sim/tools/pinecone/types.ts @@ -18,7 +18,7 @@ export interface PineconeMatchResponse { export interface PineconeResponse extends ToolResponse { output: { matches?: PineconeMatchResponse[] - upsertedCount?: number + statusText?: string data?: Array<{ values: number[] vector_type: 'dense' | 'sparse' diff --git a/apps/sim/tools/pinecone/upsert_text.ts b/apps/sim/tools/pinecone/upsert_text.ts index b17ddf42a6..bf86d75309 100644 --- a/apps/sim/tools/pinecone/upsert_text.ts +++ b/apps/sim/tools/pinecone/upsert_text.ts @@ -71,22 +71,11 @@ export const upsertTextTool: ToolConfig { - // Handle empty response (201 Created) - if (response.status === 201) { - return { - success: true, - output: { - statusText: 'Created', - }, - } - } - - // Handle response with content - const data = await response.json() + // Pinecone upsert returns 201 Created with empty body on success return { - success: true, + success: response.status === 201, output: { - upsertedCount: data.upsertedCount || 0, + statusText: response.status === 201 ? 'Created' : response.statusText, }, } }, @@ -96,9 +85,5 @@ export const upsertTextTool: ToolConfig { - return `https://${params.projectId}.supabase.co/storage/v1/object/${params.bucket}/${params.path}` + // Combine folder path and fileName, ensuring proper formatting + let fullPath = params.fileName + if (params.path) { + // Ensure path ends with / and doesn't have double slashes + const folderPath = params.path.endsWith('/') ? params.path : `${params.path}/` + fullPath = `${folderPath}${params.fileName}` + } + return `https://${params.projectId}.supabase.co/storage/v1/object/${params.bucket}/${fullPath}` }, method: 'POST', headers: (params) => { diff --git a/apps/sim/tools/supabase/types.ts b/apps/sim/tools/supabase/types.ts index 433087e65b..898026f1ba 100644 --- a/apps/sim/tools/supabase/types.ts +++ b/apps/sim/tools/supabase/types.ts @@ -132,7 +132,8 @@ export interface SupabaseStorageUploadParams { apiKey: string projectId: string bucket: string - path: string + fileName: string + path?: string fileContent: string contentType?: string upsert?: boolean diff --git a/apps/sim/tools/typeform/create_form.ts b/apps/sim/tools/typeform/create_form.ts index f3ba45ea4f..cc6220d3e4 100644 --- a/apps/sim/tools/typeform/create_form.ts +++ b/apps/sim/tools/typeform/create_form.ts @@ -98,7 +98,12 @@ export const createFormTool: ToolConfig - theme: Record - workspace?: { - href: string - } - fields: Array> - thankyou_screens?: Array> - _links: Record - [key: string]: any + message: string } } diff --git a/apps/sim/tools/typeform/update_form.ts b/apps/sim/tools/typeform/update_form.ts index 47989b810c..54e66d85bf 100644 --- a/apps/sim/tools/typeform/update_form.ts +++ b/apps/sim/tools/typeform/update_form.ts @@ -44,54 +44,54 @@ export const updateFormTool: ToolConfig { - const data = await response.json() + // Check if response has content + const text = await response.text() + // Handle empty responses + if (!text || text.trim() === '') { + if (response.ok) { + // Success with no content (e.g., 204 No Content) + return { + success: true, + output: { + message: 'Form updated successfully', + }, + } + } + throw new Error(`Request failed with status ${response.status}: ${response.statusText}`) + } + + // Try to parse as JSON + let data: any + try { + data = JSON.parse(text) + } catch { + // If response is not OK and not JSON, throw with the raw text + if (!response.ok) { + throw new Error(`Request failed with status ${response.status}: ${text.slice(0, 200)}`) + } + throw new Error(`Invalid JSON response: ${text.slice(0, 200)}`) + } + + // Handle error responses from Typeform API + if (!response.ok) { + const errorMessage = data.description || data.message || data.error || JSON.stringify(data) + throw new Error(`Typeform API error (${response.status}): ${errorMessage}`) + } + + // Return simple success message (Typeform PATCH returns minimal/no content on success) return { success: true, - output: data, + output: { + message: 'Form updated successfully', + }, } }, outputs: { - id: { - type: 'string', - description: 'Updated form unique identifier', - }, - title: { + message: { type: 'string', - description: 'Form title', - }, - type: { - type: 'string', - description: 'Form type', - }, - settings: { - type: 'object', - description: 'Form settings', - }, - theme: { - type: 'object', - description: 'Theme reference', - }, - workspace: { - type: 'object', - description: 'Workspace reference', - }, - fields: { - type: 'array', - description: 'Array of form fields', - }, - welcome_screens: { - type: 'array', - description: 'Array of welcome screens', - }, - thankyou_screens: { - type: 'array', - description: 'Array of thank you screens', - }, - _links: { - type: 'object', - description: 'Related resource links', + description: 'Success confirmation message', }, }, }