File Explorer

Bundled file explorer module for browsing, editing, and uploading filesystem content.

What it does

file-explorer is a bundled module for navigating filesystem paths in the running Clapilot environment (workspace/container-mounted paths). It combines a Finder-style file table with an inline editor/preview pane: browse and search folders, open and edit text files, preview browser-renderable documents, create folders, and upload individual files or whole folders.

Since March 6, 2026, file-explorer is rendered directly inside the React app shell instead of an iframe (it was the first bundled module to move). The module backend contract stayed unchanged; only the UI host moved into the standard Clapilot view hierarchy.

How to open it

  • Web: /modules/file-explorer (bundled module, slug file-explorer; manifest bundled-modules/file-explorer/module.json, entry: index.html, renderer: react, icon folder).
  • Backend: bundled-modules/file-explorer/api/handler.mjs.
  • React host UI: src/components/modules/file-explorer-module.tsx; route integration: src/app/(app)/modules/[slug]/page.tsx.
  • Legacy bundled frontend assets remain in bundled-modules/file-explorer/ui/index.ts (compiled to ui/index.js) but are not the active runtime while renderer is react.

Key workflows

Browse and search

  1. Pick a root or type a path in the path selector; absolute and relative navigation are supported, with back/forward history buttons.
  2. When the open directory has a parent, the table shows a .. row pinned above the entries (hidden while searching); clicking it navigates to the parent folder, same as the Up button in the list header.
  3. Sort the Finder-style table by name, modified time, size, or type (ascending/descending).
  4. Use the search input to search the currently opened folder and all subfolders by name (GET /search). Results show the relative subfolder location under each entry name; a truncation hint appears when the capped recursive walk stops early.
  5. The last opened directory is restored after a browser reload: the current path is mirrored into the ?path= URL query parameter and into localStorage, so reloading (or sharing the URL) reopens the same folder. If the remembered path no longer exists, the module falls back to the default root.

Open, preview, and edit files

  1. Open a text file to edit it in the inline editor canvas on the right side of the file list, then save it back (POST /save-file).
  2. Images, PDFs, audio, and video render inline in the right-side preview pane instead of falling back to the text editor; browser-renderable files are not blocked by a fixed inline preview size limit.
  3. Download fetches the raw file.

Manage and upload

  1. Row actions: Open, Download, Copy Path, Rename, Delete, New Folder Here, Upload Files/Folder Here. They are available both from the row ... dropdown and from a right-click context menu on any row.
  2. Right-clicking the empty list area opens a folder context menu with New Folder, Upload Files, Upload Folder, and Refresh for the current directory.
  3. Create-folder, rename, and delete flows use app-styled dialogs (no browser-native prompt/confirm).
  4. Folder upload uses browser directory selection (webkitdirectory) and preserves nested relative paths.
  5. Drag & drop supports both individual files and folders and uploads into the currently open directory. Single-file uploads go through the same upload-folder endpoint as folder uploads.

How the agent can drive it

The File Explorer is UI-only: there are no file_explorer_* agent tools. Agents that need filesystem access use their native runtime shell/file tool contracts instead. Other clients may reuse the module API directly — for example, the Apple client downloads chat media through the authenticated GET /api/modules/file-explorer/api/download route.

Module API reference

Base: /api/modules/file-explorer/api

RoutePurpose
GET /rootslist available root paths
GET /list?path=<path>list a directory
GET /search?path=<path>&q=<query>recursive name search under a directory; returns { results, truncated }, capped at 200 results / depth 12 / 25k scanned entries / 8s, skipping node_modules, .git, .next, and /proc, /sys, /dev, /run
GET /file?path=<path>read file content
GET /download?path=<path>download raw file
POST /mkdir ({ path })create a folder
POST /rename ({ path, newName })rename a file/folder
POST /delete ({ path })delete a file/folder
POST /upload-folder (multipart/form-data with targetPath + repeated files)upload files or whole folders
POST /save-file ({ path, content })save inline-edited text content

Configuration & limits

  • Paths are restricted to the roots exposed by GET /roots, and the backend enforces that restriction on every read and write: any resolved path outside the allowed roots is rejected with 403. GET /roots is derived from the same allowlist the API enforces, so it never advertises a root that would be rejected or that does not exist on disk.
  • Containment is enforced on the resolved real path, not on the requested string: existing targets are resolved with realpath, and creation targets are resolved through the nearest existing parent directory plus the remaining lexical suffix. A symlink inside an allowed root that points outside it (and a dangling symlink) is rejected with 403 for every read, write, rename, delete, upload, and download. Authorization runs immediately before each filesystem operation; a residual TOCTOU window remains for callers that can already create symlinks inside an allowed root.
  • Default roots for normal workspace users are the workspace directory and any paths configured via FILE_EXPLORER_ROOTS. The workspace directory is resolved with the same precedence as src/lib/runtime-env.ts (CLAPILOT_WORKSPACE_DIR, then OPENCLAW_WORKSPACE_DIR, default /app/workspace), so the module agrees with the app in Docker and in npm run dev:local-native.
  • The module directory is a read-only root for non-privileged users. It holds product code that the module API route imports and executes (api/handler.mjs), so save-file, mkdir, rename, delete, and upload-folder are rejected with 403 there.
  • The runtime state directory (CLAPILOTAICORE_HOME / CLAPILOTAICORE_STATE_DIR, default /app/workspace/.clapilotaicore) is denied for non-privileged users and hidden from listings and search, because it holds clapilotaicore.json, system-auth.json, CODEX_HOME, and the CLI homes (provider API keys and auth tokens).
  • rename and delete reject a target that is an allowed root itself, so a single request can no longer move or recursively wipe a whole root.
  • The Container Root (/) entry is a privileged root: it is only offered — and only accepted by the API — for admins on an instance with Developer mode enabled, or for trusted service/agent callers. This holds even when / is configured via FILE_EXPLORER_ROOTS. Non-privileged users previously reached the whole container filesystem through this root; the gate is declared in src/lib/module-store/developer-mode-modules.ts and injected per request by src/app/api/modules/[slug]/api/[...endpointPath]/route.ts.
  • The module uses the same app-shell layout behavior as native Clapilot pages, so chat/sidebar sizing is handled by the main React hierarchy instead of iframe host offsets.

Troubleshooting

  • A file opens as garbled text: non-text formats render in the preview pane only when the browser can display them; other binary formats should be downloaded instead of opened.
  • Uploads land in the wrong place: drag & drop always uploads into the currently open directory; navigate there first or use Upload Folder Here on the target row.

File context menu

Right-click a file for Rename, Copy, Share, Download and Delete. Copy creates an independent sibling file without overwriting existing files. Share opens Email or Chat (Channel/Direct message); printable PDF/images also support Fax and Post. Sharing prepares a revocable document link or a review screen and never sends immediately. All filesystem operations retain File Manager path restrictions.

Internal chat sharing links to /modules/file-explorer?path=<parent>&file=<absolute-path> with URL-encoded values. Opening the link loads the parent directory and selects and opens the exact file, retaining File Manager authorization. A missing or unauthorized linked file does not fall back to a different directory. The Apple clients open these authenticated file links in-app.

Internal links in chat automatically show authenticated inline media or a clickable reference to the original item. Videos have playback controls, images have previews, and other documents/module items use document-style references. This also applies to existing messages and the Apple clients; no exported copy or public link is generated.