agent-orchestrator/backend/internal/cli/launch.go

85 lines
1.9 KiB
Go

package cli
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"github.com/spf13/cobra"
"github.com/aoagents/agent-orchestrator/backend/internal/agentlaunch"
)
func newLaunchCommand(ctx *commandContext) *cobra.Command {
return &cobra.Command{
Use: "launch",
Short: "Launch an AO-managed agent process (internal)",
Hidden: true,
Args: noArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
return ctx.launchAgent(cmd.Context())
},
}
}
func (c *commandContext) launchAgent(ctx context.Context) error {
specPath := strings.TrimSpace(os.Getenv(agentlaunch.EnvSpecPath))
if specPath == "" {
return errors.New("launch: AO_LAUNCH_SPEC is required")
}
spec, err := agentlaunch.ReadAndRemove(specPath)
if err != nil {
return fmt.Errorf("launch: %w", err)
}
env := withoutLaunchSpecEnv(os.Environ())
launchErr := c.runLaunchCommand(ctx, spec.WorkspacePath, spec.Argv, env)
if len(spec.FallbackArgv) == 0 {
return launchErr
}
if launchErr != nil {
_, _ = fmt.Fprintf(c.deps.Err, "\r\n[ao launch] agent process exited: %v\r\n", launchErr)
}
return c.runLaunchCommand(ctx, spec.WorkspacePath, spec.FallbackArgv, env)
}
func (c *commandContext) runLaunchCommand(ctx context.Context, dir string, argv, env []string) error {
if len(argv) == 0 {
return errors.New("launch: command argv is required")
}
cmd := exec.CommandContext(ctx, argv[0], argv[1:]...)
cmd.Dir = dir
cmd.Env = env
cmd.Stdin = c.deps.In
cmd.Stdout = c.deps.Out
cmd.Stderr = c.deps.Err
return cmd.Run()
}
func withoutLaunchSpecEnv(env []string) []string {
cleaned := env[:0]
for _, pair := range env {
key, _, ok := strings.Cut(pair, "=")
if !ok {
cleaned = append(cleaned, pair)
continue
}
if envKeyEqual(key, agentlaunch.EnvSpecPath) {
continue
}
cleaned = append(cleaned, pair)
}
return cleaned
}
func envKeyEqual(a, b string) bool {
if runtime.GOOS == "windows" {
return strings.EqualFold(a, b)
}
return a == b
}