Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions apps/docs/content/docs/en/tools/file.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: File
description: Read and parse multiple files
description: Read and write workspace files
---

import { BlockInfoCard } from "@/components/ui/block-info-card"
Expand All @@ -27,7 +27,7 @@ The File Parser tool is particularly useful for scenarios where your agents need

## Usage Instructions

Upload files directly or import from external URLs to get UserFile objects for use in other blocks.
Read and parse files from uploads or URLs, write new workspace files, or append content to existing files.



Expand All @@ -52,4 +52,45 @@ Parse one or more uploaded files or files from URLs (text, PDF, CSV, images, etc
| `files` | file[] | Parsed files as UserFile objects |
| `combinedContent` | string | Combined content of all parsed files |

### `file_write`

Create a new workspace file. If a file with the same name already exists, a numeric suffix is added (e.g.,

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileName` | string | Yes | File name \(e.g., "data.csv"\). If a file with this name exists, a numeric suffix is added automatically. |
| `content` | string | Yes | The text content to write to the file. |
| `contentType` | string | No | MIME type for new files \(e.g., "text/plain"\). Auto-detected from file extension if omitted. |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | File ID |
| `name` | string | File name |
| `size` | number | File size in bytes |
| `url` | string | URL to access the file |

### `file_append`

Append content to an existing workspace file. The file must already exist. Content is added to the end of the file.

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fileName` | string | Yes | Name of an existing workspace file to append to. |
| `content` | string | Yes | The text content to append to the file. |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | File ID |
| `name` | string | File name |
| `size` | number | File size in bytes |
| `url` | string | URL to access the file |


2 changes: 1 addition & 1 deletion apps/docs/content/docs/en/tools/profound.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"

<BlockInfoCard
type="profound"
color="#1A1A2E"
color="#000000"
/>

{/* MANUAL-CONTENT-START:intro */}
Expand Down
23 changes: 18 additions & 5 deletions apps/sim/app/(landing)/integrations/data/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -2993,13 +2993,26 @@
"type": "file_v3",
"slug": "file",
"name": "File",
"description": "Read and parse multiple files",
"longDescription": "Upload files directly or import from external URLs to get UserFile objects for use in other blocks.",
"description": "Read and write workspace files",
"longDescription": "Read and parse files from uploads or URLs, write new workspace files, or append content to existing files.",
"bgColor": "#40916C",
"iconName": "DocumentIcon",
"docsUrl": "https://docs.sim.ai/tools/file",
"operations": [],
"operationCount": 0,
"operations": [
{
"name": "Read",
"description": "Parse one or more uploaded files or files from URLs (text, PDF, CSV, images, etc.)"
},
{
"name": "Write",
"description": "Create a new workspace file. If a file with the same name already exists, a numeric suffix is added (e.g., "
},
{
"name": "Append",
"description": "Append content to an existing workspace file. The file must already exist. Content is added to the end of the file."
}
],
"operationCount": 3,
"triggers": [],
"triggerCount": 0,
"authType": "none",
Expand Down Expand Up @@ -8617,7 +8630,7 @@
"name": "Profound",
"description": "AI visibility and analytics with Profound",
"longDescription": "Track how your brand appears across AI platforms. Monitor visibility scores, sentiment, citations, bot traffic, referrals, content optimization, and prompt volumes with Profound.",
"bgColor": "#1A1A2E",
"bgColor": "#000000",
"iconName": "ProfoundIcon",
"docsUrl": "https://docs.sim.ai/tools/profound",
"operations": [
Expand Down
8 changes: 4 additions & 4 deletions apps/sim/app/api/emails/preview/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const emailTemplates = {
trigger: 'api',
duration: '2.3s',
cost: '$0.0042',
logUrl: 'https://sim.ai/workspace/ws_123/logs?search=exec_abc123',
logUrl: 'https://sim.ai/workspace/ws_123/logs?executionId=exec_abc123',
}),
'workflow-notification-error': () =>
renderWorkflowNotificationEmail({
Expand All @@ -109,7 +109,7 @@ const emailTemplates = {
trigger: 'webhook',
duration: '1.1s',
cost: '$0.0021',
logUrl: 'https://sim.ai/workspace/ws_123/logs?search=exec_abc123',
logUrl: 'https://sim.ai/workspace/ws_123/logs?executionId=exec_abc123',
}),
'workflow-notification-alert': () =>
renderWorkflowNotificationEmail({
Expand All @@ -118,7 +118,7 @@ const emailTemplates = {
trigger: 'schedule',
duration: '45.2s',
cost: '$0.0156',
logUrl: 'https://sim.ai/workspace/ws_123/logs?search=exec_abc123',
logUrl: 'https://sim.ai/workspace/ws_123/logs?executionId=exec_abc123',
alertReason: '3 consecutive failures detected',
}),
'workflow-notification-full': () =>
Expand All @@ -128,7 +128,7 @@ const emailTemplates = {
trigger: 'api',
duration: '12.5s',
cost: '$0.0234',
logUrl: 'https://sim.ai/workspace/ws_123/logs?search=exec_abc123',
logUrl: 'https://sim.ai/workspace/ws_123/logs?executionId=exec_abc123',
finalOutput: { processed: 150, skipped: 3, status: 'completed' },
rateLimits: {
sync: { requestsPerMinute: 60, remaining: 45 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { useParams } from 'next/navigation'
import { Button, Combobox } from '@/components/emcn/components'
import { Progress } from '@/components/ui/progress'
import { cn } from '@/lib/core/utils/cn'
import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace'
import { getExtensionFromMimeType } from '@/lib/uploads/utils/file-utils'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'

Expand Down Expand Up @@ -150,8 +150,6 @@ export function FileUpload({
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlockId)
const [uploadingFiles, setUploadingFiles] = useState<UploadingFile[]>([])
const [uploadProgress, setUploadProgress] = useState(0)
const [workspaceFiles, setWorkspaceFiles] = useState<WorkspaceFileRecord[]>([])
const [loadingWorkspaceFiles, setLoadingWorkspaceFiles] = useState(false)
const [uploadError, setUploadError] = useState<string | null>(null)
const [inputValue, setInputValue] = useState('')

Expand All @@ -163,25 +161,13 @@ export function FileUpload({
const params = useParams()
const workspaceId = params?.workspaceId as string

const value = isPreview ? previewValue : storeValue

const loadWorkspaceFiles = async () => {
if (!workspaceId || isPreview) return

try {
setLoadingWorkspaceFiles(true)
const response = await fetch(`/api/workspaces/${workspaceId}/files`)
const data = await response.json()
const {
data: workspaceFiles = [],
isLoading: loadingWorkspaceFiles,
refetch: refetchWorkspaceFiles,
} = useWorkspaceFiles(isPreview ? '' : workspaceId)

if (data.success) {
setWorkspaceFiles(data.files || [])
}
} catch (error) {
logger.error('Error loading workspace files:', error)
} finally {
setLoadingWorkspaceFiles(false)
}
}
const value = isPreview ? previewValue : storeValue

/**
* Checks if a file's MIME type matches the accepted types
Expand Down Expand Up @@ -226,10 +212,6 @@ export function FileUpload({
return !isAlreadySelected
})

useEffect(() => {
void loadWorkspaceFiles()
}, [workspaceId])

/**
* Opens file dialog
*/
Expand Down Expand Up @@ -394,7 +376,7 @@ export function FileUpload({
setUploadError(null)

if (workspaceId) {
void loadWorkspaceFiles()
void refetchWorkspaceFiles()
}

if (uploadedFiles.length === 1) {
Expand Down Expand Up @@ -726,7 +708,7 @@ export function FileUpload({
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
if (open) void refetchWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : '+ Add More'}
disabled={disabled || loadingWorkspaceFiles}
Expand All @@ -746,7 +728,7 @@ export function FileUpload({
onInputChange={handleComboboxChange}
onClear={(e) => handleRemoveFile(filesArray[0], e)}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
if (open) void refetchWorkspaceFiles()
}}
disabled={disabled}
isLoading={loadingWorkspaceFiles}
Expand All @@ -763,7 +745,7 @@ export function FileUpload({
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
if (open) void refetchWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : 'Select or upload file'}
disabled={disabled || loadingWorkspaceFiles}
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/background/workspace-notification-delivery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ function formatCost(cost?: Record<string, unknown>): string {
}

function buildLogUrl(workspaceId: string, executionId: string): string {
return `${getBaseUrl()}/workspace/${workspaceId}/logs?search=${encodeURIComponent(executionId)}`
return `${getBaseUrl()}/workspace/${workspaceId}/logs?executionId=${encodeURIComponent(executionId)}`
}

function formatAlertReason(alertConfig: AlertConfig): string {
Expand Down
51 changes: 36 additions & 15 deletions apps/sim/blocks/blocks/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,23 +318,26 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
condition: { field: 'operation', value: 'file_write' },
mode: 'advanced',
},
{
id: 'appendFile',
title: 'File',
type: 'file-upload' as SubBlockType,
canonicalParamId: 'appendFileInput',
acceptedTypes: '.txt,.md,.json,.csv,.xml,.html,.htm,.yaml,.yml,.log,.rtf',
placeholder: 'Select or upload a workspace file',
mode: 'basic',
condition: { field: 'operation', value: 'file_append' },
required: { field: 'operation', value: 'file_append' },
},
{
id: 'appendFileName',
title: 'File',
type: 'dropdown' as SubBlockType,
placeholder: 'Select a workspace file...',
type: 'short-input' as SubBlockType,
canonicalParamId: 'appendFileInput',
placeholder: 'File name (e.g., notes.md)',
mode: 'advanced',
condition: { field: 'operation', value: 'file_append' },
required: { field: 'operation', value: 'file_append' },
options: [],
fetchOptions: async () => {
const { useWorkflowRegistry } = await import('@/stores/workflows/registry/store')
const workspaceId = useWorkflowRegistry.getState().hydration.workspaceId
if (!workspaceId) return []
const response = await fetch(`/api/workspaces/${workspaceId}/files`)
const data = await response.json()
if (!data.success || !data.files) return []
return data.files.map((f: { name: string }) => ({ label: f.name, id: f.name }))
},
},
{
id: 'appendContent',
Expand Down Expand Up @@ -362,8 +365,26 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
}

if (operation === 'file_append') {
const appendInput = params.appendFileInput
if (!appendInput) {
throw new Error('File is required for append')
}

let fileName: string
if (typeof appendInput === 'string') {
fileName = appendInput.trim()
} else {
const normalized = normalizeFileInput(appendInput, { single: true })
const file = normalized as Record<string, unknown> | null
fileName = (file?.name as string) ?? ''
}

if (!fileName) {
throw new Error('Could not determine file name')
}

return {
fileName: params.appendFileName,
fileName,
content: params.appendContent,
workspaceId: params._context?.workspaceId,
}
Expand Down Expand Up @@ -408,12 +429,12 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
},
inputs: {
operation: { type: 'string', description: 'Operation to perform (read, write, or append)' },
fileInput: { type: 'json', description: 'File input for read (canonical param)' },
fileInput: { type: 'json', description: 'File input for read' },
fileType: { type: 'string', description: 'File type for read' },
fileName: { type: 'string', description: 'Name for a new file (write)' },
content: { type: 'string', description: 'File content to write' },
contentType: { type: 'string', description: 'MIME content type for write' },
appendFileName: { type: 'string', description: 'Name of existing file to append to' },
appendFileInput: { type: 'json', description: 'File to append to' },
appendContent: { type: 'string', description: 'Content to append to file' },
},
outputs: {
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/blocks/blocks/profound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const ProfoundBlock: BlockConfig = {
category: 'tools',
integrationType: IntegrationType.Analytics,
tags: ['seo', 'data-analytics'],
bgColor: '#1A1A2E',
bgColor: '#000000',
icon: ProfoundIcon,
authMode: AuthMode.ApiKey,

Expand Down
Loading