Browse Source

fix bugs in cli

david 4 weeks ago
parent
commit
769e339b75
3 changed files with 168 additions and 36 deletions
  1. 2 14
      arp_cli/cmd/repl.go
  2. 147 21
      arp_cli/cmd/workflow.go
  3. 19 1
      arp_cli/main.go

+ 2 - 14
arp_cli/cmd/repl.go

@@ -199,21 +199,9 @@ func (r *REPL) injectSessionURL(args []string) []string {
 		}
 	}
 
-	// Find the position after the command name but before subcommands
-	// For "arp_cli subcommand --flag", insert after "arp_cli"
+	// Insert --url flag right after the app name, before any subcommands
+	// This ensures global flags are processed correctly by urfave/cli/v3
 	insertPos := 1
-	if len(args) > 1 && !strings.HasPrefix(args[1], "-") {
-		// There's a subcommand, insert URL flag before it
-		// Actually, we want to insert after the subcommand name
-		insertPos = 2
-		if len(args) > 2 && !strings.HasPrefix(args[2], "-") {
-			// There's a nested subcommand (e.g., workflow template list)
-			insertPos = 3
-			if len(args) > 3 && !strings.HasPrefix(args[3], "-") {
-				insertPos = 4
-			}
-		}
-	}
 
 	// Insert --url flag with value
 	newArgs := make([]string, 0, len(args)+2)

+ 147 - 21
arp_cli/cmd/workflow.go

@@ -64,6 +64,11 @@ Workflow nodes represent individual steps in a running workflow.`,
 						Usage:  "Create a new workflow template",
 						Action: workflowTemplateCreate,
 						Flags: []cli.Flag{
+							&cli.StringFlag{
+								Name:    "file",
+								Aliases: []string{"f"},
+								Usage:   "Path to JSON file containing template data",
+							},
 							&cli.StringFlag{
 								Name:    "name",
 								Aliases: []string{"n"},
@@ -76,7 +81,7 @@ Workflow nodes represent individual steps in a running workflow.`,
 							},
 							&cli.StringFlag{
 								Name:    "definition",
-								Aliases: []string{"f"},
+								Aliases: []string{"D"},
 								Usage:   "Workflow definition (JSON string or @filename)",
 							},
 							&cli.BoolFlag{
@@ -136,6 +141,19 @@ Workflow nodes represent individual steps in a running workflow.`,
 							},
 						},
 					},
+					{
+						Name:   "example",
+						Usage:  "Generate an example template file",
+						Action: workflowTemplateExample,
+						Flags: []cli.Flag{
+							&cli.StringFlag{
+								Name:    "output",
+								Aliases: []string{"o"},
+								Usage:   "Output file path",
+								Value:   "workflow_template.json",
+							},
+						},
+					},
 				},
 			},
 			// WorkflowInstance commands
@@ -273,6 +291,14 @@ Workflow nodes represent individual steps in a running workflow.`,
 	}
 }
 
