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,97 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { OneDriveCopyResponse, OneDriveToolParams } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OneDriveCopyTool')
|
||||
|
||||
/**
|
||||
* Microsoft Graph processes driveItem copies asynchronously: a successful request returns
|
||||
* `202 Accepted` with a `Location` header pointing to a monitor URL, not the copied item itself.
|
||||
* See https://learn.microsoft.com/en-us/graph/api/driveitem-copy
|
||||
*/
|
||||
export const copyTool: ToolConfig<OneDriveToolParams, OneDriveCopyResponse> = {
|
||||
id: 'onedrive_copy',
|
||||
name: 'Copy OneDrive File',
|
||||
description: 'Copy a file or folder to another location within OneDrive',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file or folder to copy',
|
||||
},
|
||||
destinationFolderId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the destination parent folder',
|
||||
},
|
||||
destinationFileName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional new name for the copy (defaults to the original name)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(params.fileId || '')}/copy`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
parentReference: { id: params.destinationFolderId },
|
||||
...(params.destinationFileName && { name: params.destinationFileName }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: OneDriveToolParams) => {
|
||||
if (response.status !== 202) {
|
||||
const data = await response.json().catch(() => ({}))
|
||||
throw new Error(data.error?.message || 'Failed to start OneDrive copy')
|
||||
}
|
||||
|
||||
const monitorUrl = response.headers.get('location') || undefined
|
||||
|
||||
logger.info('OneDrive copy accepted for async processing', {
|
||||
fileId: params?.fileId,
|
||||
monitorUrl,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sourceFileId: params?.fileId || '',
|
||||
name: params?.destinationFileName,
|
||||
monitorUrl,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the copy request was accepted' },
|
||||
sourceFileId: { type: 'string', description: 'The ID of the file or folder that was copied' },
|
||||
name: { type: 'string', description: 'The requested name for the copy, if provided' },
|
||||
monitorUrl: {
|
||||
type: 'string',
|
||||
description:
|
||||
'URL to poll for the status of the asynchronous copy operation (copy completes in the background)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import type { OneDriveToolParams, OneDriveUploadResponse } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createFolderTool: ToolConfig<OneDriveToolParams, OneDriveUploadResponse> = {
|
||||
id: 'onedrive_create_folder',
|
||||
name: 'Create Folder in OneDrive',
|
||||
description: 'Create a new folder in OneDrive',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
folderName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the folder to create (e.g., "My Documents", "Project Files")',
|
||||
},
|
||||
folderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Parent folder ID to create the folder in (e.g., "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36M")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
// Use specific parent folder URL if parentId is provided
|
||||
const parentFolderId = params.folderId?.trim()
|
||||
if (parentFolderId) {
|
||||
return `https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(parentFolderId)}/children`
|
||||
}
|
||||
return 'https://graph.microsoft.com/v1.0/me/drive/root/children'
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
return {
|
||||
name: params.folderName,
|
||||
folder: {}, // Required facet for folder creation in Microsoft Graph API
|
||||
'@microsoft.graph.conflictBehavior': 'rename', // Handle name conflicts
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
mimeType: 'application/vnd.microsoft.graph.folder',
|
||||
webViewLink: data.webUrl,
|
||||
size: data.size,
|
||||
createdTime: data.createdDateTime,
|
||||
modifiedTime: data.lastModifiedDateTime,
|
||||
parentReference: data.parentReference,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the folder was created successfully' },
|
||||
file: {
|
||||
type: 'object',
|
||||
description:
|
||||
'The created folder object with metadata including id, name, webViewLink, and timestamps',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { OneDriveShareLinkResponse, OneDriveToolParams } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createShareLinkTool: ToolConfig<OneDriveToolParams, OneDriveShareLinkResponse> = {
|
||||
id: 'onedrive_create_share_link',
|
||||
name: 'Create OneDrive Sharing Link',
|
||||
description: 'Create a view or edit sharing link for a OneDrive file or folder',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file or folder to share',
|
||||
},
|
||||
linkType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Type of link to create: "view" (read-only), "edit" (read-write), or "embed"',
|
||||
},
|
||||
linkScope: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Who can use the link: "anonymous" (anyone), "organization" (tenant members), or "users" (specific people)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(params.fileId || '')}/createLink`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
type: params.linkType || 'view',
|
||||
...(params.linkScope && { scope: params.linkScope }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
link: {
|
||||
type: data.link?.type,
|
||||
scope: data.link?.scope,
|
||||
webUrl: data.link?.webUrl,
|
||||
webHtml: data.link?.webHtml,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the sharing link was created successfully' },
|
||||
link: {
|
||||
type: 'object',
|
||||
description: 'The created sharing link, including its type, scope, and URL',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { OneDriveDeleteResponse, OneDriveToolParams } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OneDriveDeleteTool')
|
||||
|
||||
export const deleteTool: ToolConfig<OneDriveToolParams, OneDriveDeleteResponse> = {
|
||||
id: 'onedrive_delete',
|
||||
name: 'Delete File from OneDrive',
|
||||
description: 'Delete a file or folder from OneDrive',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The ID of the file or folder to delete (e.g., "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36M")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
return `https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(params.fileId || '')}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: OneDriveToolParams) => {
|
||||
if (response.status === 204) {
|
||||
logger.info('Successfully deleted file from OneDrive', {
|
||||
fileId: params?.fileId,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
fileId: params?.fileId || '',
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If not 204, try to parse error
|
||||
const data = await response.json().catch(() => ({}))
|
||||
const errorMessage = data.error?.message || 'Failed to delete file'
|
||||
|
||||
logger.error('Failed to delete file from OneDrive', {
|
||||
fileId: params?.fileId,
|
||||
error: errorMessage,
|
||||
})
|
||||
|
||||
throw new Error(errorMessage)
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the file was deleted successfully' },
|
||||
deleted: { type: 'boolean', description: 'Confirmation that the file was deleted' },
|
||||
fileId: { type: 'string', description: 'The ID of the deleted file' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { OneDriveDownloadResponse, OneDriveToolParams } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const downloadTool: ToolConfig<OneDriveToolParams, OneDriveDownloadResponse> = {
|
||||
id: 'onedrive_download',
|
||||
name: 'Download File from OneDrive',
|
||||
description: 'Download a file from OneDrive',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the Microsoft Graph API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file to download (e.g., "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36M")',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional filename override (e.g., "report.pdf", "data.xlsx")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/onedrive/download',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
accessToken: params.accessToken,
|
||||
fileId: params.fileId,
|
||||
fileName: params.fileName,
|
||||
}),
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: { type: 'file', description: 'Downloaded file stored in execution files' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { OneDriveGetDriveInfoResponse, OneDriveToolParams } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getDriveInfoTool: ToolConfig<OneDriveToolParams, OneDriveGetDriveInfoResponse> = {
|
||||
id: 'onedrive_get_drive_info',
|
||||
name: 'Get OneDrive Info',
|
||||
description: 'Get information about the OneDrive drive, including storage quota',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://graph.microsoft.com/v1.0/me/drive',
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
driveId: data.id,
|
||||
driveType: data.driveType,
|
||||
webUrl: data.webUrl,
|
||||
owner: data.owner?.user?.displayName ?? null,
|
||||
quota: {
|
||||
total: data.quota?.total ?? 0,
|
||||
used: data.quota?.used ?? 0,
|
||||
remaining: data.quota?.remaining ?? 0,
|
||||
deleted: data.quota?.deleted ?? 0,
|
||||
state: data.quota?.state ?? 'normal',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the drive info was retrieved' },
|
||||
driveId: { type: 'string', description: 'The ID of the drive' },
|
||||
driveType: { type: 'string', description: 'The type of drive (e.g., "personal", "business")' },
|
||||
webUrl: { type: 'string', description: 'URL to the drive in the browser' },
|
||||
owner: { type: 'string', description: 'Display name of the drive owner', optional: true },
|
||||
quota: {
|
||||
type: 'object',
|
||||
description: 'Storage quota information in bytes (total, used, remaining, deleted, state)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import type {
|
||||
MicrosoftGraphDriveItem,
|
||||
OneDriveGetItemResponse,
|
||||
OneDriveToolParams,
|
||||
} from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getItemTool: ToolConfig<OneDriveToolParams, OneDriveGetItemResponse> = {
|
||||
id: 'onedrive_get_item',
|
||||
name: 'Get OneDrive Item Metadata',
|
||||
description: 'Get metadata for a specific OneDrive file or folder by ID, or the drive root',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The ID of the file or folder to retrieve (e.g., "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36M"). Leave empty to get the drive root folder',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const fileId = params.fileId?.trim()
|
||||
const baseUrl = fileId
|
||||
? `https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(fileId)}`
|
||||
: 'https://graph.microsoft.com/v1.0/me/drive/root'
|
||||
|
||||
const url = new URL(baseUrl)
|
||||
url.searchParams.append(
|
||||
'$select',
|
||||
'id,name,file,folder,webUrl,size,createdDateTime,lastModifiedDateTime,parentReference,@microsoft.graph.downloadUrl'
|
||||
)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data: MicrosoftGraphDriveItem = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
mimeType: data.file?.mimeType || (data.folder ? 'application/folder' : 'unknown'),
|
||||
webViewLink: data.webUrl,
|
||||
webContentLink: data['@microsoft.graph.downloadUrl'],
|
||||
size: data.size?.toString() || '0',
|
||||
createdTime: data.createdDateTime,
|
||||
modifiedTime: data.lastModifiedDateTime,
|
||||
parents: data.parentReference ? [data.parentReference.id] : [],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the item metadata was retrieved' },
|
||||
file: {
|
||||
type: 'object',
|
||||
description:
|
||||
'The file or folder metadata, including id, name, webViewLink, size, and timestamps',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { copyTool } from '@/tools/onedrive/copy'
|
||||
import { createFolderTool } from '@/tools/onedrive/create_folder'
|
||||
import { createShareLinkTool } from '@/tools/onedrive/create_share_link'
|
||||
import { deleteTool } from '@/tools/onedrive/delete'
|
||||
import { downloadTool } from '@/tools/onedrive/download'
|
||||
import { getDriveInfoTool } from '@/tools/onedrive/get_drive_info'
|
||||
import { getItemTool } from '@/tools/onedrive/get_item'
|
||||
import { listTool } from '@/tools/onedrive/list'
|
||||
import { moveTool } from '@/tools/onedrive/move'
|
||||
import { searchTool } from '@/tools/onedrive/search'
|
||||
import { uploadTool } from '@/tools/onedrive/upload'
|
||||
|
||||
export const onedriveCopyTool = copyTool
|
||||
export const onedriveCreateFolderTool = createFolderTool
|
||||
export const onedriveCreateShareLinkTool = createShareLinkTool
|
||||
export const onedriveDeleteTool = deleteTool
|
||||
export const onedriveDownloadTool = downloadTool
|
||||
export const onedriveGetDriveInfoTool = getDriveInfoTool
|
||||
export const onedriveGetItemTool = getItemTool
|
||||
export const onedriveListTool = listTool
|
||||
export const onedriveMoveTool = moveTool
|
||||
export const onedriveSearchTool = searchTool
|
||||
export const onedriveUploadTool = uploadTool
|
||||
@@ -0,0 +1,129 @@
|
||||
import type {
|
||||
MicrosoftGraphDriveItem,
|
||||
OneDriveListResponse,
|
||||
OneDriveToolParams,
|
||||
} from '@/tools/onedrive/types'
|
||||
import { escapeODataStringLiteral } from '@/tools/onedrive/utils'
|
||||
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listTool: ToolConfig<OneDriveToolParams, OneDriveListResponse> = {
|
||||
id: 'onedrive_list',
|
||||
name: 'List OneDrive Files',
|
||||
description: 'List files and folders in OneDrive',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
folderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Folder ID to list files from (e.g., "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36M")',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter files by name prefix (e.g., "report", "invoice_2024")',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of files to return (e.g., 10, 50, 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
"Continuation URL from a previous response's nextPageToken, used to fetch the next page",
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const pageToken = params.pageToken?.trim()
|
||||
if (pageToken) {
|
||||
return assertGraphNextPageUrl(pageToken)
|
||||
}
|
||||
|
||||
// Use specific folder if provided, otherwise use root
|
||||
const folderId = params.folderId?.trim()
|
||||
const encodedFolderId = folderId ? encodeURIComponent(folderId) : ''
|
||||
const baseUrl = encodedFolderId
|
||||
? `https://graph.microsoft.com/v1.0/me/drive/items/${encodedFolderId}/children`
|
||||
: 'https://graph.microsoft.com/v1.0/me/drive/root/children'
|
||||
|
||||
const url = new URL(baseUrl)
|
||||
|
||||
// Use Microsoft Graph $select parameter
|
||||
url.searchParams.append(
|
||||
'$select',
|
||||
'id,name,file,folder,webUrl,size,createdDateTime,lastModifiedDateTime,parentReference,@microsoft.graph.downloadUrl'
|
||||
)
|
||||
|
||||
// Add name filter if query provided
|
||||
if (params.query) {
|
||||
url.searchParams.append(
|
||||
'$filter',
|
||||
`startswith(name,'${escapeODataStringLiteral(params.query)}')`
|
||||
)
|
||||
}
|
||||
|
||||
// Add pagination
|
||||
if (params.pageSize) {
|
||||
url.searchParams.append('$top', Number(params.pageSize).toString())
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
files: data.value.map((item: MicrosoftGraphDriveItem) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
mimeType: item.file?.mimeType || (item.folder ? 'application/folder' : 'unknown'),
|
||||
webViewLink: item.webUrl,
|
||||
webContentLink: item['@microsoft.graph.downloadUrl'],
|
||||
size: item.size?.toString() || '0',
|
||||
createdTime: item.createdDateTime,
|
||||
modifiedTime: item.lastModifiedDateTime,
|
||||
parents: item.parentReference ? [item.parentReference.id] : [],
|
||||
})),
|
||||
// Use the actual @odata.nextLink URL as the continuation token
|
||||
nextPageToken: getGraphNextPageUrl(data),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether files were listed successfully' },
|
||||
files: { type: 'array', description: 'Array of file and folder objects with metadata' },
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for retrieving the next page of results (optional)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { OneDriveMoveResponse, OneDriveToolParams } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OneDriveMoveTool')
|
||||
|
||||
export const moveTool: ToolConfig<OneDriveToolParams, OneDriveMoveResponse> = {
|
||||
id: 'onedrive_move',
|
||||
name: 'Move or Rename OneDrive File',
|
||||
description: 'Move a file or folder to a new parent folder, rename it, or both',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
fileId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the file or folder to move or rename',
|
||||
},
|
||||
destinationFolderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the destination parent folder (omit to only rename in place)',
|
||||
},
|
||||
newName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The new name for the file or folder (omit to only move)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(params.fileId || '')}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
if (!params.destinationFolderId && !params.newName) {
|
||||
throw new Error('Provide a destination folder, a new name, or both')
|
||||
}
|
||||
|
||||
return {
|
||||
...(params.destinationFolderId && {
|
||||
parentReference: { id: params.destinationFolderId },
|
||||
}),
|
||||
...(params.newName && { name: params.newName }),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: OneDriveToolParams) => {
|
||||
const data = await response.json()
|
||||
|
||||
logger.info('Successfully moved/renamed OneDrive item', {
|
||||
fileId: params?.fileId,
|
||||
newName: data.name,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
mimeType: data.file?.mimeType || (data.folder ? 'application/folder' : 'unknown'),
|
||||
webViewLink: data.webUrl,
|
||||
size: data.size?.toString(),
|
||||
createdTime: data.createdDateTime,
|
||||
modifiedTime: data.lastModifiedDateTime,
|
||||
parents: data.parentReference ? [data.parentReference.id] : [],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the move or rename was successful' },
|
||||
file: {
|
||||
type: 'object',
|
||||
description: 'The updated file object with its new name and/or parent folder',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
MicrosoftGraphDriveItem,
|
||||
OneDriveSearchResponse,
|
||||
OneDriveToolParams,
|
||||
} from '@/tools/onedrive/types'
|
||||
import { escapeODataStringLiteral } from '@/tools/onedrive/utils'
|
||||
import { assertGraphNextPageUrl, getGraphNextPageUrl } from '@/tools/sharepoint/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const searchTool: ToolConfig<OneDriveToolParams, OneDriveSearchResponse> = {
|
||||
id: 'onedrive_search',
|
||||
name: 'Search OneDrive Files',
|
||||
description:
|
||||
'Search for files and folders across OneDrive by name, metadata, or content (recursive)',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search text matched against file name, metadata, and content. Not required when paginating with pageToken',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return (e.g., 10, 50, 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
"Continuation URL from a previous response's nextPageToken, used to fetch the next page",
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const pageToken = params.pageToken?.trim()
|
||||
if (pageToken) {
|
||||
return assertGraphNextPageUrl(pageToken)
|
||||
}
|
||||
|
||||
const query = params.query?.trim()
|
||||
if (!query) {
|
||||
throw new Error('A search query is required')
|
||||
}
|
||||
|
||||
const url = new URL(
|
||||
`https://graph.microsoft.com/v1.0/me/drive/root/search(q='${encodeURIComponent(escapeODataStringLiteral(query))}')`
|
||||
)
|
||||
url.searchParams.append(
|
||||
'$select',
|
||||
'id,name,file,folder,webUrl,size,createdDateTime,lastModifiedDateTime,parentReference,@microsoft.graph.downloadUrl'
|
||||
)
|
||||
if (params.pageSize) {
|
||||
url.searchParams.append('$top', Number(params.pageSize).toString())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
files: (data.value || []).map((item: MicrosoftGraphDriveItem) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
mimeType: item.file?.mimeType || (item.folder ? 'application/folder' : 'unknown'),
|
||||
webViewLink: item.webUrl,
|
||||
webContentLink: item['@microsoft.graph.downloadUrl'],
|
||||
size: item.size?.toString() || '0',
|
||||
createdTime: item.createdDateTime,
|
||||
modifiedTime: item.lastModifiedDateTime,
|
||||
parents: item.parentReference ? [item.parentReference.id] : [],
|
||||
})),
|
||||
nextPageToken: getGraphNextPageUrl(data),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the search completed successfully' },
|
||||
files: {
|
||||
type: 'array',
|
||||
description: 'Array of file and folder objects matching the search query',
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for retrieving the next page of results (optional)',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
import type { UserFile } from '@/executor/types'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface MicrosoftGraphDriveItem {
|
||||
id: string
|
||||
name: string
|
||||
file?: {
|
||||
mimeType: string
|
||||
}
|
||||
folder?: {
|
||||
childCount: number
|
||||
}
|
||||
webUrl: string
|
||||
createdDateTime: string
|
||||
lastModifiedDateTime: string
|
||||
size?: number
|
||||
'@microsoft.graph.downloadUrl'?: string
|
||||
parentReference?: {
|
||||
id: string
|
||||
driveId: string
|
||||
path: string
|
||||
}
|
||||
thumbnails?: Array<{
|
||||
small?: { url: string }
|
||||
medium?: { url: string }
|
||||
large?: { url: string }
|
||||
}>
|
||||
createdBy?: {
|
||||
user?: {
|
||||
displayName?: string
|
||||
email?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OneDriveFile {
|
||||
id: string
|
||||
name: string
|
||||
mimeType: string
|
||||
webViewLink?: string
|
||||
webContentLink?: string
|
||||
size?: string
|
||||
createdTime?: string
|
||||
modifiedTime?: string
|
||||
parents?: string[]
|
||||
}
|
||||
|
||||
export interface OneDriveListResponse extends ToolResponse {
|
||||
output: {
|
||||
files: OneDriveFile[]
|
||||
nextPageToken?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveUploadResponse extends ToolResponse {
|
||||
output: {
|
||||
file: OneDriveFile
|
||||
excelWriteResult?: {
|
||||
success: boolean
|
||||
updatedRange?: string
|
||||
updatedRows?: number
|
||||
updatedColumns?: number
|
||||
updatedCells?: number
|
||||
error?: string
|
||||
details?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveDownloadResponse extends ToolResponse {
|
||||
output: {
|
||||
file: {
|
||||
name: string
|
||||
mimeType: string
|
||||
data: Buffer | string // Buffer for direct use, string for base64-encoded data
|
||||
size: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveDeleteResponse extends ToolResponse {
|
||||
output: {
|
||||
fileId: string
|
||||
deleted: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
files: OneDriveFile[]
|
||||
nextPageToken?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveMoveResponse extends ToolResponse {
|
||||
output: {
|
||||
file: OneDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveCopyResponse extends ToolResponse {
|
||||
output: {
|
||||
sourceFileId: string
|
||||
name?: string
|
||||
monitorUrl?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveShareLinkResponse extends ToolResponse {
|
||||
output: {
|
||||
link: {
|
||||
type: string
|
||||
scope?: string
|
||||
webUrl: string
|
||||
webHtml?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveGetItemResponse extends ToolResponse {
|
||||
output: {
|
||||
file: OneDriveFile
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveGetDriveInfoResponse extends ToolResponse {
|
||||
output: {
|
||||
driveId: string
|
||||
driveType: string
|
||||
webUrl: string
|
||||
owner: string | null
|
||||
quota: {
|
||||
total: number
|
||||
used: number
|
||||
remaining: number
|
||||
deleted: number
|
||||
state: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface OneDriveToolParams {
|
||||
accessToken: string
|
||||
folderId?: string
|
||||
folderName?: string
|
||||
fileId?: string
|
||||
fileName?: string
|
||||
file?: UserFile
|
||||
content?: string
|
||||
mimeType?: string
|
||||
query?: string
|
||||
pageSize?: number
|
||||
pageToken?: string
|
||||
exportMimeType?: string
|
||||
// Optional Excel write parameters (used when creating an .xlsx without file content)
|
||||
values?:
|
||||
| (string | number | boolean | null)[][]
|
||||
| Array<Record<string, string | number | boolean | null>>
|
||||
// Move/rename parameters
|
||||
destinationFolderId?: string
|
||||
newName?: string
|
||||
// Copy parameters
|
||||
destinationFileName?: string
|
||||
// Sharing link parameters
|
||||
linkType?: 'view' | 'edit' | 'embed'
|
||||
linkScope?: 'anonymous' | 'organization' | 'users'
|
||||
}
|
||||
|
||||
export type OneDriveResponse =
|
||||
| OneDriveUploadResponse
|
||||
| OneDriveDownloadResponse
|
||||
| OneDriveListResponse
|
||||
| OneDriveDeleteResponse
|
||||
| OneDriveSearchResponse
|
||||
| OneDriveMoveResponse
|
||||
| OneDriveCopyResponse
|
||||
| OneDriveShareLinkResponse
|
||||
| OneDriveGetItemResponse
|
||||
| OneDriveGetDriveInfoResponse
|
||||
@@ -0,0 +1,170 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { OneDriveToolParams, OneDriveUploadResponse } from '@/tools/onedrive/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OneDriveUploadTool')
|
||||
|
||||
export const uploadTool: ToolConfig<OneDriveToolParams, OneDriveUploadResponse> = {
|
||||
id: 'onedrive_upload',
|
||||
name: 'Upload to OneDrive',
|
||||
description: 'Upload a file to OneDrive',
|
||||
version: '1.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'onedrive',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the OneDrive API',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The name of the file to upload (e.g., "report.pdf", "data.xlsx")',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'The file to upload (binary)',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The text content to upload (if no file is provided)',
|
||||
},
|
||||
mimeType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The MIME type of the file to create (e.g., text/plain for .txt, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for .xlsx)',
|
||||
},
|
||||
folderId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Folder ID to upload the file to (e.g., "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36M")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
if (params.file || isExcelFile) {
|
||||
return '/api/tools/onedrive/upload'
|
||||
}
|
||||
|
||||
let fileName = params.fileName || 'untitled'
|
||||
|
||||
if (!fileName.endsWith('.txt')) {
|
||||
fileName = `${fileName.replace(/\.[^.]*$/, '')}.txt`
|
||||
}
|
||||
|
||||
const parentFolderId = params.folderId?.trim()
|
||||
if (parentFolderId) {
|
||||
return `https://graph.microsoft.com/v1.0/me/drive/items/${encodeURIComponent(parentFolderId)}:/${fileName}:/content`
|
||||
}
|
||||
return `https://graph.microsoft.com/v1.0/me/drive/root:/${fileName}:/content`
|
||||
},
|
||||
method: (params) => {
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
return params.file || isExcelFile ? 'POST' : 'PUT'
|
||||
},
|
||||
headers: (params) => {
|
||||
const headers: Record<string, string> = {}
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
|
||||
if (params.file || isExcelFile) {
|
||||
headers['Content-Type'] = 'application/json'
|
||||
} else {
|
||||
headers.Authorization = `Bearer ${params.accessToken}`
|
||||
headers['Content-Type'] = 'text/plain'
|
||||
}
|
||||
return headers
|
||||
},
|
||||
body: (params) => {
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
|
||||
if (params.file || isExcelFile) {
|
||||
return {
|
||||
accessToken: params.accessToken,
|
||||
fileName: params.fileName,
|
||||
file: params.file,
|
||||
folderId: params.folderId,
|
||||
...(params.mimeType && { mimeType: params.mimeType }),
|
||||
...(params.values && { values: params.values }),
|
||||
}
|
||||
}
|
||||
|
||||
return params.content || ''
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: OneDriveToolParams) => {
|
||||
const data = await response.json()
|
||||
|
||||
const isExcelFile =
|
||||
params?.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
|
||||
if ((params?.file || isExcelFile) && data.success !== undefined) {
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to upload file')
|
||||
}
|
||||
|
||||
logger.info('Successfully uploaded file to OneDrive via custom API', {
|
||||
fileId: data.output?.file?.id,
|
||||
fileName: data.output?.file?.name,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
}
|
||||
|
||||
const fileData = data
|
||||
|
||||
logger.info('Successfully uploaded file to OneDrive', {
|
||||
fileId: fileData.id,
|
||||
fileName: fileData.name,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: {
|
||||
id: fileData.id,
|
||||
name: fileData.name,
|
||||
mimeType: fileData.file?.mimeType || params?.mimeType || 'text/plain',
|
||||
webViewLink: fileData.webUrl,
|
||||
webContentLink: fileData['@microsoft.graph.downloadUrl'],
|
||||
size: fileData.size,
|
||||
createdTime: fileData.createdDateTime,
|
||||
modifiedTime: fileData.lastModifiedDateTime,
|
||||
parentReference: fileData.parentReference,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the file was uploaded successfully' },
|
||||
file: {
|
||||
type: 'object',
|
||||
description:
|
||||
'The uploaded file object with metadata including id, name, webViewLink, webContentLink, and timestamps',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { OneDriveToolParams } from '@/tools/onedrive/types'
|
||||
|
||||
/**
|
||||
* Escapes a value for embedding in an OData string literal (e.g. search(q='...'),
|
||||
* $filter startswith(name,'...')). OData V4 escapes a literal single quote by doubling
|
||||
* it — encodeURIComponent does not touch `'`, so this must run before URL-encoding.
|
||||
*/
|
||||
export function escapeODataStringLiteral(value: string): string {
|
||||
return value.replace(/'/g, "''")
|
||||
}
|
||||
|
||||
export type ExcelCell = string | number | boolean | null
|
||||
export type ExcelArrayValues = ExcelCell[][]
|
||||
export type ExcelObjectValues = Array<Record<string, ExcelCell>>
|
||||
export type NormalizedExcelValues = ExcelArrayValues | ExcelObjectValues
|
||||
|
||||
/**
|
||||
* Ensures Excel values are always represented as arrays before hitting downstream tooling.
|
||||
* Accepts JSON strings, array-of-arrays, or array-of-objects and normalizes them.
|
||||
*/
|
||||
export function normalizeExcelValues(values: unknown): NormalizedExcelValues | undefined {
|
||||
if (values === null || values === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (typeof values === 'string') {
|
||||
const trimmed = values.trim()
|
||||
if (!trimmed) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed)
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new Error('Excel values must be an array of rows or array of objects')
|
||||
}
|
||||
return parsed as NormalizedExcelValues
|
||||
} catch (_error) {
|
||||
throw new Error('Invalid JSON format for values')
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(values)) {
|
||||
return values as NormalizedExcelValues
|
||||
}
|
||||
|
||||
throw new Error('Excel values must be an array of rows or array of objects')
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience helper for contexts that expect the narrower ToolParams typing.
|
||||
*/
|
||||
export function normalizeExcelValuesForToolParams(
|
||||
values: unknown
|
||||
): OneDriveToolParams['values'] | undefined {
|
||||
const normalized = normalizeExcelValues(values)
|
||||
return normalized as OneDriveToolParams['values'] | undefined
|
||||
}
|
||||
Reference in New Issue
Block a user