mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
feat(oauth): added google drive credentials, fixed some oauth bugs
This commit is contained in:
@@ -40,9 +40,7 @@ export async function GET(request: NextRequest) {
|
||||
const accounts = await db
|
||||
.select()
|
||||
.from(account)
|
||||
.where(
|
||||
and(eq(account.userId, session.user.id), like(account.providerId, `${baseProvider}-%`))
|
||||
)
|
||||
.where(and(eq(account.userId, session.user.id), eq(account.providerId, provider)))
|
||||
|
||||
// Transform accounts into credentials
|
||||
const credentials = await Promise.all(
|
||||
|
||||
@@ -55,7 +55,9 @@ export function CredentialSelector({
|
||||
if (!open) return
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const response = await fetch(`/api/auth/oauth/credentials?provider=${provider}`)
|
||||
const providerId = getProviderId()
|
||||
|
||||
const response = await fetch(`/api/auth/oauth/credentials?provider=${providerId}`)
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setCredentials(data.credentials)
|
||||
@@ -173,11 +175,10 @@ export function CredentialSelector({
|
||||
|
||||
// Get provider icon
|
||||
const getProviderIcon = (provider: OAuthProvider) => {
|
||||
switch (provider) {
|
||||
const baseProvider = provider.split('-')[0] as OAuthProvider
|
||||
switch (baseProvider) {
|
||||
case 'google':
|
||||
return <GoogleIcon className="h-4 w-4" />
|
||||
case 'google-email':
|
||||
return <GmailIcon className="h-4 w-4" />
|
||||
case 'github':
|
||||
return <GithubIcon className="h-4 w-4" />
|
||||
case 'twitter':
|
||||
|
||||
165
blocks/blocks/drive.ts
Normal file
165
blocks/blocks/drive.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { GoogleDriveIcon } from '@/components/icons'
|
||||
import {
|
||||
GoogleDriveDownloadResponse,
|
||||
GoogleDriveListResponse,
|
||||
GoogleDriveUploadResponse,
|
||||
} from '@/tools/drive/types'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
type GoogleDriveResponse =
|
||||
| GoogleDriveUploadResponse
|
||||
| GoogleDriveDownloadResponse
|
||||
| GoogleDriveListResponse
|
||||
|
||||
export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
type: 'google_drive',
|
||||
name: 'Google Drive',
|
||||
description: 'Upload, download, and list files in Google Drive',
|
||||
longDescription:
|
||||
'Integrate Google Drive functionality to manage files and folders. Upload new files, download existing ones, and list contents of folders using OAuth authentication. Supports file operations with custom MIME types and folder organization.',
|
||||
category: 'tools',
|
||||
bgColor: '#1EA362',
|
||||
icon: GoogleDriveIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
// { label: 'Upload File', id: 'upload' },
|
||||
// { label: 'Download File', id: 'download' },
|
||||
{ label: 'List Files', id: 'list' },
|
||||
],
|
||||
},
|
||||
// Google Drive Credentials
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Google Drive Account',
|
||||
type: 'oauth-input',
|
||||
layout: 'full',
|
||||
provider: 'google-drive',
|
||||
serviceId: 'google-drive',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/drive'],
|
||||
placeholder: 'Select Google Drive account',
|
||||
},
|
||||
// Upload Fields
|
||||
{
|
||||
id: 'fileName',
|
||||
title: 'File Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Name for the uploaded file (e.g., document.txt)',
|
||||
condition: { field: 'operation', value: 'upload' },
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Content',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Content to upload to the file',
|
||||
condition: { field: 'operation', value: 'upload' },
|
||||
},
|
||||
{
|
||||
id: 'mimeType',
|
||||
title: 'MIME Type',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder:
|
||||
'File MIME type (default: text/plain, e.g., text/plain, application/json, text/csv)',
|
||||
condition: { field: 'operation', value: 'upload' },
|
||||
},
|
||||
{
|
||||
id: 'folderId',
|
||||
title: 'Parent Folder ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the parent folder (optional, leave empty for root folder)',
|
||||
condition: { field: 'operation', value: 'upload' },
|
||||
},
|
||||
// Download Fields
|
||||
{
|
||||
id: 'fileId',
|
||||
title: 'File ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the file to download (find in file URL or by listing files)',
|
||||
condition: { field: 'operation', value: 'download' },
|
||||
},
|
||||
// List Fields
|
||||
{
|
||||
id: 'folderId',
|
||||
title: 'Folder ID',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'ID of the folder to list (optional, leave empty for root folder)',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Search for specific files (e.g., name contains "report")',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
},
|
||||
{
|
||||
id: 'pageSize',
|
||||
title: 'Results Per Page',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Number of results (default: 100, max: 1000)',
|
||||
condition: { field: 'operation', value: 'list' },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['google_drive_upload', 'google_drive_download', 'google_drive_list'],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'upload':
|
||||
return 'google_drive_upload'
|
||||
case 'download':
|
||||
return 'google_drive_download'
|
||||
case 'list':
|
||||
return 'google_drive_list'
|
||||
default:
|
||||
throw new Error(`Invalid Google Drive operation: ${params.operation}`)
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, ...rest } = params
|
||||
// Convert pageSize to number if it exists
|
||||
const pageSize = rest.pageSize ? parseInt(rest.pageSize as string, 10) : undefined
|
||||
return {
|
||||
...rest,
|
||||
pageSize,
|
||||
credential,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', required: true },
|
||||
credential: { type: 'string', required: true },
|
||||
// Upload operation inputs
|
||||
fileName: { type: 'string', required: false },
|
||||
content: { type: 'string', required: false },
|
||||
mimeType: { type: 'string', required: false },
|
||||
// Download operation inputs
|
||||
fileId: { type: 'string', required: false },
|
||||
// List operation inputs
|
||||
folderId: { type: 'string', required: false },
|
||||
query: { type: 'string', required: false },
|
||||
pageSize: { type: 'number', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
content: 'string',
|
||||
metadata: 'json',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { AgentBlock } from './blocks/agent'
|
||||
import { ApiBlock } from './blocks/api'
|
||||
import { ConditionBlock } from './blocks/condition'
|
||||
import { CrewAIVisionBlock } from './blocks/crewai'
|
||||
import { GoogleDriveBlock } from './blocks/drive'
|
||||
import { EvaluatorBlock } from './blocks/evaluator'
|
||||
import { ExaBlock } from './blocks/exa'
|
||||
import { FirecrawlBlock } from './blocks/firecrawl'
|
||||
@@ -49,6 +50,7 @@ export {
|
||||
OpenAIBlock,
|
||||
ExaBlock,
|
||||
RedditBlock,
|
||||
GoogleDriveBlock,
|
||||
}
|
||||
|
||||
// Registry of all block configurations, alphabetically sorted
|
||||
@@ -57,6 +59,7 @@ const blocks: Record<string, BlockConfig> = {
|
||||
api: ApiBlock,
|
||||
condition: ConditionBlock,
|
||||
crewai_vision: CrewAIVisionBlock,
|
||||
google_drive: GoogleDriveBlock,
|
||||
evaluator: EvaluatorBlock,
|
||||
exa: ExaBlock,
|
||||
firecrawl: FirecrawlBlock,
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import { Check } from 'lucide-react'
|
||||
import { GithubIcon, GmailIcon, GoogleIcon, xIcon as XIcon } from '@/components/icons'
|
||||
import {
|
||||
GithubIcon,
|
||||
GmailIcon,
|
||||
GoogleDriveIcon,
|
||||
GoogleIcon,
|
||||
xIcon as XIcon,
|
||||
} from '@/components/icons'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
@@ -28,6 +34,7 @@ const PROVIDER_NAMES: Record<OAuthProvider, string> = {
|
||||
github: 'GitHub',
|
||||
google: 'Google',
|
||||
'google-email': 'Gmail',
|
||||
'google-drive': 'Google Drive',
|
||||
twitter: 'X (Twitter)',
|
||||
}
|
||||
|
||||
@@ -36,6 +43,7 @@ const PROVIDER_ICONS: Record<OAuthProvider, React.FC<React.SVGProps<SVGSVGElemen
|
||||
github: GithubIcon,
|
||||
google: GoogleIcon,
|
||||
'google-email': GmailIcon,
|
||||
'google-drive': GoogleDriveIcon,
|
||||
twitter: XIcon,
|
||||
}
|
||||
|
||||
@@ -44,6 +52,7 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'https://www.googleapis.com/auth/gmail.send': 'Send emails on your behalf',
|
||||
'https://www.googleapis.com/auth/gmail.readonly': 'View and read your email messages',
|
||||
'https://www.googleapis.com/auth/drive': 'View and manage your Google Drive files',
|
||||
'https://www.googleapis.com/auth/drive.file': 'View and manage your Google Drive files',
|
||||
'https://www.googleapis.com/auth/calendar': 'View and manage your calendar',
|
||||
'https://www.googleapis.com/auth/userinfo.email': 'View your email address',
|
||||
'https://www.googleapis.com/auth/userinfo.profile': 'View your basic profile info',
|
||||
|
||||
65
tools/drive/download.ts
Normal file
65
tools/drive/download.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { ToolConfig } from '../types'
|
||||
import { GoogleDriveDownloadResponse, GoogleDriveToolParams } from './types'
|
||||
|
||||
export const downloadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveDownloadResponse> = {
|
||||
id: 'google_drive_download',
|
||||
name: 'Download from Google Drive',
|
||||
description: 'Download a file from Google Drive',
|
||||
version: '1.0',
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
additionalScopes: ['https://www.googleapis.com/auth/drive'],
|
||||
},
|
||||
params: {
|
||||
accessToken: { type: 'string', required: true },
|
||||
fileId: { type: 'string', required: true },
|
||||
},
|
||||
request: {
|
||||
url: (params) => `https://www.googleapis.com/drive/v3/files/${params.fileId}?alt=media`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.error?.message || 'Failed to download file from Google Drive')
|
||||
}
|
||||
|
||||
// Get file metadata
|
||||
const metadataResponse = await fetch(
|
||||
`https://www.googleapis.com/drive/v3/files/${response.url.split('files/')[1].split('?')[0]}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: response.headers.get('Authorization') || '',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const metadata = await metadataResponse.json()
|
||||
const content = await response.text()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: metadata.id,
|
||||
name: metadata.name,
|
||||
mimeType: metadata.mimeType,
|
||||
webViewLink: metadata.webViewLink,
|
||||
webContentLink: metadata.webContentLink,
|
||||
size: metadata.size,
|
||||
createdTime: metadata.createdTime,
|
||||
modifiedTime: metadata.modifiedTime,
|
||||
parents: metadata.parents,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
transformError: (error) => {
|
||||
return error.message || 'An error occurred while downloading from Google Drive'
|
||||
},
|
||||
}
|
||||
113
tools/drive/export.ts
Normal file
113
tools/drive/export.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { ToolConfig } from '../types'
|
||||
import { GoogleDriveDownloadResponse } from './types'
|
||||
import { GoogleDriveToolParams } from './types'
|
||||
|
||||
export const exportTool: ToolConfig<
|
||||
GoogleDriveToolParams & { mimeType?: string },
|
||||
GoogleDriveDownloadResponse
|
||||
> = {
|
||||
id: 'google_drive_export',
|
||||
name: 'Export from Google Drive',
|
||||
description: 'Export a Google Workspace file (Docs, Sheets, Slides) from Google Drive',
|
||||
version: '1.0',
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
additionalScopes: ['https://www.googleapis.com/auth/drive'],
|
||||
},
|
||||
params: {
|
||||
accessToken: { type: 'string', required: true },
|
||||
fileId: { type: 'string', required: true },
|
||||
mimeType: { type: 'string', required: false },
|
||||
},
|
||||
request: {
|
||||
url: (params) => {
|
||||
const exportMimeType = params.mimeType || 'application/pdf'
|
||||
return `https://www.googleapis.com/drive/v3/files/${params.fileId}/export?mimeType=${encodeURIComponent(exportMimeType)}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
console.error('Google Drive export error:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error,
|
||||
fileId: response.url.split('files/')[1]?.split('?')[0],
|
||||
})
|
||||
throw new Error(
|
||||
`Failed to export file from Google Drive: ${response.status} ${response.statusText} - ${error.error?.message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
// Get file metadata
|
||||
const fileId = response.url.split('files/')[1]?.split('?')[0]
|
||||
const metadataResponse = await fetch(`https://www.googleapis.com/drive/v3/files/${fileId}`, {
|
||||
headers: {
|
||||
Authorization: response.headers.get('Authorization') || '',
|
||||
},
|
||||
})
|
||||
|
||||
if (!metadataResponse.ok) {
|
||||
const metadataError = await metadataResponse.json()
|
||||
console.error('Google Drive metadata error:', {
|
||||
status: metadataResponse.status,
|
||||
statusText: metadataResponse.statusText,
|
||||
error: metadataError,
|
||||
})
|
||||
throw new Error(
|
||||
`Failed to get file metadata: ${metadataResponse.status} ${metadataResponse.statusText} - ${metadataError.error?.message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const metadata = await metadataResponse.json()
|
||||
let content
|
||||
try {
|
||||
content = await response.text()
|
||||
} catch (error: any) {
|
||||
console.error('Error reading response content:', {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
error: JSON.stringify(error),
|
||||
})
|
||||
throw new Error(`Failed to read file content: ${error?.message || 'Unknown error'}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
content,
|
||||
metadata: {
|
||||
id: metadata.id,
|
||||
name: metadata.name,
|
||||
mimeType: metadata.mimeType,
|
||||
webViewLink: metadata.webViewLink,
|
||||
webContentLink: metadata.webContentLink,
|
||||
size: metadata.size,
|
||||
createdTime: metadata.createdTime,
|
||||
modifiedTime: metadata.modifiedTime,
|
||||
parents: metadata.parents,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
transformError: (error: any) => {
|
||||
console.error('Export tool error:', {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
error: JSON.stringify(error, null, 2),
|
||||
})
|
||||
if (typeof error === 'string') {
|
||||
return error
|
||||
}
|
||||
return (
|
||||
error.message ||
|
||||
JSON.stringify(error, null, 2) ||
|
||||
'An error occurred while exporting from Google Drive'
|
||||
)
|
||||
},
|
||||
}
|
||||
79
tools/drive/list.ts
Normal file
79
tools/drive/list.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ToolConfig } from '../types'
|
||||
import { GoogleDriveListResponse, GoogleDriveToolParams } from './types'
|
||||
|
||||
export const listTool: ToolConfig<GoogleDriveToolParams, GoogleDriveListResponse> = {
|
||||
id: 'google_drive_list',
|
||||
name: 'List Google Drive Files',
|
||||
description: 'List files and folders in Google Drive',
|
||||
version: '1.0',
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
additionalScopes: ['https://www.googleapis.com/auth/drive'],
|
||||
},
|
||||
params: {
|
||||
accessToken: { type: 'string', required: true },
|
||||
folderId: { type: 'string', required: false },
|
||||
query: { type: 'string', required: false },
|
||||
pageSize: { type: 'number', required: false },
|
||||
pageToken: { type: 'string', required: false },
|
||||
},
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://www.googleapis.com/drive/v3/files')
|
||||
url.searchParams.append(
|
||||
'fields',
|
||||
'files(id,name,mimeType,webViewLink,webContentLink,size,createdTime,modifiedTime,parents),nextPageToken'
|
||||
)
|
||||
|
||||
if (params.folderId) {
|
||||
url.searchParams.append('q', `'${params.folderId}' in parents`)
|
||||
}
|
||||
if (params.query) {
|
||||
const existingQ = url.searchParams.get('q')
|
||||
const queryPart = `name contains '${params.query}'`
|
||||
url.searchParams.set('q', existingQ ? `${existingQ} and ${queryPart}` : queryPart)
|
||||
}
|
||||
if (params.pageSize) {
|
||||
url.searchParams.append('pageSize', 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 list Google Drive files')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
files: data.files.map((file: any) => ({
|
||||
id: file.id,
|
||||
name: file.name,
|
||||
mimeType: file.mimeType,
|
||||
webViewLink: file.webViewLink,
|
||||
webContentLink: file.webContentLink,
|
||||
size: file.size,
|
||||
createdTime: file.createdTime,
|
||||
modifiedTime: file.modifiedTime,
|
||||
parents: file.parents,
|
||||
})),
|
||||
nextPageToken: data.nextPageToken,
|
||||
},
|
||||
}
|
||||
},
|
||||
transformError: (error) => {
|
||||
return error.message || 'An error occurred while listing Google Drive files'
|
||||
},
|
||||
}
|
||||
45
tools/drive/types.ts
Normal file
45
tools/drive/types.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { ToolResponse } from '../types'
|
||||
|
||||
export interface GoogleDriveFile {
|
||||
id: string
|
||||
name: string
|
||||
mimeType: string
|
||||
webViewLink?: string
|
||||
webContentLink?: string
|
||||
size?: string
|
||||
createdTime?: string
|
||||
modifiedTime?: string
|
||||
parents?: string[]
|
||||
}
|
||||
|
||||
export interface GoogleDriveListResponse extends ToolResponse {
|
||||
output: {
|
||||
files: GoogleDriveFile[]
|
||||
nextPageToken?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleDriveUploadResponse extends ToolResponse {
|
||||
output: {
|
||||
file: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleDriveDownloadResponse extends ToolResponse {
|
||||
output: {
|
||||
content: string
|
||||
metadata: GoogleDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export interface GoogleDriveToolParams {
|
||||
accessToken: string
|
||||
folderId?: string
|
||||
fileId?: string
|
||||
fileName?: string
|
||||
content?: string
|
||||
mimeType?: string
|
||||
query?: string
|
||||
pageSize?: number
|
||||
pageToken?: string
|
||||
}
|
||||
77
tools/drive/upload.ts
Normal file
77
tools/drive/upload.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { ToolConfig } from '../types'
|
||||
import { GoogleDriveToolParams, GoogleDriveUploadResponse } from './types'
|
||||
|
||||
export const uploadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveUploadResponse> = {
|
||||
id: 'google_drive_upload',
|
||||
name: 'Upload to Google Drive',
|
||||
description: 'Upload a file to Google Drive',
|
||||
version: '1.0',
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
additionalScopes: ['https://www.googleapis.com/auth/drive.file'],
|
||||
},
|
||||
params: {
|
||||
accessToken: { type: 'string', required: true },
|
||||
fileName: { type: 'string', required: true },
|
||||
content: { type: 'string', required: true },
|
||||
mimeType: { type: 'string', required: false },
|
||||
folderId: { type: 'string', required: false },
|
||||
},
|
||||
request: {
|
||||
url: 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'multipart/related; boundary=boundary',
|
||||
}),
|
||||
body: (params) => {
|
||||
const metadata = {
|
||||
name: params.fileName,
|
||||
...(params.folderId ? { parents: [params.folderId] } : {}),
|
||||
}
|
||||
|
||||
const mimeType = params.mimeType || 'text/plain'
|
||||
|
||||
const body = `--boundary
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
|
||||
${JSON.stringify(metadata)}
|
||||
|
||||
--boundary
|
||||
Content-Type: ${mimeType}
|
||||
|
||||
${params.content}
|
||||
--boundary--`
|
||||
|
||||
return { body }
|
||||
},
|
||||
},
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || 'Failed to upload file to Google Drive')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
mimeType: data.mimeType,
|
||||
webViewLink: data.webViewLink,
|
||||
webContentLink: data.webContentLink,
|
||||
size: data.size,
|
||||
createdTime: data.createdTime,
|
||||
modifiedTime: data.modifiedTime,
|
||||
parents: data.parents,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
transformError: (error) => {
|
||||
return error.message || 'An error occurred while uploading to Google Drive'
|
||||
},
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useCustomToolsStore } from '@/stores/custom-tools/store'
|
||||
import { useEnvironmentStore } from '@/stores/settings/environment/store'
|
||||
import { visionTool as crewAIVision } from './crewai/vision'
|
||||
import { downloadTool as driveDownloadTool } from './drive/download'
|
||||
import { listTool as driveListTool } from './drive/list'
|
||||
import { uploadTool as driveUploadTool } from './drive/upload'
|
||||
import { answerTool as exaAnswer } from './exa/answer'
|
||||
import { findSimilarLinksTool as exaFindSimilarLinks } from './exa/findSimilarLinks'
|
||||
import { getContentsTool as exaGetContents } from './exa/getContents'
|
||||
@@ -77,6 +80,9 @@ export const tools: Record<string, ToolConfig> = {
|
||||
exa_find_similar_links: exaFindSimilarLinks,
|
||||
exa_answer: exaAnswer,
|
||||
reddit_hot_posts: redditHotPosts,
|
||||
google_drive_download: driveDownloadTool,
|
||||
google_drive_list: driveListTool,
|
||||
google_drive_upload: driveUploadTool,
|
||||
}
|
||||
|
||||
// Get a tool by its ID
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
||||
export type OAuthProvider = 'google' | 'google-email' | 'github' | 'twitter'
|
||||
export type OAuthProvider = 'google' | 'google-email' | 'google-drive' | 'github' | 'twitter'
|
||||
|
||||
export interface ToolResponse {
|
||||
success: boolean // Whether the tool execution was successful
|
||||
|
||||
Reference in New Issue
Block a user