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,82 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxCopyFileParams, BoxUploadFileResponse } from './types'
|
||||
import { UPLOAD_FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxCopyFileTool: ToolConfig<BoxCopyFileParams, BoxUploadFileResponse> = {
|
||||
id: 'box_copy_file',
|
||||
name: 'Box Copy File',
|
||||
description: 'Copy a file to another folder in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to copy',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the destination folder',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional new name for the copied file',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}/copy`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
parent: { id: params.parentFolderId.trim() },
|
||||
}
|
||||
if (params.name) body.name = params.name
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
size: data.size ?? 0,
|
||||
sha1: data.sha1 ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: UPLOAD_FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxCreateFolderParams, BoxFolderResponse } from './types'
|
||||
import { FOLDER_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxCreateFolderTool: ToolConfig<BoxCreateFolderParams, BoxFolderResponse> = {
|
||||
id: 'box_create_folder',
|
||||
name: 'Box Create Folder',
|
||||
description: 'Create a new folder in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the new folder',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the parent folder (use "0" for root)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.box.com/2.0/folders',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
name: params.name,
|
||||
parent: { id: params.parentFolderId.trim() },
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FOLDER_OUTPUT_PROPERTIES,
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { BoxDeleteFileParams } from './types'
|
||||
|
||||
export const boxDeleteFileTool: ToolConfig<BoxDeleteFileParams, ToolResponse> = {
|
||||
id: 'box_delete_file',
|
||||
name: 'Box Delete File',
|
||||
description: 'Delete a file from Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (response.status === 204) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
message: 'File deleted successfully',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the file was successfully deleted' },
|
||||
message: { type: 'string', description: 'Success confirmation message' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { BoxDeleteFolderParams } from './types'
|
||||
|
||||
export const boxDeleteFolderTool: ToolConfig<BoxDeleteFolderParams, ToolResponse> = {
|
||||
id: 'box_delete_folder',
|
||||
name: 'Box Delete Folder',
|
||||
description: 'Delete a folder from Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
folderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the folder to delete',
|
||||
},
|
||||
recursive: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Delete folder and all its contents recursively',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.recursive) queryParams.set('recursive', 'true')
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.box.com/2.0/folders/${params.folderId.trim()}${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (response.status === 204) {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
deleted: true,
|
||||
message: 'Folder deleted successfully',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the folder was successfully deleted' },
|
||||
message: { type: 'string', description: 'Success confirmation message' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxDownloadFileParams, BoxDownloadFileResponse } from './types'
|
||||
|
||||
export const boxDownloadFileTool: ToolConfig<BoxDownloadFileParams, BoxDownloadFileResponse> = {
|
||||
id: 'box_download_file',
|
||||
name: 'Box Download File',
|
||||
description: 'Download a file from Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to download',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}/content`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
if (response.status === 202) {
|
||||
const retryAfter = response.headers.get('retry-after') || 'a few'
|
||||
throw new Error(`File is not yet ready for download. Retry after ${retryAfter} seconds.`)
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(errorText || `Failed to download file: ${response.status}`)
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type') || 'application/octet-stream'
|
||||
const contentDisposition = response.headers.get('content-disposition')
|
||||
let fileName = 'download'
|
||||
|
||||
if (contentDisposition) {
|
||||
const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)
|
||||
if (match?.[1]) {
|
||||
fileName = match[1].replace(/['"]/g, '')
|
||||
}
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer()
|
||||
const buffer = Buffer.from(arrayBuffer)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
name: fileName,
|
||||
mimeType: contentType,
|
||||
data: buffer.toString('base64'),
|
||||
size: buffer.length,
|
||||
},
|
||||
content: buffer.toString('base64'),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'file',
|
||||
description: 'Downloaded file stored in execution files',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Base64 encoded file content',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxFileInfoResponse, BoxGetFileInfoParams } from './types'
|
||||
import { FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxGetFileInfoTool: ToolConfig<BoxGetFileInfoParams, BoxFileInfoResponse> = {
|
||||
id: 'box_get_file_info',
|
||||
name: 'Box Get File Info',
|
||||
description: 'Get detailed information about a file in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to get information about',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
size: data.size ?? 0,
|
||||
sha1: data.sha1 ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
createdBy: data.created_by
|
||||
? {
|
||||
id: data.created_by.id,
|
||||
name: data.created_by.name,
|
||||
login: data.created_by.login,
|
||||
}
|
||||
: null,
|
||||
modifiedBy: data.modified_by
|
||||
? {
|
||||
id: data.modified_by.id,
|
||||
name: data.modified_by.name,
|
||||
login: data.modified_by.login,
|
||||
}
|
||||
: null,
|
||||
ownedBy: data.owned_by
|
||||
? {
|
||||
id: data.owned_by.id,
|
||||
name: data.owned_by.name,
|
||||
login: data.owned_by.login,
|
||||
}
|
||||
: null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
sharedLink: data.shared_link ?? null,
|
||||
tags: data.tags ?? [],
|
||||
commentCount: data.comment_count ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export { boxCopyFileTool } from '@/tools/box/copy_file'
|
||||
export { boxCreateFolderTool } from '@/tools/box/create_folder'
|
||||
export { boxDeleteFileTool } from '@/tools/box/delete_file'
|
||||
export { boxDeleteFolderTool } from '@/tools/box/delete_folder'
|
||||
export { boxDownloadFileTool } from '@/tools/box/download_file'
|
||||
export { boxGetFileInfoTool } from '@/tools/box/get_file_info'
|
||||
export { boxListFolderItemsTool } from '@/tools/box/list_folder_items'
|
||||
export { boxSearchTool } from '@/tools/box/search'
|
||||
export { boxUpdateFileTool } from '@/tools/box/update_file'
|
||||
export { boxUploadFileTool } from '@/tools/box/upload_file'
|
||||
@@ -0,0 +1,98 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxFolderItemsResponse, BoxListFolderItemsParams } from './types'
|
||||
import { FOLDER_ITEMS_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxListFolderItemsTool: ToolConfig<BoxListFolderItemsParams, BoxFolderItemsResponse> =
|
||||
{
|
||||
id: 'box_list_folder_items',
|
||||
name: 'Box List Folder Items',
|
||||
description: 'List files and folders in a Box folder',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
folderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the folder to list items from (use "0" for root)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of items to return per page',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The offset for pagination',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort field: id, name, date, or size',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction: ASC or DESC',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.limit !== undefined) queryParams.set('limit', String(params.limit))
|
||||
if (params.offset !== undefined) queryParams.set('offset', String(params.offset))
|
||||
if (params.sort) queryParams.set('sort', params.sort)
|
||||
if (params.direction) queryParams.set('direction', params.direction)
|
||||
const qs = queryParams.toString()
|
||||
return `https://api.box.com/2.0/folders/${params.folderId.trim()}/items${qs ? `?${qs}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
entries: (data.entries ?? []).map((item: Record<string, unknown>) => ({
|
||||
type: item.type ?? '',
|
||||
id: item.id ?? '',
|
||||
name: item.name ?? '',
|
||||
size: item.size ?? null,
|
||||
createdAt: item.created_at ?? null,
|
||||
modifiedAt: item.modified_at ?? null,
|
||||
})),
|
||||
totalCount: data.total_count ?? 0,
|
||||
offset: data.offset ?? 0,
|
||||
limit: data.limit ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FOLDER_ITEMS_OUTPUT_PROPERTIES,
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxSearchParams, BoxSearchResponse } from './types'
|
||||
import { SEARCH_RESULT_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxSearchTool: ToolConfig<BoxSearchParams, BoxSearchResponse> = {
|
||||
id: 'box_search',
|
||||
name: 'Box Search',
|
||||
description: 'Search for files and folders in Box',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The search query string',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The offset for pagination',
|
||||
},
|
||||
ancestorFolderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Restrict search to a specific folder and its subfolders',
|
||||
},
|
||||
fileExtensions: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated file extensions to filter by (e.g., pdf,docx)',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Restrict to a specific content type: file, folder, or web_link',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.set('query', params.query)
|
||||
if (params.limit !== undefined) queryParams.set('limit', String(params.limit))
|
||||
if (params.offset !== undefined) queryParams.set('offset', String(params.offset))
|
||||
if (params.ancestorFolderId)
|
||||
queryParams.set('ancestor_folder_ids', params.ancestorFolderId.trim())
|
||||
if (params.fileExtensions) queryParams.set('file_extensions', params.fileExtensions)
|
||||
if (params.type) queryParams.set('type', params.type)
|
||||
return `https://api.box.com/2.0/search?${queryParams.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
results: (data.entries ?? []).map((item: Record<string, unknown>) => ({
|
||||
type: item.type ?? '',
|
||||
id: item.id ?? '',
|
||||
name: item.name ?? '',
|
||||
size: item.size ?? null,
|
||||
createdAt: item.created_at ?? null,
|
||||
modifiedAt: item.modified_at ?? null,
|
||||
parentId: (item.parent as Record<string, unknown> | undefined)?.id ?? null,
|
||||
parentName: (item.parent as Record<string, unknown> | undefined)?.name ?? null,
|
||||
})),
|
||||
totalCount: data.total_count ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: SEARCH_RESULT_OUTPUT_PROPERTIES,
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
import type { OutputProperty, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface BoxUploadFileParams {
|
||||
accessToken: string
|
||||
parentFolderId: string
|
||||
file?: unknown
|
||||
fileContent?: string
|
||||
fileName?: string
|
||||
}
|
||||
|
||||
export interface BoxDownloadFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface BoxGetFileInfoParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface BoxListFolderItemsParams {
|
||||
accessToken: string
|
||||
folderId: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
sort?: string
|
||||
direction?: string
|
||||
}
|
||||
|
||||
export interface BoxCreateFolderParams {
|
||||
accessToken: string
|
||||
name: string
|
||||
parentFolderId: string
|
||||
}
|
||||
|
||||
export interface BoxDeleteFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
}
|
||||
|
||||
export interface BoxDeleteFolderParams {
|
||||
accessToken: string
|
||||
folderId: string
|
||||
recursive?: boolean
|
||||
}
|
||||
|
||||
export interface BoxCopyFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
parentFolderId: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface BoxSearchParams {
|
||||
accessToken: string
|
||||
query: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
ancestorFolderId?: string
|
||||
fileExtensions?: string
|
||||
type?: string
|
||||
}
|
||||
|
||||
export interface BoxUpdateFileParams {
|
||||
accessToken: string
|
||||
fileId: string
|
||||
name?: string
|
||||
description?: string
|
||||
parentFolderId?: string
|
||||
tags?: string
|
||||
}
|
||||
|
||||
export interface BoxUploadFileResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
size: number
|
||||
sha1: string | null
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
parentId: string | null
|
||||
parentName: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxDownloadFileResponse extends ToolResponse {
|
||||
output: {
|
||||
file: {
|
||||
name: string
|
||||
mimeType: string
|
||||
data: string
|
||||
size: number
|
||||
}
|
||||
content: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxFileInfoResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
size: number
|
||||
sha1: string | null
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
createdBy: { id: string; name: string; login: string } | null
|
||||
modifiedBy: { id: string; name: string; login: string } | null
|
||||
ownedBy: { id: string; name: string; login: string } | null
|
||||
parentId: string | null
|
||||
parentName: string | null
|
||||
sharedLink: Record<string, unknown> | null
|
||||
tags: string[]
|
||||
commentCount: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxFolderItemsResponse extends ToolResponse {
|
||||
output: {
|
||||
entries: Array<Record<string, unknown>>
|
||||
totalCount: number
|
||||
offset: number
|
||||
limit: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxFolderResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
createdAt: string | null
|
||||
modifiedAt: string | null
|
||||
parentId: string | null
|
||||
parentName: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BoxSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
results: Array<Record<string, unknown>>
|
||||
totalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
const USER_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
name: { type: 'string', description: 'User name' },
|
||||
login: { type: 'string', description: 'User email/login' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const FILE_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'File ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
description: { type: 'string', description: 'File description', optional: true },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
sha1: { type: 'string', description: 'SHA1 hash of file content', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
createdBy: {
|
||||
type: 'object',
|
||||
description: 'User who created the file',
|
||||
optional: true,
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
modifiedBy: {
|
||||
type: 'object',
|
||||
description: 'User who last modified the file',
|
||||
optional: true,
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
ownedBy: {
|
||||
type: 'object',
|
||||
description: 'User who owns the file',
|
||||
optional: true,
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
sharedLink: { type: 'json', description: 'Shared link details', optional: true },
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'File tags',
|
||||
items: { type: 'string' },
|
||||
optional: true,
|
||||
},
|
||||
commentCount: { type: 'number', description: 'Number of comments', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const FOLDER_ITEMS_OUTPUT_PROPERTIES = {
|
||||
entries: {
|
||||
type: 'array',
|
||||
description: 'List of items in the folder',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Item type (file, folder, web_link)' },
|
||||
id: { type: 'string', description: 'Item ID' },
|
||||
name: { type: 'string', description: 'Item name' },
|
||||
size: { type: 'number', description: 'Item size in bytes', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: { type: 'number', description: 'Total number of items in the folder' },
|
||||
offset: { type: 'number', description: 'Current pagination offset' },
|
||||
limit: { type: 'number', description: 'Current pagination limit' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const FOLDER_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Folder ID' },
|
||||
name: { type: 'string', description: 'Folder name' },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const SEARCH_RESULT_OUTPUT_PROPERTIES = {
|
||||
results: {
|
||||
type: 'array',
|
||||
description: 'Search results',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
type: { type: 'string', description: 'Item type (file, folder, web_link)' },
|
||||
id: { type: 'string', description: 'Item ID' },
|
||||
name: { type: 'string', description: 'Item name' },
|
||||
size: { type: 'number', description: 'Item size in bytes', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: { type: 'number', description: 'Total number of matching results' },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
export const UPLOAD_FILE_OUTPUT_PROPERTIES = {
|
||||
id: { type: 'string', description: 'File ID' },
|
||||
name: { type: 'string', description: 'File name' },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
sha1: { type: 'string', description: 'SHA1 hash of file content', optional: true },
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
modifiedAt: { type: 'string', description: 'Last modified timestamp', optional: true },
|
||||
parentId: { type: 'string', description: 'Parent folder ID', optional: true },
|
||||
parentName: { type: 'string', description: 'Parent folder name', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
@@ -0,0 +1,124 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxFileInfoResponse, BoxUpdateFileParams } from './types'
|
||||
import { FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxUpdateFileTool: ToolConfig<BoxUpdateFileParams, BoxFileInfoResponse> = {
|
||||
id: 'box_update_file',
|
||||
name: 'Box Update File',
|
||||
description: 'Update file info in Box (rename, move, change description, add tags)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to update',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name for the file',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description for the file (max 256 characters)',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Move the file to a different folder by specifying the folder ID',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated tags to set on the file',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.box.com/2.0/files/${params.fileId.trim()}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.name) body.name = params.name
|
||||
if (params.description !== undefined) body.description = params.description
|
||||
if (params.parentFolderId) body.parent = { id: params.parentFolderId.trim() }
|
||||
if (params.tags)
|
||||
body.tags = params.tags
|
||||
.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter(Boolean)
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || `Box API error: ${response.status}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
size: data.size ?? 0,
|
||||
sha1: data.sha1 ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
modifiedAt: data.modified_at ?? null,
|
||||
createdBy: data.created_by
|
||||
? {
|
||||
id: data.created_by.id,
|
||||
name: data.created_by.name,
|
||||
login: data.created_by.login,
|
||||
}
|
||||
: null,
|
||||
modifiedBy: data.modified_by
|
||||
? {
|
||||
id: data.modified_by.id,
|
||||
name: data.modified_by.name,
|
||||
login: data.modified_by.login,
|
||||
}
|
||||
: null,
|
||||
ownedBy: data.owned_by
|
||||
? {
|
||||
id: data.owned_by.id,
|
||||
name: data.owned_by.name,
|
||||
login: data.owned_by.login,
|
||||
}
|
||||
: null,
|
||||
parentId: data.parent?.id ?? null,
|
||||
parentName: data.parent?.name ?? null,
|
||||
sharedLink: data.shared_link ?? null,
|
||||
tags: data.tags ?? [],
|
||||
commentCount: data.comment_count ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { BoxUploadFileParams, BoxUploadFileResponse } from './types'
|
||||
import { UPLOAD_FILE_OUTPUT_PROPERTIES } from './types'
|
||||
|
||||
export const boxUploadFileTool: ToolConfig<BoxUploadFileParams, BoxUploadFileResponse> = {
|
||||
id: 'box_upload_file',
|
||||
name: 'Box Upload File',
|
||||
description: 'Upload a file to a Box folder',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'box',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Box API',
|
||||
},
|
||||
parentFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the folder to upload the file to (use "0" for root)',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The file to upload (UserFile object)',
|
||||
},
|
||||
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 override',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/box/upload',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
accessToken: params.accessToken,
|
||||
parentFolderId: params.parentFolderId,
|
||||
file: params.file,
|
||||
fileContent: params.fileContent,
|
||||
fileName: params.fileName,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to upload file')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: UPLOAD_FILE_OUTPUT_PROPERTIES,
|
||||
}
|
||||
Reference in New Issue
Block a user