File Explorer
The File Explorer lets you browse the runner’s filesystem directly from the PizzaPi web UI — no terminal or SSH required. It appears as a docked side panel alongside the session viewer, showing the directory tree rooted at the agent’s current working directory.
Opening the File Explorer
Section titled “Opening the File Explorer”Toggle the File Explorer with the keyboard shortcut ⌘⇧E (macOS) or Ctrl+Shift+E (Windows/Linux). You can also click the folder icon button in the session viewer header bar (desktop only; on mobile use the keyboard shortcut).
The button only appears when the active session has a connected runner with a known working directory (cwd). If no runner is connected, the button is hidden.
Panel Position
Section titled “Panel Position”On desktop, the File Explorer opens as a docked panel inside the combined panel area. You can reposition it to the left, right, or bottom of the session viewer using the position picker in the panel header. Your position preference persists across sessions.
The panel also supports drag-to-reposition — click and hold the grip handle, then drag to a new edge.
Navigating Directories
Section titled “Navigating Directories”The explorer displays the agent’s working directory as a collapsible tree. Each entry shows:
- Folders — click to expand and lazy-load the directory contents from the runner. Folders show an amber folder icon that switches between closed and open states.
- Files — click to open a read-only preview. Files display a type-specific emoji icon (e.g. 🟦 for
.ts, 🐍 for.py, 🎨 for.css) and their size in human-readable format. - Symlinks — indicated with a
→suffix on the file name.
The breadcrumb bar at the top shows the current root path (cwd). Use the refresh button (↻) to re-fetch the directory listing if the filesystem has changed. Next to it, the Expand all and Collapse all buttons expand every folder up to three levels deep or collapse the whole tree.
Viewing Files
Section titled “Viewing Files”Click any file to open it in the built-in viewer. Press Escape to return to the directory tree.
Text files
Section titled “Text files”Text files open in a read-only monospaced preview with syntax-appropriate formatting. If the file exceeds 512 KB, it is truncated and a warning banner appears:
File truncated (showing first 512 KB of total size)
Markdown files
Section titled “Markdown files”Markdown (.md and .mdx) files open in a dedicated Markdown viewer with a Preview / Raw toggle. Preview renders the file with LazyStreamdown formatting; Raw shows the plain source text. Like other text files, large Markdown files are read-only and truncated at 512 KB.
Images
Section titled “Images”Image files (.png, .jpg, .jpeg, .gif, .webp, .svg, .bmp, .ico, .avif) open in a dedicated image viewer with:
- Zoom controls — zoom in, zoom out, and reset buttons with a percentage indicator
- Fit modes — toggle between “fit to view” (contain) and “actual size”
- Transparency checkerboard — a checkerboard is always shown behind rendered images, which conveniently handles images with alpha channels
- Dimensions — natural width × height shown in the header
Images are fetched as base64-encoded data with a 10 MB size limit.
Videos
Section titled “Videos”Video files (.mp4, .m4v, .mov, .webm, .ogv, .ogg) open in the browser’s native video player with playback, volume, timeline, and fullscreen controls. Playback support depends on the browser and the video’s codec.
Videos have a 10 MB preview limit. Larger files show their size instead of transferring a partial, unplayable video.
File Search (@ Mentions)
Section titled “File Search (@ Mentions)”The file search API powers the @-mention autocomplete in the message composer. When you type @ followed by a filename fragment, PizzaPi searches git-tracked files (plus untracked files that are not ignored) under the working directory and presents matching files as suggestions. If the workspace is not a git repository, the search returns an empty list with no error. This uses the same search-files endpoint described below.
REST API
Section titled “REST API”The File Explorer is backed by three server endpoints. All require an authenticated session (cookie-based auth) and verify that the requesting user owns the runner.
List files
Section titled “List files”POST /api/runners/:runnerId/filesRequest body:
{ "path": "/home/user/project/src"}Response:
{ "ok": true, "files": [ { "name": "index.ts", "path": "/home/user/project/src/index.ts", "isDirectory": false, "isSymlink": false, "size": 2048 }, { "name": "components", "path": "/home/user/project/src/components", "isDirectory": true } ]}Each entry in files includes name, path, isDirectory, isSymlink, relativePath, and size in bytes (reported for both files and directories, though directory sizes are usually not meaningful).
Read file
Section titled “Read file”POST /api/runners/:runnerId/read-fileRequest body:
{ "path": "/home/user/project/src/index.ts", "encoding": "utf8"}| Field | Type | Default | Description |
|---|---|---|---|
path | string | (required) | Absolute path to the file |
encoding | "utf8" | "base64" | "utf8" | Response encoding — use base64 for images and binary files |
rejectTruncated | boolean | false | When true, omit content if the file exceeds the encoding limit instead of returning partial content |
Response:
{ "ok": true, "content": "import React from 'react';\n...", "truncated": false, "size": 2048}Size limits depend on encoding:
| Encoding | Max size | Timeout |
|---|---|---|
utf8 | 512 KB | 15 s |
base64 | 10 MB | 30 s |
If the file exceeds the limit, content contains the first N bytes and truncated is true. When rejectTruncated is true, the response still reports size and truncated: true but omits content; video previews use this to avoid transferring unusable partial files.
Search files
Section titled “Search files”POST /api/runners/:runnerId/search-filesRequest body:
{ "cwd": "/home/user/project", "query": "Button", "limit": 50}| Field | Type | Default | Description |
|---|---|---|---|
cwd | string | (required) | Directory to search within |
query | string | (required) | Filename search string (case-insensitive substring match) |
limit | number | 100 | Maximum number of results to return |
Response:
{ "ok": true, "files": [ { "name": "Button.tsx", "path": "/home/user/project/src/components/Button.tsx" }, { "name": "ButtonGroup.tsx", "path": "/home/user/project/src/components/ButtonGroup.tsx" } ]}If query is empty, the endpoint returns { "ok": true, "files": [] } immediately without hitting the runner.
Workspace Root Enforcement
Section titled “Workspace Root Enforcement”All three endpoints enforce workspace root boundaries. The server checks the runner’s configured roots (the allowed workspace directories) and rejects any request where the path or cwd falls outside those roots.
If a request targets a path outside the allowed roots, the server responds with:
{ "error": "Runner cannot access path: /etc/passwd"}This returns HTTP 400. The check uses the cwdMatchesRoots() helper on the server side, ensuring that even crafted API requests cannot escape the workspace sandbox.
Known Limitations
Section titled “Known Limitations”- Large text files are truncated at 512 KB. The full file cannot be streamed through the explorer — use the agent’s
readtool or a terminal for large files. - Binary files (executables, archives, databases) will either fail to render or display garbled content in the text viewer. Only images have dedicated rendering support.
- No editing — the explorer is strictly read-only. There is no write-back capability.
- No file search UI in the explorer panel — file search is currently available only through the @-mention autocomplete in the composer. A dedicated search bar in the explorer panel is planned.
- Symlink targets are indicated with a
→marker but the target path is not displayed. - No syntax highlighting — text files render as plain monospaced text without language-aware coloring.