Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit30330ab

Browse files
authored
feat: add coder_workspace_edit_file MCP tool (#19629)
1 parent1e2b66f commit30330ab

File tree

9 files changed

+780
-2
lines changed

9 files changed

+780
-2
lines changed

‎agent/api.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ func (a *agent) apiHandler() http.Handler {
6262
r.Post("/api/v0/list-directory",a.HandleLS)
6363
r.Get("/api/v0/read-file",a.HandleReadFile)
6464
r.Post("/api/v0/write-file",a.HandleWriteFile)
65+
r.Post("/api/v0/edit-files",a.HandleEditFiles)
6566
r.Get("/debug/logs",a.HandleHTTPDebugLogs)
6667
r.Get("/debug/magicsock",a.HandleHTTPDebugMagicsock)
6768
r.Get("/debug/magicsock/debug-logging/{state}",a.HandleHTTPMagicsockDebugLoggingState)

‎agent/files.go‎

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ import (
1212
"strconv"
1313
"syscall"
1414

15+
"github.com/icholy/replace"
16+
"github.com/spf13/afero"
17+
"golang.org/x/text/transform"
1518
"golang.org/x/xerrors"
1619

1720
"cdr.dev/slog"
1821
"github.com/coder/coder/v2/coderd/httpapi"
1922
"github.com/coder/coder/v2/codersdk"
23+
"github.com/coder/coder/v2/codersdk/workspacesdk"
2024
)
2125

2226
typeHTTPResponseCode=int
@@ -165,3 +169,105 @@ func (a *agent) writeFile(ctx context.Context, r *http.Request, path string) (HT
165169

166170
return0,nil
167171
}
172+
173+
func (a*agent)HandleEditFiles(rw http.ResponseWriter,r*http.Request) {
174+
ctx:=r.Context()
175+
176+
varreq workspacesdk.FileEditRequest
177+
if!httpapi.Read(ctx,rw,r,&req) {
178+
return
179+
}
180+
181+
iflen(req.Files)==0 {
182+
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
183+
Message:"must specify at least one file",
184+
})
185+
return
186+
}
187+
188+
varcombinedErrerror
189+
status:=http.StatusOK
190+
for_,edit:=rangereq.Files {
191+
s,err:=a.editFile(r.Context(),edit.Path,edit.Edits)
192+
// Keep the highest response status, so 500 will be preferred over 400, etc.
193+
ifs>status {
194+
status=s
195+
}
196+
iferr!=nil {
197+
combinedErr=errors.Join(combinedErr,err)
198+
}
199+
}
200+
201+
ifcombinedErr!=nil {
202+
httpapi.Write(ctx,rw,status, codersdk.Response{
203+
Message:combinedErr.Error(),
204+
})
205+
return
206+
}
207+
208+
httpapi.Write(ctx,rw,http.StatusOK, codersdk.Response{
209+
Message:"Successfully edited file(s)",
210+
})
211+
}
212+
213+
func (a*agent)editFile(ctx context.Context,pathstring,edits []workspacesdk.FileEdit) (int,error) {
214+
ifpath=="" {
215+
returnhttp.StatusBadRequest,xerrors.New("\"path\" is required")
216+
}
217+
218+
if!filepath.IsAbs(path) {
219+
returnhttp.StatusBadRequest,xerrors.Errorf("file path must be absolute: %q",path)
220+
}
221+
222+
iflen(edits)==0 {
223+
returnhttp.StatusBadRequest,xerrors.New("must specify at least one edit")
224+
}
225+
226+
f,err:=a.filesystem.Open(path)
227+
iferr!=nil {
228+
status:=http.StatusInternalServerError
229+
switch {
230+
caseerrors.Is(err,os.ErrNotExist):
231+
status=http.StatusNotFound
232+
caseerrors.Is(err,os.ErrPermission):
233+
status=http.StatusForbidden
234+
}
235+
returnstatus,err
236+
}
237+
deferf.Close()
238+
239+
stat,err:=f.Stat()
240+
iferr!=nil {
241+
returnhttp.StatusInternalServerError,err
242+
}
243+
244+
ifstat.IsDir() {
245+
returnhttp.StatusBadRequest,xerrors.Errorf("open %s: not a file",path)
246+
}
247+
248+
transforms:=make([]transform.Transformer,len(edits))
249+
fori,edit:=rangeedits {
250+
transforms[i]=replace.String(edit.Search,edit.Replace)
251+
}
252+
253+
tmpfile,err:=afero.TempFile(a.filesystem,"",filepath.Base(path))
254+
iferr!=nil {
255+
returnhttp.StatusInternalServerError,err
256+
}
257+
defertmpfile.Close()
258+
259+
_,err=io.Copy(tmpfile,replace.Chain(f,transforms...))
260+
iferr!=nil {
261+
ifrerr:=a.filesystem.Remove(tmpfile.Name());rerr!=nil {
262+
a.logger.Warn(ctx,"unable to clean up temp file",slog.Error(rerr))
263+
}
264+
returnhttp.StatusInternalServerError,xerrors.Errorf("edit %s: %w",path,err)
265+
}
266+
267+
err=a.filesystem.Rename(tmpfile.Name(),path)
268+
iferr!=nil {
269+
returnhttp.StatusInternalServerError,err
270+
}
271+
272+
return0,nil
273+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp