mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
improvement(forwarding+excel): added forwarding and improve excel read (#1136)
* added forwarding for outlook * lint * improved excel sheet read * addressed greptile * fixed bodytext getting truncated * fixed any type * added html func --------- Co-authored-by: Adam Gough <adamgough@Mac.attlocal.net>
This commit is contained in:
@@ -22,6 +22,7 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
{ label: 'Send Email', id: 'send_outlook' },
|
||||
{ label: 'Draft Email', id: 'draft_outlook' },
|
||||
{ label: 'Read Email', id: 'read_outlook' },
|
||||
{ label: 'Forward Email', id: 'forward_outlook' },
|
||||
],
|
||||
value: () => 'send_outlook',
|
||||
},
|
||||
@@ -51,9 +52,30 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Recipient email address',
|
||||
condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['send_outlook', 'draft_outlook', 'forward_outlook'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'messageId',
|
||||
title: 'Message ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Message ID to forward',
|
||||
condition: { field: 'operation', value: ['forward_outlook'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'comment',
|
||||
title: 'Comment',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Optional comment to include when forwarding',
|
||||
condition: { field: 'operation', value: ['forward_outlook'] },
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
id: 'subject',
|
||||
title: 'Subject',
|
||||
@@ -157,7 +179,7 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['outlook_send', 'outlook_draft', 'outlook_read'],
|
||||
access: ['outlook_send', 'outlook_draft', 'outlook_read', 'outlook_forward'],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
@@ -167,6 +189,8 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
return 'outlook_read'
|
||||
case 'draft_outlook':
|
||||
return 'outlook_draft'
|
||||
case 'forward_outlook':
|
||||
return 'outlook_forward'
|
||||
default:
|
||||
throw new Error(`Invalid Outlook operation: ${params.operation}`)
|
||||
}
|
||||
@@ -197,6 +221,9 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
to: { type: 'string', description: 'Recipient email address' },
|
||||
subject: { type: 'string', description: 'Email subject' },
|
||||
body: { type: 'string', description: 'Email content' },
|
||||
// Forward operation inputs
|
||||
messageId: { type: 'string', description: 'Message ID to forward' },
|
||||
comment: { type: 'string', description: 'Optional comment for forwarding' },
|
||||
// Read operation inputs
|
||||
folder: { type: 'string', description: 'Email folder' },
|
||||
manualFolder: { type: 'string', description: 'Manual folder name' },
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { htmlToText } from 'html-to-text'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { hasProcessedMessage, markMessageAsProcessed } from '@/lib/redis'
|
||||
@@ -79,6 +80,24 @@ export interface OutlookWebhookPayload {
|
||||
rawEmail?: OutlookEmail // Only included when includeRawEmail is true
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTML content to a readable plain-text representation.
|
||||
* Keeps reasonable newlines and decodes common HTML entities.
|
||||
*/
|
||||
function convertHtmlToPlainText(html: string): string {
|
||||
if (!html) return ''
|
||||
return htmlToText(html, {
|
||||
wordwrap: false,
|
||||
selectors: [
|
||||
{ selector: 'a', options: { hideLinkHrefIfSameAsText: true, noAnchorUrl: true } },
|
||||
{ selector: 'img', format: 'skip' },
|
||||
{ selector: 'script', format: 'skip' },
|
||||
{ selector: 'style', format: 'skip' },
|
||||
],
|
||||
preserveNewlines: true,
|
||||
})
|
||||
}
|
||||
|
||||
export async function pollOutlookWebhooks() {
|
||||
logger.info('Starting Outlook webhook polling')
|
||||
|
||||
@@ -357,7 +376,18 @@ async function processOutlookEmails(
|
||||
to: email.toRecipients?.map((r) => r.emailAddress.address).join(', ') || '',
|
||||
cc: email.ccRecipients?.map((r) => r.emailAddress.address).join(', ') || '',
|
||||
date: email.receivedDateTime,
|
||||
bodyText: email.bodyPreview || '',
|
||||
bodyText: (() => {
|
||||
const content = email.body?.content || ''
|
||||
const type = (email.body?.contentType || '').toLowerCase()
|
||||
if (!content) {
|
||||
return email.bodyPreview || ''
|
||||
}
|
||||
if (type === 'text' || type === 'text/plain') {
|
||||
return content
|
||||
}
|
||||
// Default to converting HTML or unknown types
|
||||
return convertHtmlToPlainText(content)
|
||||
})(),
|
||||
bodyHtml: email.body?.content || '',
|
||||
hasAttachments: email.hasAttachments,
|
||||
isRead: email.isRead,
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
"fuse.js": "7.1.0",
|
||||
"geist": "1.4.2",
|
||||
"groq-sdk": "^0.15.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"input-otp": "^1.4.2",
|
||||
"ioredis": "^5.6.0",
|
||||
"jose": "6.0.11",
|
||||
@@ -133,6 +134,7 @@
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@trigger.dev/build": "4.0.0",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/lodash": "^4.17.16",
|
||||
|
||||
@@ -12,6 +12,61 @@ import {
|
||||
|
||||
const logger = createLogger('Tools')
|
||||
|
||||
// Extract a concise, meaningful error message from diverse API error shapes
|
||||
function getDeepApiErrorMessage(errorInfo?: {
|
||||
status?: number
|
||||
statusText?: string
|
||||
data?: any
|
||||
}): string {
|
||||
return (
|
||||
// GraphQL errors (Linear API)
|
||||
errorInfo?.data?.errors?.[0]?.message ||
|
||||
// X/Twitter API specific pattern
|
||||
errorInfo?.data?.errors?.[0]?.detail ||
|
||||
// Generic details array
|
||||
errorInfo?.data?.details?.[0]?.message ||
|
||||
// Hunter API pattern
|
||||
errorInfo?.data?.errors?.[0]?.details ||
|
||||
// Direct errors array (when errors[0] is a string or simple object)
|
||||
(Array.isArray(errorInfo?.data?.errors)
|
||||
? typeof errorInfo.data.errors[0] === 'string'
|
||||
? errorInfo.data.errors[0]
|
||||
: errorInfo.data.errors[0]?.message
|
||||
: undefined) ||
|
||||
// Notion/Discord/GitHub/Twilio pattern
|
||||
errorInfo?.data?.message ||
|
||||
// SOAP/XML fault patterns
|
||||
errorInfo?.data?.fault?.faultstring ||
|
||||
errorInfo?.data?.faultstring ||
|
||||
// Microsoft/OAuth error descriptions
|
||||
errorInfo?.data?.error_description ||
|
||||
// Airtable/Google fallback pattern
|
||||
(typeof errorInfo?.data?.error === 'object'
|
||||
? errorInfo?.data?.error?.message || JSON.stringify(errorInfo?.data?.error)
|
||||
: errorInfo?.data?.error) ||
|
||||
// HTTP status text fallback
|
||||
errorInfo?.statusText ||
|
||||
// Final fallback
|
||||
`Request failed with status ${errorInfo?.status || 'unknown'}`
|
||||
)
|
||||
}
|
||||
|
||||
// Create an Error instance from errorInfo and attach useful context
|
||||
function createTransformedErrorFromErrorInfo(errorInfo?: {
|
||||
status?: number
|
||||
statusText?: string
|
||||
data?: any
|
||||
}): Error {
|
||||
const message = getDeepApiErrorMessage(errorInfo)
|
||||
const transformed = new Error(message)
|
||||
Object.assign(transformed, {
|
||||
status: errorInfo?.status,
|
||||
statusText: errorInfo?.statusText,
|
||||
data: errorInfo?.data,
|
||||
})
|
||||
return transformed
|
||||
}
|
||||
|
||||
/**
|
||||
* Process file outputs for a tool result if execution context is available
|
||||
* Uses dynamic imports to avoid client-side bundling issues
|
||||
@@ -410,8 +465,38 @@ async function handleInternalRequest(
|
||||
|
||||
const response = await fetch(fullUrl, requestOptions)
|
||||
|
||||
// Parse response data once
|
||||
// For non-OK responses, attempt JSON first; if parsing fails, preserve legacy error expected by tests
|
||||
if (!response.ok) {
|
||||
let errorData: any
|
||||
try {
|
||||
errorData = await response.json()
|
||||
} catch (jsonError) {
|
||||
logger.error(`[${requestId}] JSON parse error for ${toolId}:`, {
|
||||
error: jsonError instanceof Error ? jsonError.message : String(jsonError),
|
||||
})
|
||||
throw new Error(`Failed to parse response from ${toolId}: ${jsonError}`)
|
||||
}
|
||||
|
||||
const { isError, errorInfo } = isErrorResponse(response, errorData)
|
||||
if (isError) {
|
||||
const errorToTransform = createTransformedErrorFromErrorInfo(errorInfo)
|
||||
|
||||
logger.error(`[${requestId}] Internal API error for ${toolId}:`, {
|
||||
status: errorInfo?.status,
|
||||
errorData: errorInfo?.data,
|
||||
})
|
||||
|
||||
throw errorToTransform
|
||||
}
|
||||
}
|
||||
|
||||
// Parse response data once with guard for empty 202 bodies
|
||||
let responseData
|
||||
const status = response.status
|
||||
if (status === 202) {
|
||||
// Many APIs (e.g., Microsoft Graph) return 202 with empty body
|
||||
responseData = { status }
|
||||
} else {
|
||||
try {
|
||||
responseData = await response.json()
|
||||
} catch (jsonError) {
|
||||
@@ -420,50 +505,14 @@ async function handleInternalRequest(
|
||||
})
|
||||
throw new Error(`Failed to parse response from ${toolId}: ${jsonError}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for error conditions
|
||||
const { isError, errorInfo } = isErrorResponse(response, responseData)
|
||||
|
||||
if (isError) {
|
||||
// Handle error case
|
||||
const errorToTransform = new Error(
|
||||
// GraphQL errors (Linear API)
|
||||
errorInfo?.data?.errors?.[0]?.message ||
|
||||
// X/Twitter API specific pattern
|
||||
errorInfo?.data?.errors?.[0]?.detail ||
|
||||
// Generic details array
|
||||
errorInfo?.data?.details?.[0]?.message ||
|
||||
// Hunter API pattern
|
||||
errorInfo?.data?.errors?.[0]?.details ||
|
||||
// Direct errors array (when errors[0] is a string or simple object)
|
||||
(Array.isArray(errorInfo?.data?.errors)
|
||||
? typeof errorInfo.data.errors[0] === 'string'
|
||||
? errorInfo.data.errors[0]
|
||||
: errorInfo.data.errors[0]?.message
|
||||
: undefined) ||
|
||||
// Notion/Discord/GitHub/Twilio pattern
|
||||
errorInfo?.data?.message ||
|
||||
// SOAP/XML fault patterns
|
||||
errorInfo?.data?.fault?.faultstring ||
|
||||
errorInfo?.data?.faultstring ||
|
||||
// Microsoft/OAuth error descriptions
|
||||
errorInfo?.data?.error_description ||
|
||||
// Airtable/Google fallback pattern
|
||||
(typeof errorInfo?.data?.error === 'object'
|
||||
? errorInfo?.data?.error?.message || JSON.stringify(errorInfo?.data?.error)
|
||||
: errorInfo?.data?.error) ||
|
||||
// HTTP status text fallback
|
||||
errorInfo?.statusText ||
|
||||
// Final fallback
|
||||
`Request failed with status ${errorInfo?.status || 'unknown'}`
|
||||
)
|
||||
|
||||
// Add error context
|
||||
Object.assign(errorToTransform, {
|
||||
status: errorInfo?.status,
|
||||
statusText: errorInfo?.statusText,
|
||||
data: errorInfo?.data,
|
||||
})
|
||||
const errorToTransform = createTransformedErrorFromErrorInfo(errorInfo)
|
||||
|
||||
logger.error(`[${requestId}] Internal API error for ${toolId}:`, {
|
||||
status: errorInfo?.status,
|
||||
|
||||
@@ -35,7 +35,8 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The range of cells to read from',
|
||||
description:
|
||||
'The range of cells to read from. Accepts "SheetName!A1:B2" for explicit ranges or just "SheetName" to read the used range of that sheet. If omitted, reads the used range of the first sheet.',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -53,10 +54,19 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
|
||||
}
|
||||
|
||||
const rangeInput = params.range.trim()
|
||||
|
||||
// If the input contains no '!', treat it as a sheet name only and fetch usedRange
|
||||
if (!rangeInput.includes('!')) {
|
||||
const sheetOnly = encodeURIComponent(rangeInput)
|
||||
return `https://graph.microsoft.com/v1.0/me/drive/items/${spreadsheetId}/workbook/worksheets('${sheetOnly}')/usedRange(valuesOnly=true)`
|
||||
}
|
||||
|
||||
const match = rangeInput.match(/^([^!]+)!(.+)$/)
|
||||
|
||||
if (!match) {
|
||||
throw new Error(`Invalid range format: "${params.range}". Use the format "Sheet1!A1:B2"`)
|
||||
throw new Error(
|
||||
`Invalid range format: "${params.range}". Use "Sheet1!A1:B2" or just "Sheet1" to read the whole sheet`
|
||||
)
|
||||
}
|
||||
|
||||
const sheetName = encodeURIComponent(match[1])
|
||||
@@ -104,7 +114,7 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
|
||||
if (!rangeResp.ok) {
|
||||
// Normalize Microsoft Graph sheet/range errors to a friendly message
|
||||
throw new Error(
|
||||
'Invalid range provided or worksheet not found. Provide a range like "Sheet1!A1:B2"'
|
||||
'Invalid range provided or worksheet not found. Provide a range like "Sheet1!A1:B2" or just the sheet name to read the whole sheet'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
154
apps/sim/tools/outlook/forward.ts
Normal file
154
apps/sim/tools/outlook/forward.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import type { OutlookForwardParams, OutlookForwardResponse } from '@/tools/outlook/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const outlookForwardTool: ToolConfig<OutlookForwardParams, OutlookForwardResponse> = {
|
||||
id: 'outlook_forward',
|
||||
name: 'Outlook Forward',
|
||||
description: 'Forward an existing Outlook message to specified recipients',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'outlook',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Outlook',
|
||||
},
|
||||
messageId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the message to forward',
|
||||
},
|
||||
to: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Recipient email address(es), comma-separated',
|
||||
},
|
||||
comment: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional comment to include with the forwarded message',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
return `https://graph.microsoft.com/v1.0/me/messages/${params.messageId}/forward`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Access token is required')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params: OutlookForwardParams): Record<string, any> => {
|
||||
const parseEmails = (emailString?: string) => {
|
||||
if (!emailString) return []
|
||||
return emailString
|
||||
.split(',')
|
||||
.map((email) => email.trim())
|
||||
.filter((email) => email.length > 0)
|
||||
.map((email) => ({ emailAddress: { address: email } }))
|
||||
}
|
||||
|
||||
const toRecipients = parseEmails(params.to)
|
||||
if (toRecipients.length === 0) {
|
||||
throw new Error('At least one recipient is required to forward a message')
|
||||
}
|
||||
|
||||
return {
|
||||
comment: params.comment ?? '',
|
||||
toRecipients,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const status = response.status
|
||||
const requestId =
|
||||
response.headers?.get('request-id') || response.headers?.get('x-ms-request-id') || undefined
|
||||
|
||||
// Graph forward action typically returns 202/204 with no body. Try to read text safely.
|
||||
let bodyText = ''
|
||||
try {
|
||||
bodyText = await response.text()
|
||||
} catch (_) {
|
||||
// ignore body read errors
|
||||
}
|
||||
|
||||
// Attempt to parse JSON if present (rare for this endpoint). Extract message identifiers if available.
|
||||
let parsed: any | undefined
|
||||
if (bodyText && bodyText.trim().length > 0) {
|
||||
try {
|
||||
parsed = JSON.parse(bodyText)
|
||||
} catch (_) {
|
||||
// non-JSON body; ignore
|
||||
}
|
||||
}
|
||||
|
||||
const messageId = parsed?.id || parsed?.messageId || parsed?.internetMessageId
|
||||
const internetMessageId = parsed?.internetMessageId
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message:
|
||||
status === 202 || status === 204
|
||||
? 'Email forwarded successfully'
|
||||
: `Email forwarded (HTTP ${status})`,
|
||||
results: {
|
||||
status: 'forwarded',
|
||||
timestamp: new Date().toISOString(),
|
||||
httpStatus: status,
|
||||
requestId,
|
||||
...(messageId ? { messageId } : {}),
|
||||
...(internetMessageId ? { internetMessageId } : {}),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: { type: 'string', description: 'Success or error message' },
|
||||
results: {
|
||||
type: 'object',
|
||||
description: 'Delivery result details',
|
||||
properties: {
|
||||
status: { type: 'string', description: 'Delivery status of the email' },
|
||||
timestamp: { type: 'string', description: 'Timestamp when email was forwarded' },
|
||||
httpStatus: {
|
||||
type: 'number',
|
||||
description: 'HTTP status code returned by the API',
|
||||
optional: true,
|
||||
},
|
||||
requestId: {
|
||||
type: 'string',
|
||||
description: 'Microsoft Graph request-id header for tracing',
|
||||
optional: true,
|
||||
},
|
||||
messageId: {
|
||||
type: 'string',
|
||||
description: 'Forwarded message ID if provided by API',
|
||||
optional: true,
|
||||
},
|
||||
internetMessageId: {
|
||||
type: 'string',
|
||||
description: 'RFC 822 Message-ID if provided',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { outlookDraftTool } from '@/tools/outlook/draft'
|
||||
import { outlookForwardTool } from '@/tools/outlook/forward'
|
||||
import { outlookReadTool } from '@/tools/outlook/read'
|
||||
import { outlookSendTool } from '@/tools/outlook/send'
|
||||
|
||||
export { outlookDraftTool, outlookReadTool, outlookSendTool }
|
||||
export { outlookDraftTool, outlookForwardTool, outlookReadTool, outlookSendTool }
|
||||
|
||||
@@ -127,9 +127,7 @@ export const outlookReadTool: ToolConfig<OutlookReadParams, OutlookReadResponse>
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Email read operation success status' },
|
||||
messageCount: { type: 'number', description: 'Number of emails retrieved' },
|
||||
messages: { type: 'array', description: 'Array of email message objects' },
|
||||
message: { type: 'string', description: 'Success or status message' },
|
||||
results: { type: 'array', description: 'Array of email message objects' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -136,3 +136,19 @@ export interface CleanedOutlookMessage {
|
||||
}
|
||||
|
||||
export type OutlookResponse = OutlookReadResponse | OutlookSendResponse | OutlookDraftResponse
|
||||
|
||||
export interface OutlookForwardParams {
|
||||
accessToken: string
|
||||
messageId: string
|
||||
to: string
|
||||
comment?: string
|
||||
}
|
||||
|
||||
export interface OutlookForwardResponse extends ToolResponse {
|
||||
output: {
|
||||
message: string
|
||||
results: any
|
||||
}
|
||||
}
|
||||
|
||||
export type OutlookExtendedResponse = OutlookResponse | OutlookForwardResponse
|
||||
|
||||
@@ -109,7 +109,12 @@ import {
|
||||
} from '@/tools/notion'
|
||||
import { onedriveCreateFolderTool, onedriveListTool, onedriveUploadTool } from '@/tools/onedrive'
|
||||
import { imageTool, embeddingsTool as openAIEmbeddings } from '@/tools/openai'
|
||||
import { outlookDraftTool, outlookReadTool, outlookSendTool } from '@/tools/outlook'
|
||||
import {
|
||||
outlookDraftTool,
|
||||
outlookForwardTool,
|
||||
outlookReadTool,
|
||||
outlookSendTool,
|
||||
} from '@/tools/outlook'
|
||||
import { parallelSearchTool } from '@/tools/parallel'
|
||||
import { perplexityChatTool } from '@/tools/perplexity'
|
||||
import {
|
||||
@@ -302,6 +307,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
outlook_read: outlookReadTool,
|
||||
outlook_send: outlookSendTool,
|
||||
outlook_draft: outlookDraftTool,
|
||||
outlook_forward: outlookForwardTool,
|
||||
linear_read_issues: linearReadIssuesTool,
|
||||
linear_create_issue: linearCreateIssueTool,
|
||||
onedrive_create_folder: onedriveCreateFolderTool,
|
||||
|
||||
@@ -79,7 +79,7 @@ export const outlookPollingTrigger: TriggerConfig = {
|
||||
},
|
||||
bodyText: {
|
||||
type: 'string',
|
||||
description: 'Plain text email body (preview)',
|
||||
description: 'Plain text email body',
|
||||
},
|
||||
bodyHtml: {
|
||||
type: 'string',
|
||||
|
||||
4
bun.lock
4
bun.lock
@@ -118,6 +118,7 @@
|
||||
"fuse.js": "7.1.0",
|
||||
"geist": "1.4.2",
|
||||
"groq-sdk": "^0.15.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"input-otp": "^1.4.2",
|
||||
"ioredis": "^5.6.0",
|
||||
"jose": "6.0.11",
|
||||
@@ -162,6 +163,7 @@
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@trigger.dev/build": "4.0.0",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/lodash": "^4.17.16",
|
||||
@@ -1378,6 +1380,8 @@
|
||||
|
||||
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||
|
||||
"@types/html-to-text": ["@types/html-to-text@9.0.4", "", {}, "sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ=="],
|
||||
|
||||
"@types/inquirer": ["@types/inquirer@8.2.12", "", { "dependencies": { "@types/through": "*", "rxjs": "^7.2.0" } }, "sha512-YxURZF2ZsSjU5TAe06tW0M3sL4UI9AMPA6dd8I72uOtppzNafcY38xkYgCZ/vsVOAyNdzHmvtTpLWilOrbP0dQ=="],
|
||||
|
||||
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||
|
||||
Reference in New Issue
Block a user