From 26377702d335261fb5e307038e0f829ab9a42fb1 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sat, 14 Mar 2026 16:34:58 +0300 Subject: Fix: view_img, exec commands --- tools/chain.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- tools/fs.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 143 insertions(+), 14 deletions(-) (limited to 'tools') diff --git a/tools/chain.go b/tools/chain.go index 73ab6cd..4afb7e5 100644 --- a/tools/chain.go +++ b/tools/chain.go @@ -239,7 +239,12 @@ func execBuiltin(name string, args []string, stdin string) string { } return "" } - data, err := os.ReadFile(args[0]) + path := args[0] + abs := path + if !filepath.IsAbs(path) { + abs = filepath.Join(fsRootDir, path) + } + data, err := os.ReadFile(abs) if err != nil { return fmt.Sprintf("[error] cat: %v", err) } @@ -266,6 +271,76 @@ func execBuiltin(name string, args []string, stdin string) string { } fsRootDir = abs return fmt.Sprintf("Changed directory to: %s", fsRootDir) + case "mkdir": + if len(args) == 0 { + return "[error] usage: mkdir [-p] " + } + createParents := false + var dirPath string + for _, a := range args { + if a == "-p" || a == "--parents" { + createParents = true + } else if dirPath == "" { + dirPath = a + } + } + if dirPath == "" { + return "[error] usage: mkdir [-p] " + } + abs := dirPath + if !filepath.IsAbs(dirPath) { + abs = filepath.Join(fsRootDir, dirPath) + } + abs = filepath.Clean(abs) + var mkdirFunc func(string, os.FileMode) error + if createParents { + mkdirFunc = os.MkdirAll + } else { + mkdirFunc = os.Mkdir + } + if err := mkdirFunc(abs, 0o755); err != nil { + return fmt.Sprintf("[error] mkdir: %v", err) + } + if createParents { + return fmt.Sprintf("Created %s (with parents)", dirPath) + } + return fmt.Sprintf("Created %s", dirPath) + case "ls": + dir := "." + for _, a := range args { + if !strings.HasPrefix(a, "-") { + dir = a + break + } + } + abs := dir + if !filepath.IsAbs(dir) { + abs = filepath.Join(fsRootDir, dir) + } + entries, err := os.ReadDir(abs) + if err != nil { + return fmt.Sprintf("[error] ls: %v", err) + } + var out strings.Builder + for _, e := range entries { + info, _ := e.Info() + if e.IsDir() { + fmt.Fprintf(&out, "d %-8s %s/\n", "-", e.Name()) + } else if info != nil { + size := info.Size() + sizeStr := fmt.Sprintf("%d", size) + if size > 1024 { + sizeStr = fmt.Sprintf("%.1fKB", float64(size)/1024) + } + fmt.Fprintf(&out, "f %-8s %s\n", sizeStr, e.Name()) + } else { + fmt.Fprintf(&out, "f %-8s %s\n", "?", e.Name()) + } + } + if out.Len() == 0 { + return "(empty directory)" + } + return strings.TrimRight(out.String(), "\n") case "go": // Allow all go subcommands if len(args) == 0 { diff --git a/tools/fs.go b/tools/fs.go index 9fc09bb..f0368f6 100644 --- a/tools/fs.go +++ b/tools/fs.go @@ -2,7 +2,9 @@ package tools import ( "encoding/base64" + "encoding/json" "fmt" + "gf-lt/models" "os" "os/exec" "path/filepath" @@ -155,27 +157,53 @@ func FsCat(args []string, stdin string) string { return string(data) } -func FsSee(args []string, stdin string) string { +func FsViewImg(args []string, stdin string) string { if len(args) == 0 { - return "[error] usage: see " + return "[error] usage: view_img " } path := args[0] - abs, err := resolvePath(path) - if err != nil { - return fmt.Sprintf("[error] %v", err) + var abs string + if filepath.IsAbs(path) { + abs = path + } else { + var err error + abs, err = resolvePath(path) + if err != nil { + return fmt.Sprintf("[error] %v", err) + } } - info, err := os.Stat(abs) - if err != nil { - return fmt.Sprintf("[error] see: %v", err) + if _, err := os.Stat(abs); err != nil { + return fmt.Sprintf("[error] view_img: %v", err) } if !IsImageFile(path) { return fmt.Sprintf("[error] not an image file: %s (use cat to read text files)", path) } - return fmt.Sprintf("Image: %s (%s)\n![image](file://%s)", path, humanSize(info.Size()), abs) + dataURL, err := models.CreateImageURLFromPath(abs) + if err != nil { + return fmt.Sprintf("[error] view_img: %v", err) + } + + result := models.MultimodalToolResp{ + Type: "multimodal_content", + Parts: []map[string]string{ + {"type": "text", "text": "Image: " + path}, + {"type": "image_url", "url": dataURL}, + }, + } + jsonResult, err := json.Marshal(result) + if err != nil { + return fmt.Sprintf("[error] view_img: %v", err) + } + return string(jsonResult) +} + +// FsSee is deprecated, use FsViewImg +func FsSee(args []string, stdin string) string { + return FsViewImg(args, stdin) } func FsWrite(args []string, stdin string) string { @@ -352,18 +380,44 @@ func FsMv(args []string, stdin string) string { func FsMkdir(args []string, stdin string) string { if len(args) == 0 { - return "[error] usage: mkdir " + return "[error] usage: mkdir [-p] " } - abs, err := resolvePath(args[0]) + createParents := false + var dirPath string + + for _, a := range args { + if a == "-p" || a == "--parents" { + createParents = true + } else if dirPath == "" { + dirPath = a + } + } + + if dirPath == "" { + return "[error] usage: mkdir [-p] " + } + + abs, err := resolvePath(dirPath) if err != nil { return fmt.Sprintf("[error] %v", err) } - if err := os.MkdirAll(abs, 0o755); err != nil { + var mkdirFunc func(string, os.FileMode) error + if createParents { + mkdirFunc = os.MkdirAll + } else { + mkdirFunc = os.Mkdir + } + + if err := mkdirFunc(abs, 0o755); err != nil { return fmt.Sprintf("[error] mkdir: %v", err) } - return fmt.Sprintf("Created %s", args[0]) + + if createParents { + return fmt.Sprintf("Created %s (with parents)", dirPath) + } + return fmt.Sprintf("Created %s", dirPath) } // Text processing commands -- cgit v1.2.3