Add show_page and show_file, and let file tools reach any path

This commit is contained in:
Ian Gulliver
2026-10-21 18:29:01 -07:00
parent 585c8ba5f8
commit 524bc0252e
11 changed files with 540 additions and 496 deletions
+22 -57
View File
@@ -33,19 +33,9 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
if params.Path != nil {
rel = *params.Path
}
name, ok := worktreeName(w, rel)
if !ok {
return
}
name, full := taskPath(t.WorktreePath, rel)
root, err := os.OpenRoot(t.WorktreePath)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
defer root.Close()
f, err := root.Open(name)
f, err := os.Open(full)
if err != nil {
writePathError(w, err)
return
@@ -73,7 +63,7 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
if de.Name() == ".git" {
continue
}
entries = append(entries, dirEntry(root, path.Join(name, de.Name()), de))
entries = append(entries, dirEntry(filepath.Join(full, de.Name()), de))
}
slices.SortFunc(entries, func(a, b api.DirEntry) int {
if (a.Type == api.DirEntryTypeDir) != (b.Type == api.DirEntryTypeDir) {
@@ -94,11 +84,15 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
listing.Truncated = true
}
real, err := realDir(t.WorktreePath, name)
real, err := realDir(t.WorktreePath, full)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
if !filepath.IsLocal(real) {
writeJSON(w, http.StatusOK, listing)
return
}
paths := []string{}
for _, e := range listing.Entries {
paths = append(paths, path.Join(real, e.Name))
@@ -114,12 +108,12 @@ func (s *Server) GetTaskDir(w http.ResponseWriter, r *http.Request, taskID strin
writeJSON(w, http.StatusOK, listing)
}
func realDir(worktree, name string) (string, error) {
func realDir(worktree, full string) (string, error) {
root, err := filepath.EvalSymlinks(worktree)
if err != nil {
return "", err
}
dir, err := filepath.EvalSymlinks(filepath.Join(worktree, name))
dir, err := filepath.EvalSymlinks(full)
if err != nil {
return "", err
}
@@ -130,14 +124,14 @@ func realDir(worktree, name string) (string, error) {
return filepath.ToSlash(rel), nil
}
func dirEntry(root *os.Root, name string, de fs.DirEntry) api.DirEntry {
func dirEntry(full string, de fs.DirEntry) api.DirEntry {
e := api.DirEntry{Name: de.Name(), Type: api.DirEntryTypeOther}
var fi fs.FileInfo
var err error
if de.Type()&fs.ModeSymlink != 0 {
e.Symlink = true
fi, err = root.Stat(name)
fi, err = os.Stat(full)
} else {
fi, err = de.Info()
}
@@ -164,19 +158,9 @@ func (s *Server) GetTaskFile(w http.ResponseWriter, r *http.Request, taskID stri
writeError(w, http.StatusBadRequest, errors.New("path is required"))
return
}
name, ok := worktreeName(w, params.Path)
if !ok {
return
}
name, full := taskPath(t.WorktreePath, params.Path)
root, err := os.OpenRoot(t.WorktreePath)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
defer root.Close()
fi, err := root.Stat(name)
fi, err := os.Stat(full)
if err != nil {
writePathError(w, err)
return
@@ -186,7 +170,7 @@ func (s *Server) GetTaskFile(w http.ResponseWriter, r *http.Request, taskID stri
return
}
f, err := root.Open(name)
f, err := os.Open(full)
if err != nil {
writePathError(w, err)
return
@@ -225,19 +209,9 @@ func (s *Server) GetTaskRaw(w http.ResponseWriter, r *http.Request, taskID strin
writeError(w, http.StatusBadRequest, errors.New("path is required"))
return
}
name, ok := worktreeName(w, params.Path)
if !ok {
return
}
name, full := taskPath(t.WorktreePath, params.Path)
root, err := os.OpenRoot(t.WorktreePath)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
defer root.Close()
f, err := root.Open(name)
f, err := os.Open(full)
if err != nil {
writePathError(w, err)
return
@@ -265,21 +239,12 @@ func (s *Server) GetTaskRaw(w http.ResponseWriter, r *http.Request, taskID strin
http.ServeContent(w, r, "", fi.ModTime(), f)
}
func worktreeName(w http.ResponseWriter, rel string) (string, bool) {
if rel == "" {
return ".", true
func taskPath(worktree, p string) (string, string) {
name := filepath.Clean(filepath.FromSlash(p))
if filepath.IsAbs(name) {
return filepath.ToSlash(name), name
}
name := filepath.FromSlash(rel)
if !filepath.IsLocal(name) {
writeError(w, http.StatusBadRequest, fmt.Errorf("%s is not a path inside the worktree", rel))
return "", false
}
name = filepath.ToSlash(filepath.Clean(name))
if slices.Contains(strings.Split(name, "/"), ".git") {
writeError(w, http.StatusBadRequest, fmt.Errorf("%s is inside .git", rel))
return "", false
}
return name, true
return filepath.ToSlash(name), filepath.Join(worktree, name)
}
func writePathError(w http.ResponseWriter, err error) {
-22
View File
@@ -9,7 +9,6 @@ import (
"path/filepath"
"strings"
"g.fc.run/ai/byre/internal/agents"
"g.fc.run/ai/byre/internal/api"
)
@@ -28,17 +27,6 @@ func (s *Server) GetTaskImage(w http.ResponseWriter, r *http.Request, taskID str
return
}
root, err := filepath.EvalSymlinks(t.WorktreePath)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
media, err := filepath.EvalSymlinks(agents.MediaDir(t.WorktreePath))
if err != nil && !errors.Is(err, fs.ErrNotExist) {
writeError(w, http.StatusInternalServerError, err)
return
}
path := params.Path
if !filepath.IsAbs(path) {
path = filepath.Join(t.WorktreePath, path)
@@ -53,11 +41,6 @@ func (s *Server) GetTaskImage(w http.ResponseWriter, r *http.Request, taskID str
return
}
if !within(root, real) && (media == "" || !within(media, real)) {
writeError(w, http.StatusForbidden, fmt.Errorf("%s is outside the task's worktree and media folder", params.Path))
return
}
contentType := worktreeImageTypes[strings.ToLower(filepath.Ext(real))]
if contentType == "" {
writeError(w, http.StatusBadRequest, fmt.Errorf("%s is not a PNG, JPEG, GIF, WebP or SVG file", params.Path))
@@ -87,8 +70,3 @@ func (s *Server) GetTaskImage(w http.ResponseWriter, r *http.Request, taskID str
w.Header().Set("Cache-Control", "private, no-cache")
http.ServeContent(w, r, "", info.ModTime(), f)
}
func within(root, path string) bool {
rel, err := filepath.Rel(root, path)
return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator))
}