Copy page
View as Markdown View this page as plain text

File System

Each thread has an isolated file system for storing files, images, and other binary data. It is addressable through ThreadState and survives execution cycles — two separate invocations of the same thread see the same file tree.

This page documents the file system APIs. For an overview of threads themselves, see Threads.

1. File Record

Files are represented by the FileRecord type:

PropertyTypeDescription
pathstringFile path (relative to thread root)
namestringFile name
mimeTypestringMIME type
storagestringImplementation-defined storage identifier
sizenumberFile size in bytes
isDirectorybooleanWhether this is a directory
metadataRecord<string, unknown>Custom metadata
widthnumberImage width in pixels (if applicable)
heightnumberImage height in pixels (if applicable)

2. Writing Files

// Write a text file
const record = await state.writeFile(
  '/data/config.json',
  JSON.stringify({ key: 'value' }),
  'application/json'
);

// Write a binary file with metadata
const imageRecord = await state.writeFile(
  '/images/photo.jpg',
  imageBuffer,
  'image/jpeg',
  { width: 1920, height: 1080 }
);

3. Reading Files

// Read file content
const data = await state.readFile('/data/config.json');
if (data) {
  const text = new TextDecoder().decode(data);
  const config = JSON.parse(text);
}

// Get file metadata without reading content
const info = await state.statFile('/images/photo.jpg');
if (info) {
  console.log(`Size: ${info.size} bytes`);
}

4. Directory Operations

// Create a directory
await state.mkdirFile('/documents');

// List directory contents
const { entries } = await state.readdirFile('/documents');
for (const entry of entries) {
  console.log(`${entry.isDirectory ? 'DIR' : 'FILE'}: ${entry.name}`);
}

// Remove an empty directory
await state.rmdirFile('/documents');

// Delete a file
await state.unlinkFile('/data/config.json');

5. Search Operations

// Search file contents
const grepResults = await state.grepFiles('error');
for (const result of grepResults) {
  console.log(`${result.path}:`);
  for (const match of result.matches) {
    console.log(`  Line ${match.line}: ${match.content}`);
  }
}

// Find files by glob pattern
const { paths } = await state.findFiles('**/*.json');
console.log('JSON files:', paths);

6. File Statistics

const stats = await state.getFileStats();
console.log(`Files: ${stats.fileCount}`);
console.log(`Directories: ${stats.directoryCount}`);
console.log(`Total size: ${stats.totalSize} bytes`);

7. Image Thumbnails

const thumbnail = await state.getFileThumbnail('/images/photo.jpg');
if (thumbnail) {
  // Use thumbnail data
}

8. Streaming Files

For large files, readFileStream provides memory-efficient access by yielding data in chunks.

8.1 Method

readFileStream(
  path: string,
  options?: ReadFileStreamOptions
): Promise<AsyncIterable<FileChunk> | null>

8.2 Options

PropertyTypeDescription
signalAbortSignalOptional abort signal for cancellation

8.3 FileChunk

PropertyTypeDescription
dataUint8ArrayRaw binary data for this chunk
indexnumber0-based index of this chunk
totalChunksnumberTotal number of chunks
isLastbooleanWhether this is the final chunk

8.4 Behavior

  1. File Not Found: Returns null if the file does not exist.
  2. External Files: Returns null for files whose storage identifier references an external system (object storage, a remote URL, etc.). External files must be fetched using their storage location.
  3. Small Files: Files under the chunk threshold are yielded as a single chunk with index: 0, totalChunks: 1, and isLast: true.
  4. Chunked Files: Large files yield multiple chunks in sequential order (0, 1, 2…).
  5. Ordering Guarantee: Chunks are always yielded in sequential order. Implementations MUST NOT yield chunks out of order.
  6. Abort Signal: When triggered, the stream stops yielding new chunks. Already-yielded chunks remain valid.

8.5 Example: Processing Large File

const stream = await state.readFileStream('/uploads/video.mp4');
if (!stream) {
  throw new Error('File not found');
}

for await (const chunk of stream) {
  console.log(`Progress: ${chunk.index + 1}/${chunk.totalChunks}`);
  // Process chunk.data without loading entire file into memory
}

8.6 Example: HTTP Streaming Response

const stream = await state.readFileStream('/uploads/large-file.bin');
if (!stream) {
  return new Response('Not Found', { status: 404 });
}

const readable = new ReadableStream({
  async start(controller) {
    for await (const chunk of stream) {
      controller.enqueue(chunk.data);
    }
    controller.close();
  }
});

return new Response(readable, {
  headers: { 'Content-Type': 'application/octet-stream' }
});