feat(kap-server): add global fs:mkdir endpoint - #2281
Conversation
Add POST /api/v1/fs:mkdir to create a directory on the host filesystem by absolute path, backing the folder picker's "new folder" action. Implemented directly on node:fs/promises.mkdir in the transport layer for now, non-recursive by design, with wire errors mapped to the existing fs.* codes (40001/40409/40411/40919).
|
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f459a0947
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const mkdirRoute = defineRoute( | ||
| { | ||
| method: 'POST', | ||
| path: '/fs::mkdir', |
There was a problem hiding this comment.
Update the API surface snapshot for fs:mkdir
Adding this documented POST route changes /openapi.json, but packages/kap-server/test/apiSurface.snapshot.test.ts derives the route table from that document and the checked-in snapshot still only contains the existing fs:browse, fs:content, and fs:home entries. In CI, that snapshot assertion will fail until the new POST /api/v1/fs:mkdir route is recorded intentionally.
Useful? React with 👍 / 👎.
The export download tests reused pooled undici keep-alive connections, so afterEach's server.close() could wait out fastify's 72s default keepAliveTimeout and die on the 10s hook timeout (flaky on CI). Send connection: close on the streamed export requests, matching the fs:content tests.
* feat(kap-server): add global fs:mkdir endpoint Add POST /api/v1/fs:mkdir to create a directory on the host filesystem by absolute path, backing the folder picker's "new folder" action. Implemented directly on node:fs/promises.mkdir in the transport layer for now, non-recursive by design, with wire errors mapped to the existing fs.* codes (40001/40409/40411/40919). * test(kap-server): update api surface snapshot for fs:mkdir * test(kap-server): stop export tests from holding server.close() open The export download tests reused pooled undici keep-alive connections, so afterEach's server.close() could wait out fastify's 72s default keepAliveTimeout and die on the 10s hook timeout (flaky on CI). Send connection: close on the streamed export requests, matching the fs:content tests.
Related Issue
No linked issue — the problem is explained below.
Problem
The global (non-session) filesystem surface under
/api/v1can browse directories (fs:browse) and read any host file (fs:content), but there is no way to create a directory. A folder-picker style UI that lets users choose a workspace root therefore cannot offer a "new folder" action: creating a directory today only works inside a session's workspace via the session-scopedfs:mkdiraction, which requires an existing session and is confined to that session's work dir.What changed
POST /api/v1/fs:mkdir(source path/fs::mkdir, served single-colon on the wire like the otherfs:routes) accepting{ path }, which creates a directory by absolute path on the host filesystem. Likefs:content, the global bearer auth is the only access gate.node:fs/promises.mkdirin the transport layer for now, since the engine deliberately has no "unconfined write" domain Service (mirroring the read side, wherefs:contentalso lives in the transport layer on host fs primitives). The route is deliberately non-recursive: a missing parent surfaces as an error instead of silently creating a deep tree the caller may have mistyped.fs.*codes: non-absolute path →40001 validation.failed, existing target (file or directory) →40919 fs.already_exists, missing parent →40409 fs.path_not_found,EACCES/EPERM→40411 fs.permission_denied. Success returns{ path }.fs:browse), relative path, existing directory/file, missing parent, and the double-colon URL 404 parity guard.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.