tech-envision commited on
Commit
7403772
·
unverified ·
2 Parent(s): b93a91c 98f8e86

Merge pull request #83 from EnvisionMindCa/codex/create-cross-platform-cli-app-for-api

Browse files
cli-go/README.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Go CLI for LLM Backend
2
+
3
+ This folder contains a cross-platform command line client written in Go. The CLI can be built as a single executable for Windows, Linux and macOS.
4
+
5
+ ## Features
6
+
7
+ - Interactive colour-coded chat sessions
8
+ - Upload documents to the API
9
+ - List, read, write and delete files in the VM
10
+
11
+ ## Building
12
+
13
+ Install Go 1.20 or later and run:
14
+
15
+ ```bash
16
+ cd cli-go
17
+ go build -o llmcli
18
+ ```
19
+
20
+ For other platforms pass `GOOS` and `GOARCH` environment variables:
21
+
22
+ ```bash
23
+ GOOS=windows GOARCH=amd64 go build -o llmcli.exe
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ```
29
+ ./llmcli --user yourname --server http://localhost:8000 chat
30
+ ```
31
+
32
+ Use `--help` on any subcommand for details.
33
+
cli-go/cmd/cat.go ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/spf13/cobra"
8
+
9
+ "llm-cli/internal/client"
10
+ )
11
+
12
+ func newCatCmd() *cobra.Command {
13
+ return &cobra.Command{
14
+ Use: "cat <path>",
15
+ Short: "Print a file from the VM",
16
+ Args: cobra.ExactArgs(1),
17
+ RunE: func(cmd *cobra.Command, args []string) error {
18
+ c := client.New(server)
19
+ content, err := c.ReadFile(context.Background(), user, args[0])
20
+ if err != nil {
21
+ return err
22
+ }
23
+ fmt.Print(content)
24
+ return nil
25
+ },
26
+ }
27
+ }
cli-go/cmd/chat.go ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cmd
2
+
3
+ import (
4
+ "bufio"
5
+ "fmt"
6
+ "os"
7
+ "strconv"
8
+
9
+ "github.com/fatih/color"
10
+ "github.com/spf13/cobra"
11
+
12
+ "llm-cli/internal/client"
13
+ )
14
+
15
+ func newChatCmd() *cobra.Command {
16
+ cmd := &cobra.Command{
17
+ Use: "chat",
18
+ Short: "Start an interactive chat session",
19
+ RunE: runChat,
20
+ }
21
+ return cmd
22
+ }
23
+
24
+ func runChat(cmd *cobra.Command, args []string) error {
25
+ ctx := cmd.Context()
26
+ c := client.New(server)
27
+
28
+ sessions, err := c.ListSessions(ctx, user)
29
+ if err != nil {
30
+ return err
31
+ }
32
+
33
+ session := "default"
34
+ if len(sessions) > 0 {
35
+ fmt.Println("Existing sessions:")
36
+ for i, s := range sessions {
37
+ fmt.Printf(" %d. %s\n", i+1, s)
38
+ }
39
+ fmt.Printf("Select session number or enter new name [%d]: ", len(sessions))
40
+ var choice string
41
+ fmt.Scanln(&choice)
42
+ if n, err := strconv.Atoi(choice); err == nil && n >= 1 && n <= len(sessions) {
43
+ session = sessions[n-1]
44
+ } else if choice != "" {
45
+ session = choice
46
+ }
47
+ }
48
+
49
+ cyan := color.New(color.FgCyan).SprintFunc()
50
+ green := color.New(color.FgGreen).SprintFunc()
51
+ yellow := color.New(color.FgYellow).SprintFunc()
52
+
53
+ fmt.Printf("Chatting as %s in session '%s'\n", green(user), session)
54
+
55
+ scanner := bufio.NewScanner(os.Stdin)
56
+ for {
57
+ fmt.Printf("%s> ", cyan("You"))
58
+ if !scanner.Scan() {
59
+ break
60
+ }
61
+ line := scanner.Text()
62
+ if line == "exit" || line == "quit" {
63
+ break
64
+ }
65
+ stream, err := c.ChatStream(ctx, user, session, line)
66
+ if err != nil {
67
+ fmt.Println("error:", err)
68
+ continue
69
+ }
70
+ r := bufio.NewReader(stream)
71
+ for {
72
+ part, err := r.ReadString('\n')
73
+ if len(part) > 0 {
74
+ fmt.Print(yellow(part))
75
+ }
76
+ if err != nil {
77
+ break
78
+ }
79
+ }
80
+ stream.Close()
81
+ }
82
+ return nil
83
+ }
cli-go/cmd/ls.go ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/spf13/cobra"
8
+
9
+ "llm-cli/internal/client"
10
+ )
11
+
12
+ func newLsCmd() *cobra.Command {
13
+ return &cobra.Command{
14
+ Use: "ls [path]",
15
+ Short: "List directory contents in the VM",
16
+ Args: cobra.MaximumNArgs(1),
17
+ RunE: func(cmd *cobra.Command, args []string) error {
18
+ path := "/data"
19
+ if len(args) == 1 {
20
+ path = args[0]
21
+ }
22
+ c := client.New(server)
23
+ entries, err := c.ListDir(context.Background(), user, path)
24
+ if err != nil {
25
+ return err
26
+ }
27
+ for _, e := range entries {
28
+ if e.IsDir {
29
+ fmt.Println(e.Name + "/")
30
+ } else {
31
+ fmt.Println(e.Name)
32
+ }
33
+ }
34
+ return nil
35
+ },
36
+ }
37
+ }
cli-go/cmd/rm.go ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+
6
+ "github.com/spf13/cobra"
7
+
8
+ "llm-cli/internal/client"
9
+ )
10
+
11
+ func newRmCmd() *cobra.Command {
12
+ return &cobra.Command{
13
+ Use: "rm <path>",
14
+ Short: "Remove a file or directory in the VM",
15
+ Args: cobra.ExactArgs(1),
16
+ RunE: func(cmd *cobra.Command, args []string) error {
17
+ c := client.New(server)
18
+ return c.DeleteFile(context.Background(), user, args[0])
19
+ },
20
+ }
21
+ }
cli-go/cmd/root.go ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cmd
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+
7
+ "github.com/spf13/cobra"
8
+ )
9
+
10
+ var (
11
+ server string
12
+ user string
13
+ )
14
+
15
+ func NewRootCmd() *cobra.Command {
16
+ cmd := &cobra.Command{
17
+ Use: "llmcli",
18
+ Short: "CLI client for the LLM backend",
19
+ }
20
+
21
+ cmd.PersistentFlags().StringVarP(&server, "server", "s", "http://localhost:8000", "API server URL")
22
+ cmd.PersistentFlags().StringVarP(&user, "user", "u", "default", "User name")
23
+
24
+ cmd.AddCommand(newChatCmd())
25
+ cmd.AddCommand(newUploadCmd())
26
+ cmd.AddCommand(newLsCmd())
27
+ cmd.AddCommand(newCatCmd())
28
+ cmd.AddCommand(newWriteCmd())
29
+ cmd.AddCommand(newRmCmd())
30
+
31
+ return cmd
32
+ }
33
+
34
+ func Execute() {
35
+ if err := NewRootCmd().Execute(); err != nil {
36
+ fmt.Fprintln(os.Stderr, err)
37
+ os.Exit(1)
38
+ }
39
+ }
cli-go/cmd/upload.go ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+
7
+ "github.com/spf13/cobra"
8
+
9
+ "llm-cli/internal/client"
10
+ )
11
+
12
+ func newUploadCmd() *cobra.Command {
13
+ return &cobra.Command{
14
+ Use: "upload [file]",
15
+ Short: "Upload a document to the VM",
16
+ Args: cobra.ExactArgs(1),
17
+ RunE: func(cmd *cobra.Command, args []string) error {
18
+ ctx := context.Background()
19
+ c := client.New(server)
20
+ path, err := c.UploadDocument(ctx, user, "default", args[0])
21
+ if err != nil {
22
+ return err
23
+ }
24
+ fmt.Println("Uploaded to", path)
25
+ return nil
26
+ },
27
+ }
28
+ }
cli-go/cmd/write.go ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "os"
6
+
7
+ "github.com/spf13/cobra"
8
+
9
+ "llm-cli/internal/client"
10
+ )
11
+
12
+ func newWriteCmd() *cobra.Command {
13
+ return &cobra.Command{
14
+ Use: "write <path> <file>",
15
+ Short: "Write a local file to a path in the VM",
16
+ Args: cobra.ExactArgs(2),
17
+ RunE: func(cmd *cobra.Command, args []string) error {
18
+ data, err := os.ReadFile(args[1])
19
+ if err != nil {
20
+ return err
21
+ }
22
+ c := client.New(server)
23
+ return c.WriteFile(context.Background(), user, args[0], string(data))
24
+ },
25
+ }
26
+ }
cli-go/go.mod ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module llm-cli
2
+
3
+ go 1.23.8
4
+
5
+ require (
6
+ github.com/fatih/color v1.16.0
7
+ github.com/spf13/cobra v1.7.0
8
+ )
9
+
10
+ require (
11
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
12
+ github.com/mattn/go-colorable v0.1.13 // indirect
13
+ github.com/mattn/go-isatty v0.0.20 // indirect
14
+ github.com/spf13/pflag v1.0.5 // indirect
15
+ golang.org/x/sys v0.14.0 // indirect
16
+ )
cli-go/go.sum ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
2
+ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
3
+ github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
4
+ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
5
+ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
6
+ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
7
+ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
8
+ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
9
+ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
10
+ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
11
+ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
12
+ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
13
+ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
14
+ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
15
+ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
16
+ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
17
+ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
18
+ golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
19
+ golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
20
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
21
+ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
cli-go/internal/client/client.go ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package client
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "encoding/json"
7
+ "fmt"
8
+ "io"
9
+ "mime/multipart"
10
+ "net/http"
11
+ "os"
12
+ "path/filepath"
13
+ "time"
14
+ )
15
+
16
+ type Client struct {
17
+ baseURL string
18
+ httpClient *http.Client
19
+ }
20
+
21
+ func New(baseURL string) *Client {
22
+ return &Client{
23
+ baseURL: baseURL,
24
+ httpClient: &http.Client{Timeout: 30 * time.Second},
25
+ }
26
+ }
27
+
28
+ type SessionInfo struct {
29
+ Sessions []string `json:"sessions"`
30
+ }
31
+
32
+ func (c *Client) ListSessions(ctx context.Context, user string) ([]string, error) {
33
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet,
34
+ fmt.Sprintf("%s/sessions/%s", c.baseURL, user), nil)
35
+ if err != nil {
36
+ return nil, err
37
+ }
38
+ resp, err := c.httpClient.Do(req)
39
+ if err != nil {
40
+ return nil, err
41
+ }
42
+ defer resp.Body.Close()
43
+ if resp.StatusCode != http.StatusOK {
44
+ return nil, fmt.Errorf("list sessions failed: %s", resp.Status)
45
+ }
46
+ var data SessionInfo
47
+ if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
48
+ return nil, err
49
+ }
50
+ return data.Sessions, nil
51
+ }
52
+
53
+ type ChatRequest struct {
54
+ User string `json:"user"`
55
+ Session string `json:"session"`
56
+ Prompt string `json:"prompt"`
57
+ }
58
+
59
+ func (c *Client) ChatStream(ctx context.Context, user, session, prompt string) (io.ReadCloser, error) {
60
+ body, err := json.Marshal(ChatRequest{User: user, Session: session, Prompt: prompt})
61
+ if err != nil {
62
+ return nil, err
63
+ }
64
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/chat/stream", bytes.NewReader(body))
65
+ if err != nil {
66
+ return nil, err
67
+ }
68
+ req.Header.Set("Content-Type", "application/json")
69
+ resp, err := c.httpClient.Do(req)
70
+ if err != nil {
71
+ return nil, err
72
+ }
73
+ if resp.StatusCode != http.StatusOK {
74
+ defer resp.Body.Close()
75
+ b, _ := io.ReadAll(resp.Body)
76
+ return nil, fmt.Errorf("chat failed: %s - %s", resp.Status, string(b))
77
+ }
78
+ return resp.Body, nil
79
+ }
80
+
81
+ type UploadResp struct {
82
+ Path string `json:"path"`
83
+ }
84
+
85
+ func (c *Client) UploadDocument(ctx context.Context, user, session, path string) (string, error) {
86
+ file, err := os.Open(path)
87
+ if err != nil {
88
+ return "", err
89
+ }
90
+ defer file.Close()
91
+
92
+ buf := &bytes.Buffer{}
93
+ writer := multipart.NewWriter(buf)
94
+ _ = writer.WriteField("user", user)
95
+ _ = writer.WriteField("session", session)
96
+ fw, err := writer.CreateFormFile("file", filepath.Base(path))
97
+ if err != nil {
98
+ return "", err
99
+ }
100
+ if _, err = io.Copy(fw, file); err != nil {
101
+ return "", err
102
+ }
103
+ writer.Close()
104
+
105
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/upload", buf)
106
+ if err != nil {
107
+ return "", err
108
+ }
109
+ req.Header.Set("Content-Type", writer.FormDataContentType())
110
+
111
+ resp, err := c.httpClient.Do(req)
112
+ if err != nil {
113
+ return "", err
114
+ }
115
+ defer resp.Body.Close()
116
+ if resp.StatusCode != http.StatusOK {
117
+ b, _ := io.ReadAll(resp.Body)
118
+ return "", fmt.Errorf("upload failed: %s - %s", resp.Status, string(b))
119
+ }
120
+ var out UploadResp
121
+ if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
122
+ return "", err
123
+ }
124
+ return out.Path, nil
125
+ }
126
+
127
+ type DirEntry struct {
128
+ Name string `json:"name"`
129
+ IsDir bool `json:"is_dir"`
130
+ }
131
+
132
+ type DirList struct {
133
+ Entries []DirEntry `json:"entries"`
134
+ }
135
+
136
+ func (c *Client) ListDir(ctx context.Context, user, path string) ([]DirEntry, error) {
137
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet,
138
+ fmt.Sprintf("%s/vm/%s/list?path=%s", c.baseURL, user, path), nil)
139
+ if err != nil {
140
+ return nil, err
141
+ }
142
+ resp, err := c.httpClient.Do(req)
143
+ if err != nil {
144
+ return nil, err
145
+ }
146
+ defer resp.Body.Close()
147
+ if resp.StatusCode != http.StatusOK {
148
+ return nil, fmt.Errorf("list dir failed: %s", resp.Status)
149
+ }
150
+ var out DirList
151
+ if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
152
+ return nil, err
153
+ }
154
+ return out.Entries, nil
155
+ }
156
+
157
+ type FileContent struct {
158
+ Content string `json:"content"`
159
+ }
160
+
161
+ func (c *Client) ReadFile(ctx context.Context, user, path string) (string, error) {
162
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet,
163
+ fmt.Sprintf("%s/vm/%s/file?path=%s", c.baseURL, user, path), nil)
164
+ if err != nil {
165
+ return "", err
166
+ }
167
+ resp, err := c.httpClient.Do(req)
168
+ if err != nil {
169
+ return "", err
170
+ }
171
+ defer resp.Body.Close()
172
+ if resp.StatusCode != http.StatusOK {
173
+ return "", fmt.Errorf("read file failed: %s", resp.Status)
174
+ }
175
+ var out FileContent
176
+ if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
177
+ return "", err
178
+ }
179
+ return out.Content, nil
180
+ }
181
+
182
+ func (c *Client) WriteFile(ctx context.Context, user, path, content string) error {
183
+ data, _ := json.Marshal(map[string]string{"path": path, "content": content})
184
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost,
185
+ fmt.Sprintf("%s/vm/%s/file", c.baseURL, user), bytes.NewReader(data))
186
+ if err != nil {
187
+ return err
188
+ }
189
+ req.Header.Set("Content-Type", "application/json")
190
+ resp, err := c.httpClient.Do(req)
191
+ if err != nil {
192
+ return err
193
+ }
194
+ defer resp.Body.Close()
195
+ if resp.StatusCode != http.StatusOK {
196
+ b, _ := io.ReadAll(resp.Body)
197
+ return fmt.Errorf("write file failed: %s - %s", resp.Status, string(b))
198
+ }
199
+ return nil
200
+ }
201
+
202
+ func (c *Client) DeleteFile(ctx context.Context, user, path string) error {
203
+ req, err := http.NewRequestWithContext(ctx, http.MethodDelete,
204
+ fmt.Sprintf("%s/vm/%s/file?path=%s", c.baseURL, user, path), nil)
205
+ if err != nil {
206
+ return err
207
+ }
208
+ resp, err := c.httpClient.Do(req)
209
+ if err != nil {
210
+ return err
211
+ }
212
+ defer resp.Body.Close()
213
+ if resp.StatusCode != http.StatusOK {
214
+ b, _ := io.ReadAll(resp.Body)
215
+ return fmt.Errorf("delete file failed: %s - %s", resp.Status, string(b))
216
+ }
217
+ return nil
218
+ }
cli-go/main.go ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import "llm-cli/cmd"
4
+
5
+ func main() {
6
+ cmd.Execute()
7
+ }