@mastra/core
Version:
Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.
359 lines (219 loc) • 10.6 kB
Markdown
# LocalFilesystem
**Added in:** `@mastra/core@1.1.0`
Stores files in a directory on the local filesystem.
> **Info:** For interface details, see [WorkspaceFilesystem Interface](https://mastra.ai/reference/workspace/filesystem).
## Usage
Add a `LocalFilesystem` to a workspace and assign it to an agent. The agent can then read, write, and manage files as part of its tasks:
```typescript
import { Agent } from '@mastra/core/agent'
import { Workspace, LocalFilesystem } from '@mastra/core/workspace'
const workspace = new Workspace({
filesystem: new LocalFilesystem({
basePath: './workspace',
}),
})
const agent = new Agent({
id: 'file-agent',
model: 'openai/gpt-5.4',
workspace,
})
// The agent now has filesystem tools available
const response = await agent.generate('List all files in the workspace')
```
## Constructor parameters
**basePath** (`string`): Base directory path on disk. All file paths are resolved relative to this directory.
**id** (`string`): Unique identifier for this filesystem instance (Default: `Auto-generated`)
**contained** (`boolean`): When true, all file operations are restricted to stay within basePath. Prevents path traversal attacks and symlink escapes. See \[containment]\(/docs/workspace/filesystem#containment). (Default: `true`)
**allowedPaths** (`string[]`): Additional directories the agent can access outside of \`basePath\`. (Default: `[]`)
**instructions** (`string | ((opts: { defaultInstructions: string; requestContext?: RequestContext }) => string)`): Custom instructions that override the default instructions returned by getInstructions(). Pass a string to fully replace them, or a function to extend them with access to the current requestContext for per-request customization.
**readOnly** (`boolean`): When true, all write operations are blocked. Read operations are still allowed. (Default: `false`)
## Properties
**id** (`string`): Filesystem instance identifier
**name** (`string`): Provider name ('LocalFilesystem')
**provider** (`string`): Provider identifier ('local')
**basePath** (`string`): The absolute base path on disk
**readOnly** (`boolean | undefined`): Whether the filesystem is in read-only mode
**allowedPaths** (`readonly string[]`): Current set of resolved allowed paths. These paths are permitted beyond basePath when containment is enabled.
## Methods
### `init()`
Initialize the filesystem. Creates the base directory if it doesn't exist.
```typescript
await filesystem.init()
```
Called by `workspace.init()`.
### Lazy initialization
LocalFilesystem initializes on first operation if not already initialized, creating the base directory automatically. Calling `init()` explicitly is optional but can be useful to pre-create directories before the first operation.
### `destroy()`
Clean up filesystem resources.
```typescript
await filesystem.destroy()
```
Called by `workspace.destroy()`.
### `setAllowedPaths(pathsOrUpdater)`
Update the allowed paths at runtime. Accepts a new paths array (replaces current) or an updater callback that receives the current paths and returns the new set.
```typescript
// Set directly
filesystem.setAllowedPaths(['/home/user/.config'])
// Update with callback
filesystem.setAllowedPaths(prev => [...prev, '/home/user/documents'])
// Clear all allowed paths
filesystem.setAllowedPaths([])
```
**Parameters:**
**pathsOrUpdater** (`string[] | ((current: readonly string[]) => string[])`): New allowed paths array or updater function receiving current paths
### `readFile(path, options?)`
Read file contents.
```typescript
const content = await filesystem.readFile('/docs/guide.md')
const buffer = await filesystem.readFile('/image.png', { encoding: 'binary' })
```
**Parameters:**
**path** (`string`): File path relative to basePath
**options** (`Options`): Configuration options.
**options.encoding** (`'utf-8' | 'binary'`): Text or binary encoding
### `writeFile(path, content, options?)`
Write content to a file.
```typescript
await filesystem.writeFile('/docs/new.md', '# New Document')
await filesystem.writeFile('/nested/path/file.md', content, { recursive: true })
```
**Parameters:**
**path** (`string`): File path relative to basePath
**content** (`string | Buffer`): File content
**options** (`Options`): Configuration options.
**options.recursive** (`boolean`): Create parent directories if they don't exist
**options.overwrite** (`boolean`): Overwrite existing file
**options.expectedMtime** (`Date`): If provided, the write fails with a StaleFileError when the file's current modification time doesn't match. Use this for optimistic concurrency control to detect external modifications between read and write.
### `appendFile(path, content)`
Append content to an existing file.
```typescript
await filesystem.appendFile('/logs/app.log', 'New log entry\n')
```
**Parameters:**
**path** (`string`): File path relative to basePath
**content** (`string | Buffer`): Content to append
### `deleteFile(path, options?)`
Delete a file.
```typescript
await filesystem.deleteFile('/docs/old.md')
await filesystem.deleteFile('/docs/maybe.md', { force: true }) // Don't throw if missing
```
**Parameters:**
**path** (`string`): File path
**options** (`Options`): Configuration options.
**options.force** (`boolean`): Don't throw error if file doesn't exist
### `copyFile(src, dest, options?)`
Copy a file to a new location.
```typescript
await filesystem.copyFile('/docs/template.md', '/docs/new-doc.md')
await filesystem.copyFile('/src/config.json', '/backup/config.json', { overwrite: false })
```
**Parameters:**
**src** (`string`): Source file path
**dest** (`string`): Destination file path
**options** (`Options`): Configuration options.
**options.overwrite** (`boolean`): Overwrite destination if it exists
### `moveFile(src, dest, options?)`
Move or rename a file.
```typescript
await filesystem.moveFile('/docs/draft.md', '/docs/final.md')
await filesystem.moveFile('/temp/upload.txt', '/files/document.txt')
```
**Parameters:**
**src** (`string`): Source file path
**dest** (`string`): Destination file path
**options** (`Options`): Configuration options.
**options.overwrite** (`boolean`): Overwrite destination if it exists
### `mkdir(path, options?)`
Create a directory.
```typescript
await filesystem.mkdir('/docs/api')
await filesystem.mkdir('/deeply/nested/path', { recursive: true })
```
**Parameters:**
**path** (`string`): Directory path
**options** (`Options`): Configuration options.
**options.recursive** (`boolean`): Create parent directories
### `rmdir(path, options?)`
Remove a directory.
```typescript
await filesystem.rmdir('/docs/old')
await filesystem.rmdir('/docs/nested', { recursive: true })
```
**Parameters:**
**path** (`string`): Directory path
**options** (`Options`): Configuration options.
**options.recursive** (`boolean`): Remove contents recursively
**options.force** (`boolean`): Don't throw if directory doesn't exist
### `readdir(path, options?)`
List directory contents.
```typescript
const entries = await filesystem.readdir('/docs')
// [{ name: 'guide.md', type: 'file' }, { name: 'api', type: 'directory' }]
```
### `exists(path)`
Check if a path exists.
```typescript
const exists = await filesystem.exists('/docs/guide.md')
```
### `stat(path)`
Get file or directory metadata.
```typescript
const stat = await filesystem.stat('/docs/guide.md')
// { type: 'file', size: 1234, modifiedAt: Date, createdAt: Date, path: '/docs/guide.md' }
```
### `getInfo()`
Returns metadata about this filesystem instance.
```typescript
const info = filesystem.getInfo()
// { id: '...', name: 'LocalFilesystem', provider: 'local', basePath: '/workspace', readOnly: false }
```
### `getInstructions(opts?)`
Returns a description of how paths work in this filesystem. When assigned to an agent, this is injected into the agent's system message.
```typescript
const instructions = filesystem.getInstructions()
// 'Local filesystem at "/workspace". Files at workspace path "/foo" are stored at "/workspace/foo" on disk.'
```
Pass `requestContext` to enable per-request customization when the `instructions` constructor option is a function:
```typescript
const instructions = filesystem.getInstructions({ requestContext })
```
**Parameters:**
**opts.requestContext** (`RequestContext`): Forwarded to the \`instructions\` function if one was provided in the constructor.
**Returns:** `string`
To override the default output, pass an `instructions` option to the constructor. See [constructor parameters](#constructor-parameters).
## Path resolution
### How `basePath` works
The `basePath` option sets the root directory for all file operations. File paths passed to methods like `readFile()` are resolved relative to this base:
- Leading slashes are stripped: `/docs/guide.md` → `docs/guide.md`
- The path is normalized and joined with basePath
- Result: `./workspace` + `docs/guide.md` → `./workspace/docs/guide.md`
```typescript
const filesystem = new LocalFilesystem({
basePath: './workspace',
})
// These all resolve to ./workspace/docs/guide.md
await filesystem.readFile('/docs/guide.md')
await filesystem.readFile('docs/guide.md')
```
### Relative paths and execution context
When you use a relative path for `basePath`, it resolves from `process.cwd()`. In Mastra projects, cwd varies depending on how you run your code:
| Context | Working directory | `./workspace` resolves to |
| -------------- | ------------------------- | ------------------------------- |
| `mastra dev` | `./src/mastra/public/` | `./src/mastra/public/workspace` |
| `mastra start` | `./.mastra/output/` | `./.mastra/output/workspace` |
| Direct script | Where you ran the command | Relative to that location |
This can cause confusion when the same relative path resolves to different locations.
### Recommended: Use absolute paths
For consistent paths across all execution contexts, use an environment variable with an absolute path:
```typescript
import { LocalFilesystem } from '@mastra/core/workspace'
const filesystem = new LocalFilesystem({
basePath: process.env.WORKSPACE_PATH!,
})
```
Set `WORKSPACE_PATH` in your environment to an absolute path like `/home/user/my-project/workspace`. This ensures the workspace path is consistent regardless of how you run your code.
## Related
- [WorkspaceFilesystem interface](https://mastra.ai/reference/workspace/filesystem)
- [Workspace class](https://mastra.ai/reference/workspace/workspace-class)
- [Workspace overview](https://mastra.ai/docs/workspace/overview)