chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import type { DropboxCopyParams, DropboxCopyResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxCopyTool: ToolConfig<DropboxCopyParams, DropboxCopyResponse> = {
|
||||
id: 'dropbox_copy',
|
||||
name: 'Dropbox Copy',
|
||||
description: 'Copy a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
fromPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The source path of the file or folder to copy',
|
||||
},
|
||||
toPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The destination path for the copied file or folder',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the file if there is a conflict at destination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/copy_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
from_path: params.fromPath.trim(),
|
||||
to_path: params.toPath.trim(),
|
||||
autorename: params.autorename ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to copy file/folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data.metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata of the copied item',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file or folder' },
|
||||
id: { type: 'string', description: 'Unique identifier', optional: true },
|
||||
name: { type: 'string', description: 'Name of the copied item' },
|
||||
path_display: { type: 'string', description: 'Display path', optional: true },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import type { DropboxCreateFolderParams, DropboxCreateFolderResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxCreateFolderTool: ToolConfig<
|
||||
DropboxCreateFolderParams,
|
||||
DropboxCreateFolderResponse
|
||||
> = {
|
||||
id: 'dropbox_create_folder',
|
||||
name: 'Dropbox Create Folder',
|
||||
description: 'Create a new folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path where the folder should be created (e.g., /new-folder)',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the folder if there is a conflict',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/create_folder_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path.trim(),
|
||||
autorename: params.autorename ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to create folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
folder: data.metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
folder: {
|
||||
type: 'object',
|
||||
description: 'The created folder metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for the folder' },
|
||||
name: { type: 'string', description: 'Name of the folder' },
|
||||
path_display: { type: 'string', description: 'Display path of the folder', optional: true },
|
||||
path_lower: { type: 'string', description: 'Lowercase path of the folder', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import type {
|
||||
DropboxCreateSharedLinkParams,
|
||||
DropboxCreateSharedLinkResponse,
|
||||
} from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxCreateSharedLinkTool: ToolConfig<
|
||||
DropboxCreateSharedLinkParams,
|
||||
DropboxCreateSharedLinkResponse
|
||||
> = {
|
||||
id: 'dropbox_create_shared_link',
|
||||
name: 'Dropbox Create Shared Link',
|
||||
description: 'Create a shareable link for a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file or folder to share',
|
||||
},
|
||||
requestedVisibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Visibility: public, team_only, or password',
|
||||
},
|
||||
linkPassword: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for the shared link (only if visibility is password)',
|
||||
},
|
||||
expires: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Expiration date in ISO 8601 format (e.g., 2025-12-31T23:59:59Z)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
path: params.path.trim(),
|
||||
}
|
||||
|
||||
const settings: Record<string, any> = {}
|
||||
|
||||
if (params.requestedVisibility) {
|
||||
settings.requested_visibility = { '.tag': params.requestedVisibility }
|
||||
}
|
||||
|
||||
if (params.linkPassword) {
|
||||
settings.link_password = params.linkPassword
|
||||
}
|
||||
|
||||
if (params.expires) {
|
||||
settings.expires = params.expires
|
||||
}
|
||||
|
||||
if (Object.keys(settings).length > 0) {
|
||||
body.settings = settings
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
// A link may already exist for this path - Dropbox includes its metadata in the error
|
||||
// when the requested settings match the existing link, so surface it as a success.
|
||||
// Per Stone's JSON rules, a union member whose payload is a struct (SharedLinkMetadata)
|
||||
// flattens that struct's fields alongside ".tag" - there is no nested "metadata" key.
|
||||
const existingLink = data.error?.shared_link_already_exists
|
||||
if (existingLink) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sharedLink: existingLink,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (data.error_summary?.includes('shared_link_already_exists')) {
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
'A shared link already exists for this path with different settings. Use Dropbox List Shared Links to retrieve it.',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to create shared link',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sharedLink: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sharedLink: {
|
||||
type: 'object',
|
||||
description: 'The created shared link',
|
||||
properties: {
|
||||
url: { type: 'string', description: 'The shared link URL' },
|
||||
name: { type: 'string', description: 'Name of the shared item' },
|
||||
path_lower: {
|
||||
type: 'string',
|
||||
description: 'Lowercase path of the shared item',
|
||||
optional: true,
|
||||
},
|
||||
expires: { type: 'string', description: 'Expiration date if set', optional: true },
|
||||
link_permissions: {
|
||||
type: 'object',
|
||||
description: 'Permissions for the shared link',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import type { DropboxDeleteParams, DropboxDeleteResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxDeleteTool: ToolConfig<DropboxDeleteParams, DropboxDeleteResponse> = {
|
||||
id: 'dropbox_delete',
|
||||
name: 'Dropbox Delete',
|
||||
description: 'Delete a file or folder in Dropbox (moves to trash)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file or folder to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/delete_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path.trim(),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to delete file/folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data.metadata,
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata of the deleted item',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file, folder, or deleted' },
|
||||
name: { type: 'string', description: 'Name of the deleted item' },
|
||||
path_display: { type: 'string', description: 'Display path', optional: true },
|
||||
},
|
||||
},
|
||||
deleted: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the deletion was successful',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { httpHeaderSafeJson } from '@/lib/core/utils/validation'
|
||||
import type { DropboxDownloadParams, DropboxDownloadResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxDownloadTool: ToolConfig<DropboxDownloadParams, DropboxDownloadResponse> = {
|
||||
id: 'dropbox_download',
|
||||
name: 'Dropbox Download File',
|
||||
description: 'Download a file from Dropbox with metadata and content',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file to download (e.g., /folder/document.pdf)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://content.dropboxapi.com/2/files/download',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Dropbox-API-Arg': httpHeaderSafeJson({ path: params.path.trim() }),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
return {
|
||||
success: false,
|
||||
error: errorText || 'Failed to download file',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
const apiResultHeader =
|
||||
response.headers.get('dropbox-api-result') || response.headers.get('Dropbox-API-Result')
|
||||
const metadata = apiResultHeader ? JSON.parse(apiResultHeader) : undefined
|
||||
const contentType = response.headers.get('content-type') || 'application/octet-stream'
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
const buffer = Buffer.from(arrayBuffer)
|
||||
const resolvedName = metadata?.name || params?.path?.split('/').pop() || 'download'
|
||||
|
||||
let temporaryLink: string | undefined
|
||||
if (params?.accessToken) {
|
||||
try {
|
||||
const linkResponse = await fetch('https://api.dropboxapi.com/2/files/get_temporary_link', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ path: params.path.trim() }),
|
||||
})
|
||||
if (linkResponse.ok) {
|
||||
const linkData = await linkResponse.json()
|
||||
temporaryLink = linkData.link
|
||||
}
|
||||
} catch {
|
||||
temporaryLink = undefined
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
name: resolvedName,
|
||||
mimeType: contentType,
|
||||
data: buffer.toString('base64'),
|
||||
size: buffer.length,
|
||||
},
|
||||
content: buffer.toString('base64'),
|
||||
metadata,
|
||||
temporaryLink,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'file',
|
||||
description: 'Downloaded file stored in execution files',
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'The file metadata',
|
||||
},
|
||||
temporaryLink: {
|
||||
type: 'string',
|
||||
description: 'Temporary link to download the file (valid for ~4 hours)',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Base64 encoded file content (if fetched)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import type { DropboxGetMetadataParams, DropboxGetMetadataResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxGetMetadataTool: ToolConfig<
|
||||
DropboxGetMetadataParams,
|
||||
DropboxGetMetadataResponse
|
||||
> = {
|
||||
id: 'dropbox_get_metadata',
|
||||
name: 'Dropbox Get Metadata',
|
||||
description: 'Get metadata for a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file or folder to get metadata for',
|
||||
},
|
||||
includeMediaInfo: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include media info for photos/videos',
|
||||
},
|
||||
includeDeleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include deleted files in results',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/get_metadata',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path.trim(),
|
||||
include_media_info: params.includeMediaInfo ?? false,
|
||||
include_deleted: params.includeDeleted ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to get metadata',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata for the file or folder',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file, folder, or deleted' },
|
||||
id: { type: 'string', description: 'Unique identifier', optional: true },
|
||||
name: { type: 'string', description: 'Name of the item' },
|
||||
path_display: { type: 'string', description: 'Display path', optional: true },
|
||||
path_lower: { type: 'string', description: 'Lowercase path', optional: true },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)', optional: true },
|
||||
client_modified: {
|
||||
type: 'string',
|
||||
description: 'Client modification time (files only)',
|
||||
optional: true,
|
||||
},
|
||||
server_modified: {
|
||||
type: 'string',
|
||||
description: 'Server modification time (files only)',
|
||||
optional: true,
|
||||
},
|
||||
rev: { type: 'string', description: 'Revision identifier (files only)', optional: true },
|
||||
content_hash: {
|
||||
type: 'string',
|
||||
description: 'Content hash (files only)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { dropboxCopyTool } from '@/tools/dropbox/copy'
|
||||
import { dropboxCreateFolderTool } from '@/tools/dropbox/create_folder'
|
||||
import { dropboxCreateSharedLinkTool } from '@/tools/dropbox/create_shared_link'
|
||||
import { dropboxDeleteTool } from '@/tools/dropbox/delete'
|
||||
import { dropboxDownloadTool } from '@/tools/dropbox/download'
|
||||
import { dropboxGetMetadataTool } from '@/tools/dropbox/get_metadata'
|
||||
import { dropboxListFolderTool } from '@/tools/dropbox/list_folder'
|
||||
import { dropboxListRevisionsTool } from '@/tools/dropbox/list_revisions'
|
||||
import { dropboxListSharedLinksTool } from '@/tools/dropbox/list_shared_links'
|
||||
import { dropboxMoveTool } from '@/tools/dropbox/move'
|
||||
import { dropboxRestoreTool } from '@/tools/dropbox/restore'
|
||||
import { dropboxSearchTool } from '@/tools/dropbox/search'
|
||||
import { dropboxUploadTool } from '@/tools/dropbox/upload'
|
||||
|
||||
export {
|
||||
dropboxCopyTool,
|
||||
dropboxCreateFolderTool,
|
||||
dropboxCreateSharedLinkTool,
|
||||
dropboxDeleteTool,
|
||||
dropboxDownloadTool,
|
||||
dropboxGetMetadataTool,
|
||||
dropboxListFolderTool,
|
||||
dropboxListRevisionsTool,
|
||||
dropboxListSharedLinksTool,
|
||||
dropboxMoveTool,
|
||||
dropboxRestoreTool,
|
||||
dropboxSearchTool,
|
||||
dropboxUploadTool,
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import type { DropboxListFolderParams, DropboxListFolderResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxListFolderTool: ToolConfig<DropboxListFolderParams, DropboxListFolderResponse> =
|
||||
{
|
||||
id: 'dropbox_list_folder',
|
||||
name: 'Dropbox List Folder',
|
||||
description: 'List the contents of a folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the folder to list (use "" for root)',
|
||||
},
|
||||
recursive: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, list contents recursively',
|
||||
},
|
||||
includeDeleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include deleted files/folders',
|
||||
},
|
||||
includeMediaInfo: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include media info for photos/videos',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return (default: 500)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/list_folder',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path.trim() === '/' ? '' : params.path.trim(),
|
||||
recursive: params.recursive ?? false,
|
||||
include_deleted: params.includeDeleted ?? false,
|
||||
include_media_info: params.includeMediaInfo ?? false,
|
||||
limit: params.limit,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to list folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
entries: data.entries,
|
||||
cursor: data.cursor,
|
||||
hasMore: data.has_more,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
entries: {
|
||||
type: 'array',
|
||||
description: 'List of files and folders in the directory',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file, folder, or deleted' },
|
||||
id: { type: 'string', description: 'Unique identifier', optional: true },
|
||||
name: { type: 'string', description: 'Name of the file/folder' },
|
||||
path_display: { type: 'string', description: 'Display path', optional: true },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for pagination',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
DropboxListRevisionsParams,
|
||||
DropboxListRevisionsResponse,
|
||||
} from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxListRevisionsTool: ToolConfig<
|
||||
DropboxListRevisionsParams,
|
||||
DropboxListRevisionsResponse
|
||||
> = {
|
||||
id: 'dropbox_list_revisions',
|
||||
name: 'Dropbox List Revisions',
|
||||
description: 'List the revision history for a file in Dropbox (files only, not folders)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file to list revisions for',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of revisions to return, 1-100 (default: 10)',
|
||||
},
|
||||
beforeRev: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Only return revisions before this one. Pass the rev of the last revision from a previous call to fetch the next page.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/list_revisions',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
path: params.path.trim(),
|
||||
mode: 'path',
|
||||
limit: params.limit ?? 10,
|
||||
}
|
||||
if (params.beforeRev) {
|
||||
body.before_rev = params.beforeRev.trim()
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to list revisions',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
entries: data.entries || [],
|
||||
isDeleted: data.is_deleted ?? false,
|
||||
hasMore: data.has_more ?? false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
entries: {
|
||||
type: 'array',
|
||||
description: 'The revisions for the file, most recent first',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for this revision' },
|
||||
name: { type: 'string', description: 'Name of the file' },
|
||||
path_display: { type: 'string', description: 'Display path', optional: true },
|
||||
rev: { type: 'string', description: 'Revision identifier, pass to Restore' },
|
||||
size: { type: 'number', description: 'Size of this revision in bytes' },
|
||||
server_modified: { type: 'string', description: 'Server modification time' },
|
||||
},
|
||||
},
|
||||
},
|
||||
isDeleted: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the file identified by the latest revision is deleted or moved',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more revisions available',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import type {
|
||||
DropboxListSharedLinksParams,
|
||||
DropboxListSharedLinksResponse,
|
||||
} from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxListSharedLinksTool: ToolConfig<
|
||||
DropboxListSharedLinksParams,
|
||||
DropboxListSharedLinksResponse
|
||||
> = {
|
||||
id: 'dropbox_list_shared_links',
|
||||
name: 'Dropbox List Shared Links',
|
||||
description: 'List shared links for a path, or for the entire account if no path is given',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Path to list shared links for. If omitted, lists all shared links.',
|
||||
},
|
||||
directOnly: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, only return links directly to the path, not parent folder links',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Cursor from a previous call to fetch the next page of results',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/sharing/list_shared_links',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.path) {
|
||||
const trimmedPath = params.path.trim()
|
||||
// Dropbox only returns every shared link on the account when `path` is omitted
|
||||
// entirely; sending "" scopes the results to the root folder instead. Since our UI
|
||||
// tells users "/" means "list all links", omit the field rather than sending "".
|
||||
if (trimmedPath !== '/' && trimmedPath !== '') {
|
||||
body.path = trimmedPath
|
||||
}
|
||||
}
|
||||
if (params.directOnly !== undefined) body.direct_only = params.directOnly
|
||||
if (params.cursor) body.cursor = params.cursor
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to list shared links',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
links: data.links ?? [],
|
||||
hasMore: data.has_more ?? false,
|
||||
cursor: data.cursor,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
links: {
|
||||
type: 'array',
|
||||
description: 'Shared links applicable to the path argument',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file or folder' },
|
||||
url: { type: 'string', description: 'The shared link URL' },
|
||||
name: { type: 'string', description: 'Name of the shared item' },
|
||||
path_lower: {
|
||||
type: 'string',
|
||||
description: 'Lowercase path of the shared item',
|
||||
optional: true,
|
||||
},
|
||||
expires: { type: 'string', description: 'Expiration date if set', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for pagination (only returned when no path is given)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import type { DropboxMoveParams, DropboxMoveResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxMoveTool: ToolConfig<DropboxMoveParams, DropboxMoveResponse> = {
|
||||
id: 'dropbox_move',
|
||||
name: 'Dropbox Move',
|
||||
description: 'Move or rename a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
fromPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The source path of the file or folder to move',
|
||||
},
|
||||
toPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The destination path for the moved file or folder',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the file if there is a conflict at destination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/move_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
from_path: params.fromPath.trim(),
|
||||
to_path: params.toPath.trim(),
|
||||
autorename: params.autorename ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to move file/folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data.metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata of the moved item',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file or folder' },
|
||||
id: { type: 'string', description: 'Unique identifier', optional: true },
|
||||
name: { type: 'string', description: 'Name of the moved item' },
|
||||
path_display: { type: 'string', description: 'Display path', optional: true },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import type { DropboxRestoreParams, DropboxRestoreResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxRestoreTool: ToolConfig<DropboxRestoreParams, DropboxRestoreResponse> = {
|
||||
id: 'dropbox_restore',
|
||||
name: 'Dropbox Restore',
|
||||
description: 'Restore a specific revision of a file to the given path',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path to save the restored file to',
|
||||
},
|
||||
rev: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The revision identifier to restore (from Dropbox List Revisions)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/restore',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path.trim(),
|
||||
rev: params.rev.trim(),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to restore file',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata of the restored file',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for the file' },
|
||||
name: { type: 'string', description: 'Name of the file' },
|
||||
path_display: { type: 'string', description: 'Display path of the file', optional: true },
|
||||
path_lower: { type: 'string', description: 'Lowercase path of the file', optional: true },
|
||||
size: { type: 'number', description: 'Size of the file in bytes' },
|
||||
rev: { type: 'string', description: 'Revision identifier of the restored file' },
|
||||
server_modified: { type: 'string', description: 'Server modification time' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import type { DropboxSearchParams, DropboxSearchResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxSearchTool: ToolConfig<DropboxSearchParams, DropboxSearchResponse> = {
|
||||
id: 'dropbox_search',
|
||||
name: 'Dropbox Search',
|
||||
description: 'Search for files and folders in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The search query',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Dropbox folder path to limit search scope (e.g., /folder/subfolder)',
|
||||
},
|
||||
fileExtensions: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Comma-separated list of file extensions to filter by (e.g., pdf,xlsx)',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return (default: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/search_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
query: params.query,
|
||||
}
|
||||
|
||||
const options: Record<string, any> = {}
|
||||
|
||||
if (params.path) {
|
||||
const trimmedPath = params.path.trim()
|
||||
options.path = trimmedPath === '/' ? '' : trimmedPath
|
||||
}
|
||||
|
||||
if (params.fileExtensions) {
|
||||
const extensions = params.fileExtensions
|
||||
.split(',')
|
||||
.map((ext) => ext.trim())
|
||||
.filter((ext) => ext.length > 0)
|
||||
if (extensions.length > 0) {
|
||||
options.file_extensions = extensions
|
||||
}
|
||||
}
|
||||
|
||||
if (params.maxResults) {
|
||||
options.max_results = params.maxResults
|
||||
}
|
||||
|
||||
if (Object.keys(options).length > 0) {
|
||||
body.options = options
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to search files',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
matches: data.matches || [],
|
||||
hasMore: data.has_more || false,
|
||||
cursor: data.cursor,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
matches: {
|
||||
type: 'array',
|
||||
description: 'Search results',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
match_type: {
|
||||
type: 'object',
|
||||
description: 'Type of match: filename, content, or both',
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'File or folder metadata',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for pagination',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
import type { UserFileLike } from '@/lib/core/utils/user-file'
|
||||
import type { ToolFileData, ToolResponse } from '@/tools/types'
|
||||
|
||||
// ===== Core Types =====
|
||||
|
||||
interface DropboxFileMetadata {
|
||||
'.tag': 'file'
|
||||
id: string
|
||||
name: string
|
||||
path_display?: string
|
||||
path_lower?: string
|
||||
size: number
|
||||
client_modified: string
|
||||
server_modified: string
|
||||
rev: string
|
||||
content_hash?: string
|
||||
is_downloadable?: boolean
|
||||
}
|
||||
|
||||
interface DropboxFolderMetadata {
|
||||
'.tag': 'folder'
|
||||
id: string
|
||||
name: string
|
||||
path_display?: string
|
||||
path_lower?: string
|
||||
}
|
||||
|
||||
interface DropboxDeletedMetadata {
|
||||
'.tag': 'deleted'
|
||||
name: string
|
||||
path_display?: string
|
||||
path_lower?: string
|
||||
}
|
||||
|
||||
export type DropboxMetadata = DropboxFileMetadata | DropboxFolderMetadata | DropboxDeletedMetadata
|
||||
|
||||
interface DropboxSharedLinkMetadata {
|
||||
url: string
|
||||
name: string
|
||||
path_lower: string
|
||||
link_permissions: {
|
||||
can_revoke: boolean
|
||||
resolved_visibility: {
|
||||
'.tag': 'public' | 'team_only' | 'password' | 'team_and_password' | 'shared_folder_only'
|
||||
}
|
||||
revoke_failure_reason?: {
|
||||
'.tag': string
|
||||
}
|
||||
}
|
||||
expires?: string
|
||||
id?: string
|
||||
}
|
||||
|
||||
interface DropboxSearchMatch {
|
||||
match_type: {
|
||||
'.tag': 'filename' | 'content' | 'both'
|
||||
}
|
||||
metadata: {
|
||||
'.tag': 'metadata'
|
||||
metadata: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Base Params =====
|
||||
|
||||
interface DropboxBaseParams {
|
||||
accessToken?: string
|
||||
}
|
||||
|
||||
// ===== Upload Params =====
|
||||
|
||||
export interface DropboxUploadParams extends DropboxBaseParams {
|
||||
path: string
|
||||
file?: UserFileLike
|
||||
// Legacy field for backwards compatibility
|
||||
fileContent?: string
|
||||
fileName?: string
|
||||
mode?: 'add' | 'overwrite'
|
||||
autorename?: boolean
|
||||
mute?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxUploadResponse extends ToolResponse {
|
||||
output: {
|
||||
file?: DropboxFileMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Download Params =====
|
||||
|
||||
export interface DropboxDownloadParams extends DropboxBaseParams {
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface DropboxDownloadResponse extends ToolResponse {
|
||||
output: {
|
||||
file?: ToolFileData
|
||||
content?: string // Base64 encoded file content
|
||||
metadata?: DropboxFileMetadata
|
||||
temporaryLink?: string
|
||||
}
|
||||
}
|
||||
|
||||
// ===== List Folder Params =====
|
||||
|
||||
export interface DropboxListFolderParams extends DropboxBaseParams {
|
||||
path: string
|
||||
recursive?: boolean
|
||||
includeDeleted?: boolean
|
||||
includeMediaInfo?: boolean
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface DropboxListFolderResponse extends ToolResponse {
|
||||
output: {
|
||||
entries?: DropboxMetadata[]
|
||||
cursor?: string
|
||||
hasMore?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Create Folder Params =====
|
||||
|
||||
export interface DropboxCreateFolderParams extends DropboxBaseParams {
|
||||
path: string
|
||||
autorename?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxCreateFolderResponse extends ToolResponse {
|
||||
output: {
|
||||
folder?: DropboxFolderMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Delete Params =====
|
||||
|
||||
export interface DropboxDeleteParams extends DropboxBaseParams {
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface DropboxDeleteResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
deleted?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Copy Params =====
|
||||
|
||||
export interface DropboxCopyParams extends DropboxBaseParams {
|
||||
fromPath: string
|
||||
toPath: string
|
||||
autorename?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxCopyResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Move Params =====
|
||||
|
||||
export interface DropboxMoveParams extends DropboxBaseParams {
|
||||
fromPath: string
|
||||
toPath: string
|
||||
autorename?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxMoveResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Get Metadata Params =====
|
||||
|
||||
export interface DropboxGetMetadataParams extends DropboxBaseParams {
|
||||
path: string
|
||||
includeMediaInfo?: boolean
|
||||
includeDeleted?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxGetMetadataResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Create Shared Link Params =====
|
||||
|
||||
export interface DropboxCreateSharedLinkParams extends DropboxBaseParams {
|
||||
path: string
|
||||
requestedVisibility?: 'public' | 'team_only' | 'password'
|
||||
linkPassword?: string
|
||||
expires?: string
|
||||
}
|
||||
|
||||
export interface DropboxCreateSharedLinkResponse extends ToolResponse {
|
||||
output: {
|
||||
sharedLink?: DropboxSharedLinkMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Search Params =====
|
||||
|
||||
export interface DropboxSearchParams extends DropboxBaseParams {
|
||||
query: string
|
||||
path?: string
|
||||
fileExtensions?: string
|
||||
maxResults?: number
|
||||
}
|
||||
|
||||
export interface DropboxSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
matches?: DropboxSearchMatch[]
|
||||
hasMore?: boolean
|
||||
cursor?: string
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Get Temporary Link Params =====
|
||||
|
||||
interface DropboxGetTemporaryLinkParams extends DropboxBaseParams {
|
||||
path: string
|
||||
}
|
||||
|
||||
interface DropboxGetTemporaryLinkResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxFileMetadata
|
||||
link?: string
|
||||
}
|
||||
}
|
||||
|
||||
// ===== List Shared Links Params =====
|
||||
|
||||
export interface DropboxListSharedLinksParams extends DropboxBaseParams {
|
||||
path?: string
|
||||
directOnly?: boolean
|
||||
cursor?: string
|
||||
}
|
||||
|
||||
export interface DropboxListSharedLinksResponse extends ToolResponse {
|
||||
output: {
|
||||
links?: DropboxSharedLinkMetadata[]
|
||||
hasMore?: boolean
|
||||
cursor?: string
|
||||
}
|
||||
}
|
||||
|
||||
// ===== List Revisions Params =====
|
||||
|
||||
interface DropboxFileRevision {
|
||||
'.tag': 'file'
|
||||
id: string
|
||||
name: string
|
||||
path_display?: string
|
||||
path_lower?: string
|
||||
size: number
|
||||
rev: string
|
||||
server_modified: string
|
||||
}
|
||||
|
||||
export interface DropboxListRevisionsParams extends DropboxBaseParams {
|
||||
path: string
|
||||
limit?: number
|
||||
beforeRev?: string
|
||||
}
|
||||
|
||||
export interface DropboxListRevisionsResponse extends ToolResponse {
|
||||
output: {
|
||||
entries?: DropboxFileRevision[]
|
||||
isDeleted?: boolean
|
||||
hasMore?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Restore Params =====
|
||||
|
||||
export interface DropboxRestoreParams extends DropboxBaseParams {
|
||||
path: string
|
||||
rev: string
|
||||
}
|
||||
|
||||
export interface DropboxRestoreResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxFileMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Combined Response Type =====
|
||||
|
||||
export type DropboxResponse =
|
||||
| DropboxUploadResponse
|
||||
| DropboxDownloadResponse
|
||||
| DropboxListFolderResponse
|
||||
| DropboxCreateFolderResponse
|
||||
| DropboxDeleteResponse
|
||||
| DropboxCopyResponse
|
||||
| DropboxMoveResponse
|
||||
| DropboxGetMetadataResponse
|
||||
| DropboxCreateSharedLinkResponse
|
||||
| DropboxSearchResponse
|
||||
| DropboxGetTemporaryLinkResponse
|
||||
| DropboxListSharedLinksResponse
|
||||
| DropboxListRevisionsResponse
|
||||
| DropboxRestoreResponse
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { DropboxUploadParams, DropboxUploadResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxUploadTool: ToolConfig<DropboxUploadParams, DropboxUploadResponse> = {
|
||||
id: 'dropbox_upload',
|
||||
name: 'Dropbox Upload File',
|
||||
description: 'Upload a file to Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The path in Dropbox where the file should be saved (e.g., /folder/document.pdf)',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The file to upload (UserFile object)',
|
||||
},
|
||||
// Legacy field for backwards compatibility - hidden from UI
|
||||
fileContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Legacy: base64 encoded file content',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional filename (used if path is a folder)',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Write mode: add (default) or overwrite',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the file if there is a conflict',
|
||||
},
|
||||
mute: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: "If true, don't notify the user about this upload",
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/dropbox/upload',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
accessToken: params.accessToken,
|
||||
path: params.path.trim(),
|
||||
file: params.file,
|
||||
fileContent: params.fileContent,
|
||||
fileName: params.fileName,
|
||||
mode: params.mode,
|
||||
autorename: params.autorename,
|
||||
mute: params.mute,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response): Promise<DropboxUploadResponse> => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error || 'Failed to upload file',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'object',
|
||||
description: 'The uploaded file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for the file' },
|
||||
name: { type: 'string', description: 'Name of the file' },
|
||||
path_display: { type: 'string', description: 'Display path of the file', optional: true },
|
||||
path_lower: { type: 'string', description: 'Lowercase path of the file', optional: true },
|
||||
size: { type: 'number', description: 'Size of the file in bytes' },
|
||||
client_modified: { type: 'string', description: 'Client modification time' },
|
||||
server_modified: { type: 'string', description: 'Server modification time' },
|
||||
rev: { type: 'string', description: 'Revision identifier' },
|
||||
content_hash: {
|
||||
type: 'string',
|
||||
description: 'Content hash for the file',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user