mirror of
https://github.com/FlowiseAI/Flowise.git
synced 2026-10-12 02:02:17 +00:00
added normalize content to fix potential read grep and glob issues with mimeTypes and Uint8Array, made sure edit and grep must have minimum string in it before performing action
This commit is contained in:
@@ -43,6 +43,19 @@ describe('StateBackend — write/read', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('write of Uint8Array to a text-mimeType path is decoded to a string on read', async () => {
|
||||
const backend = new StateBackend()
|
||||
const bytes = new TextEncoder().encode('hi from bytes')
|
||||
await backend.write('/workspace/decoded.txt', bytes)
|
||||
const result = await backend.read('/workspace/decoded.txt')
|
||||
expect('content' in result).toBe(true)
|
||||
if ('content' in result) {
|
||||
expect(typeof result.content).toBe('string')
|
||||
expect(result.content).toBe('hi from bytes')
|
||||
expect(result.mimeType).toBe('text/plain')
|
||||
}
|
||||
})
|
||||
|
||||
it('constructor accepts initialFiles and makes them readable', async () => {
|
||||
const now = Date.now()
|
||||
const initialFiles: Record<string, FileData> = {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
ReadResult,
|
||||
WriteResult
|
||||
} from '../BackendProtocol'
|
||||
import { escapeRegex, getMimeType, globToRegex, isTextMimeType, paginateLines } from '../utils'
|
||||
import { escapeRegex, getMimeType, globToRegex, isTextMimeType, normalizeContent, paginateLines } from '../utils'
|
||||
|
||||
type FileStore = Record<string, FileData>
|
||||
|
||||
@@ -21,7 +21,10 @@ export class StateBackend implements BackendProtocol {
|
||||
protected files: FileStore
|
||||
|
||||
constructor(initialFiles: FileStore = {}) {
|
||||
this.files = { ...initialFiles }
|
||||
this.files = {}
|
||||
for (const [path, data] of Object.entries(initialFiles)) {
|
||||
this.files[path] = { ...data, content: normalizeContent(data.content, data.mimeType) }
|
||||
}
|
||||
}
|
||||
|
||||
async write(filePath: string, content: string | Uint8Array): Promise<WriteResult> {
|
||||
@@ -30,7 +33,7 @@ export class StateBackend implements BackendProtocol {
|
||||
}
|
||||
const mimeType = getMimeType(filePath)
|
||||
const now = Date.now()
|
||||
const data: FileData = { content, mimeType, created_at: now, modified_at: now }
|
||||
const data: FileData = { content: normalizeContent(content, mimeType), mimeType, created_at: now, modified_at: now }
|
||||
this.files[filePath] = data
|
||||
const filesUpdate: FilesUpdate = { [filePath]: data }
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export function buildFsTools(backend: BackendProtocol, onFilesUpdate?: (update:
|
||||
'Edit an existing text file by replacing a specific string. Fails if the string appears more than once unless replace_all is true.',
|
||||
schema: z.object({
|
||||
file_path: z.string().describe('Absolute path of the file to edit (e.g. /workspace/hello.txt)'),
|
||||
old_str: z.string().describe('Exact string to find and replace'),
|
||||
old_str: z.string().min(1).describe('Exact string to find and replace'),
|
||||
new_str: z.string().describe('String to replace it with'),
|
||||
replace_all: z.boolean().optional().describe('Replace every occurrence instead of requiring exactly one (default false)')
|
||||
}),
|
||||
@@ -87,7 +87,7 @@ export function buildFsTools(backend: BackendProtocol, onFilesUpdate?: (update:
|
||||
name: 'grep_files',
|
||||
description: 'Search file contents for a regex pattern. Returns matching lines with file path and line number.',
|
||||
schema: z.object({
|
||||
pattern: z.string().describe('Regex pattern to search for (e.g. "function|const")'),
|
||||
pattern: z.string().min(1).describe('Regex pattern to search for (e.g. "function|const")'),
|
||||
dir_path: z.string().optional().describe('Directory to search in (default /). Pass null to search all files.'),
|
||||
glob: z.string().optional().describe('Optional glob to filter filenames (e.g. *.ts)')
|
||||
}),
|
||||
|
||||
@@ -93,6 +93,15 @@ export function isTextMimeType(mimeType: string): boolean {
|
||||
return TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p)) || TEXT_MIME_EXACT.has(mimeType)
|
||||
}
|
||||
|
||||
// Coerce content to match its mimeType so backends can rely on
|
||||
// "text mimeType ⇒ string content, binary mimeType ⇒ Uint8Array content" as an invariant.
|
||||
export function normalizeContent(content: string | Uint8Array, mimeType: string): string | Uint8Array {
|
||||
if (isTextMimeType(mimeType)) {
|
||||
return typeof content === 'string' ? content : new TextDecoder().decode(content)
|
||||
}
|
||||
return content instanceof Uint8Array ? content : new TextEncoder().encode(content)
|
||||
}
|
||||
|
||||
export function paginateLines(text: string, offset: number, limit: number): { content: string; truncated: boolean } {
|
||||
const lines = text.split('\n')
|
||||
const truncated = lines.length > offset + limit
|
||||
|
||||
Reference in New Issue
Block a user