package cmd import ( "context" "encoding/json" "fmt" "os" "strings" "gogs.dmsc.dev/arp/arp_cli/client" "gogs.dmsc.dev/arp/arp_cli/config" "github.com/AlecAivazis/survey/v2" "github.com/olekukonko/tablewriter" "github.com/urfave/cli/v3" ) // ChannelCommand returns the channel command func ChannelCommand() *cli.Command { return &cli.Command{ Name: "channel", Usage: "Manage channels", Description: `Manage ARP channels. Channels are chat conversations between users/agents. Use this command to create, list, update, and delete channels.`, Commands: []*cli.Command{ { Name: "list", Aliases: []string{"ls"}, Usage: "List all channels", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "json", Aliases: []string{"j"}, Usage: "Output as JSON", }, }, Action: channelList, }, { Name: "get", Usage: "Get a channel by ID", Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Aliases: []string{"i"}, Usage: "Channel ID", Required: true, }, &cli.BoolFlag{ Name: "json", Aliases: []string{"j"}, Usage: "Output as JSON", }, }, Action: channelGet, }, { Name: "create", Usage: "Create a new channel", Action: channelCreate, Flags: []cli.Flag{ &cli.StringSliceFlag{ Name: "participants", Aliases: []string{"p"}, Usage: "Participant user IDs", }, }, }, { Name: "update", Usage: "Update a channel", Action: channelUpdate, Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Aliases: []string{"i"}, Usage: "Channel ID", Required: true, }, &cli.StringSliceFlag{ Name: "participants", Aliases: []string{"p"}, Usage: "Participant user IDs", }, }, }, { Name: "delete", Usage: "Delete a channel", Action: channelDelete, Flags: []cli.Flag{ &cli.StringFlag{ Name: "id", Aliases: []string{"i"}, Usage: "Channel ID", Required: true, }, &cli.BoolFlag{ Name: "yes", Aliases: []string{"y"}, Usage: "Skip confirmation", }, }, }, }, } } type Channel struct { ID string `json:"id"` Participants []struct { ID string `json:"id"` Email string `json:"email"` } `json:"participants"` CreatedAt string `json:"createdAt"` UpdatedAt string `json:"updatedAt"` } func channelList(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) query := "query Channels { channels { id participants { id email } createdAt updatedAt } }" resp, err := c.Query(query, nil) if err != nil { return err } var result struct { Channels []Channel `json:"channels"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if cmd.Bool("json") { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(result.Channels) } if len(result.Channels) == 0 { fmt.Println("No channels found.") return nil } table := tablewriter.NewWriter(os.Stdout) table.Header([]string{"ID", "Participants", "Created At"}) for _, ch := range result.Channels { participants := make([]string, len(ch.Participants)) for i, p := range ch.Participants { participants[i] = p.Email } table.Append([]string{ch.ID, strings.Join(participants, ", "), ch.CreatedAt}) } table.Render() return nil } func channelGet(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) id := cmd.String("id") query := "query Channel($id: ID!) { channel(id: $id) { id participants { id email } createdAt updatedAt } }" resp, err := c.Query(query, map[string]interface{}{"id": id}) if err != nil { return err } var result struct { Channel *Channel `json:"channel"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.Channel == nil { return fmt.Errorf("channel not found") } if cmd.Bool("json") { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(result.Channel) } ch := result.Channel fmt.Printf("ID: %s\n", ch.ID) fmt.Printf("Participants:\n") for _, p := range ch.Participants { fmt.Printf(" - %s (%s)\n", p.Email, p.ID) } fmt.Printf("Created At: %s\n", ch.CreatedAt) fmt.Printf("Updated At: %s\n", ch.UpdatedAt) return nil } func channelCreate(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } participants := cmd.StringSlice("participants") if len(participants) == 0 { var participantsStr string prompt := &survey.Input{Message: "Participant user IDs (comma-separated):"} if err := survey.AskOne(prompt, &participantsStr, survey.WithValidator(survey.Required)); err != nil { return err } for _, p := range strings.Split(participantsStr, ",") { participants = append(participants, strings.TrimSpace(p)) } } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) mutation := `mutation CreateChannel($input: NewChannel!) { createChannel(input: $input) { id participants { id email } createdAt updatedAt } }` input := map[string]interface{}{ "participants": participants, } resp, err := c.Mutation(mutation, map[string]interface{}{"input": input}) if err != nil { return err } var result struct { CreateChannel *Channel `json:"createChannel"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.CreateChannel == nil { return fmt.Errorf("failed to create channel") } fmt.Printf("Channel created successfully!\n") fmt.Printf("ID: %s\n", result.CreateChannel.ID) return nil } func channelUpdate(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } id := cmd.String("id") participants := cmd.StringSlice("participants") if len(participants) == 0 { fmt.Println("No updates provided. Use flags to specify what to update.") return nil } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) input := map[string]interface{}{ "participants": participants, } mutation := `mutation UpdateChannel($id: ID!, $input: UpdateChannelInput!) { updateChannel(id: $id, input: $input) { id participants { id email } createdAt updatedAt } }` resp, err := c.Mutation(mutation, map[string]interface{}{"id": id, "input": input}) if err != nil { return err } var result struct { UpdateChannel *Channel `json:"updateChannel"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.UpdateChannel == nil { return fmt.Errorf("channel not found") } fmt.Printf("Channel updated successfully!\n") fmt.Printf("ID: %s\n", result.UpdateChannel.ID) return nil } func channelDelete(ctx context.Context, cmd *cli.Command) error { cfg, err := config.Load() if err != nil { return err } if err := RequireAuth(cfg); err != nil { return err } id := cmd.String("id") skipConfirm := cmd.Bool("yes") if !skipConfirm { confirm := false prompt := &survey.Confirm{ Message: fmt.Sprintf("Are you sure you want to delete channel %s?", id), Default: false, } if err := survey.AskOne(prompt, &confirm); err != nil { return err } if !confirm { fmt.Println("Deletion cancelled.") return nil } } c := client.New(cfg.ServerURL) c.SetToken(cfg.Token) mutation := `mutation DeleteChannel($id: ID!) { deleteChannel(id: $id) }` resp, err := c.Mutation(mutation, map[string]interface{}{"id": id}) if err != nil { return err } var result struct { DeleteChannel bool `json:"deleteChannel"` } if err := json.Unmarshal(resp.Data, &result); err != nil { return err } if result.DeleteChannel { fmt.Printf("Channel %s deleted successfully.\n", id) } else { fmt.Printf("Failed to delete channel %s.\n", id) } return nil }