mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
Merge #761 feat(picker): New folder button for Create New's custom folder picker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
9ba5a89123
9 files changed
+63
-31
No files matched your search
@@ -905,10 +905,10 @@ html and svg are the ones deliberately kept out.
|
||||
`POST /api/filesystem/mkdir` with `{ path, name, sessionId?, showHidden? }` makes one
|
||||
folder directly inside `path`, which must be a folder the path picker may browse, and
|
||||
answers `{ path }`. `name` is one path segment (letters, numbers, spaces and `_ - . ~`, not
|
||||
starting with a dot or `~`). `400` for a cases folder (use Add Case → New Case) or a `path`
|
||||
that is not a directory, `403` for a blocked tree or a `path` outside the browse roots,
|
||||
`404` for a `path` that does not exist, `409` when the folder already exists, and `422`
|
||||
when the mount under `path` does not answer.
|
||||
starting with a dot or `~`). `400` for a cases folder (use Add Case → Create New with no
|
||||
custom folder) or a `path` that is not a directory, `403` for a blocked tree or a `path`
|
||||
outside the browse roots, `404` for a `path` that does not exist, `409` when the folder
|
||||
already exists, and `422` when the mount under `path` does not answer.
|
||||
|
||||
## Rendered HTML files
|
||||
|
||||
|
||||
@@ -437,7 +437,7 @@ The terminal provider scans `terminalLinkLine()`, which extends `terminalPathLin
|
||||
|
||||
### Filesystem path picker
|
||||
|
||||
**Filesystem path picker** (Link Existing "Browse" button + the extended mobile keyboard's `📁 Path` key): a lazy one-directory-at-a-time browser over `GET /api/filesystem/browse`, with `GET /api/filesystem/preview` serving the tapped file. It starts at the active session's working directory (falling back to the Codeman Cases root, then `/mnt/d`, then the first root), hides dot entries, and inserts the chosen path **without** Enter so the prompt is not submitted. The companion `⌫ All` key clears only the current unsent prompt buffer and must never emit the agent's `/clear` command. **New folder** (opt-in per caller, `allowNewFolder`; Link Existing has it): `POST /api/filesystem/mkdir` `{ path, name, sessionId? }` creates ONE folder (non-recursive) directly inside a folder the picker may browse, resolved through the same roots, ownership and blocked-tree rules as browse (`403` outside them or in a blocked tree or unwritable, `404` missing parent, `409` exists, `400` a parent that is a file, or a name outside letters, digits, space and `_ - . ~`, containing `..`, starting with a dot or a tilde or over 255 bytes, so every name it accepts is one Link Existing accepts, and `400` a parent that is a cases directory, where a folder is already a case (Add Case > New Case makes one there); `showHidden` is forwarded like browse's); the picker reloads the folder and selects the new one. A picker that browses somewhere this route cannot create in (the container-workdir picker, `fetchListing`) must not set the flag.
|
||||
**Filesystem path picker** (every Add Case "Browse" button: Link Existing, Create New's custom folder, Clone Repo, Docker; + the extended mobile keyboard's `📁 Path` key): a lazy one-directory-at-a-time browser over `GET /api/filesystem/browse`, with `GET /api/filesystem/preview` serving the tapped file (Docker's Container Workdir picker passes its own `fetchListing` over `POST /api/docker-cases/browse` instead, admin-only, which always lists dot entries and skips the allowlist, blocked-tree and ownership rules below). It starts at the form field's value or, for the `📁 Path` key, the active session's working directory (falling back to the Codeman Cases root, then `/mnt/d`, then the first root) and hides dot entries. A Browse fills its form field; the `📁 Path` key inserts the chosen path **without** Enter so the prompt is not submitted. The companion `⌫ All` key clears only the current unsent prompt buffer and must never emit the agent's `/clear` command. **New folder** (opt-in per caller, `allowNewFolder`; Link Existing and Create New's custom folder have it): `POST /api/filesystem/mkdir` `{ path, name, sessionId? }` creates ONE folder (non-recursive) directly inside a folder the picker may browse, resolved through the same roots, ownership and blocked-tree rules as browse (`403` outside them or in a blocked tree or unwritable, `404` missing parent, `409` exists, `400` a parent that is a file, or a name outside letters, digits, space and `_ - . ~`, containing `..`, starting with a dot or a tilde or over 255 bytes, so every name it accepts is one Link Existing accepts, and `400` a parent that is a cases directory, where a folder is already a case (Add Case > Create New with no custom folder makes one there); `showHidden` is forwarded like browse's); the picker reloads the folder and selects the new one. A picker that browses somewhere this route cannot create in (the container-workdir picker, `fetchListing`) must not set the flag.
|
||||
|
||||
⚠️ **This is a second file-serving surface, so it carries the same confinement burden as [Attachments](#attachments) and does not inherit it automatically.** Traversal is allowlisted to Home, `CASES_DIR`, `/mnt/d`, or extra roots explicitly configured via `CODEMAN_FILE_PICKER_ROOTS`; sensitive trees are blocked and symlink escapes are rejected after `realpath` resolution rather than before. Without the realpath step a symlink inside an allowed root would walk straight out of it. `preview` reuses the shared conversion cache and the **global** `document-conversion-limiter`, which is what stops N concurrent large-document previews from forking N multi-minute converter processes. Content types are pinned: images and PDF inline, DOCX/PPTX through the converters, and Markdown/TXT/JSON as inert `text/plain` (never `text/html`, which would be stored XSS on our own origin). Size caps are 2MB for text and 50MB for binary/document previews.
|
||||
|
||||
|
||||
@@ -160,30 +160,38 @@ their workspace is bind-mounted at the same path.
|
||||
|
||||
## The path picker
|
||||
|
||||
For choosing a path rather than typing one. It appears in two places:
|
||||
For choosing a path rather than typing one. It appears in these places:
|
||||
|
||||
- **Browse** in **Add Case → Link Existing**.
|
||||
- **Browse** in **Add Case → Create New**, under **Create in a custom folder**.
|
||||
- **Browse** in **Add Case → Clone Repo**, under **Clone into a custom folder**.
|
||||
- **Browse** on the folder fields of **Add Case → Docker**.
|
||||
- The **📁 Path** key on the mobile keyboard bar.
|
||||
|
||||
It browses one directory at a time and can show hidden entries on request. The current
|
||||
folder is an editable field: type or paste a path and press Enter (or **Go**) to jump
|
||||
straight there, and a full file path lands in its folder with that file selected. The
|
||||
**Sort** control orders each listing by name or by modified time (newest first is the
|
||||
quick way to the file an agent just wrote), with folders always ahead of files; the
|
||||
choice is remembered per device. The picker inserts the path into your prompt
|
||||
**without** pressing Enter, so nothing is submitted by accident. Its sibling **⌫ All**
|
||||
key clears the unsent prompt, and never sends the agent's `/clear` command.
|
||||
It browses one directory at a time and can show hidden entries on request (the Docker
|
||||
**Container Workdir** picker always lists them). The current folder is an editable
|
||||
field: type or paste a path and press Enter (or **Go**) to jump straight there, and a
|
||||
full file path lands in its folder with that file selected. The **Sort** control orders
|
||||
each listing by name or by modified time (newest first is the quick way to the file an
|
||||
agent just wrote), with folders always ahead of files; the choice is remembered per
|
||||
device. A **Browse** button fills the field beside it. The **📁 Path** key inserts the
|
||||
path into your prompt **without** pressing Enter, so nothing is submitted by accident.
|
||||
Its sibling **⌫ All** key clears the unsent prompt, and never sends the agent's `/clear`
|
||||
command.
|
||||
|
||||
In **Link Existing**'s picker, **+📁** (New folder) makes a folder inside the folder you
|
||||
are viewing and selects it. Names use letters, numbers, spaces and `_ - . ~` (not starting
|
||||
with a dot or `~`). The cases folder is refused, since every folder there is already a
|
||||
case: use **Add Case → New Case** instead.
|
||||
In the **Link Existing** and **Create New** (custom folder) pickers, **+📁** (New folder)
|
||||
makes a folder inside the folder you are viewing and selects it. Names use letters,
|
||||
numbers, spaces and `_ - . ~` (not starting with a dot or `~`). The cases folder is
|
||||
refused, since every folder there is already a case: pick a folder outside it, or make
|
||||
the case with **Add Case → Create New** (no custom folder).
|
||||
|
||||
This is a separate file-serving surface from the viewer, with its own rules: it allowlists
|
||||
your home directory, the cases directory, and anything in `CODEMAN_FILE_PICKER_ROOTS`, and
|
||||
blocks sensitive trees. In multi-user mode a non-admin gets only their own user space as a
|
||||
root, because per-user spaces live inside the home directory and a home-directory root would
|
||||
expose everyone.
|
||||
expose everyone. The Docker **Container Workdir** picker lists the inside of the container
|
||||
instead, read-only, so these host rules do not apply to it; it is admin-only in multi-user
|
||||
mode.
|
||||
|
||||
## Files into a session
|
||||
|
||||
|
||||
Reference in new issue
Block a user