You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -25,6 +25,12 @@ type RunService interface {
|
||||
Logs(ctx context.Context, c workspace.Caller, profileID uuid.UUID, since int64, limit int) ([]run.LogLineResp, error)
|
||||
}
|
||||
|
||||
// ExecService 是一次性 exec 工具依赖的窄接口,*run.ExecService 直接满足。
|
||||
type ExecService interface {
|
||||
RunCommand(ctx context.Context, c workspace.Caller, req run.ExecCommandReq) (run.ExecCommandResp, error)
|
||||
RunTests(ctx context.Context, c workspace.Caller, req run.ExecTestsReq) (run.ExecTestsResp, error)
|
||||
}
|
||||
|
||||
// registerRunTools 注册 run profile 工具。日志只提供快照式 get_run_logs,
|
||||
// 流式订阅(WebSocket)不经 MCP 暴露。
|
||||
//
|
||||
@@ -40,6 +46,8 @@ func registerRunTools(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
registerRestartRunProfile(srv, deps)
|
||||
registerGetRunStatus(srv, deps)
|
||||
registerGetRunLogs(srv, deps)
|
||||
registerRunCommand(srv, deps)
|
||||
registerRunTests(srv, deps)
|
||||
}
|
||||
|
||||
// runProfileInWorkspace 校验 profile 归属于给定 workspace 并返回其 UUID。
|
||||
@@ -324,3 +332,97 @@ func registerGetRunLogs(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
return nil, getRunLogsOut{Lines: lines}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== run_command =====
|
||||
|
||||
type runCommandIn struct {
|
||||
WorkspaceID string `json:"workspace_id" jsonschema:"workspace UUID"`
|
||||
WorktreeID string `json:"worktree_id,omitempty" jsonschema:"worktree UUID; empty runs in workspace main_path"`
|
||||
Command string `json:"command" jsonschema:"executable to run, e.g. go / npm (non-empty)"`
|
||||
Args []string `json:"args,omitempty" jsonschema:"command arguments"`
|
||||
Env map[string]string `json:"env,omitempty" jsonschema:"ephemeral env overrides (plaintext, not persisted)"`
|
||||
TimeoutSec int `json:"timeout_sec,omitempty" jsonschema:"timeout in seconds; clamped to server max"`
|
||||
}
|
||||
|
||||
func registerRunCommand(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "run_command", Description: "Run one command to completion in the workspace (main_path or a worktree) and return exit code, stdout, stderr, duration. Sandboxed: only whitelisted env vars are passed; requires project write access."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in runCommandIn) (*mcpsdk.CallToolResult, run.ExecCommandResp, error) {
|
||||
if err := CheckToolFromCtx(ctx, "run_command"); err != nil {
|
||||
return nil, run.ExecCommandResp{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, run.ExecCommandResp{}, err
|
||||
}
|
||||
w, wc, err := scopedWorkspace(ctx, deps, c, in.WorkspaceID)
|
||||
if err != nil {
|
||||
return nil, run.ExecCommandResp{}, err
|
||||
}
|
||||
if in.WorktreeID != "" {
|
||||
if _, err := worktreeInWorkspace(ctx, deps, wc, w.ID, in.WorktreeID); err != nil {
|
||||
return nil, run.ExecCommandResp{}, err
|
||||
}
|
||||
}
|
||||
resp, err := deps.Execs.RunCommand(ctx, wc, run.ExecCommandReq{
|
||||
WorkspaceID: w.ID.String(),
|
||||
WorktreeID: in.WorktreeID,
|
||||
Command: in.Command,
|
||||
Args: in.Args,
|
||||
Env: in.Env,
|
||||
TimeoutSec: in.TimeoutSec,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, run.ExecCommandResp{}, err
|
||||
}
|
||||
return nil, resp, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== run_tests =====
|
||||
|
||||
type runTestsIn struct {
|
||||
WorkspaceID string `json:"workspace_id" jsonschema:"workspace UUID"`
|
||||
WorktreeID string `json:"worktree_id,omitempty" jsonschema:"worktree UUID; empty runs in workspace main_path"`
|
||||
Command string `json:"command" jsonschema:"test runner, e.g. go (non-empty)"`
|
||||
Args []string `json:"args,omitempty" jsonschema:"args, e.g. ['test','-json','./...']"`
|
||||
Format string `json:"format,omitempty" jsonschema:"output format: gotest|junit|vitest|auto (default auto)"`
|
||||
Env map[string]string `json:"env,omitempty" jsonschema:"ephemeral env overrides (plaintext, not persisted)"`
|
||||
TimeoutSec int `json:"timeout_sec,omitempty" jsonschema:"timeout in seconds; clamped to server max"`
|
||||
}
|
||||
|
||||
func registerRunTests(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "run_tests", Description: "Run a test command and parse its output (go test -json / JUnit XML / vitest JSON) into a structured pass/fail summary. Returns raw output plus a summary; never fails on parse errors."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in runTestsIn) (*mcpsdk.CallToolResult, run.ExecTestsResp, error) {
|
||||
if err := CheckToolFromCtx(ctx, "run_tests"); err != nil {
|
||||
return nil, run.ExecTestsResp{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, run.ExecTestsResp{}, err
|
||||
}
|
||||
w, wc, err := scopedWorkspace(ctx, deps, c, in.WorkspaceID)
|
||||
if err != nil {
|
||||
return nil, run.ExecTestsResp{}, err
|
||||
}
|
||||
if in.WorktreeID != "" {
|
||||
if _, err := worktreeInWorkspace(ctx, deps, wc, w.ID, in.WorktreeID); err != nil {
|
||||
return nil, run.ExecTestsResp{}, err
|
||||
}
|
||||
}
|
||||
resp, err := deps.Execs.RunTests(ctx, wc, run.ExecTestsReq{
|
||||
WorkspaceID: w.ID.String(),
|
||||
WorktreeID: in.WorktreeID,
|
||||
Command: in.Command,
|
||||
Args: in.Args,
|
||||
Format: in.Format,
|
||||
Env: in.Env,
|
||||
TimeoutSec: in.TimeoutSec,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, run.ExecTestsResp{}, err
|
||||
}
|
||||
return nil, resp, nil
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user