Why: Squad runs were one file workspace and one JSON file the leader had to emit, so agents, issues, and retries had no durable queue. Evidence: GOWORK=off go test -count=1 -timeout 180s ./... Impact: /v1/runs stays. New routes mount only when the orchestration database opens. SSE for a run id now skips other runs. External CLIs are not a sandbox. Refs: feature/orchestration-platform S1-S7 Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
3.9 KiB
Go
131 lines
3.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"agent-native/runtime/llm"
|
|
"agent-squad/platform"
|
|
)
|
|
|
|
func cmdPlatform(args []string) int {
|
|
if len(args) < 2 {
|
|
fmt.Fprintln(os.Stderr, "用法: agent-squad", args[0], "create|import|assign|claim|fire ...")
|
|
return 2
|
|
}
|
|
fs := flag.NewFlagSet(args[0], flag.ContinueOnError)
|
|
data := fs.String("data", dataDir(), "数据目录")
|
|
workspace := fs.String("workspace", "", "workspace id")
|
|
name := fs.String("name", "", "名称")
|
|
if err := fs.Parse(args[2:]); err != nil {
|
|
return 2
|
|
}
|
|
st, err := platform.Open(filepath.Join(*data, "orchestration.db"))
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
defer st.Close()
|
|
switch args[0] + " " + args[1] {
|
|
case "agent create":
|
|
a, err := st.CreateAgent(platform.Agent{WorkspaceID: *workspace, Name: *name})
|
|
return printPlatform(a, err)
|
|
case "skill import":
|
|
if fs.NArg() < 1 {
|
|
fmt.Fprintln(os.Stderr, "skill import 需要目录或 zip 路径")
|
|
return 2
|
|
}
|
|
sk, err := st.ImportSkill(*workspace, fs.Arg(0), "rename")
|
|
return printPlatform(sk, err)
|
|
case "squad create":
|
|
if fs.NArg() < 1 {
|
|
fmt.Fprintln(os.Stderr, "squad create 需要 leader agent id")
|
|
return 2
|
|
}
|
|
sq, err := st.CreateSquad(platform.Squad{WorkspaceID: *workspace, Name: *name, LeaderAgentID: fs.Arg(0)}, nil)
|
|
return printPlatform(sq, err)
|
|
case "issue create":
|
|
issue, err := st.CreateIssue(platform.Issue{WorkspaceID: *workspace, Title: *name, Status: "todo"})
|
|
return printPlatform(issue, err)
|
|
case "issue assign":
|
|
if fs.NArg() < 3 {
|
|
fmt.Fprintln(os.Stderr, "issue assign <issue> <agent|squad> <id>")
|
|
return 2
|
|
}
|
|
err := st.AssignIssue(fs.Arg(0), fs.Arg(1), fs.Arg(2), true)
|
|
return printPlatform(map[string]bool{"ok": err == nil}, err)
|
|
case "autopilot create":
|
|
if fs.NArg() < 2 {
|
|
fmt.Fprintln(os.Stderr, "autopilot create <agent-id> <create_issue|run_only>")
|
|
return 2
|
|
}
|
|
a, err := st.CreateAutopilot(platform.Autopilot{
|
|
WorkspaceID: *workspace, Title: *name, Runbook: *name,
|
|
AssigneeKind: "agent", AssigneeID: fs.Arg(0), ExecutionMode: fs.Arg(1),
|
|
})
|
|
return printPlatform(a, err)
|
|
case "runtime probe":
|
|
rts, err := st.RegisterProbed(*workspace)
|
|
return printPlatform(rts, err)
|
|
case "daemon claim":
|
|
if fs.NArg() < 1 {
|
|
fmt.Fprintln(os.Stderr, "daemon claim <runtime-id>")
|
|
return 2
|
|
}
|
|
task, ok, err := st.Claim(fs.Arg(0), time.Now())
|
|
return printPlatform(map[string]any{"ok": ok, "task": task}, err)
|
|
case "chat create":
|
|
c, err := st.CreateChat(platform.ChatSession{WorkspaceID: *workspace, OwnerID: *name})
|
|
return printPlatform(c, err)
|
|
case "project create":
|
|
p, err := st.CreateProject(platform.Project{WorkspaceID: *workspace, Name: *name})
|
|
return printPlatform(p, err)
|
|
case "daemon run-native":
|
|
if fs.NArg() < 2 {
|
|
fmt.Fprintln(os.Stderr, "daemon run-native <task-id> <workdir>")
|
|
return 2
|
|
}
|
|
task, err := st.TaskByToken(fs.Arg(0))
|
|
if err != nil {
|
|
var idErr error
|
|
task, idErr = taskByID(st, fs.Arg(0))
|
|
if idErr != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
}
|
|
text, err := st.RunInProcess(context.Background(), task, fs.Arg(1), scriptedOK)
|
|
return printPlatform(map[string]string{"text": text}, err)
|
|
default:
|
|
fmt.Fprintln(os.Stderr, "未知编排命令", args[0], args[1])
|
|
return 2
|
|
}
|
|
}
|
|
|
|
func taskByID(st *platform.Store, id string) (platform.Task, error) {
|
|
return st.TaskByToken(id)
|
|
}
|
|
|
|
func scriptedOK(ctx context.Context, model string, messages []llm.Message, td []llm.ToolDecl, opts llm.StreamOptions) <-chan llm.Event {
|
|
ch := make(chan llm.Event, 1)
|
|
ch <- llm.Event{Kind: llm.KindMessageEnd, Message: llm.Message{Role: llm.RoleAssistant, Content: "ok", StopReason: llm.StopEnd}}
|
|
close(ch)
|
|
return ch
|
|
}
|
|
|
|
func printPlatform(v any, err error) int {
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return 1
|
|
}
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
_ = enc.Encode(v)
|
|
return 0
|
|
}
|