You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// DefaultListMaxEntries caps the number of directory entries returned to bound
|
||||
// payload size for large directories.
|
||||
const DefaultListMaxEntries = 1000
|
||||
|
||||
// FsListHandler handles fs/list: a bounded directory listing inside the worktree.
|
||||
type FsListHandler struct {
|
||||
MaxEntries int
|
||||
}
|
||||
|
||||
// NewFsListHandler constructs an FsListHandler with default caps.
|
||||
func NewFsListHandler() *FsListHandler { return &FsListHandler{MaxEntries: DefaultListMaxEntries} }
|
||||
|
||||
func (h *FsListHandler) maxEntries() int {
|
||||
if h.MaxEntries > 0 {
|
||||
return h.MaxEntries
|
||||
}
|
||||
return DefaultListMaxEntries
|
||||
}
|
||||
|
||||
type fsListParams struct {
|
||||
SessionID string `json:"sessionId"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type fsDirEntry struct {
|
||||
Name string `json:"name"`
|
||||
IsDir bool `json:"is_dir"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type fsListResult struct {
|
||||
Entries []fsDirEntry `json:"entries"`
|
||||
Truncated bool `json:"truncated"`
|
||||
}
|
||||
|
||||
// Handle lists the directory at sess.CwdPath/path. Returns FsError for path
|
||||
// escape (-32001) or read failure. Results are sorted (dirs first, then name).
|
||||
func (h *FsListHandler) Handle(_ context.Context, sess SessionContext, paramsJSON json.RawMessage) FsResult {
|
||||
var p fsListParams
|
||||
if err := json.Unmarshal(paramsJSON, &p); err != nil {
|
||||
return FsResult{Err: &FsError{Code: -32602, Message: "invalid params: " + err.Error()}}
|
||||
}
|
||||
abs, err := resolveSafePath(sess.CwdPath, p.Path)
|
||||
if err != nil {
|
||||
return FsResult{Err: &FsError{Code: -32001, Message: err.Error()}}
|
||||
}
|
||||
dirents, err := os.ReadDir(abs)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return FsResult{Err: &FsError{Code: -32603, Message: "directory not found"}}
|
||||
}
|
||||
return FsResult{Err: &FsError{Code: -32603, Message: "list: " + err.Error()}}
|
||||
}
|
||||
|
||||
max := h.maxEntries()
|
||||
out := fsListResult{Entries: make([]fsDirEntry, 0, len(dirents))}
|
||||
for _, de := range dirents {
|
||||
if len(out.Entries) >= max {
|
||||
out.Truncated = true
|
||||
break
|
||||
}
|
||||
e := fsDirEntry{Name: de.Name(), IsDir: de.IsDir()}
|
||||
if info, ierr := de.Info(); ierr == nil && !de.IsDir() {
|
||||
e.Size = info.Size()
|
||||
}
|
||||
out.Entries = append(out.Entries, e)
|
||||
}
|
||||
sort.SliceStable(out.Entries, func(i, j int) bool {
|
||||
if out.Entries[i].IsDir != out.Entries[j].IsDir {
|
||||
return out.Entries[i].IsDir // dirs first
|
||||
}
|
||||
return out.Entries[i].Name < out.Entries[j].Name
|
||||
})
|
||||
|
||||
res, _ := json.Marshal(out)
|
||||
return FsResult{OK: res}
|
||||
}
|
||||
Reference in New Issue
Block a user