package cli import ( "context" "encoding/json" "fmt" "strings" "github.com/j3ssie/osmedeus/v5/internal/core" "github.com/j3ssie/osmedeus/v5/internal/database" "github.com/j3ssie/osmedeus/v5/internal/executor" "github.com/j3ssie/osmedeus/v5/internal/terminal" "github.com/spf13/cobra" ) // runStatusCmd - show status of a specific run var runStatusCmd = &cobra.Command{ Use: "status ", Short: "Show status of a workflow run", Long: UsageRunStatus(), Args: cobra.ExactArgs(1), RunE: runRunStatus, } // runCancelCmd - cancel a running workflow var runCancelCmd = &cobra.Command{ Use: "cancel ", Short: "Cancel a running workflow", Long: UsageRunCancel(), Args: cobra.ExactArgs(1), RunE: runRunCancel, } func init() { runCmd.AddCommand(runStatusCmd) runCmd.AddCommand(runCancelCmd) } func runRunStatus(cmd *cobra.Command, args []string) error { if err := connectDB(); err != nil { return err } defer func() { _ = database.Close() }() runUUID := args[0] ctx := context.Background() run, err := database.GetRunByID(ctx, runUUID, false, false) if err != nil { return fmt.Errorf("run not found: %w", err) } if globalJSON { jsonBytes, err := json.Marshal(run) if err != nil { return fmt.Errorf("failed to format run: %w", err) } fmt.Println(string(jsonBytes)) return nil } // Key-value display printer := terminal.NewPrinter() printer.Info("Run Details") fmt.Println() printField("run_uuid", run.RunUUID) printField("workflow", run.WorkflowName) printField("kind", run.WorkflowKind) printField("target", run.Target) printField("workspace", run.Workspace) printField("status", terminal.ColorizeStatus(run.Status)) printField("progress", fmt.Sprintf("%d/%d steps", run.CompletedSteps, run.TotalSteps)) printField("trigger_type", run.TriggerType) if run.StartedAt != nil { printField("started_at", run.StartedAt.String()) } if run.CompletedAt != nil { printField("completed_at", run.CompletedAt.String()) } if run.ErrorMessage != "" { printField("error", run.ErrorMessage) } if run.CurrentPID > 0 { printField("pid", fmt.Sprintf("%d", run.CurrentPID)) } return nil } // printField prints a key-value pair with aligned formatting. func printField(key, value string) { fmt.Printf(" %-16s %s\n", key+":", value) } func runRunCancel(cmd *cobra.Command, args []string) error { if err := connectDB(); err != nil { return err } defer func() { _ = database.Close() }() runUUID := args[0] ctx := context.Background() run, err := database.GetRunByID(ctx, runUUID, false, false) if err != nil { return fmt.Errorf("run not found: %w", err) } if run.Status != string(core.RunStatusPending) && run.Status != string(core.RunStatusRunning) { return fmt.Errorf("cannot cancel run with status '%s'", run.Status) } var killedPIDs []int var killedSessions []string var killMethod string // Try to cancel via control plane first controlPlane := executor.GetRunControlPlane() controlPlanePIDs, controlPlaneSessions, controlPlaneErr := controlPlane.Cancel(run.RunUUID) if controlPlaneErr == nil && (len(controlPlanePIDs) > 0 || len(controlPlaneSessions) > 0) { killedPIDs = controlPlanePIDs killedSessions = controlPlaneSessions killMethod = "control_plane" } else if run.CurrentPID > 0 { if core.KillProcessAndChildren(run.CurrentPID) { killedPIDs = []int{run.CurrentPID} killMethod = "database_pid" } } // Update database status if err := database.UpdateRunStatus(ctx, run.RunUUID, string(core.RunStatusCancelled), "Cancelled via CLI"); err != nil { return fmt.Errorf("failed to update run status: %w", err) } if globalJSON { response := map[string]interface{}{ "message": "Run cancelled successfully", "run_uuid": run.RunUUID, "killed_pids": killedPIDs, "killed_tmux_sessions": killedSessions, "kill_method": killMethod, } jsonBytes, err := json.Marshal(response) if err != nil { return fmt.Errorf("failed to format response: %w", err) } fmt.Println(string(jsonBytes)) return nil } printer := terminal.NewPrinter() printer.Success("Run %s cancelled", run.RunUUID) if len(killedPIDs) > 0 { fmt.Printf(" Terminated %d process(es) via %s\n", len(killedPIDs), killMethod) } if len(killedSessions) > 0 { fmt.Printf(" Killed %d tmux session(s): %s\n", len(killedSessions), strings.Join(killedSessions, ", ")) } if len(killedPIDs) == 0 && len(killedSessions) == 0 { fmt.Println(" No active processes found to terminate") } return nil }