+// WorkflowTemplateFile represents a workflow template stored in a JSON file
+type WorkflowTemplateFile struct {
+	Name        string      `json:"name"`
+	Description string      `json:"description"`
+	Definition  interface{} `json:"definition"`
+	IsActive    bool        `json:"isActive"`
+}
+
 // WorkflowTemplate represents a workflow template
 type WorkflowTemplate struct {
 	ID          string `json:"id"`
@@ -430,32 +456,71 @@ func workflowTemplateCreate(ctx context.Context, cmd *cli.Command) error {
 		return err
 	}
 
-	name := cmd.String("name")
-	description := cmd.String("description")
-	definition := cmd.String("definition")
-	isActive := cmd.Bool("active")
+	var name, description, definition string
+	var isActive bool
 
-	if name == "" {
-		prompt := &survey.Input{Message: "Template name:"}
-		if err := survey.AskOne(prompt, &name, survey.WithValidator(survey.Required)); err != nil {
-			return err
+	// Check if a file was provided
+	filePath := cmd.String("file")
+	if filePath != "" {
+		// Read and parse the template file
+		data, err := os.ReadFile(filePath)
+		if err != nil {
+			return fmt.Errorf("failed to read file: %w", err)
 		}
-	}
 
-	if description == "" {
-		prompt := &survey.Input{Message: "Description (optional):"}
-		survey.AskOne(prompt, &description)
-	}
+		var templateFile WorkflowTemplateFile
+		if err := json.Unmarshal(data, &templateFile); err != nil {
+			return fmt.Errorf("failed to parse template file: %w", err)
+		}
 
-	if definition == "" {
-		prompt := &survey.Multiline{Message: "Workflow definition (JSON):"}
-		if err := survey.AskOne(prompt, &definition, survey.WithValidator(survey.Required)); err != nil {
-			return err
+		// Validate required fields
+		if templateFile.Name == "" {
+			return fmt.Errorf("name is required in template file")
+		}
+		if templateFile.Definition == nil {
+			return fmt.Errorf("definition is required in template file")
+		}
+
+		name = templateFile.Name
+		description = templateFile.Description
+		isActive = templateFile.IsActive
+
+		// Marshal definition to JSON string
+		defBytes, err := json.Marshal(templateFile.Definition)
+		if err != nil {
+			return fmt.Errorf("failed to marshal definition: %w", err)
+		}
+		definition = string(defBytes)
+	} else {
+		// Use command-line flags
+		name = cmd.String("name")
+		description = cmd.String("description")
+		definition = cmd.String("definition")
+		isActive = cmd.Bool("active")
+
+		// Interactive prompts for missing values
+		if name == "" {
+			prompt := &survey.Input{Message: "Template name:"}
+			if err := survey.AskOne(prompt, &name, survey.WithValidator(survey.Required)); err != nil {
+				return err
+			}
+		}
+
+		if description == "" {
+			prompt := &survey.Input{Message: "Description (optional):"}
+			survey.AskOne(prompt, &description)
+		}
+
+		if definition == "" {
+			prompt := &survey.Multiline{Message: "Workflow definition (JSON):"}
+			if err := survey.AskOne(prompt, &definition, survey.WithValidator(survey.Required)); err != nil {
+				return err
+			}
 		}
-	}
 
-	// Handle file input for definition
-	definition = ReadFileOrString(definition)
+		// Handle file input for definition
+		definition = ReadFileOrString(definition)
+	}
 
 	mutation := `mutation CreateWorkflowTemplate($input: NewWorkflowTemplate!) { createWorkflowTemplate(input: $input) { id name description definition isActive createdBy { id email } createdAt updatedAt } }`
 
@@ -604,6 +669,67 @@ func workflowTemplateDelete(ctx context.Context, cmd *cli.Command) error {
 	return nil
 }
 
+func workflowTemplateExample(ctx context.Context, cmd *cli.Command) error {
+	outputPath := cmd.String("output")
+
+	// Example template content
+	exampleTemplate := WorkflowTemplateFile{
+		Name:        "Standard Approval Workflow",
+		Description: "A simple linear approval workflow for testing",
+		Definition: map[string]interface{}{
+			"nodes": map[string]interface{}{
+				"start": map[string]interface{}{
+					"type":      "task",
+					"title":     "Initial Review",
+					"content":   "Review the incoming request",
+					"assignee":  "1",
+					"dependsOn": []interface{}{},
+				},
+				"analysis": map[string]interface{}{
+					"type":      "task",
+					"title":     "Data Analysis",
+					"content":   "Analyze the data",
+					"assignee":  "2",
+					"dependsOn": []interface{}{"start"},
+				},
+				"approval": map[string]interface{}{
+					"type":      "task",
+					"title":     "Manager Approval",
+					"content":   "Approve the request",
+					"assignee":  "3",
+					"dependsOn": []interface{}{"analysis"},
+				},
+				"complete": map[string]interface{}{
+					"type":      "task",
+					"title":     "Complete",
+					"content":   "Finalize",
+					"assignee":  "1",
+					"dependsOn": []interface{}{"approval"},
+				},
+			},
+		},
+		IsActive: true,
+	}
+
+	// Marshal to JSON with indentation
+	data, err := json.MarshalIndent(exampleTemplate, "", "  ")
+	if err != nil {
+		return fmt.Errorf("failed to marshal example template: %w", err)
+	}
+
+	// Write to file
+	err = os.WriteFile(outputPath, data, 0644)
+	if err != nil {
+		return fmt.Errorf("failed to write example file: %w", err)
+	}
+
+	fmt.Printf("Example workflow template written to: %s\n", outputPath)
+	fmt.Println("Edit the file to customize your workflow, then use:")
+	fmt.Printf("  arp_cli workflow template create --file %s\n", outputPath)
+
+	return nil
+}
+
 // WorkflowInstance operations
 
 func workflowInstanceList(ctx context.Context, cmd *cli.Command) error {

+ 19 - 1
arp_cli/main.go

@@ -28,8 +28,26 @@ func main() {
 	// Build the CLI app
 	app := buildApp()
 
-	// If no arguments provided (only program name), start REPL mode
+	// If no arguments provided (only program name), perform login then start REPL mode
 	if len(os.Args) <= 1 {
+		// Create a command context for login
+		loginCmd := &cli.Command{
+			Name: "login",
+			Flags: []cli.Flag{
+				&cli.StringFlag{Name: "url"},
+				&cli.StringFlag{Name: "email"},
+				&cli.StringFlag{Name: "password"},
+			},
+			Action: cmd.LoginCommand().Action,
+		}
+
+		// Execute login command
+		if err := loginCmd.Run(ctx, os.Args); err != nil {
+			fmt.Fprintf(os.Stderr, "Login failed: %v\n", err)
+			os.Exit(1)
+		}
+
+		// Start REPL mode after successful login
 		repl := cmd.NewREPL(app)
 		if err := repl.Run(ctx); err != nil {
 			fmt.Fprintf(os.Stderr, "Error: %v\n", err)