Skip to content
Prev Previous commit
Next Next commit
Add file block write operation
  • Loading branch information
Theodore Li committed Mar 19, 2026
commit 7d0baa4825a0067b8b53afd033c2689fa470aa29
71 changes: 24 additions & 47 deletions apps/sim/app/api/tools/sim-file/manage/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import {
deleteWorkspaceFile,
downloadWorkspaceFile,
getWorkspaceFile,
getWorkspaceFileByName,
Expand Down Expand Up @@ -75,26 +74,31 @@ export async function POST(request: NextRequest) {
}

if (fileName && !fileId) {
if (append) {
const existing = await getWorkspaceFileByName(workspaceId, fileName)
if (existing) {
const existing = await getWorkspaceFileByName(workspaceId, fileName)

if (existing) {
let finalContent: string
if (append) {
const existingBuffer = await downloadWorkspaceFile(existing)
const existingContent = existingBuffer.toString('utf-8')
const finalContent = existingContent + content
const fileBuffer = Buffer.from(finalContent, 'utf-8')
await updateWorkspaceFileContent(workspaceId, existing.id, userId, fileBuffer)

logger.info('File appended by name', {
fileId: existing.id,
name: existing.name,
size: fileBuffer.length,
})

return NextResponse.json({
success: true,
data: { id: existing.id, name: existing.name, size: fileBuffer.length },
})
finalContent = existingBuffer.toString('utf-8') + content
} else {
finalContent = content ?? ''
}

const fileBuffer = Buffer.from(finalContent, 'utf-8')
await updateWorkspaceFileContent(workspaceId, existing.id, userId, fileBuffer)

logger.info('File overwritten by name', {
fileId: existing.id,
name: existing.name,
size: fileBuffer.length,
append,
})

return NextResponse.json({
success: true,
data: { id: existing.id, name: existing.name, size: fileBuffer.length },
})
}

const mimeType = inferContentType(fileName, contentType)
Expand Down Expand Up @@ -162,36 +166,9 @@ export async function POST(request: NextRequest) {
)
}

case 'delete': {
const fileId = body.fileId as string | undefined
if (!fileId) {
return NextResponse.json(
{ success: false, error: 'fileId is required for delete operation' },
{ status: 400 }
)
}

const fileRecord = await getWorkspaceFile(workspaceId, fileId)
if (!fileRecord) {
return NextResponse.json(
{ success: false, error: `File with ID "${fileId}" not found` },
{ status: 404 }
)
}

await deleteWorkspaceFile(workspaceId, fileId)

logger.info('Sim file deleted', { fileId, name: fileRecord.name })

return NextResponse.json({
success: true,
data: { id: fileId, name: fileRecord.name },
})
}

default:
return NextResponse.json(
{ success: false, error: `Unknown operation: ${operation}. Supported: write, delete` },
{ success: false, error: `Unknown operation: ${operation}. Supported: write` },
{ status: 400 }
)
}
Expand Down
29 changes: 10 additions & 19 deletions apps/sim/blocks/blocks/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,9 @@ export const FileV2Block: BlockConfig<FileParserOutput> = {
export const FileV3Block: BlockConfig<FileParserV3Output> = {
type: 'file_v3',
name: 'File',
description: 'Read, write, or delete workspace files',
description: 'Read and write workspace files',
longDescription:
'Read and parse files from uploads or URLs, write new or update existing workspace resource files (with optional append), or delete workspace files.',
'Read and parse files from uploads or URLs, or write workspace resource files. Writing by name creates the file if it does not exist, or overwrites it if it does. Use append mode to add content to existing files.',
docsLink: 'https://docs.sim.ai/tools/file',
category: 'tools',
bgColor: '#40916C',
Expand All @@ -262,7 +262,6 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
options: [
{ label: 'Read', id: 'file_parser_v3' },
{ label: 'Write', id: 'file_write' },
{ label: 'Delete', id: 'file_delete' },
],
value: () => 'file_parser_v3',
},
Expand Down Expand Up @@ -293,16 +292,15 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
id: 'fileName',
title: 'File Name',
type: 'short-input' as SubBlockType,
placeholder: 'New file name (e.g., data.csv)',
placeholder: 'File name (e.g., data.csv) — overwrites if exists',
condition: { field: 'operation', value: 'file_write' },
},
{
id: 'fileId',
title: 'File ID',
type: 'short-input' as SubBlockType,
placeholder: 'Existing file ID',
condition: { field: 'operation', value: ['file_write', 'file_delete'] },
required: { field: 'operation', value: 'file_delete' },
placeholder: 'Existing file ID to update',
condition: { field: 'operation', value: 'file_write' },
},
{
id: 'content',
Expand All @@ -328,7 +326,7 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
},
],
tools: {
access: ['file_parser_v3', 'file_write', 'file_delete'],
access: ['file_parser_v3', 'file_write'],
config: {
tool: (params) => params.operation || 'file_parser_v3',
params: (params) => {
Expand All @@ -345,13 +343,6 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
}
}

Comment thread
TheodoreSpeaks marked this conversation as resolved.
if (operation === 'file_delete') {
return {
fileId: params.fileId,
workspaceId: params._context?.workspaceId,
}
}

const fileInput = params.fileInput
if (!fileInput) {
logger.error('No file input provided')
Expand Down Expand Up @@ -388,11 +379,11 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform (read, write, delete)' },
operation: { type: 'string', description: 'Operation to perform (read or write)' },
fileInput: { type: 'json', description: 'File input for read (canonical param)' },
fileType: { type: 'string', description: 'File type for read' },
fileName: { type: 'string', description: 'Name for a new file (write)' },
fileId: { type: 'string', description: 'ID of an existing file (write/delete)' },
fileId: { type: 'string', description: 'ID of an existing file to update (write)' },
content: { type: 'string', description: 'File content to write' },
contentType: { type: 'string', description: 'MIME content type for write' },
append: { type: 'string', description: 'Whether to append content (write)' },
Expand All @@ -408,11 +399,11 @@ export const FileV3Block: BlockConfig<FileParserV3Output> = {
},
id: {
type: 'string',
description: 'File ID (write/delete)',
description: 'File ID (write)',
},
name: {
type: 'string',
description: 'File name (write/delete)',
description: 'File name (write)',
},
size: {
type: 'number',
Expand Down
6 changes: 3 additions & 3 deletions apps/sim/lib/core/config/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export const isTest = env.NODE_ENV === 'test'
/**
* Is this the hosted version of the application
*/
export const isHosted =
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.sim.ai' ||
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.staging.sim.ai'
export const isHosted = true;
// getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.sim.ai' ||
// getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.staging.sim.ai'

/**
* Is billing enforcement enabled
Expand Down
46 changes: 0 additions & 46 deletions apps/sim/tools/file/delete.ts

This file was deleted.

1 change: 0 additions & 1 deletion apps/sim/tools/file/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { fileParserTool, fileParserV2Tool, fileParserV3Tool } from '@/tools/file/parser'

export { fileDeleteTool } from '@/tools/file/delete'
export { fileWriteTool } from '@/tools/file/write'

export const fileParseTool = fileParserTool
Expand Down
7 changes: 4 additions & 3 deletions apps/sim/tools/file/write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ export const fileWriteTool: ToolConfig<FileWriteParams, ToolResponse> = {
id: 'file_write',
name: 'File Write',
description:
'Write content to a workspace resource file. Provide fileName to create a new file, or fileId to update an existing one. Use append mode to add content to the end of an existing file.',
'Write content to a workspace resource file. Provide fileName to create a new file or overwrite an existing one with the same name. Provide fileId to update a specific file. Use append mode to add content to the end instead of replacing.',
version: '1.0.0',

params: {
fileName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Name for a new file (e.g., "data.csv"). Provide this to create a new file.',
description:
'File name (e.g., "data.csv"). Creates the file if it does not exist, or overwrites/appends to it if it does.',
},
fileId: {
type: 'string',
Expand All @@ -48,7 +49,7 @@ export const fileWriteTool: ToolConfig<FileWriteParams, ToolResponse> = {
default: false,
visibility: 'user-only',
description:
'When true, appends content to the end of an existing file instead of replacing it.',
'When true, appends content to the end of an existing file. When false (default), replaces the file content entirely.',
},
},

Expand Down
2 changes: 0 additions & 2 deletions apps/sim/tools/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,6 @@ import {
fathomListTeamsTool,
} from '@/tools/fathom'
import {
fileDeleteTool,
fileParserV2Tool,
fileParserV3Tool,
fileParseTool,
Expand Down Expand Up @@ -2423,7 +2422,6 @@ export const tools: Record<string, ToolConfig> = {
file_parser_v2: fileParserV2Tool,
file_parser_v3: fileParserV3Tool,
file_write: fileWriteTool,
file_delete: fileDeleteTool,
firecrawl_scrape: firecrawlScrapeTool,
firecrawl_search: firecrawlSearchTool,
firecrawl_crawl: firecrawlCrawlTool,
Expand Down