mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar (#3338)
* feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar * fix(google-drive): remove dead transformResponse from move tool
This commit is contained in:
119
apps/sim/tools/gmail/create_label.ts
Normal file
119
apps/sim/tools/gmail/create_label.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailCreateLabelParams {
|
||||
accessToken: string
|
||||
name: string
|
||||
messageListVisibility?: string
|
||||
labelListVisibility?: string
|
||||
}
|
||||
|
||||
interface GmailCreateLabelResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
messageListVisibility?: string
|
||||
labelListVisibility?: string
|
||||
type?: string
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailCreateLabelV2Tool: ToolConfig<GmailCreateLabelParams, GmailCreateLabelResponse> =
|
||||
{
|
||||
id: 'gmail_create_label_v2',
|
||||
name: 'Gmail Create Label',
|
||||
description: 'Create a new label in Gmail',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Display name for the new label',
|
||||
},
|
||||
messageListVisibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Visibility of messages with this label in the message list (show or hide)',
|
||||
},
|
||||
labelListVisibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Visibility of the label in the label list (labelShow, labelShowIfUnread, or labelHide)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${GMAIL_API_BASE}/labels`,
|
||||
method: 'POST',
|
||||
headers: (params: GmailCreateLabelParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: GmailCreateLabelParams) => {
|
||||
const body: Record<string, string> = { name: params.name }
|
||||
if (params.messageListVisibility) {
|
||||
body.messageListVisibility = params.messageListVisibility
|
||||
}
|
||||
if (params.labelListVisibility) {
|
||||
body.labelListVisibility = params.labelListVisibility
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '', name: '' },
|
||||
error: data.error?.message || 'Failed to create label',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
messageListVisibility: data.messageListVisibility ?? null,
|
||||
labelListVisibility: data.labelListVisibility ?? null,
|
||||
type: data.type ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Label ID' },
|
||||
name: { type: 'string', description: 'Label display name' },
|
||||
messageListVisibility: {
|
||||
type: 'string',
|
||||
description: 'Visibility of messages with this label',
|
||||
optional: true,
|
||||
},
|
||||
labelListVisibility: {
|
||||
type: 'string',
|
||||
description: 'Visibility of the label in the label list',
|
||||
optional: true,
|
||||
},
|
||||
type: { type: 'string', description: 'Label type (system or user)', optional: true },
|
||||
},
|
||||
}
|
||||
76
apps/sim/tools/gmail/delete_draft.ts
Normal file
76
apps/sim/tools/gmail/delete_draft.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailDeleteDraftParams {
|
||||
accessToken: string
|
||||
draftId: string
|
||||
}
|
||||
|
||||
interface GmailDeleteDraftResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
deleted: boolean
|
||||
draftId: string
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailDeleteDraftV2Tool: ToolConfig<GmailDeleteDraftParams, GmailDeleteDraftResponse> =
|
||||
{
|
||||
id: 'gmail_delete_draft_v2',
|
||||
name: 'Gmail Delete Draft',
|
||||
description: 'Delete a specific draft from Gmail',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
draftId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the draft to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailDeleteDraftParams) => `${GMAIL_API_BASE}/drafts/${params.draftId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params: GmailDeleteDraftParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: GmailDeleteDraftParams) => {
|
||||
if (!response.ok) {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: false,
|
||||
output: { deleted: false, draftId: params?.draftId ?? '' },
|
||||
error: data.error?.message || 'Failed to delete draft',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
draftId: params?.draftId ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the draft was successfully deleted' },
|
||||
draftId: { type: 'string', description: 'ID of the deleted draft' },
|
||||
},
|
||||
}
|
||||
76
apps/sim/tools/gmail/delete_label.ts
Normal file
76
apps/sim/tools/gmail/delete_label.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailDeleteLabelParams {
|
||||
accessToken: string
|
||||
labelId: string
|
||||
}
|
||||
|
||||
interface GmailDeleteLabelResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
deleted: boolean
|
||||
labelId: string
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailDeleteLabelV2Tool: ToolConfig<GmailDeleteLabelParams, GmailDeleteLabelResponse> =
|
||||
{
|
||||
id: 'gmail_delete_label_v2',
|
||||
name: 'Gmail Delete Label',
|
||||
description: 'Delete a label from Gmail',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
labelId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the label to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailDeleteLabelParams) => `${GMAIL_API_BASE}/labels/${params.labelId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params: GmailDeleteLabelParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: GmailDeleteLabelParams) => {
|
||||
if (!response.ok) {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: false,
|
||||
output: { deleted: false, labelId: params?.labelId ?? '' },
|
||||
error: data.error?.message || 'Failed to delete label',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
labelId: params?.labelId ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the label was successfully deleted' },
|
||||
labelId: { type: 'string', description: 'ID of the deleted label' },
|
||||
},
|
||||
}
|
||||
117
apps/sim/tools/gmail/get_draft.ts
Normal file
117
apps/sim/tools/gmail/get_draft.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailGetDraftParams {
|
||||
accessToken: string
|
||||
draftId: string
|
||||
}
|
||||
|
||||
interface GmailGetDraftResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
id: string
|
||||
messageId?: string
|
||||
threadId?: string
|
||||
to?: string
|
||||
from?: string
|
||||
subject?: string
|
||||
body?: string
|
||||
labelIds?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailGetDraftV2Tool: ToolConfig<GmailGetDraftParams, GmailGetDraftResponse> = {
|
||||
id: 'gmail_get_draft_v2',
|
||||
name: 'Gmail Get Draft',
|
||||
description: 'Get a specific draft from Gmail by its ID',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
draftId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the draft to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailGetDraftParams) => `${GMAIL_API_BASE}/drafts/${params.draftId}?format=full`,
|
||||
method: 'GET',
|
||||
headers: (params: GmailGetDraftParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '' },
|
||||
error: data.error?.message || 'Failed to get draft',
|
||||
}
|
||||
}
|
||||
|
||||
const message = data.message || {}
|
||||
const headers = message.payload?.headers || []
|
||||
const getHeader = (name: string): string | undefined =>
|
||||
headers.find((h: Record<string, string>) => h.name.toLowerCase() === name.toLowerCase())
|
||||
?.value
|
||||
|
||||
let body = ''
|
||||
if (message.payload?.body?.data) {
|
||||
body = Buffer.from(message.payload.body.data, 'base64').toString()
|
||||
} else if (message.payload?.parts) {
|
||||
const textPart = message.payload.parts.find(
|
||||
(part: Record<string, unknown>) => part.mimeType === 'text/plain'
|
||||
)
|
||||
if (textPart?.body?.data) {
|
||||
body = Buffer.from(textPart.body.data, 'base64').toString()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
messageId: message.id ?? undefined,
|
||||
threadId: message.threadId ?? undefined,
|
||||
to: getHeader('To'),
|
||||
from: getHeader('From'),
|
||||
subject: getHeader('Subject'),
|
||||
body: body || undefined,
|
||||
labelIds: message.labelIds ?? undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Draft ID' },
|
||||
messageId: { type: 'string', description: 'Gmail message ID', optional: true },
|
||||
threadId: { type: 'string', description: 'Gmail thread ID', optional: true },
|
||||
to: { type: 'string', description: 'Recipient email address', optional: true },
|
||||
from: { type: 'string', description: 'Sender email address', optional: true },
|
||||
subject: { type: 'string', description: 'Draft subject', optional: true },
|
||||
body: { type: 'string', description: 'Draft body text', optional: true },
|
||||
labelIds: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Draft labels',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
136
apps/sim/tools/gmail/get_thread.ts
Normal file
136
apps/sim/tools/gmail/get_thread.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailGetThreadParams {
|
||||
accessToken: string
|
||||
threadId: string
|
||||
format?: string
|
||||
}
|
||||
|
||||
interface GmailGetThreadResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
id: string
|
||||
historyId?: string
|
||||
messages: Array<{
|
||||
id: string
|
||||
threadId: string
|
||||
labelIds?: string[]
|
||||
snippet?: string
|
||||
from?: string
|
||||
to?: string
|
||||
subject?: string
|
||||
date?: string
|
||||
body?: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailGetThreadV2Tool: ToolConfig<GmailGetThreadParams, GmailGetThreadResponse> = {
|
||||
id: 'gmail_get_thread_v2',
|
||||
name: 'Gmail Get Thread',
|
||||
description: 'Get a specific email thread from Gmail, including all messages in the thread',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
threadId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the thread to retrieve',
|
||||
},
|
||||
format: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Format to return the messages in (full, metadata, or minimal). Defaults to full.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailGetThreadParams) => {
|
||||
const format = params.format || 'full'
|
||||
return `${GMAIL_API_BASE}/threads/${params.threadId}?format=${format}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: GmailGetThreadParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '', messages: [] },
|
||||
error: data.error?.message || 'Failed to get thread',
|
||||
}
|
||||
}
|
||||
|
||||
const messages = (data.messages || []).map((message: Record<string, unknown>) => {
|
||||
const payload = message.payload as Record<string, unknown> | undefined
|
||||
const headers = (payload?.headers as Array<Record<string, string>>) || []
|
||||
const getHeader = (name: string): string | undefined =>
|
||||
headers.find((h) => h.name.toLowerCase() === name.toLowerCase())?.value
|
||||
|
||||
let body = ''
|
||||
const payloadBody = payload?.body as Record<string, unknown> | undefined
|
||||
if (payloadBody?.data) {
|
||||
body = Buffer.from(payloadBody.data as string, 'base64').toString()
|
||||
} else if (payload?.parts) {
|
||||
const parts = payload.parts as Array<Record<string, unknown>>
|
||||
const textPart = parts.find((part) => part.mimeType === 'text/plain')
|
||||
const textBody = textPart?.body as Record<string, unknown> | undefined
|
||||
if (textBody?.data) {
|
||||
body = Buffer.from(textBody.data as string, 'base64').toString()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: message.id,
|
||||
threadId: message.threadId,
|
||||
labelIds: message.labelIds ?? null,
|
||||
snippet: message.snippet ?? null,
|
||||
from: getHeader('From') ?? null,
|
||||
to: getHeader('To') ?? null,
|
||||
subject: getHeader('Subject') ?? null,
|
||||
date: getHeader('Date') ?? null,
|
||||
body: body || null,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
historyId: data.historyId ?? null,
|
||||
messages,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Thread ID' },
|
||||
historyId: { type: 'string', description: 'History ID', optional: true },
|
||||
messages: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of messages in the thread with id, from, to, subject, date, body, and labels',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
import { gmailAddLabelTool, gmailAddLabelV2Tool } from '@/tools/gmail/add_label'
|
||||
import { gmailArchiveTool, gmailArchiveV2Tool } from '@/tools/gmail/archive'
|
||||
import { gmailCreateLabelV2Tool } from '@/tools/gmail/create_label'
|
||||
import { gmailDeleteTool, gmailDeleteV2Tool } from '@/tools/gmail/delete'
|
||||
import { gmailDeleteDraftV2Tool } from '@/tools/gmail/delete_draft'
|
||||
import { gmailDeleteLabelV2Tool } from '@/tools/gmail/delete_label'
|
||||
import { gmailDraftTool, gmailDraftV2Tool } from '@/tools/gmail/draft'
|
||||
import { gmailGetDraftV2Tool } from '@/tools/gmail/get_draft'
|
||||
import { gmailGetThreadV2Tool } from '@/tools/gmail/get_thread'
|
||||
import { gmailListDraftsV2Tool } from '@/tools/gmail/list_drafts'
|
||||
import { gmailListLabelsV2Tool } from '@/tools/gmail/list_labels'
|
||||
import { gmailListThreadsV2Tool } from '@/tools/gmail/list_threads'
|
||||
import { gmailMarkReadTool, gmailMarkReadV2Tool } from '@/tools/gmail/mark_read'
|
||||
import { gmailMarkUnreadTool, gmailMarkUnreadV2Tool } from '@/tools/gmail/mark_unread'
|
||||
import { gmailMoveTool, gmailMoveV2Tool } from '@/tools/gmail/move'
|
||||
@@ -9,7 +17,9 @@ import { gmailReadTool, gmailReadV2Tool } from '@/tools/gmail/read'
|
||||
import { gmailRemoveLabelTool, gmailRemoveLabelV2Tool } from '@/tools/gmail/remove_label'
|
||||
import { gmailSearchTool, gmailSearchV2Tool } from '@/tools/gmail/search'
|
||||
import { gmailSendTool, gmailSendV2Tool } from '@/tools/gmail/send'
|
||||
import { gmailTrashThreadV2Tool } from '@/tools/gmail/trash_thread'
|
||||
import { gmailUnarchiveTool, gmailUnarchiveV2Tool } from '@/tools/gmail/unarchive'
|
||||
import { gmailUntrashThreadV2Tool } from '@/tools/gmail/untrash_thread'
|
||||
|
||||
export {
|
||||
gmailSendTool,
|
||||
@@ -36,4 +46,14 @@ export {
|
||||
gmailAddLabelV2Tool,
|
||||
gmailRemoveLabelTool,
|
||||
gmailRemoveLabelV2Tool,
|
||||
gmailListDraftsV2Tool,
|
||||
gmailGetDraftV2Tool,
|
||||
gmailDeleteDraftV2Tool,
|
||||
gmailCreateLabelV2Tool,
|
||||
gmailDeleteLabelV2Tool,
|
||||
gmailListLabelsV2Tool,
|
||||
gmailGetThreadV2Tool,
|
||||
gmailListThreadsV2Tool,
|
||||
gmailTrashThreadV2Tool,
|
||||
gmailUntrashThreadV2Tool,
|
||||
}
|
||||
|
||||
126
apps/sim/tools/gmail/list_drafts.ts
Normal file
126
apps/sim/tools/gmail/list_drafts.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailListDraftsParams {
|
||||
accessToken: string
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
query?: string
|
||||
}
|
||||
|
||||
interface GmailListDraftsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
drafts: Array<{
|
||||
id: string
|
||||
messageId: string
|
||||
threadId: string
|
||||
}>
|
||||
resultSizeEstimate: number
|
||||
nextPageToken?: string
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailListDraftsV2Tool: ToolConfig<GmailListDraftsParams, GmailListDraftsResponse> = {
|
||||
id: 'gmail_list_drafts_v2',
|
||||
name: 'Gmail List Drafts',
|
||||
description: 'List all drafts in a Gmail account',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of drafts to return (default: 100, max: 500)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token for paginated results',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search query to filter drafts (same syntax as Gmail search)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailListDraftsParams) => {
|
||||
const searchParams = new URLSearchParams()
|
||||
if (params.maxResults) {
|
||||
searchParams.append('maxResults', Number(params.maxResults).toString())
|
||||
}
|
||||
if (params.pageToken) {
|
||||
searchParams.append('pageToken', params.pageToken)
|
||||
}
|
||||
if (params.query) {
|
||||
searchParams.append('q', params.query)
|
||||
}
|
||||
const qs = searchParams.toString()
|
||||
return `${GMAIL_API_BASE}/drafts${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: GmailListDraftsParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { drafts: [], resultSizeEstimate: 0 },
|
||||
error: data.error?.message || 'Failed to list drafts',
|
||||
}
|
||||
}
|
||||
|
||||
const drafts = (data.drafts || []).map((draft: Record<string, unknown>) => ({
|
||||
id: draft.id,
|
||||
messageId: (draft.message as Record<string, unknown>)?.id ?? null,
|
||||
threadId: (draft.message as Record<string, unknown>)?.threadId ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
drafts,
|
||||
resultSizeEstimate: data.resultSizeEstimate ?? 0,
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
drafts: {
|
||||
type: 'json',
|
||||
description: 'Array of draft objects with id, messageId, and threadId',
|
||||
},
|
||||
resultSizeEstimate: {
|
||||
type: 'number',
|
||||
description: 'Estimated total number of drafts',
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
81
apps/sim/tools/gmail/list_labels.ts
Normal file
81
apps/sim/tools/gmail/list_labels.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailListLabelsParams {
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
interface GmailListLabelsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
labels: Array<{
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
messageListVisibility?: string
|
||||
labelListVisibility?: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailListLabelsV2Tool: ToolConfig<GmailListLabelsParams, GmailListLabelsResponse> = {
|
||||
id: 'gmail_list_labels_v2',
|
||||
name: 'Gmail List Labels',
|
||||
description: 'List all labels in a Gmail account',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${GMAIL_API_BASE}/labels`,
|
||||
method: 'GET',
|
||||
headers: (params: GmailListLabelsParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { labels: [] },
|
||||
error: data.error?.message || 'Failed to list labels',
|
||||
}
|
||||
}
|
||||
|
||||
const labels = (data.labels || []).map((label: Record<string, unknown>) => ({
|
||||
id: label.id,
|
||||
name: label.name,
|
||||
type: label.type ?? null,
|
||||
messageListVisibility: label.messageListVisibility ?? null,
|
||||
labelListVisibility: label.labelListVisibility ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: { labels },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
labels: {
|
||||
type: 'json',
|
||||
description: 'Array of label objects with id, name, type, and visibility settings',
|
||||
},
|
||||
},
|
||||
}
|
||||
140
apps/sim/tools/gmail/list_threads.ts
Normal file
140
apps/sim/tools/gmail/list_threads.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailListThreadsParams {
|
||||
accessToken: string
|
||||
maxResults?: number
|
||||
pageToken?: string
|
||||
query?: string
|
||||
labelIds?: string
|
||||
}
|
||||
|
||||
interface GmailListThreadsResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
threads: Array<{
|
||||
id: string
|
||||
snippet: string
|
||||
historyId: string
|
||||
}>
|
||||
resultSizeEstimate: number
|
||||
nextPageToken?: string
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailListThreadsV2Tool: ToolConfig<GmailListThreadsParams, GmailListThreadsResponse> =
|
||||
{
|
||||
id: 'gmail_list_threads_v2',
|
||||
name: 'Gmail List Threads',
|
||||
description: 'List email threads in a Gmail account',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of threads to return (default: 100, max: 500)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token for paginated results',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search query to filter threads (same syntax as Gmail search)',
|
||||
},
|
||||
labelIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated label IDs to filter threads by',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailListThreadsParams) => {
|
||||
const searchParams = new URLSearchParams()
|
||||
if (params.maxResults) {
|
||||
searchParams.append('maxResults', Number(params.maxResults).toString())
|
||||
}
|
||||
if (params.pageToken) {
|
||||
searchParams.append('pageToken', params.pageToken)
|
||||
}
|
||||
if (params.query) {
|
||||
searchParams.append('q', params.query)
|
||||
}
|
||||
if (params.labelIds) {
|
||||
const labels = params.labelIds.split(',').map((l) => l.trim())
|
||||
for (const label of labels) {
|
||||
searchParams.append('labelIds', label)
|
||||
}
|
||||
}
|
||||
const qs = searchParams.toString()
|
||||
return `${GMAIL_API_BASE}/threads${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: GmailListThreadsParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { threads: [], resultSizeEstimate: 0 },
|
||||
error: data.error?.message || 'Failed to list threads',
|
||||
}
|
||||
}
|
||||
|
||||
const threads = (data.threads || []).map((thread: Record<string, unknown>) => ({
|
||||
id: thread.id,
|
||||
snippet: thread.snippet ?? '',
|
||||
historyId: thread.historyId ?? '',
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
threads,
|
||||
resultSizeEstimate: data.resultSizeEstimate ?? 0,
|
||||
nextPageToken: data.nextPageToken ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
threads: {
|
||||
type: 'json',
|
||||
description: 'Array of thread objects with id, snippet, and historyId',
|
||||
},
|
||||
resultSizeEstimate: {
|
||||
type: 'number',
|
||||
description: 'Estimated total number of threads',
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
78
apps/sim/tools/gmail/trash_thread.ts
Normal file
78
apps/sim/tools/gmail/trash_thread.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailTrashThreadParams {
|
||||
accessToken: string
|
||||
threadId: string
|
||||
}
|
||||
|
||||
interface GmailTrashThreadResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
id: string
|
||||
trashed: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailTrashThreadV2Tool: ToolConfig<GmailTrashThreadParams, GmailTrashThreadResponse> =
|
||||
{
|
||||
id: 'gmail_trash_thread_v2',
|
||||
name: 'Gmail Trash Thread',
|
||||
description: 'Move an email thread to trash in Gmail',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
threadId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the thread to trash',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailTrashThreadParams) => `${GMAIL_API_BASE}/threads/${params.threadId}/trash`,
|
||||
method: 'POST',
|
||||
headers: (params: GmailTrashThreadParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: () => ({}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '', trashed: false },
|
||||
error: data.error?.message || 'Failed to trash thread',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
trashed: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Thread ID' },
|
||||
trashed: { type: 'boolean', description: 'Whether the thread was successfully trashed' },
|
||||
},
|
||||
}
|
||||
84
apps/sim/tools/gmail/untrash_thread.ts
Normal file
84
apps/sim/tools/gmail/untrash_thread.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
interface GmailUntrashThreadParams {
|
||||
accessToken: string
|
||||
threadId: string
|
||||
}
|
||||
|
||||
interface GmailUntrashThreadResponse {
|
||||
success: boolean
|
||||
output: {
|
||||
id: string
|
||||
untrashed: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const gmailUntrashThreadV2Tool: ToolConfig<
|
||||
GmailUntrashThreadParams,
|
||||
GmailUntrashThreadResponse
|
||||
> = {
|
||||
id: 'gmail_untrash_thread_v2',
|
||||
name: 'Gmail Untrash Thread',
|
||||
description: 'Remove an email thread from trash in Gmail',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-email',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Gmail API',
|
||||
},
|
||||
threadId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the thread to untrash',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: GmailUntrashThreadParams) =>
|
||||
`${GMAIL_API_BASE}/threads/${params.threadId}/untrash`,
|
||||
method: 'POST',
|
||||
headers: (params: GmailUntrashThreadParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: () => ({}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '', untrashed: false },
|
||||
error: data.error?.message || 'Failed to untrash thread',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id,
|
||||
untrashed: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Thread ID' },
|
||||
untrashed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the thread was successfully removed from trash',
|
||||
},
|
||||
},
|
||||
}
|
||||
155
apps/sim/tools/google_calendar/freebusy.ts
Normal file
155
apps/sim/tools/google_calendar/freebusy.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import {
|
||||
CALENDAR_API_BASE,
|
||||
type GoogleCalendarApiFreeBusyResponse,
|
||||
type GoogleCalendarFreeBusyParams,
|
||||
type GoogleCalendarFreeBusyResponse,
|
||||
} from '@/tools/google_calendar/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const freebusyTool: ToolConfig<
|
||||
GoogleCalendarFreeBusyParams,
|
||||
GoogleCalendarFreeBusyResponse
|
||||
> = {
|
||||
id: 'google_calendar_freebusy',
|
||||
name: 'Google Calendar Free/Busy',
|
||||
description: 'Query free/busy information for one or more Google Calendars',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-calendar',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Access token for Google Calendar API',
|
||||
},
|
||||
calendarIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated calendar IDs to query (e.g., "primary,other@example.com")',
|
||||
},
|
||||
timeMin: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start of the time range (RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z)',
|
||||
},
|
||||
timeMax: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End of the time range (RFC3339 timestamp, e.g., 2025-06-04T00:00:00Z)',
|
||||
},
|
||||
timeZone: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'IANA time zone (e.g., "UTC", "America/New_York"). Defaults to UTC.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${CALENDAR_API_BASE}/freeBusy`,
|
||||
method: 'POST',
|
||||
headers: (params: GoogleCalendarFreeBusyParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params: GoogleCalendarFreeBusyParams) => {
|
||||
const ids = params.calendarIds
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
return {
|
||||
timeMin: params.timeMin,
|
||||
timeMax: params.timeMax,
|
||||
timeZone: params.timeZone || 'UTC',
|
||||
items: ids.map((id) => ({ id })),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: GoogleCalendarApiFreeBusyResponse = await response.json()
|
||||
|
||||
const calendarIds = Object.keys(data.calendars || {})
|
||||
const totalBusy = calendarIds.reduce((sum, id) => {
|
||||
return sum + (data.calendars[id]?.busy?.length || 0)
|
||||
}, 0)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content: `Found ${totalBusy} busy period${totalBusy !== 1 ? 's' : ''} across ${calendarIds.length} calendar${calendarIds.length !== 1 ? 's' : ''}`,
|
||||
metadata: {
|
||||
timeMin: data.timeMin,
|
||||
timeMax: data.timeMax,
|
||||
calendars: data.calendars,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
content: { type: 'string', description: 'Summary of free/busy results' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Free/busy data with time range and per-calendar busy periods',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
interface GoogleCalendarFreeBusyV2Response {
|
||||
success: boolean
|
||||
output: {
|
||||
timeMin: string
|
||||
timeMax: string
|
||||
calendars: Record<
|
||||
string,
|
||||
{
|
||||
busy: Array<{ start: string; end: string }>
|
||||
errors?: Array<{ domain: string; reason: string }>
|
||||
}
|
||||
>
|
||||
}
|
||||
}
|
||||
|
||||
export const freebusyV2Tool: ToolConfig<
|
||||
GoogleCalendarFreeBusyParams,
|
||||
GoogleCalendarFreeBusyV2Response
|
||||
> = {
|
||||
id: 'google_calendar_freebusy_v2',
|
||||
name: 'Google Calendar Free/Busy',
|
||||
description:
|
||||
'Query free/busy information for one or more Google Calendars. Returns API-aligned fields only.',
|
||||
version: '2.0.0',
|
||||
oauth: freebusyTool.oauth,
|
||||
params: freebusyTool.params,
|
||||
request: freebusyTool.request,
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: GoogleCalendarApiFreeBusyResponse = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
timeMin: data.timeMin,
|
||||
timeMax: data.timeMax,
|
||||
calendars: data.calendars,
|
||||
},
|
||||
}
|
||||
},
|
||||
outputs: {
|
||||
timeMin: { type: 'string', description: 'Start of the queried time range' },
|
||||
timeMax: { type: 'string', description: 'End of the queried time range' },
|
||||
calendars: {
|
||||
type: 'json',
|
||||
description: 'Per-calendar free/busy data with busy periods and any errors',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createTool, createV2Tool } from '@/tools/google_calendar/create'
|
||||
import { deleteTool, deleteV2Tool } from '@/tools/google_calendar/delete'
|
||||
import { freebusyTool, freebusyV2Tool } from '@/tools/google_calendar/freebusy'
|
||||
import { getTool, getV2Tool } from '@/tools/google_calendar/get'
|
||||
import { instancesTool, instancesV2Tool } from '@/tools/google_calendar/instances'
|
||||
import { inviteTool, inviteV2Tool } from '@/tools/google_calendar/invite'
|
||||
@@ -11,6 +12,7 @@ import { updateTool, updateV2Tool } from '@/tools/google_calendar/update'
|
||||
|
||||
export const googleCalendarCreateTool = createTool
|
||||
export const googleCalendarDeleteTool = deleteTool
|
||||
export const googleCalendarFreeBusyTool = freebusyTool
|
||||
export const googleCalendarGetTool = getTool
|
||||
export const googleCalendarInstancesTool = instancesTool
|
||||
export const googleCalendarInviteTool = inviteTool
|
||||
@@ -22,6 +24,7 @@ export const googleCalendarUpdateTool = updateTool
|
||||
|
||||
export const googleCalendarCreateV2Tool = createV2Tool
|
||||
export const googleCalendarDeleteV2Tool = deleteV2Tool
|
||||
export const googleCalendarFreeBusyV2Tool = freebusyV2Tool
|
||||
export const googleCalendarGetV2Tool = getV2Tool
|
||||
export const googleCalendarInstancesV2Tool = instancesV2Tool
|
||||
export const googleCalendarInviteV2Tool = inviteV2Tool
|
||||
|
||||
@@ -90,6 +90,14 @@ export interface GoogleCalendarInstancesParams extends BaseGoogleCalendarParams
|
||||
showDeleted?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleCalendarFreeBusyParams {
|
||||
accessToken: string
|
||||
calendarIds: string // Comma-separated calendar IDs (e.g., "primary,other@example.com")
|
||||
timeMin: string // RFC3339 timestamp (e.g., 2025-06-03T00:00:00Z)
|
||||
timeMax: string // RFC3339 timestamp (e.g., 2025-06-04T00:00:00Z)
|
||||
timeZone?: string // IANA time zone (e.g., "UTC", "America/New_York")
|
||||
}
|
||||
|
||||
export interface GoogleCalendarListCalendarsParams {
|
||||
accessToken: string
|
||||
minAccessRole?: 'freeBusyReader' | 'reader' | 'writer' | 'owner'
|
||||
@@ -109,6 +117,7 @@ export type GoogleCalendarToolParams =
|
||||
| GoogleCalendarInviteParams
|
||||
| GoogleCalendarMoveParams
|
||||
| GoogleCalendarInstancesParams
|
||||
| GoogleCalendarFreeBusyParams
|
||||
| GoogleCalendarListCalendarsParams
|
||||
|
||||
interface EventMetadata {
|
||||
@@ -341,6 +350,36 @@ export interface GoogleCalendarInstancesResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleCalendarFreeBusyResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: {
|
||||
timeMin: string
|
||||
timeMax: string
|
||||
calendars: Record<
|
||||
string,
|
||||
{
|
||||
busy: Array<{ start: string; end: string }>
|
||||
errors?: Array<{ domain: string; reason: string }>
|
||||
}
|
||||
>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleCalendarApiFreeBusyResponse {
|
||||
kind: string
|
||||
timeMin: string
|
||||
timeMax: string
|
||||
calendars: Record<
|
||||
string,
|
||||
{
|
||||
busy: Array<{ start: string; end: string }>
|
||||
errors?: Array<{ domain: string; reason: string }>
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
export interface GoogleCalendarListCalendarsResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
@@ -373,4 +412,5 @@ export type GoogleCalendarResponse =
|
||||
| GoogleCalendarDeleteResponse
|
||||
| GoogleCalendarMoveResponse
|
||||
| GoogleCalendarInstancesResponse
|
||||
| GoogleCalendarFreeBusyResponse
|
||||
| GoogleCalendarListCalendarsResponse
|
||||
|
||||
@@ -7,6 +7,8 @@ import { getContentTool } from '@/tools/google_drive/get_content'
|
||||
import { getFileTool } from '@/tools/google_drive/get_file'
|
||||
import { listTool } from '@/tools/google_drive/list'
|
||||
import { listPermissionsTool } from '@/tools/google_drive/list_permissions'
|
||||
import { moveTool } from '@/tools/google_drive/move'
|
||||
import { searchTool } from '@/tools/google_drive/search'
|
||||
import { shareTool } from '@/tools/google_drive/share'
|
||||
import { trashTool } from '@/tools/google_drive/trash'
|
||||
import { unshareTool } from '@/tools/google_drive/unshare'
|
||||
@@ -23,6 +25,8 @@ export const googleDriveGetContentTool = getContentTool
|
||||
export const googleDriveGetFileTool = getFileTool
|
||||
export const googleDriveListTool = listTool
|
||||
export const googleDriveListPermissionsTool = listPermissionsTool
|
||||
export const googleDriveMoveTool = moveTool
|
||||
export const googleDriveSearchTool = searchTool
|
||||
export const googleDriveShareTool = shareTool
|
||||
export const googleDriveTrashTool = trashTool
|
||||
export const googleDriveUnshareTool = unshareTool
|
||||
|
||||
145
apps/sim/tools/google_drive/move.ts
Normal file
145
apps/sim/tools/google_drive/move.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveMoveParams extends GoogleDriveToolParams {
|
||||
fileId: string
|
||||
destinationFolderId: string
|
||||
removeFromCurrent?: boolean
|
||||
}
|
||||
|
||||
interface GoogleDriveMoveResponse extends ToolResponse {
|
||||
output: {
|
||||
file: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export const moveTool: ToolConfig<GoogleDriveMoveParams, GoogleDriveMoveResponse> = {
|
||||
id: 'google_drive_move',
|
||||
name: 'Move Google Drive File',
|
||||
description: 'Move a file or folder to a different folder in Google Drive',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file or folder to move',
|
||||
},
|
||||
destinationFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the destination folder',
|
||||
},
|
||||
removeFromCurrent: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Whether to remove the file from its current parent folder (default: true). Set to false to add the file to the destination without removing it from the current location.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://www.googleapis.com/drive/v3/files',
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
directExecution: async (params) => {
|
||||
const fileId = params.fileId?.trim()
|
||||
const destinationFolderId = params.destinationFolderId?.trim()
|
||||
const removeFromCurrent = params.removeFromCurrent !== false
|
||||
|
||||
if (!fileId) {
|
||||
throw new Error('fileId is required')
|
||||
}
|
||||
if (!destinationFolderId) {
|
||||
throw new Error('destinationFolderId is required')
|
||||
}
|
||||
|
||||
const headers = {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
// Build the PATCH URL with addParents
|
||||
const url = new URL(`https://www.googleapis.com/drive/v3/files/${fileId}`)
|
||||
url.searchParams.append('addParents', destinationFolderId)
|
||||
url.searchParams.append('fields', ALL_FILE_FIELDS)
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
|
||||
if (removeFromCurrent) {
|
||||
// Fetch current parents so we can remove them
|
||||
const metadataUrl = new URL(`https://www.googleapis.com/drive/v3/files/${fileId}`)
|
||||
metadataUrl.searchParams.append('fields', 'parents')
|
||||
metadataUrl.searchParams.append('supportsAllDrives', 'true')
|
||||
|
||||
const metadataResponse = await fetch(metadataUrl.toString(), { headers })
|
||||
|
||||
if (!metadataResponse.ok) {
|
||||
const errorData = await metadataResponse.json()
|
||||
throw new Error(errorData.error?.message || 'Failed to retrieve file metadata')
|
||||
}
|
||||
|
||||
const metadata = await metadataResponse.json()
|
||||
if (metadata.parents && metadata.parents.length > 0) {
|
||||
url.searchParams.append('removeParents', metadata.parents.join(','))
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'PATCH',
|
||||
headers,
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to move Google Drive file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'json',
|
||||
description: 'The moved file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Google Drive file ID' },
|
||||
kind: { type: 'string', description: 'Resource type identifier' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
webViewLink: { type: 'string', description: 'URL to view in browser' },
|
||||
parents: { type: 'json', description: 'Parent folder IDs' },
|
||||
createdTime: { type: 'string', description: 'File creation time' },
|
||||
modifiedTime: { type: 'string', description: 'Last modification time' },
|
||||
owners: { type: 'json', description: 'List of file owners' },
|
||||
size: { type: 'string', description: 'File size in bytes' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
145
apps/sim/tools/google_drive/search.ts
Normal file
145
apps/sim/tools/google_drive/search.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import type { GoogleDriveFile, GoogleDriveToolParams } from '@/tools/google_drive/types'
|
||||
import { ALL_FILE_FIELDS } from '@/tools/google_drive/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
interface GoogleDriveSearchParams extends GoogleDriveToolParams {
|
||||
query: string
|
||||
pageSize?: number
|
||||
pageToken?: string
|
||||
}
|
||||
|
||||
interface GoogleDriveSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
files: GoogleDriveFile[]
|
||||
nextPageToken?: string
|
||||
}
|
||||
}
|
||||
|
||||
export const searchTool: ToolConfig<GoogleDriveSearchParams, GoogleDriveSearchResponse> = {
|
||||
id: 'google_drive_search',
|
||||
name: 'Search Google Drive Files',
|
||||
description:
|
||||
'Search for files in Google Drive using advanced query syntax (e.g., fullText contains, mimeType, modifiedTime, etc.)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Google Drive query string using advanced search syntax (e.g., "fullText contains \'budget\'", "mimeType = \'application/pdf\'", "modifiedTime > \'2024-01-01\'")',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of files to return (default: 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Token for fetching the next page of results',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://www.googleapis.com/drive/v3/files')
|
||||
url.searchParams.append('fields', `files(${ALL_FILE_FIELDS}),nextPageToken`)
|
||||
url.searchParams.append('corpora', 'allDrives')
|
||||
url.searchParams.append('supportsAllDrives', 'true')
|
||||
url.searchParams.append('includeItemsFromAllDrives', 'true')
|
||||
|
||||
// The query is passed directly as Google Drive query syntax
|
||||
const conditions = ['trashed = false']
|
||||
if (params.query?.trim()) {
|
||||
conditions.push(params.query.trim())
|
||||
}
|
||||
url.searchParams.append('q', conditions.join(' and '))
|
||||
|
||||
if (params.pageSize) {
|
||||
url.searchParams.append('pageSize', Number(params.pageSize).toString())
|
||||
}
|
||||
if (params.pageToken) {
|
||||
url.searchParams.append('pageToken', params.pageToken)
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to search Google Drive files')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
files: data.files || [],
|
||||
nextPageToken: data.nextPageToken,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
files: {
|
||||
type: 'array',
|
||||
description: 'Array of file metadata objects matching the search query',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Google Drive file ID' },
|
||||
kind: { type: 'string', description: 'Resource type identifier' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
description: { type: 'string', description: 'File description' },
|
||||
originalFilename: { type: 'string', description: 'Original uploaded filename' },
|
||||
fullFileExtension: { type: 'string', description: 'Full file extension' },
|
||||
fileExtension: { type: 'string', description: 'File extension' },
|
||||
owners: { type: 'json', description: 'List of file owners' },
|
||||
permissions: { type: 'json', description: 'File permissions' },
|
||||
shared: { type: 'boolean', description: 'Whether file is shared' },
|
||||
ownedByMe: { type: 'boolean', description: 'Whether owned by current user' },
|
||||
starred: { type: 'boolean', description: 'Whether file is starred' },
|
||||
trashed: { type: 'boolean', description: 'Whether file is in trash' },
|
||||
createdTime: { type: 'string', description: 'File creation time' },
|
||||
modifiedTime: { type: 'string', description: 'Last modification time' },
|
||||
lastModifyingUser: { type: 'json', description: 'User who last modified the file' },
|
||||
webViewLink: { type: 'string', description: 'URL to view in browser' },
|
||||
webContentLink: { type: 'string', description: 'Direct download URL' },
|
||||
iconLink: { type: 'string', description: 'URL to file icon' },
|
||||
thumbnailLink: { type: 'string', description: 'URL to thumbnail' },
|
||||
size: { type: 'string', description: 'File size in bytes' },
|
||||
parents: { type: 'json', description: 'Parent folder IDs' },
|
||||
driveId: { type: 'string', description: 'Shared drive ID' },
|
||||
capabilities: { type: 'json', description: 'User capabilities on file' },
|
||||
version: { type: 'string', description: 'Version number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of results',
|
||||
},
|
||||
},
|
||||
}
|
||||
156
apps/sim/tools/google_sheets/delete_rows.ts
Normal file
156
apps/sim/tools/google_sheets/delete_rows.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface GoogleSheetsV2DeleteRowsParams {
|
||||
accessToken: string
|
||||
spreadsheetId: string
|
||||
sheetId: number
|
||||
startIndex: number
|
||||
endIndex: number
|
||||
}
|
||||
|
||||
export interface GoogleSheetsV2DeleteRowsResponse extends ToolResponse {
|
||||
output: {
|
||||
spreadsheetId: string
|
||||
sheetId: number
|
||||
deletedRowRange: string
|
||||
metadata: {
|
||||
spreadsheetId: string
|
||||
spreadsheetUrl: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteRowsV2Tool: ToolConfig<
|
||||
GoogleSheetsV2DeleteRowsParams,
|
||||
GoogleSheetsV2DeleteRowsResponse
|
||||
> = {
|
||||
id: 'google_sheets_delete_rows_v2',
|
||||
name: 'Delete Rows from Google Sheets V2',
|
||||
description: 'Delete rows from a sheet in a Google Sheets spreadsheet',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-sheets',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the Google Sheets API',
|
||||
},
|
||||
spreadsheetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Sheets spreadsheet ID',
|
||||
},
|
||||
sheetId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The numeric ID of the sheet/tab (not the sheet name). Use Get Spreadsheet to find sheet IDs.',
|
||||
},
|
||||
startIndex: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The start row index (0-based, inclusive) of the rows to delete',
|
||||
},
|
||||
endIndex: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The end row index (0-based, exclusive) of the rows to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const spreadsheetId = params.spreadsheetId?.trim()
|
||||
if (!spreadsheetId) {
|
||||
throw new Error('Spreadsheet ID is required')
|
||||
}
|
||||
|
||||
return `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}:batchUpdate`
|
||||
},
|
||||
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) => {
|
||||
if (params.sheetId === undefined || params.sheetId === null) {
|
||||
throw new Error('Sheet ID is required')
|
||||
}
|
||||
if (params.startIndex === undefined || params.startIndex === null) {
|
||||
throw new Error('Start index is required')
|
||||
}
|
||||
if (params.endIndex === undefined || params.endIndex === null) {
|
||||
throw new Error('End index is required')
|
||||
}
|
||||
|
||||
return {
|
||||
requests: [
|
||||
{
|
||||
deleteDimension: {
|
||||
range: {
|
||||
sheetId: params.sheetId,
|
||||
dimension: 'ROWS',
|
||||
startIndex: params.startIndex,
|
||||
endIndex: params.endIndex,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: GoogleSheetsV2DeleteRowsParams) => {
|
||||
await response.json()
|
||||
|
||||
const spreadsheetId = params?.spreadsheetId ?? ''
|
||||
const startIndex = params?.startIndex ?? 0
|
||||
const endIndex = params?.endIndex ?? 0
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
spreadsheetId,
|
||||
sheetId: params?.sheetId ?? 0,
|
||||
deletedRowRange: `rows ${startIndex} to ${endIndex}`,
|
||||
metadata: {
|
||||
spreadsheetId,
|
||||
spreadsheetUrl: `https://docs.google.com/spreadsheets/d/${spreadsheetId}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spreadsheetId: { type: 'string', description: 'Google Sheets spreadsheet ID' },
|
||||
sheetId: { type: 'number', description: 'The numeric ID of the sheet' },
|
||||
deletedRowRange: {
|
||||
type: 'string',
|
||||
description: 'Description of the deleted row range',
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Spreadsheet metadata including ID and URL',
|
||||
properties: {
|
||||
spreadsheetId: { type: 'string', description: 'Google Sheets spreadsheet ID' },
|
||||
spreadsheetUrl: { type: 'string', description: 'Spreadsheet URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
123
apps/sim/tools/google_sheets/delete_sheet.ts
Normal file
123
apps/sim/tools/google_sheets/delete_sheet.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface GoogleSheetsV2DeleteSheetParams {
|
||||
accessToken: string
|
||||
spreadsheetId: string
|
||||
sheetId: number
|
||||
}
|
||||
|
||||
export interface GoogleSheetsV2DeleteSheetResponse extends ToolResponse {
|
||||
output: {
|
||||
spreadsheetId: string
|
||||
deletedSheetId: number
|
||||
metadata: {
|
||||
spreadsheetId: string
|
||||
spreadsheetUrl: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteSheetV2Tool: ToolConfig<
|
||||
GoogleSheetsV2DeleteSheetParams,
|
||||
GoogleSheetsV2DeleteSheetResponse
|
||||
> = {
|
||||
id: 'google_sheets_delete_sheet_v2',
|
||||
name: 'Delete Sheet V2',
|
||||
description: 'Delete a sheet/tab from a Google Sheets spreadsheet',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-sheets',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the Google Sheets API',
|
||||
},
|
||||
spreadsheetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Sheets spreadsheet ID',
|
||||
},
|
||||
sheetId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The numeric ID of the sheet/tab to delete (not the sheet name). Use Get Spreadsheet to find sheet IDs.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const spreadsheetId = params.spreadsheetId?.trim()
|
||||
if (!spreadsheetId) {
|
||||
throw new Error('Spreadsheet ID is required')
|
||||
}
|
||||
|
||||
return `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}:batchUpdate`
|
||||
},
|
||||
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) => {
|
||||
if (params.sheetId === undefined || params.sheetId === null) {
|
||||
throw new Error('Sheet ID is required')
|
||||
}
|
||||
|
||||
return {
|
||||
requests: [
|
||||
{
|
||||
deleteSheet: {
|
||||
sheetId: params.sheetId,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: GoogleSheetsV2DeleteSheetParams) => {
|
||||
await response.json()
|
||||
|
||||
const spreadsheetId = params?.spreadsheetId ?? ''
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
spreadsheetId,
|
||||
deletedSheetId: params?.sheetId ?? 0,
|
||||
metadata: {
|
||||
spreadsheetId,
|
||||
spreadsheetUrl: `https://docs.google.com/spreadsheets/d/${spreadsheetId}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spreadsheetId: { type: 'string', description: 'Google Sheets spreadsheet ID' },
|
||||
deletedSheetId: { type: 'number', description: 'The numeric ID of the deleted sheet' },
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Spreadsheet metadata including ID and URL',
|
||||
properties: {
|
||||
spreadsheetId: { type: 'string', description: 'Google Sheets spreadsheet ID' },
|
||||
spreadsheetUrl: { type: 'string', description: 'Spreadsheet URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
86
apps/sim/tools/google_sheets/delete_spreadsheet.ts
Normal file
86
apps/sim/tools/google_sheets/delete_spreadsheet.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface GoogleSheetsV2DeleteSpreadsheetParams {
|
||||
accessToken: string
|
||||
spreadsheetId: string
|
||||
}
|
||||
|
||||
export interface GoogleSheetsV2DeleteSpreadsheetResponse extends ToolResponse {
|
||||
output: {
|
||||
spreadsheetId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteSpreadsheetV2Tool: ToolConfig<
|
||||
GoogleSheetsV2DeleteSpreadsheetParams,
|
||||
GoogleSheetsV2DeleteSpreadsheetResponse
|
||||
> = {
|
||||
id: 'google_sheets_delete_spreadsheet_v2',
|
||||
name: 'Delete Spreadsheet V2',
|
||||
description: 'Permanently delete a Google Sheets spreadsheet using the Google Drive API',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-sheets',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the Google Sheets API',
|
||||
},
|
||||
spreadsheetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the Google Sheets spreadsheet to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const spreadsheetId = params.spreadsheetId?.trim()
|
||||
if (!spreadsheetId) {
|
||||
throw new Error('Spreadsheet ID is required')
|
||||
}
|
||||
|
||||
return `https://www.googleapis.com/drive/v3/files/${spreadsheetId}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Access token is required')
|
||||
}
|
||||
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: GoogleSheetsV2DeleteSpreadsheetParams) => {
|
||||
const spreadsheetId = params?.spreadsheetId ?? ''
|
||||
|
||||
if (response.status === 204 || response.ok) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
spreadsheetId,
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
throw new Error(data.error?.message ?? 'Failed to delete spreadsheet')
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spreadsheetId: { type: 'string', description: 'The ID of the deleted spreadsheet' },
|
||||
deleted: { type: 'boolean', description: 'Whether the spreadsheet was successfully deleted' },
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,9 @@ import { batchUpdateV2Tool } from '@/tools/google_sheets/batch_update'
|
||||
import { clearV2Tool } from '@/tools/google_sheets/clear'
|
||||
import { copySheetV2Tool } from '@/tools/google_sheets/copy_sheet'
|
||||
import { createSpreadsheetV2Tool } from '@/tools/google_sheets/create_spreadsheet'
|
||||
import { deleteRowsV2Tool } from '@/tools/google_sheets/delete_rows'
|
||||
import { deleteSheetV2Tool } from '@/tools/google_sheets/delete_sheet'
|
||||
import { deleteSpreadsheetV2Tool } from '@/tools/google_sheets/delete_spreadsheet'
|
||||
import { getSpreadsheetV2Tool } from '@/tools/google_sheets/get_spreadsheet'
|
||||
import { readTool, readV2Tool } from '@/tools/google_sheets/read'
|
||||
import { updateTool, updateV2Tool } from '@/tools/google_sheets/update'
|
||||
@@ -28,3 +31,6 @@ export const googleSheetsBatchGetV2Tool = batchGetV2Tool
|
||||
export const googleSheetsBatchUpdateV2Tool = batchUpdateV2Tool
|
||||
export const googleSheetsBatchClearV2Tool = batchClearV2Tool
|
||||
export const googleSheetsCopySheetV2Tool = copySheetV2Tool
|
||||
export const googleSheetsDeleteRowsV2Tool = deleteRowsV2Tool
|
||||
export const googleSheetsDeleteSheetV2Tool = deleteSheetV2Tool
|
||||
export const googleSheetsDeleteSpreadsheetV2Tool = deleteSpreadsheetV2Tool
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type { GoogleSheetsV2DeleteRowsResponse } from '@/tools/google_sheets/delete_rows'
|
||||
import type { GoogleSheetsV2DeleteSheetResponse } from '@/tools/google_sheets/delete_sheet'
|
||||
import type { GoogleSheetsV2DeleteSpreadsheetResponse } from '@/tools/google_sheets/delete_spreadsheet'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface GoogleSheetsRange {
|
||||
@@ -146,6 +149,9 @@ export type GoogleSheetsV2Response =
|
||||
| GoogleSheetsV2BatchUpdateResponse
|
||||
| GoogleSheetsV2BatchClearResponse
|
||||
| GoogleSheetsV2CopySheetResponse
|
||||
| GoogleSheetsV2DeleteRowsResponse
|
||||
| GoogleSheetsV2DeleteSheetResponse
|
||||
| GoogleSheetsV2DeleteSpreadsheetResponse
|
||||
|
||||
// V2 Clear Types
|
||||
export interface GoogleSheetsV2ClearParams {
|
||||
|
||||
@@ -578,10 +578,18 @@ import {
|
||||
gmailAddLabelV2Tool,
|
||||
gmailArchiveTool,
|
||||
gmailArchiveV2Tool,
|
||||
gmailCreateLabelV2Tool,
|
||||
gmailDeleteDraftV2Tool,
|
||||
gmailDeleteLabelV2Tool,
|
||||
gmailDeleteTool,
|
||||
gmailDeleteV2Tool,
|
||||
gmailDraftTool,
|
||||
gmailDraftV2Tool,
|
||||
gmailGetDraftV2Tool,
|
||||
gmailGetThreadV2Tool,
|
||||
gmailListDraftsV2Tool,
|
||||
gmailListLabelsV2Tool,
|
||||
gmailListThreadsV2Tool,
|
||||
gmailMarkReadTool,
|
||||
gmailMarkReadV2Tool,
|
||||
gmailMarkUnreadTool,
|
||||
@@ -596,8 +604,10 @@ import {
|
||||
gmailSearchV2Tool,
|
||||
gmailSendTool,
|
||||
gmailSendV2Tool,
|
||||
gmailTrashThreadV2Tool,
|
||||
gmailUnarchiveTool,
|
||||
gmailUnarchiveV2Tool,
|
||||
gmailUntrashThreadV2Tool,
|
||||
} from '@/tools/gmail'
|
||||
import {
|
||||
gongAggregateActivityTool,
|
||||
@@ -626,6 +636,8 @@ import {
|
||||
googleCalendarCreateV2Tool,
|
||||
googleCalendarDeleteTool,
|
||||
googleCalendarDeleteV2Tool,
|
||||
googleCalendarFreeBusyTool,
|
||||
googleCalendarFreeBusyV2Tool,
|
||||
googleCalendarGetTool,
|
||||
googleCalendarGetV2Tool,
|
||||
googleCalendarInstancesTool,
|
||||
@@ -654,6 +666,8 @@ import {
|
||||
googleDriveGetFileTool,
|
||||
googleDriveListPermissionsTool,
|
||||
googleDriveListTool,
|
||||
googleDriveMoveTool,
|
||||
googleDriveSearchTool,
|
||||
googleDriveShareTool,
|
||||
googleDriveTrashTool,
|
||||
googleDriveUnshareTool,
|
||||
@@ -714,6 +728,9 @@ import {
|
||||
googleSheetsClearV2Tool,
|
||||
googleSheetsCopySheetV2Tool,
|
||||
googleSheetsCreateSpreadsheetV2Tool,
|
||||
googleSheetsDeleteRowsV2Tool,
|
||||
googleSheetsDeleteSheetV2Tool,
|
||||
googleSheetsDeleteSpreadsheetV2Tool,
|
||||
googleSheetsGetSpreadsheetV2Tool,
|
||||
googleSheetsReadTool,
|
||||
googleSheetsReadV2Tool,
|
||||
@@ -2472,6 +2489,16 @@ export const tools: Record<string, ToolConfig> = {
|
||||
gmail_add_label_v2: gmailAddLabelV2Tool,
|
||||
gmail_remove_label: gmailRemoveLabelTool,
|
||||
gmail_remove_label_v2: gmailRemoveLabelV2Tool,
|
||||
gmail_create_label_v2: gmailCreateLabelV2Tool,
|
||||
gmail_delete_draft_v2: gmailDeleteDraftV2Tool,
|
||||
gmail_delete_label_v2: gmailDeleteLabelV2Tool,
|
||||
gmail_get_draft_v2: gmailGetDraftV2Tool,
|
||||
gmail_get_thread_v2: gmailGetThreadV2Tool,
|
||||
gmail_list_drafts_v2: gmailListDraftsV2Tool,
|
||||
gmail_list_labels_v2: gmailListLabelsV2Tool,
|
||||
gmail_list_threads_v2: gmailListThreadsV2Tool,
|
||||
gmail_trash_thread_v2: gmailTrashThreadV2Tool,
|
||||
gmail_untrash_thread_v2: gmailUntrashThreadV2Tool,
|
||||
whatsapp_send_message: whatsappSendMessageTool,
|
||||
x_write: xWriteTool,
|
||||
x_read: xReadTool,
|
||||
@@ -2868,6 +2895,8 @@ export const tools: Record<string, ToolConfig> = {
|
||||
google_drive_get_file: googleDriveGetFileTool,
|
||||
google_drive_list: googleDriveListTool,
|
||||
google_drive_list_permissions: googleDriveListPermissionsTool,
|
||||
google_drive_move: googleDriveMoveTool,
|
||||
google_drive_search: googleDriveSearchTool,
|
||||
google_drive_share: googleDriveShareTool,
|
||||
google_drive_trash: googleDriveTrashTool,
|
||||
google_drive_unshare: googleDriveUnshareTool,
|
||||
@@ -2909,6 +2938,9 @@ export const tools: Record<string, ToolConfig> = {
|
||||
google_sheets_batch_update_v2: googleSheetsBatchUpdateV2Tool,
|
||||
google_sheets_batch_clear_v2: googleSheetsBatchClearV2Tool,
|
||||
google_sheets_copy_sheet_v2: googleSheetsCopySheetV2Tool,
|
||||
google_sheets_delete_rows_v2: googleSheetsDeleteRowsV2Tool,
|
||||
google_sheets_delete_sheet_v2: googleSheetsDeleteSheetV2Tool,
|
||||
google_sheets_delete_spreadsheet_v2: googleSheetsDeleteSpreadsheetV2Tool,
|
||||
google_slides_read: googleSlidesReadTool,
|
||||
google_slides_write: googleSlidesWriteTool,
|
||||
google_slides_create: googleSlidesCreateTool,
|
||||
@@ -3508,6 +3540,8 @@ export const tools: Record<string, ToolConfig> = {
|
||||
google_calendar_quick_add_v2: googleCalendarQuickAddV2Tool,
|
||||
google_calendar_update: googleCalendarUpdateTool,
|
||||
google_calendar_update_v2: googleCalendarUpdateV2Tool,
|
||||
google_calendar_freebusy: googleCalendarFreeBusyTool,
|
||||
google_calendar_freebusy_v2: googleCalendarFreeBusyV2Tool,
|
||||
google_forms_get_responses: googleFormsGetResponsesTool,
|
||||
google_forms_get_form: googleFormsGetFormTool,
|
||||
google_forms_create_form: googleFormsCreateFormTool,
|
||||
|
||||
Reference in New Issue
Block a user