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

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+97
View File
@@ -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)',
},
},
}
+88
View File
@@ -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',
},
},
}
+76
View File
@@ -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' },
},
}
+52
View 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' },
},
}
+64
View File
@@ -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)',
},
},
}
+84
View File
@@ -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',
},
},
}
+23
View File
@@ -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
+129
View File
@@ -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)',
},
},
}
+99
View File
@@ -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',
},
},
}
+114
View File
@@ -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)',
},
},
}
+179
View File
@@ -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
+170
View File
@@ -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',
},
},
}
+58
View File
@@ -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
}