diff --git a/go.mod b/go.mod index 4e86fb3..1c17412 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/flashcatcloud/flashduty-cli go 1.25.1 require ( - github.com/flashcatcloud/go-flashduty v0.5.2 + github.com/flashcatcloud/go-flashduty v0.5.3-0.20260602031007-62b37649b2f0 github.com/mattn/go-runewidth v0.0.23 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 57e5863..a5b9db3 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/flashcatcloud/go-flashduty v0.5.2 h1:mYg/M0jqkil30WTLdICVtTJVGxEIGmae/3zBpRkwLRQ= -github.com/flashcatcloud/go-flashduty v0.5.2/go.mod h1:aA0RtZEs0AYOwwdNKdtVeD8YMOdnmVY1zAlVD+9Ovx8= +github.com/flashcatcloud/go-flashduty v0.5.3-0.20260602031007-62b37649b2f0 h1:mk9ryHQVssVA3qqyH4ryqeWa6sW0tYdww3JWalN3ZH0= +github.com/flashcatcloud/go-flashduty v0.5.3-0.20260602031007-62b37649b2f0/go.mod h1:aA0RtZEs0AYOwwdNKdtVeD8YMOdnmVY1zAlVD+9Ovx8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= diff --git a/internal/cli/alert.go b/internal/cli/alert.go index 8f50c8b..a08254e 100644 --- a/internal/cli/alert.go +++ b/internal/cli/alert.go @@ -34,6 +34,7 @@ func newAlertListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List alerts", + Long: curatedLong("List alerts within a time window, optionally filtered by severity, channel, active/recovered/muted state. No server-side title/text filter — to search by title, pipe --json to jq: 'select(.title|test(\"pat\";\"i\"))'. --limit max 100; --since/--until window must be < 31 days.", "Alerts", "ReadList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { if active && recovered { @@ -115,6 +116,7 @@ func newAlertGetCmd() *cobra.Command { return &cobra.Command{ Use: "get ", Short: "Get alert detail", + Long: curatedLong("Get the full detail of a single alert by ID.", "Alerts", "ReadInfo"), Args: requireArgs("alert_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -177,6 +179,7 @@ func newAlertEventsCmd() *cobra.Command { return &cobra.Command{ Use: "events ", Short: "List alert events", + Long: curatedLong("List the individual events that compose a given alert.", "Alerts", "ReadEventList"), Args: requireArgs("alert_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -212,6 +215,7 @@ func newAlertTimelineCmd() *cobra.Command { cmd := &cobra.Command{ Use: "timeline ", Short: "View alert timeline", + Long: curatedLong("View the chronological feed of timeline events (actions, state changes) for an alert.", "Alerts", "ReadFeed"), Args: requireArgs("alert_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { diff --git a/internal/cli/alert_event.go b/internal/cli/alert_event.go index f2111b6..b8b0283 100644 --- a/internal/cli/alert_event.go +++ b/internal/cli/alert_event.go @@ -27,6 +27,7 @@ func newAlertEventListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List alert events globally", + Long: curatedLong("List alert events across all alerts within a time window, optionally filtered by severity, channel, or integration type.", "Alerts", "EventReadList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) diff --git a/internal/cli/audit.go b/internal/cli/audit.go index bc6c2ca..4ad6b00 100644 --- a/internal/cli/audit.go +++ b/internal/cli/audit.go @@ -27,6 +27,7 @@ func newAuditSearchCmd() *cobra.Command { cmd := &cobra.Command{ Use: "search", Short: "Search audit logs", + Long: curatedLong("Search audit logs within a time window, optionally filtered by person and operation type. The --since/--until window must be < 90 days; --limit max is 99.", "AuditLogs", "Search"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -106,8 +107,8 @@ func newAuditSearchCmd() *cobra.Command { cmd.Flags().StringVar(&since, "since", "7d", "Start time") cmd.Flags().StringVar(&until, "until", "now", "End time") cmd.Flags().Int64Var(&person, "person", 0, "Filter by person ID") - cmd.Flags().StringVar(&operation, "operation", "", "Filter by operation type") - cmd.Flags().IntVar(&limit, "limit", 20, "Max results") + cmd.Flags().StringVar(&operation, "operation", "", "Filter by exact operation name(s) from 'fduty audit operation-list' (e.g. monitRule:write:update); comma-separate to match several in one call. Prefixes do NOT match (\"monitRule\" returns nothing).") + cmd.Flags().IntVar(&limit, "limit", 20, "Max results (max 99)") cmd.Flags().IntVar(&page, "page", 1, "Page number") return cmd diff --git a/internal/cli/change.go b/internal/cli/change.go index de410d7..4c6cd61 100644 --- a/internal/cli/change.go +++ b/internal/cli/change.go @@ -23,10 +23,12 @@ func newChangeListCmd() *cobra.Command { var channel string var since, until string var limit, page int + var query, integration string cmd := &cobra.Command{ Use: "list", Short: "List changes", + Long: curatedLong("List changes recorded in the change feed. Time window must be < 31 days; --limit max is 100.", "Changes", "List"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -65,6 +67,14 @@ func newChangeListCmd() *cobra.Command { } input.ChannelIDs = channelIDs } + if integration != "" { + integrationIDs, err := parseIntSlice(integration) + if err != nil { + return fmt.Errorf("invalid --integration: %w", err) + } + input.IntegrationIDs = integrationIDs + } + input.Query = query result, _, err := ctx.Client.Changes.List(cmdContext(ctx.Cmd), input) if err != nil { @@ -85,9 +95,11 @@ func newChangeListCmd() *cobra.Command { } cmd.Flags().StringVar(&channel, "channel", "", "Comma-separated channel IDs") - cmd.Flags().StringVar(&since, "since", "24h", "Start time") - cmd.Flags().StringVar(&until, "until", "now", "End time") - cmd.Flags().IntVar(&limit, "limit", 20, "Max results") + cmd.Flags().StringVar(&query, "query", "", "Free-text/regex search over change fields") + cmd.Flags().StringVar(&integration, "integration", "", "Comma-separated reporting integration IDs") + cmd.Flags().StringVar(&since, "since", "24h", "Start time (accepts 7d/24h/now, RFC3339, or Unix epoch; window must be < 31 days)") + cmd.Flags().StringVar(&until, "until", "now", "End time (accepts 7d/24h/now, RFC3339, or Unix epoch)") + cmd.Flags().IntVar(&limit, "limit", 20, "Max results (max 100)") cmd.Flags().IntVar(&page, "page", 1, "Page number") return cmd diff --git a/internal/cli/channel.go b/internal/cli/channel.go index 44f72d9..bf02020 100644 --- a/internal/cli/channel.go +++ b/internal/cli/channel.go @@ -43,6 +43,7 @@ func newChannelListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List channels", + Long: curatedLong("List channels in the account, optionally filtered by name.", "Channels", "ChannelList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { // Legacy parity: the hand-written SDK called /channel/list with an diff --git a/internal/cli/command.go b/internal/cli/command.go index 245fdf6..09a9081 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -14,12 +14,12 @@ import ( // runCommand and passed to the command's handler function. Client is the // typed go-flashduty SDK every command calls through. type RunContext struct { - Client *flashduty.Client - Cmd *cobra.Command - Args []string - Writer io.Writer - Printer output.Printer - Format output.Format + Client *flashduty.Client + Cmd *cobra.Command + Args []string + Writer io.Writer + Printer output.Printer + Format output.Format } // Structured reports whether output should be a machine-readable dump (JSON or @@ -36,12 +36,12 @@ func runCommand(cmd *cobra.Command, args []string, fn func(ctx *RunContext) erro return err } ctx := &RunContext{ - Client: client, - Cmd: cmd, - Args: args, - Writer: cmd.OutOrStdout(), - Printer: newPrinter(cmd.OutOrStdout()), - Format: currentOutputFormat(), + Client: client, + Cmd: cmd, + Args: args, + Writer: cmd.OutOrStdout(), + Printer: newPrinter(cmd.OutOrStdout()), + Format: currentOutputFormat(), } return fn(ctx) } @@ -73,6 +73,15 @@ func (ctx *RunContext) WriteResult(message string) { writeResult(ctx.Writer, message) } +// WriteRaw writes a non-JSON response body (e.g. a CSV/file download surfaced +// on Response.Raw by the *export endpoints) straight to the output writer, so +// shell redirection (`> file.csv`) captures the bytes verbatim instead of the +// canned "OK: POST ..." acknowledgment. +func (ctx *RunContext) WriteRaw(body []byte) error { + _, err := ctx.Writer.Write(body) + return err +} + // WriteResultJSON outputs structured data in JSON or TOON mode, or a // human-readable message in table mode. JSON stays indented (byte-compatible // with the legacy --json path); TOON routes through the SDK marshaller. diff --git a/internal/cli/command_test.go b/internal/cli/command_test.go index 16f39e8..849ce3c 100644 --- a/internal/cli/command_test.go +++ b/internal/cli/command_test.go @@ -248,82 +248,6 @@ func TestCommandIncidentTimelineEmpty(t *testing.T) { } } -// --------------------------------------------------------------------------- -// Test 263: statuspage create-incident result with change_id -// --------------------------------------------------------------------------- - -func TestCommandStatusPageCreateIncidentWithChangeID(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"change_id": 12345} - - out, err := execCommand("statuspage", "create-incident", "--page-id", "1", "--title", "Outage") - if err != nil { - t.Fatalf("[#263] unexpected error: %v", err) - } - - expected := "Status incident created: 12345" - if !strings.Contains(out, expected) { - t.Errorf("[#263] expected output containing %q, got:\n%s", expected, out) - } -} - -func TestCommandStatusPageCreateIncidentWithChangeID_JSON(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"change_id": 12345} - - out, err := execCommand("statuspage", "create-incident", "--page-id", "1", "--title", "Outage", "--json") - if err != nil { - t.Fatalf("[#263/json] unexpected error: %v", err) - } - - var parsed map[string]string - if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &parsed); err != nil { - t.Fatalf("[#263/json] failed to parse JSON output: %v\nraw output:\n%s", err, out) - } - if !strings.Contains(parsed["message"], "12345") { - t.Errorf("[#263/json] expected message containing '12345', got %q", parsed["message"]) - } -} - -// --------------------------------------------------------------------------- -// Test 264: statuspage create-incident result without change_id -// --------------------------------------------------------------------------- - -func TestCommandStatusPageCreateIncidentWithoutChangeID(t *testing.T) { - saveAndResetGlobals(t) - newGFStub(t) - - out, err := execCommand("statuspage", "create-incident", "--page-id", "1", "--title", "Outage") - if err != nil { - t.Fatalf("[#264] unexpected error: %v", err) - } - - expected := "Status incident created successfully." - if !strings.Contains(out, expected) { - t.Errorf("[#264] expected output containing %q, got:\n%s", expected, out) - } -} - -func TestCommandStatusPageCreateIncidentWithoutChangeID_JSON(t *testing.T) { - saveAndResetGlobals(t) - newGFStub(t) - - out, err := execCommand("statuspage", "create-incident", "--page-id", "1", "--title", "Outage", "--json") - if err != nil { - t.Fatalf("[#264/json] unexpected error: %v", err) - } - - var parsed map[string]string - if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &parsed); err != nil { - t.Fatalf("[#264/json] failed to parse JSON output: %v\nraw output:\n%s", err, out) - } - if parsed["message"] != "Status incident created successfully." { - t.Errorf("[#264/json] expected message %q, got %q", "Status incident created successfully.", parsed["message"]) - } -} - // --------------------------------------------------------------------------- // Test 321: member list with PersonInfos // --------------------------------------------------------------------------- diff --git a/internal/cli/coverage_test.go b/internal/cli/coverage_test.go new file mode 100644 index 0000000..c9dc212 --- /dev/null +++ b/internal/cli/coverage_test.go @@ -0,0 +1,129 @@ +package cli + +import ( + "encoding/json" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/spf13/cobra" +) + +// loadSpecPaths reads every GET/POST operation from the openapi spec shipped in +// the linked go-flashduty module — the same spec cligen generates against — +// returning operationId -> path. +func loadSpecPaths(t *testing.T) map[string]string { + t.Helper() + out, err := exec.Command("go", "list", "-m", "-f", "{{.Dir}}", "github.com/flashcatcloud/go-flashduty").Output() + if err != nil { + t.Fatalf("locate go-flashduty module: %v", err) + } + specPath := filepath.Join(strings.TrimSpace(string(out)), "openapi", "openapi.en.json") + data, err := os.ReadFile(specPath) + if err != nil { + t.Fatalf("read spec: %v", err) + } + var spec struct { + Paths map[string]map[string]struct { + OperationID string `json:"operationId"` + Tags []string `json:"tags"` + } `json:"paths"` + } + if err := json.Unmarshal(data, &spec); err != nil { + t.Fatalf("parse spec: %v", err) + } + ids := map[string]string{} + for path, methods := range spec.Paths { + for verb, op := range methods { + v := strings.ToUpper(verb) + if v != "GET" && v != "POST" { + continue + } + if op.OperationID == "" || len(op.Tags) == 0 { + continue + } + ids[op.OperationID] = path + } + } + return ids +} + +// pathCommand derives the path-is-king command for an API path: the first +// segment is the group, the remaining segments hyphen-join into the verb. This +// mirrors cligen's cliGroup/cliVerb (spec path segments are already kebab-case, +// so a plain join matches). +func pathCommand(apiPath string) string { + var segs []string + for _, s := range strings.Split(apiPath, "/") { + if s != "" { + segs = append(segs, s) + } + } + if len(segs) == 0 { + return "" + } + if len(segs) == 1 { + return segs[0] + } + return segs[0] + " " + strings.Join(segs[1:], "-") +} + +// leafCommandPaths walks the real rootCmd (built by init()) and returns the set +// of every command path a user can invoke, minus the "flashduty " root prefix. +func leafCommandPaths() map[string]bool { + set := map[string]bool{} + var walk func(c *cobra.Command) + walk = func(c *cobra.Command) { + for _, sub := range c.Commands() { + path := strings.TrimPrefix(sub.CommandPath(), "flashduty ") + set[path] = true + walk(sub) + } + } + walk(rootCmd) + return set +} + +// TestEveryOperationHasPathCommand is the core invariant of the path-is-king +// command tree: every public operation in the spec is reachable at the command +// derived mechanically from its API path (group = first segment, verb = the +// rest hyphen-joined). An agent that knows only the API path can always invoke +// the operation without guessing — generated commands provide the path-name and +// curated commands win the exact name when they already own it. +func TestEveryOperationHasPathCommand(t *testing.T) { + specPaths := loadSpecPaths(t) + leaves := leafCommandPaths() + var missing []string + for _, apiPath := range specPaths { + cmd := pathCommand(apiPath) + if !leaves[cmd] { + missing = append(missing, cmd+" (<= "+apiPath+")") + } + } + if len(missing) > 0 { + t.Errorf("%d operations have no command at their path-name:\n %s", + len(missing), strings.Join(missing, "\n ")) + } + t.Logf("path-is-king: all %d operations reachable at their path-name", len(specPaths)) +} + +// TestGeneratorTargetsFullSpec asserts the generator emitted a command for every +// spec operation (no gaps, no phantom manifest entries from a stale run). +func TestGeneratorTargetsFullSpec(t *testing.T) { + specPaths := loadSpecPaths(t) + gen := map[string]bool{} + for _, id := range generatedOpIDs { + gen[id] = true + if _, ok := specPaths[id]; !ok { + t.Errorf("manifest op %q is not in the current spec (regenerate cligen)", id) + } + } + for id := range specPaths { + if !gen[id] { + t.Errorf("op %q has no generated command (regenerate cligen)", id) + } + } + t.Logf("generator targets %d/%d spec operations", len(gen), len(specPaths)) +} diff --git a/internal/cli/escalation_rule.go b/internal/cli/escalation_rule.go deleted file mode 100644 index 955f6fe..0000000 --- a/internal/cli/escalation_rule.go +++ /dev/null @@ -1,96 +0,0 @@ -package cli - -import ( - "fmt" - "strconv" - - "github.com/flashcatcloud/go-flashduty" - "github.com/spf13/cobra" - - "github.com/flashcatcloud/flashduty-cli/internal/output" -) - -func newEscalationRuleCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "escalation-rule", - Short: "Manage escalation rules", - } - cmd.AddCommand(newEscalationRuleListCmd()) - return cmd -} - -func newEscalationRuleListCmd() *cobra.Command { - var channelID int64 - var channelName string - - cmd := &cobra.Command{ - Use: "list", - Short: "List escalation rules for a channel", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - // Resolve channel name to ID if needed - if channelID == 0 && channelName != "" { - resolved, err := resolveChannelID(ctx, channelName) - if err != nil { - return err - } - channelID = resolved - } - - if channelID == 0 { - return fmt.Errorf("--channel or --channel-name is required") - } - - result, _, err := ctx.Client.Channels.ChannelEscalateRuleList(cmdContext(ctx.Cmd), &flashduty.ChannelScopedListRequest{ - ChannelID: channelID, - }) - if err != nil { - return err - } - - cols := []output.Column{ - {Header: "ID", Field: func(v any) string { return v.(flashduty.EscalateRuleItem).RuleID }}, - {Header: "NAME", Field: func(v any) string { return v.(flashduty.EscalateRuleItem).RuleName }}, - {Header: "CHANNEL", Field: func(v any) string { return v.(flashduty.EscalateRuleItem).ChannelName }}, - {Header: "STATUS", Field: func(v any) string { return v.(flashduty.EscalateRuleItem).Status }}, - {Header: "PRIORITY", Field: func(v any) string { - return strconv.FormatInt(v.(flashduty.EscalateRuleItem).Priority, 10) - }}, - {Header: "LAYERS", Field: func(v any) string { - return strconv.Itoa(len(v.(flashduty.EscalateRuleItem).Layers)) - }}, - } - - return ctx.Printer.Print(result.Items, cols) - }) - }, - } - - cmd.Flags().Int64Var(&channelID, "channel", 0, "Channel ID") - cmd.Flags().StringVar(&channelName, "channel-name", "", "Channel name (resolved to ID)") - - return cmd -} - -// resolveChannelID resolves a channel name to its ID. -func resolveChannelID(ctx *RunContext, name string) (int64, error) { - result, _, err := ctx.Client.Channels.ChannelList(cmdContext(ctx.Cmd), &flashduty.ListChannelsRequest{ - ChannelName: name, - }) - if err != nil { - return 0, fmt.Errorf("failed to resolve channel name: %w", err) - } - - switch len(result.Items) { - case 0: - return 0, fmt.Errorf("no channel found matching %q", name) - case 1: - return result.Items[0].ChannelID, nil - default: - _, _ = fmt.Fprintln(ctx.Cmd.OutOrStdout(), "Multiple channels match:") - for _, ch := range result.Items { - _, _ = fmt.Fprintf(ctx.Cmd.OutOrStdout(), " %d %s\n", ch.ChannelID, ch.ChannelName) - } - return 0, fmt.Errorf("multiple channels match %q, use --channel to specify", name) - } -} diff --git a/internal/cli/field.go b/internal/cli/field.go index 33de606..7682664 100644 --- a/internal/cli/field.go +++ b/internal/cli/field.go @@ -24,6 +24,7 @@ func newFieldListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List custom fields", + Long: curatedLong("List custom fields, optionally filtered by exact field name.", "AlertEnrichment", "FieldReadList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { result, _, err := ctx.Client.AlertEnrichment.FieldReadList(cmdContext(ctx.Cmd), &flashduty.FieldListRequest{}) diff --git a/internal/cli/gen_support.go b/internal/cli/gen_support.go new file mode 100644 index 0000000..c15c9db --- /dev/null +++ b/internal/cli/gen_support.go @@ -0,0 +1,173 @@ +package cli + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + + "github.com/spf13/cobra" +) + +// This file is the hand-written runtime support for the generated commands in +// zz_generated_*.go (produced by internal/cmd/cligen). Generated files stay +// pure data + wiring; all shared behavior lives here so it can be reviewed and +// tested like normal code. + +// genAssembleBody builds a request body map from an optional --data JSON blob +// overlaid with explicitly-set typed flags. Flags win over --data so an agent +// can pass a JSON skeleton and override one field. setFlags is called after the +// --data merge to stamp the changed scalar flags. +func genAssembleBody(dataJSON string, setFlags func(body map[string]any)) (map[string]any, error) { + body := map[string]any{} + if dataJSON != "" { + if err := json.Unmarshal([]byte(dataJSON), &body); err != nil { + return nil, fmt.Errorf("invalid --data JSON: %w", err) + } + } + setFlags(body) + return body, nil +} + +// responseHelp returns the rendered "Response fields" block for an SDK +// Service.Method (from the generated responseHelpBySDKMethod table), or "" when +// the response has no documented schema. Curated commands append it to their +// Long so they show the same output shape as the generated commands. +func responseHelp(service, method string) string { + return responseHelpBySDKMethod[service+"."+method] +} + +// curatedLong composes a curated command's Long help from an intro paragraph +// plus the shared spec-derived Response-fields block for the SDK method it +// calls, so agents read output field names instead of guessing them with jq. +func curatedLong(intro, service, method string) string { + if rh := responseHelp(service, method); rh != "" { + return intro + "\n\n" + rh + } + return intro +} + +// genBindBody marshals the assembled body map into the typed request struct so +// the call benefits from the SDK's wire encoding (nullable pointers, etc.). +// +// POST request structs tag fields with `json`, so json.Unmarshal binds them. +// GET query structs tag fields with `url` and carry NO json tag, so the +// Unmarshal pass silently skips every field, leaving the request empty and the +// command un-driveable. After the json pass, additively bind any url-tagged +// field from the body by its url wire-name. For POST structs the url pass is a +// no-op, so existing behavior is unchanged. +func genBindBody(body map[string]any, req any) error { + b, err := json.Marshal(body) + if err != nil { + return fmt.Errorf("failed to encode request: %w", err) + } + if err := json.Unmarshal(b, req); err != nil { + return fmt.Errorf("failed to bind request: %w", err) + } + bindURLTagged(body, reflect.ValueOf(req)) + return nil +} + +// bindURLTagged fills fields carrying a `url` struct tag (GET query params) from +// the body map keyed by the url wire-name. json.Unmarshal cannot reach these +// because they lack a json tag. The 6 GET request types use only int64/string +// fields; values arrive as Go ints/strings (typed flags) or float64/string +// (--data JSON), all coerced here. +func bindURLTagged(body map[string]any, rv reflect.Value) { + for rv.Kind() == reflect.Ptr { + if rv.IsNil() { + return + } + rv = rv.Elem() + } + if rv.Kind() != reflect.Struct { + return + } + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + if f.Anonymous { + bindURLTagged(body, rv.Field(i)) + continue + } + if f.Tag.Get("json") != "" { // json-tagged: already bound by Unmarshal + continue + } + wire := strings.Split(f.Tag.Get("url"), ",")[0] + if wire == "" || wire == "-" { + continue + } + raw, ok := body[wire] + if !ok { + continue + } + fv := rv.Field(i) + if !fv.CanSet() { + continue + } + switch fv.Kind() { + case reflect.String: + if s, ok := raw.(string); ok { + fv.SetString(s) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch n := raw.(type) { + case float64: + fv.SetInt(int64(n)) + case int64: + fv.SetInt(n) + case int: + fv.SetInt(int64(n)) + case json.Number: + if iv, err := n.Int64(); err == nil { + fv.SetInt(iv) + } + } + } + } +} + +// printGenericResult renders a generated command's typed response. Generated +// commands have no curated column set, so in machine-readable mode (TOON/JSON) +// it marshals the whole value — which is what the agent reads — and in human +// table mode it falls back to pretty JSON rather than a blank table. +func printGenericResult(ctx *RunContext, data any) error { + if ctx.Structured() { + return ctx.Printer.Print(data, nil) + } + out, err := json.MarshalIndent(data, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal output: %w", err) + } + _, err = fmt.Fprintln(ctx.Writer, string(out)) + return err +} + +// genGroup finds an existing subcommand named `name` under parent, or creates a +// group command with that name. This lets generated commands attach to the same +// group a curated command already owns (partial-coverage services) and lets a +// multi-segment API path build its intermediate group chain idempotently. +func genGroup(parent *cobra.Command, name, short string) *cobra.Command { + for _, c := range parent.Commands() { + if c.Name() == name { + return c + } + } + g := &cobra.Command{Use: name, Short: short} + parent.AddCommand(g) + return g +} + +// genAddLeaf attaches a generated leaf command under parent unless a command +// with the same name already exists there. A curated command always wins the +// exact path-name (it registers first, in init()), so its richer implementation +// keeps the canonical command while the generated twin is harmlessly dropped; +// the operation remains reachable at its path-name either way. +func genAddLeaf(parent *cobra.Command, leaf *cobra.Command) { + for _, c := range parent.Commands() { + if c.Name() == leaf.Name() { + return + } + } + parent.AddCommand(leaf) +} diff --git a/internal/cli/incident.go b/internal/cli/incident.go index e1b5ed8..52d9908 100644 --- a/internal/cli/incident.go +++ b/internal/cli/incident.go @@ -82,6 +82,7 @@ func newIncidentListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List incidents", + Long: curatedLong("List incidents matching the given filters. The --since/--until window must be < 31 days; --limit max is 100.", "Incidents", "List"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -126,7 +127,7 @@ func newIncidentListCmd() *cobra.Command { cmd.Flags().Int64Var(&channelID, "channel", 0, "Filter by channel ID") cmd.Flags().StringVar(&query, "query", "", "Free-text search across title/labels/content (also resolves a 24-char incident ID or 6-char incident num to a direct lookup)") cmd.Flags().StringVar(&nums, "nums", "", "Comma-separated short incident ids (num, the 6-char id shown in the UI) to filter by") - cmd.Flags().StringVar(&since, "since", "24h", "Start time (duration, date, datetime, or unix timestamp)") + cmd.Flags().StringVar(&since, "since", "24h", "Start time (duration, date, datetime, or unix timestamp; --since→--until window must be < 31 days)") cmd.Flags().StringVar(&until, "until", "now", "End time") cmd.Flags().IntVar(&limit, "limit", 20, "Max results (max 100)") cmd.Flags().IntVar(&page, "page", 1, "Page number") @@ -194,6 +195,7 @@ func newIncidentGetCmd() *cobra.Command { return &cobra.Command{ Use: "get [ ...]", Short: "Get incident details", + Long: curatedLong("Get details for one or more incidents by ID.", "Incidents", "List"), Args: requireArgs("incident_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -535,6 +537,7 @@ func newIncidentTimelineCmd() *cobra.Command { return &cobra.Command{ Use: "timeline ", Short: "View incident timeline", + Long: curatedLong("View the timeline (feed entries) for one or more incidents.", "Incidents", "Feed"), Args: requireArgs("incident_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -593,6 +596,7 @@ func newIncidentAlertsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "alerts ", Short: "View incident alerts", + Long: curatedLong("View the alerts attached to an incident.", "Incidents", "AlertList"), Args: requireArgs("incident_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -631,6 +635,7 @@ func newIncidentSimilarCmd() *cobra.Command { cmd := &cobra.Command{ Use: "similar ", Short: "Find similar incidents", + Long: curatedLong("Find past incidents similar to the given incident.", "Incidents", "PastList"), Args: requireArgs("incident_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -1109,10 +1114,10 @@ func newIncidentWarRoomListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list ", Short: "List incident war rooms", - Long: `List war rooms attached to an incident. + Long: curatedLong(`List war rooms attached to an incident. Use this to discover chat IDs and integration IDs for follow-up commands such -as get, delete, and add-member.`, +as get, delete, and add-member.`, "Incidents", "WarRoomList"), Example: ` flashduty incident war-room list inc_123 flashduty incident war-room list inc_123 --integration 42`, Args: requireArgs("incident_id"), @@ -1140,11 +1145,11 @@ func newIncidentWarRoomGetCmd() *cobra.Command { cmd := &cobra.Command{ Use: "get ", Short: "Get incident war room details", - Long: `Get incident war room details by IM chat ID. + Long: curatedLong(`Get incident war room details by IM chat ID. This command requires --integration because chat IDs are scoped to an IM integration. Use 'flashduty incident war-room list' with an incident ID to find -the chat ID and integration ID for an incident.`, +the chat ID and integration ID for an incident.`, "Incidents", "WarRoomDetail"), Example: ` flashduty incident war-room list inc_123 flashduty incident war-room get chat_123 --integration 42`, Args: requireArgs("chat_id"), @@ -1262,10 +1267,10 @@ func newIncidentWarRoomDefaultObserversCmd() *cobra.Command { return &cobra.Command{ Use: "default-observers ", Short: "Preview historical responders for war-room observer invitation", - Long: `Preview historical responders eligible for war-room observer invitation. + Long: curatedLong(`Preview historical responders eligible for war-room observer invitation. This is a read-only preview of the users FlashDuty would add when ---add-observers is used during war-room creation.`, +--add-observers is used during war-room creation.`, "Incidents", "ReadGetWarRoomDefaultObservers"), Example: ` flashduty incident war-room default-observers inc_123 flashduty incident war-room create inc_123 --add-observers`, Args: requireArgs("incident_id"), @@ -1318,6 +1323,7 @@ func newIncidentFeedCmd() *cobra.Command { cmd := &cobra.Command{ Use: "feed ", Short: "View incident feed (paginated timeline)", + Long: curatedLong("View the paginated feed (timeline entries) for an incident.", "Incidents", "Feed"), Args: requireArgs("incident_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -1408,6 +1414,7 @@ func newIncidentDetailCmd() *cobra.Command { return &cobra.Command{ Use: "detail ", Short: "View full incident detail with AI summary", + Long: curatedLong("View full incident detail, including the AI summary, root cause, and resolution.", "Incidents", "Info"), Args: requireArgs("incident_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { diff --git a/internal/cli/insight.go b/internal/cli/insight.go index a98ddf3..492ab08 100644 --- a/internal/cli/insight.go +++ b/internal/cli/insight.go @@ -29,6 +29,7 @@ func newInsightTeamCmd() *cobra.Command { cmd := &cobra.Command{ Use: "team", Short: "Query insights by team", + Long: curatedLong("Query incident response insight metrics aggregated by team over a time window.", "Analytics", "ByTeam"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -92,6 +93,7 @@ func newInsightChannelCmd() *cobra.Command { cmd := &cobra.Command{ Use: "channel", Short: "Query insights by channel", + Long: curatedLong("Query incident response insight metrics aggregated by channel over a time window.", "Analytics", "ByChannel"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -155,6 +157,7 @@ func newInsightResponderCmd() *cobra.Command { cmd := &cobra.Command{ Use: "responder", Short: "Query insights by responder", + Long: curatedLong("Query incident response insight metrics aggregated by responder over a time window.", "Analytics", "ByResponder"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -213,6 +216,7 @@ func newInsightTopAlertsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "top-alerts", Short: "Query top alert sources by label", + Long: curatedLong("Query the top-K noisiest alert sources grouped by a label dimension over a time window.", "Analytics", "TopkAlertsByLabel"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -251,7 +255,7 @@ func newInsightTopAlertsCmd() *cobra.Command { }, } - cmd.Flags().StringVar(&label, "label", "", "Label key to group by (e.g., \"integration_name\")") + cmd.Flags().StringVar(&label, "label", "", "Group-by label dimension: one of [check, resource] (required)") cmd.Flags().StringVar(&since, "since", "7d", "Start time") cmd.Flags().StringVar(&until, "until", "now", "End time") cmd.Flags().IntVar(&limit, "limit", 10, "Top K results") @@ -267,6 +271,7 @@ func newInsightIncidentsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "incidents", Short: "Query incidents with performance metrics", + Long: curatedLong("List incidents with per-incident performance metrics (MTTA, MTTR, notifications) over a time window.", "Analytics", "IncidentList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) diff --git a/internal/cli/mcp.go b/internal/cli/mcp.go deleted file mode 100644 index 6f4347d..0000000 --- a/internal/cli/mcp.go +++ /dev/null @@ -1,87 +0,0 @@ -package cli - -import ( - "fmt" - "strings" - - "github.com/flashcatcloud/go-flashduty" - "github.com/spf13/cobra" -) - -func newMCPCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "mcp", - Short: "Manage MCP server registrations", - } - cmd.AddCommand(newMCPCreateCmd()) - return cmd -} - -func newMCPCreateCmd() *cobra.Command { - var ( - serverName string - description string - transport string - command string - argsFlag []string - envEntries []string - url string - headerEntries []string - connectTimeout int - callTimeout int - teamID int64 - ) - - cmd := &cobra.Command{ - Use: "create", - Short: "Register an MCP server", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - if strings.TrimSpace(serverName) == "" { - return fmt.Errorf("--server-name is required") - } - envMap, err := parseKVSlice(envEntries) - if err != nil { - return fmt.Errorf("invalid --env: %w", err) - } - headerMap, err := parseKVSlice(headerEntries) - if err != nil { - return fmt.Errorf("invalid --headers: %w", err) - } - input := &flashduty.McpServerCreateRequest{ - ServerName: serverName, - Description: description, - Transport: transport, - Command: command, - Args: argsFlag, - Env: envMap, - URL: url, - Headers: headerMap, - ConnectTimeout: int64(connectTimeout), - CallTimeout: int64(callTimeout), - TeamID: teamID, - } - result, _, err := ctx.Client.McpServers.WriteServerCreate(cmdContext(ctx.Cmd), input) - if err != nil { - return err - } - return ctx.WriteResultJSON(result, - fmt.Sprintf("MCP server registered: %s (status: %s)", result.ServerID, result.Status)) - }) - }, - } - - cmd.Flags().StringVar(&serverName, "server-name", "", "MCP server display name (required)") - cmd.Flags().StringVar(&description, "description", "", "Server description") - cmd.Flags().StringVar(&transport, "transport", "streamable-http", "Transport: stdio|sse|streamable-http") - cmd.Flags().StringVar(&command, "command", "", "Executable (stdio transport)") - cmd.Flags().StringSliceVar(&argsFlag, "args", nil, "Executable args (stdio transport, repeatable)") - cmd.Flags().StringSliceVar(&envEntries, "env", nil, "Env entries KEY=VALUE (repeatable)") - cmd.Flags().StringVar(&url, "url", "", "URL (sse / streamable-http)") - cmd.Flags().StringSliceVar(&headerEntries, "headers", nil, "Header entries KEY=VALUE (repeatable)") - cmd.Flags().IntVar(&connectTimeout, "connect-timeout", 10, "Connection timeout in seconds") - cmd.Flags().IntVar(&callTimeout, "call-timeout", 60, "Tool-call timeout in seconds") - cmd.Flags().Int64Var(&teamID, "team-id", 0, "Team scope (0 = account-scope)") - - return cmd -} diff --git a/internal/cli/mcp_test.go b/internal/cli/mcp_test.go deleted file mode 100644 index 8c12d75..0000000 --- a/internal/cli/mcp_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package cli - -import ( - "strings" - "testing" -) - -func TestMCPCreateFlagSurface(t *testing.T) { - cmd := newMCPCreateCmd() - flags := cmd.Flags() - for _, name := range []string{ - "server-name", "description", "transport", - "command", "args", "env", "url", "headers", - "connect-timeout", "call-timeout", "team-id", - } { - if flags.Lookup(name) == nil { - t.Errorf("flag --%s not registered", name) - } - } -} - -func TestMCPCreateRejectsEmptyServerName(t *testing.T) { - saveAndResetGlobals(t) - // The empty-name guard fires inside the handler before WriteServerCreate is - // ever called, so a stub server that records no request is sufficient. - stub := newGFStub(t) - - _, err := execCommand("mcp", "create") - if err == nil { - t.Fatal("expected error for empty --server-name, got nil") - } - if !strings.Contains(err.Error(), "--server-name is required") { - t.Fatalf("expected error %q, got %q", "--server-name is required", err.Error()) - } - if stub.requests != 0 { - t.Fatalf("expected no request to reach the server, got %d", stub.requests) - } -} - -func TestCommandMCPCreate(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"server_id": "srv-1", "status": "enabled"} - - out, err := execCommand("mcp", "create", - "--server-name", "demo", - "--transport", "streamable-http", - "--url", "https://mcp.example/sse", - "--connect-timeout", "15", - "--call-timeout", "90", - "--team-id", "7", - ) - if err != nil { - t.Fatalf("[mcp-create] unexpected error: %v", err) - } - if stub.lastPath != "/safari/mcp/server/create" { - t.Fatalf("[mcp-create] expected /safari/mcp/server/create, got %q", stub.lastPath) - } - if stub.lastBody["server_name"] != "demo" || stub.lastBody["transport"] != "streamable-http" || stub.lastBody["url"] != "https://mcp.example/sse" { - t.Fatalf("[mcp-create] unexpected input: %#v", stub.lastBody) - } - if stub.lastBody["connect_timeout"] != float64(15) || stub.lastBody["call_timeout"] != float64(90) || stub.lastBody["team_id"] != float64(7) { - t.Fatalf("[mcp-create] unexpected numeric input: %#v", stub.lastBody) - } - if !strings.Contains(out, "MCP server registered: srv-1 (status: enabled)") { - t.Fatalf("[mcp-create] unexpected output:\n%s", out) - } -} diff --git a/internal/cli/member.go b/internal/cli/member.go index 7dd0a21..1498f28 100644 --- a/internal/cli/member.go +++ b/internal/cli/member.go @@ -22,10 +22,12 @@ func newMemberCmd() *cobra.Command { func newMemberListCmd() *cobra.Command { var name, email string var page int + var roleID int64 cmd := &cobra.Command{ Use: "list", Short: "List members", + Long: curatedLong("List members in your account.", "Members", "MemberList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { // go-flashduty's MemberListRequest exposes a single search @@ -40,6 +42,9 @@ func newMemberListCmd() *cobra.Command { Query: query, } req.Page = page + if roleID != 0 { + req.RoleID = uint64(roleID) + } result, _, err := ctx.Client.Members.MemberList(cmdContext(ctx.Cmd), req) if err != nil { @@ -78,6 +83,7 @@ func newMemberListCmd() *cobra.Command { cmd.Flags().StringVar(&name, "name", "", "Search by name") cmd.Flags().StringVar(&email, "email", "", "Search by email") cmd.Flags().IntVar(&page, "page", 1, "Page number") + cmd.Flags().Int64Var(&roleID, "role-id", 0, "Filter to members holding this role ID") return cmd } diff --git a/internal/cli/monit_agent.go b/internal/cli/monit_agent.go index f5dbc6f..9e46b14 100644 --- a/internal/cli/monit_agent.go +++ b/internal/cli/monit_agent.go @@ -23,6 +23,7 @@ func newMonitAgentCatalogCmd() *cobra.Command { cmd := &cobra.Command{ Use: "catalog", Short: "List the diagnostic tools the agent exposes for a target", + Long: curatedLong("List the diagnostic tools a monit-agent exposes for a target.", "Diagnostics", "ToolsCatalog"), RunE: func(cmd *cobra.Command, args []string) error { if targetLocator == "" { return fmt.Errorf("--target-locator is required") @@ -56,6 +57,7 @@ func newMonitAgentInvokeCmd() *cobra.Command { cmd := &cobra.Command{ Use: "invoke", Short: "Run up to 8 monit-agent tools concurrently on a target", + Long: curatedLong("Run up to 8 monit-agent diagnostic tools concurrently on a target and return their output.", "Diagnostics", "ToolsInvoke"), RunE: func(cmd *cobra.Command, args []string) error { if targetLocator == "" { return fmt.Errorf("--target-locator is required") diff --git a/internal/cli/monit_query.go b/internal/cli/monit_query.go index 499ea37..e12769b 100644 --- a/internal/cli/monit_query.go +++ b/internal/cli/monit_query.go @@ -29,6 +29,7 @@ func newMonitQueryDiagnoseCmd() *cobra.Command { cmd := &cobra.Command{ Use: "diagnose", Short: "Pre-clustered RCA findings (log_patterns or metric_trends)", + Long: curatedLong("Run pre-clustered RCA over a datasource window, returning log_patterns or metric_trends findings.", "Diagnostics", "QueryDiagnose"), RunE: func(cmd *cobra.Command, args []string) error { if dsType == "" || dsName == "" || inputQuery == "" { return fmt.Errorf("--ds-type, --ds-name, --input-query are required") @@ -92,6 +93,7 @@ func newMonitQueryRowsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "rows", Short: "Raw datasource passthrough (returns values/rows as the datasource itself would)", + Long: curatedLong("Raw datasource passthrough returning values/rows as the datasource itself would.", "Diagnostics", "QueryRows"), RunE: func(cmd *cobra.Command, args []string) error { if dsType == "" || dsName == "" || expr == "" { return fmt.Errorf("--ds-type, --ds-name, --expr are required") diff --git a/internal/cli/oncall.go b/internal/cli/oncall.go index dd1d2ee..7b3342f 100644 --- a/internal/cli/oncall.go +++ b/internal/cli/oncall.go @@ -39,6 +39,7 @@ func newOncallWhoCmd() *cobra.Command { cmd := &cobra.Command{ Use: "who", Short: "Show who is currently on call", + Long: curatedLong("Show who is currently on call across schedules within a time window, optionally filtered by team or schedule name. Returns person_ids (numeric) only; resolve names/phones by dumping 'fduty member list' and joining client-side (member list has no by-id lookup).", "Schedules", "List"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -113,6 +114,7 @@ func newOncallScheduleListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List schedules", + Long: curatedLong("List on-call schedules within a time window, optionally filtered by team or schedule name.", "Schedules", "List"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { startTime, err := timeutil.Parse(since) @@ -185,6 +187,7 @@ func newOncallScheduleGetCmd() *cobra.Command { cmd := &cobra.Command{ Use: "get ", Short: "Get schedule detail", + Long: curatedLong("Get the full detail of a single schedule by ID, including its computed on-call slots over a time window.", "Schedules", "Info"), Args: requireArgs("schedule_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { diff --git a/internal/cli/postmortem.go b/internal/cli/postmortem.go deleted file mode 100644 index 1b18d3d..0000000 --- a/internal/cli/postmortem.go +++ /dev/null @@ -1,99 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/flashcatcloud/go-flashduty" - "github.com/spf13/cobra" - - "github.com/flashcatcloud/flashduty-cli/internal/output" - "github.com/flashcatcloud/flashduty-cli/internal/timeutil" -) - -func newPostmortemCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "postmortem", - Short: "Manage post-mortems", - } - cmd.AddCommand(newPostmortemListCmd()) - return cmd -} - -func newPostmortemListCmd() *cobra.Command { - var status, channel, team, since, until string - var limit, page int - - cmd := &cobra.Command{ - Use: "list", - Short: "List post-mortem reports", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - req := &flashduty.ListPostMortemsRequest{ - Status: status, - } - req.Page = page - req.Limit = limit - - if channel != "" { - channelIDs, err := parseIntSlice(channel) - if err != nil { - return fmt.Errorf("invalid --channel: %w", err) - } - req.ChannelIDs = channelIDs - } - - if team != "" { - teamIDs, err := parseIntSlice(team) - if err != nil { - return fmt.Errorf("invalid --team: %w", err) - } - req.TeamIDs = teamIDs - } - - if since != "" { - startTime, err := timeutil.Parse(since) - if err != nil { - return fmt.Errorf("invalid --since: %w", err) - } - req.CreatedAtStartSeconds = startTime - } - - if until != "" { - endTime, err := timeutil.Parse(until) - if err != nil { - return fmt.Errorf("invalid --until: %w", err) - } - req.CreatedAtEndSeconds = endTime - } - - result, _, err := ctx.Client.Incidents.PostMortemList(cmdContext(ctx.Cmd), req) - if err != nil { - return err - } - - cols := []output.Column{ - {Header: "ID", Field: func(v any) string { return v.(flashduty.PostMortemMeta).PostMortemID }}, - {Header: "TITLE", MaxWidth: 50, Field: func(v any) string { return v.(flashduty.PostMortemMeta).Title }}, - {Header: "STATUS", Field: func(v any) string { return v.(flashduty.PostMortemMeta).Status }}, - {Header: "CHANNEL", Field: func(v any) string { return v.(flashduty.PostMortemMeta).ChannelName }}, - {Header: "CREATED", Field: func(v any) string { - return output.FormatTime(v.(flashduty.PostMortemMeta).CreatedAtSeconds) - }}, - } - - return ctx.PrintList(result.Items, cols, len(result.Items), page, int(result.Total)) - }) - }, - } - - cmd.Flags().StringVar(&status, "status", "", "Filter: drafting or published") - cmd.Flags().StringVar(&channel, "channel", "", "Comma-separated channel IDs") - registerEnumFlag(cmd, "status", "drafting", "published") - cmd.Flags().StringVar(&team, "team", "", "Comma-separated team IDs") - cmd.Flags().StringVar(&since, "since", "", "Created after (time filter)") - cmd.Flags().StringVar(&until, "until", "", "Created before (time filter)") - cmd.Flags().IntVar(&limit, "limit", 20, "Max results") - cmd.Flags().IntVar(&page, "page", 1, "Page number") - - return cmd -} diff --git a/internal/cli/root.go b/internal/cli/root.go index e8a4afd..260155d 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -83,15 +83,12 @@ func init() { rootCmd.AddCommand(newMemberCmd()) rootCmd.AddCommand(newTeamCmd()) rootCmd.AddCommand(newChannelCmd()) - rootCmd.AddCommand(newEscalationRuleCmd()) rootCmd.AddCommand(newFieldCmd()) - rootCmd.AddCommand(newStatusPageCmd()) rootCmd.AddCommand(newTemplateCmd()) // Phase 1 rootCmd.AddCommand(newAlertCmd()) rootCmd.AddCommand(newAlertEventCmd()) - rootCmd.AddCommand(newPostmortemCmd()) // Phase 2 rootCmd.AddCommand(newOncallCmd()) @@ -103,12 +100,13 @@ func init() { rootCmd.AddCommand(newWhoamiCmd()) rootCmd.AddCommand(newUpdateCmd()) - // CLI Phase 1 - rootCmd.AddCommand(newMCPCmd()) - - // CLI Phase 2 + // Diagnostics entry points (value-add over the raw API). rootCmd.AddCommand(newMonitQueryCmd()) rootCmd.AddCommand(newMonitAgentCmd()) + + // Generated commands (full OpenAPI coverage). Registered AFTER curated + // commands so curated leaves win on any name conflict (see genAddLeaf). + registerGenerated(rootCmd) } // Execute runs the root command. diff --git a/internal/cli/status_page.go b/internal/cli/status_page.go deleted file mode 100644 index 2cf9ed9..0000000 --- a/internal/cli/status_page.go +++ /dev/null @@ -1,251 +0,0 @@ -package cli - -import ( - "fmt" - "strconv" - "strings" - "time" - - "github.com/flashcatcloud/go-flashduty" - "github.com/spf13/cobra" - - "github.com/flashcatcloud/flashduty-cli/internal/output" -) - -func newStatusPageCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "statuspage", - Short: "Manage status pages", - } - cmd.AddCommand(newStatusPageListCmd()) - cmd.AddCommand(newStatusPageChangesCmd()) - cmd.AddCommand(newStatusPageCreateIncidentCmd()) - cmd.AddCommand(newStatusPageCreateTimelineCmd()) - cmd.AddCommand(newStatusPageMigrateCmd()) - return cmd -} - -func newStatusPageListCmd() *cobra.Command { - var ids string - - cmd := &cobra.Command{ - Use: "list", - Short: "List status pages", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - pageIDs, err := parseIntSlice(ids) - if err != nil { - return fmt.Errorf("invalid --id: %w", err) - } - - result, _, err := ctx.Client.StatusPages.ReadPageList(cmdContext(ctx.Cmd)) - if err != nil { - return err - } - - // ReadPageList lists every status page; the legacy SDK supported a - // server-side page-id filter, so preserve --id by filtering here. - pages := result.Items - if len(pageIDs) > 0 { - want := make(map[int64]struct{}, len(pageIDs)) - for _, id := range pageIDs { - want[id] = struct{}{} - } - filtered := make([]flashduty.StatusPageItem, 0, len(pages)) - for _, p := range pages { - if _, ok := want[p.PageID]; ok { - filtered = append(filtered, p) - } - } - pages = filtered - } - - cols := []output.Column{ - {Header: "ID", Field: func(v any) string { return strconv.FormatInt(v.(flashduty.StatusPageItem).PageID, 10) }}, - {Header: "NAME", Field: func(v any) string { return v.(flashduty.StatusPageItem).Name }}, - {Header: "SLUG", Field: func(v any) string { return v.(flashduty.StatusPageItem).URLName }}, - // STATUS reads the account's overall_status, which the - // /status-page/list endpoint does not return. The legacy SDK - // likewise never populated it, so this column stays empty — - // preserved here to keep the table shape identical. - {Header: "STATUS", Field: func(v any) string { return "" }}, - {Header: "COMPONENTS", Field: func(v any) string { - comps := v.(flashduty.StatusPageItem).Components - names := make([]string, 0, len(comps)) - for _, c := range comps { - names = append(names, c.Name) - } - return strings.Join(names, ", ") - }}, - } - - return ctx.Printer.Print(pages, cols) - }) - }, - } - - cmd.Flags().StringVar(&ids, "id", "", "Filter by page IDs (comma-separated)") - - return cmd -} - -func newStatusPageChangesCmd() *cobra.Command { - var pageID int64 - var changeType string - - cmd := &cobra.Command{ - Use: "changes", - Short: "List active status page changes", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - result, _, err := ctx.Client.StatusPages.ChangeActiveList(cmdContext(ctx.Cmd), &flashduty.StatusPagesChangeActiveListRequest{ - PageID: pageID, - Type: changeType, - }) - if err != nil { - return err - } - - cols := []output.Column{ - {Header: "ID", Field: func(v any) string { return strconv.FormatInt(v.(flashduty.StatusPageChangeItem).ChangeID, 10) }}, - {Header: "TITLE", MaxWidth: 50, Field: func(v any) string { return v.(flashduty.StatusPageChangeItem).Title }}, - {Header: "TYPE", Field: func(v any) string { return v.(flashduty.StatusPageChangeItem).Type }}, - {Header: "STATUS", Field: func(v any) string { return v.(flashduty.StatusPageChangeItem).Status }}, - // The active-list endpoint returns the event's scheduled window - // (start_at_seconds / close_at_seconds), not the row's created/ - // updated timestamps the legacy SDK reported. The CREATED/UPDATED - // headers are preserved to keep the table shape identical; they now - // reflect the event start and (scheduled) close times. - {Header: "CREATED", Field: func(v any) string { return output.FormatTime(v.(flashduty.StatusPageChangeItem).StartAtSeconds) }}, - {Header: "UPDATED", Field: func(v any) string { return output.FormatTime(v.(flashduty.StatusPageChangeItem).CloseAtSeconds) }}, - } - - return ctx.Printer.Print(result.Items, cols) - }) - }, - } - - cmd.Flags().Int64Var(&pageID, "page-id", 0, "Page ID (required)") - cmd.Flags().StringVar(&changeType, "type", "", "Change type: incident or maintenance (required)") - registerEnumFlag(cmd, "type", "incident", "maintenance") - _ = cmd.MarkFlagRequired("page-id") - _ = cmd.MarkFlagRequired("type") - - return cmd -} - -func newStatusPageCreateIncidentCmd() *cobra.Command { - var pageID int64 - var title, message, components string - var notify bool - - cmd := &cobra.Command{ - Use: "create-incident", - Short: "Create a status page incident", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - // Replicate the legacy SDK's request shaping exactly: default the - // status to "investigating", build a single timeline update carrying - // the message and any parsed component_changes, and fall back to the - // title when no message was supplied. This keeps the wire payload - // byte-for-byte equivalent so the migration introduces no drift. - const status = "investigating" - - update := flashduty.CreateStatusPageChangeRequestUpdatesItem{ - AtSeconds: time.Now().Unix(), - Status: status, - } - if message != "" { - update.Description = message - } - if components != "" { - for _, part := range parseStringSlice(components) { - kv := strings.SplitN(part, ":", 2) - if len(kv) == 2 { - update.ComponentChanges = append(update.ComponentChanges, flashduty.CreateStatusPageChangeRequestUpdatesItemComponentChangesItem{ - ComponentID: strings.TrimSpace(kv[0]), - Status: strings.TrimSpace(kv[1]), - }) - } else if len(kv) == 1 && kv[0] != "" { - update.ComponentChanges = append(update.ComponentChanges, flashduty.CreateStatusPageChangeRequestUpdatesItemComponentChangesItem{ - ComponentID: strings.TrimSpace(kv[0]), - Status: "partial_outage", - }) - } - } - } - - description := message - if description == "" { - description = title - } - - result, _, err := ctx.Client.StatusPages.ChangeCreate(cmdContext(ctx.Cmd), &flashduty.CreateStatusPageChangeRequest{ - PageID: pageID, - Title: title, - Type: "incident", - Status: status, - Description: description, - Updates: []flashduty.CreateStatusPageChangeRequestUpdatesItem{update}, - NotifySubscribers: notify, - }) - if err != nil { - return err - } - - if result != nil && result.ChangeID != 0 { - ctx.WriteResult(fmt.Sprintf("Status incident created: %d", result.ChangeID)) - return nil - } - ctx.WriteResult("Status incident created successfully.") - return nil - }) - }, - } - - cmd.Flags().Int64Var(&pageID, "page-id", 0, "Page ID (required)") - cmd.Flags().StringVar(&title, "title", "", "Title (required, max 255 chars)") - cmd.Flags().StringVar(&message, "message", "", "Initial update message") - cmd.Flags().StringVar(&components, "components", "", "Affected components (format: id1:status,id2:status; incident statuses: operational, degraded, partial_outage, full_outage; maintenance statuses: operational, under_maintenance)") - cmd.Flags().BoolVar(¬ify, "notify", false, "Notify subscribers") - _ = cmd.MarkFlagRequired("page-id") - _ = cmd.MarkFlagRequired("title") - - return cmd -} - -func newStatusPageCreateTimelineCmd() *cobra.Command { - var pageID, changeID int64 - var message, status string - - cmd := &cobra.Command{ - Use: "create-timeline", - Short: "Add a timeline update to a status page change", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - _, _, err := ctx.Client.StatusPages.ChangeTimelineCreate(cmdContext(ctx.Cmd), &flashduty.CreateStatusPageChangeTimelineRequest{ - PageID: pageID, - ChangeID: changeID, - Description: message, - Status: status, - }) - if err != nil { - return err - } - - ctx.WriteResult("Timeline update added.") - return nil - }) - }, - } - - cmd.Flags().Int64Var(&pageID, "page-id", 0, "Page ID (required)") - cmd.Flags().Int64Var(&changeID, "change", 0, "Change ID (required)") - cmd.Flags().StringVar(&message, "message", "", "Message (required)") - cmd.Flags().StringVar(&status, "status", "", "Status (incident: investigating, identified, monitoring, resolved; maintenance: scheduled, ongoing, completed)") - _ = cmd.MarkFlagRequired("page-id") - _ = cmd.MarkFlagRequired("change") - _ = cmd.MarkFlagRequired("message") - - return cmd -} diff --git a/internal/cli/status_page_migrate.go b/internal/cli/status_page_migrate.go deleted file mode 100644 index f5a65bc..0000000 --- a/internal/cli/status_page_migrate.go +++ /dev/null @@ -1,293 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/flashcatcloud/go-flashduty" - "github.com/spf13/cobra" -) - -const migrationSourceAtlassian = "atlassian" - -func newStatusPageMigrateCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "migrate", - Short: "Manage status page migration jobs", - } - cmd.AddCommand(newStatusPageMigrateStructureCmd()) - cmd.AddCommand(newStatusPageMigrateEmailSubscribersCmd()) - cmd.AddCommand(newStatusPageMigrateStatusCmd()) - cmd.AddCommand(newStatusPageMigrateCancelCmd()) - return cmd -} - -func newStatusPageMigrateStructureCmd() *cobra.Command { - var source string - var sourcePageID string - var sourceAPIKey string - var urlName string - - cmd := &cobra.Command{ - Use: "structure", - Short: "Start structure and history migration", - RunE: func(cmd *cobra.Command, args []string) error { - if err := validateMigrationSource(source); err != nil { - return err - } - return runCommand(cmd, args, func(ctx *RunContext) error { - req := &flashduty.MigrateStatusPageStructureRequest{ - APIKey: sourceAPIKey, - SourcePageID: sourcePageID, - } - // url_name is *string (tri-state): set it only when the user - // provided one, so a nil pointer reuses the source page's name. - if urlName != "" { - req.URLName = flashduty.String(urlName) - } - result, _, err := ctx.Client.StatusPages.MigrateStructure(cmdContext(ctx.Cmd), req) - if err != nil { - return err - } - - return printMigrationStart(ctx, "structure", source, sourcePageID, 0, result) - }) - }, - } - - cmd.Flags().StringVar(&source, "from", "", "Migration source provider (required)") - cmd.Flags().StringVar(&sourcePageID, "source-page-id", "", "Source page ID in the provider (required)") - cmd.Flags().StringVar(&sourceAPIKey, "api-key", "", "Source provider API key (required)") - cmd.Flags().StringVar(&urlName, "url-name", "", "Optional URL name for a newly created Flashduty public status page; fails if the source page is already mapped to a different URL name") - _ = cmd.MarkFlagRequired("from") - _ = cmd.MarkFlagRequired("source-page-id") - _ = cmd.MarkFlagRequired("api-key") - - return cmd -} - -func newStatusPageMigrateEmailSubscribersCmd() *cobra.Command { - var source string - var sourcePageID string - var sourceAPIKey string - var targetPageID int64 - - cmd := &cobra.Command{ - Use: "email-subscribers", - Short: "Start email subscriber migration", - RunE: func(cmd *cobra.Command, args []string) error { - if err := validateMigrationSource(source); err != nil { - return err - } - return runCommand(cmd, args, func(ctx *RunContext) error { - result, _, err := ctx.Client.StatusPages.MigrateEmailSubscribers(cmdContext(ctx.Cmd), &flashduty.MigrateStatusPageEmailSubscribersRequest{ - APIKey: sourceAPIKey, - SourcePageID: sourcePageID, - TargetPageID: targetPageID, - }) - if err != nil { - return err - } - - return printMigrationStart(ctx, "email-subscribers", source, sourcePageID, targetPageID, result) - }) - }, - } - - cmd.Flags().StringVar(&source, "from", "", "Migration source provider (required)") - cmd.Flags().StringVar(&sourcePageID, "source-page-id", "", "Source page ID in the provider (required)") - cmd.Flags().StringVar(&sourceAPIKey, "api-key", "", "Source provider API key (required)") - cmd.Flags().Int64Var(&targetPageID, "target-page-id", 0, "Target Flashduty status page ID (required)") - _ = cmd.MarkFlagRequired("from") - _ = cmd.MarkFlagRequired("source-page-id") - _ = cmd.MarkFlagRequired("api-key") - _ = cmd.MarkFlagRequired("target-page-id") - - return cmd -} - -func newStatusPageMigrateStatusCmd() *cobra.Command { - var jobID string - - cmd := &cobra.Command{ - Use: "status", - Short: "Show migration job status", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - job, _, err := ctx.Client.StatusPages.MigrationStatus(cmdContext(ctx.Cmd), &flashduty.StatusPagesMigrationStatusRequest{ - JobID: jobID, - }) - if err != nil { - return err - } - - return printMigrationStatus(ctx, job) - }) - }, - } - - cmd.Flags().StringVar(&jobID, "job-id", "", "Migration job ID (required)") - _ = cmd.MarkFlagRequired("job-id") - - return cmd -} - -func newStatusPageMigrateCancelCmd() *cobra.Command { - var jobID string - - cmd := &cobra.Command{ - Use: "cancel", - Short: "Cancel a running migration job", - RunE: func(cmd *cobra.Command, args []string) error { - return runCommand(cmd, args, func(ctx *RunContext) error { - if _, err := ctx.Client.StatusPages.MigrationCancel(cmdContext(ctx.Cmd), &flashduty.CancelStatusPageMigrationRequest{ - JobID: jobID, - }); err != nil { - return err - } - - if ctx.Structured() { - statusCmd := "flashduty statuspage migrate status --job-id " + jobID - return ctx.Printer.Print(map[string]any{ - "job_id": jobID, - "status": "cancel_requested", - "command": statusCmd, - "next_command": statusCmd, - }, nil) - } - - out := ctx.Writer - if _, err := fmt.Fprintln(out, "Cancellation requested."); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Job ID: %s\n\n", jobID); err != nil { - return err - } - if _, err := fmt.Fprintln(out, "Check progress with:"); err != nil { - return err - } - _, err := fmt.Fprintf(out, " flashduty statuspage migrate status --job-id %s\n", jobID) - return err - }) - }, - } - - cmd.Flags().StringVar(&jobID, "job-id", "", "Migration job ID (required)") - _ = cmd.MarkFlagRequired("job-id") - - return cmd -} - -func validateMigrationSource(source string) error { - if source != migrationSourceAtlassian { - return fmt.Errorf("unsupported migration source: %q (supported: %s)", source, migrationSourceAtlassian) - } - return nil -} - -func printMigrationStart(ctx *RunContext, migrationType, source, sourcePageID string, targetPageID int64, result *flashduty.StatusPageMigrationStartResponse) error { - if ctx.Structured() { - payload := map[string]any{ - "type": migrationType, - "source": source, - "source_page_id": sourcePageID, - "job_id": result.JobID, - } - if targetPageID > 0 { - payload["target_page_id"] = targetPageID - } - payload["next_command"] = "flashduty statuspage migrate status --job-id " + result.JobID - return ctx.Printer.Print(payload, nil) - } - - out := ctx.Writer - if _, err := fmt.Fprintln(out, "Migration started."); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Type: %s\n", migrationType); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Source: %s\n", source); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Source page: %s\n", sourcePageID); err != nil { - return err - } - if targetPageID > 0 { - if _, err := fmt.Fprintf(out, "Target page ID: %d\n", targetPageID); err != nil { - return err - } - } - if _, err := fmt.Fprintf(out, "Job ID: %s\n\n", result.JobID); err != nil { - return err - } - if _, err := fmt.Fprintln(out, "Check progress with:"); err != nil { - return err - } - _, err := fmt.Fprintf(out, " flashduty statuspage migrate status --job-id %s\n", result.JobID) - return err -} - -func printMigrationStatus(ctx *RunContext, job *flashduty.StatusPageMigrationJob) error { - if ctx.Structured() { - return ctx.Printer.Print(job, nil) - } - - out := ctx.Writer - if _, err := fmt.Fprintf(out, "Job ID: %s\n", job.JobID); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Source page: %s\n", job.SourcePageID); err != nil { - return err - } - if job.TargetPageID > 0 { - if _, err := fmt.Fprintf(out, "Target page ID: %d\n", job.TargetPageID); err != nil { - return err - } - } - if _, err := fmt.Fprintf(out, "Phase: %s\n", job.Phase); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Status: %s\n", job.Status); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Progress: %d/%d\n", job.Progress.CompletedSteps, job.Progress.TotalSteps); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Sections imported: %d\n", job.Progress.SectionsImported); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Components imported: %d\n", job.Progress.ComponentsImported); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Incidents imported: %d\n", job.Progress.IncidentsImported); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Maintenances imported: %d\n", job.Progress.MaintenancesImported); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Subscribers imported: %d\n", job.Progress.SubscribersImported); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Subscribers skipped: %d\n", job.Progress.SubscribersSkipped); err != nil { - return err - } - if _, err := fmt.Fprintf(out, "Templates imported: %d\n", job.Progress.TemplatesImported); err != nil { - return err - } - if job.Error != "" { - if _, err := fmt.Fprintf(out, "Error: %s\n", job.Error); err != nil { - return err - } - } - if len(job.Progress.Warnings) > 0 { - if _, err := fmt.Fprintln(out, "Warnings:"); err != nil { - return err - } - for _, warning := range job.Progress.Warnings { - if _, err := fmt.Fprintf(out, "- %s\n", warning); err != nil { - return err - } - } - } - return nil -} diff --git a/internal/cli/status_page_migrate_test.go b/internal/cli/status_page_migrate_test.go deleted file mode 100644 index 037da92..0000000 --- a/internal/cli/status_page_migrate_test.go +++ /dev/null @@ -1,390 +0,0 @@ -package cli - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/flashcatcloud/go-flashduty" -) - -// TestCommandStatusPageMigrateStructureSendsSDKInput asserts the structure -// command POSTs to /status-page/migrate-structure with the api_key and -// source_page_id wire fields and renders the returned job id. -func TestCommandStatusPageMigrateStructureSendsSDKInput(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"job_id": "job-1"} - - out, err := execCommand("statuspage", "migrate", "structure", - "--from", "atlassian", - "--source-page-id", "src-1", - "--api-key", "atlassian-secret", - ) - if err != nil { - t.Fatalf("execCommand: %v", err) - } - - if stub.lastPath != "/status-page/migrate-structure" { - t.Fatalf("expected /status-page/migrate-structure, got %q", stub.lastPath) - } - if stub.lastBody["api_key"] != "atlassian-secret" { - t.Errorf("api_key = %v, want atlassian-secret", stub.lastBody["api_key"]) - } - if stub.lastBody["source_page_id"] != "src-1" { - t.Errorf("source_page_id = %v, want src-1", stub.lastBody["source_page_id"]) - } - // url_name is an optional *string; when --url-name is not passed it stays - // nil and omitempty keeps it off the wire. - if _, ok := stub.lastBody["url_name"]; ok { - t.Errorf("url_name should not be sent when --url-name is omitted, got %#v", stub.lastBody["url_name"]) - } - if !strings.Contains(out, "Job ID: job-1") { - t.Errorf("missing job id in output:\n%s", out) - } - if !strings.Contains(out, "flashduty statuspage migrate status --job-id job-1") { - t.Errorf("missing status hint in output:\n%s", out) - } -} - -// TestCommandStatusPageMigrateStructureForwardsURLName: MigrateStatusPageStructureRequest -// now carries url_name (*string), so --url-name is forwarded to the SDK as the -// url_name wire field — matching legacy behavior. -func TestCommandStatusPageMigrateStructureForwardsURLName(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"job_id": "job-2"} - - _, err := execCommand("statuspage", "migrate", "structure", - "--from", "atlassian", - "--source-page-id", "src-1", - "--api-key", "atlassian-secret", - "--url-name", "customer-facing-status", - ) - if err != nil { - t.Fatalf("execCommand: %v", err) - } - if stub.requests != 1 { - t.Errorf("expected exactly 1 request, got %d", stub.requests) - } - if stub.lastBody["url_name"] != "customer-facing-status" { - t.Errorf("url_name = %#v, want customer-facing-status", stub.lastBody["url_name"]) - } -} - -func TestCommandStatusPageMigrateStructureHelpDescribesURLNameBehavior(t *testing.T) { - cmd := newStatusPageMigrateStructureCmd() - flag := cmd.Flags().Lookup("url-name") - if flag == nil { - t.Fatal("expected --url-name flag to be registered") - } - - for _, want := range []string{ - "newly created Flashduty public status page", - "already mapped to a different URL name", - } { - if !strings.Contains(flag.Usage, want) { - t.Errorf("--url-name usage missing %q: %s", want, flag.Usage) - } - } -} - -func TestCommandStatusPageMigrateStructureRejectsUnsupportedSource(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - - _, err := execCommand("statuspage", "migrate", "structure", - "--from", "pagerduty", - "--source-page-id", "src-1", - "--api-key", "x", - ) - if err == nil { - t.Fatal("expected error for unsupported source") - } - if !strings.Contains(err.Error(), "unsupported migration source") { - t.Errorf("unexpected error: %v", err) - } - if !strings.Contains(err.Error(), "atlassian") { - t.Errorf("error should mention supported source 'atlassian': %v", err) - } - if stub.requests != 0 { - t.Errorf("client should not have been called for unsupported source, got %d request(s)", stub.requests) - } -} - -// TestCommandStatusPageMigrateStructureValidatesBeforeClient locks ordering: -// an invalid --from must surface its validation error before any -// client-build / auth work — matching PR #1 behavior. -func TestCommandStatusPageMigrateStructureValidatesBeforeClient(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - - _, err := execCommand("statuspage", "migrate", "structure", - "--from", "pagerduty", - "--source-page-id", "src-1", - "--api-key", "x", - ) - if err == nil { - t.Fatal("expected validation error") - } - if !strings.Contains(err.Error(), "unsupported migration source") { - t.Errorf("got %v; want validation error about source", err) - } - if stub.requests != 0 { - t.Errorf("client must not run when --from is invalid, got %d request(s)", stub.requests) - } -} - -// TestCommandStatusPageMigrateEmailSubscribersValidatesBeforeClient: same -// ordering guarantee for the subscribers variant. -func TestCommandStatusPageMigrateEmailSubscribersValidatesBeforeClient(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - - _, err := execCommand("statuspage", "migrate", "email-subscribers", - "--from", "pagerduty", - "--source-page-id", "src-1", - "--target-page-id", "1", - "--api-key", "x", - ) - if err == nil { - t.Fatal("expected validation error") - } - if !strings.Contains(err.Error(), "unsupported migration source") { - t.Errorf("got %v; want validation error about source", err) - } - if stub.requests != 0 { - t.Errorf("client must not run when --from is invalid, got %d request(s)", stub.requests) - } -} - -func TestCommandStatusPageMigrateStructureJSON(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"job_id": "job-1"} - - out, err := execCommand("--json", "statuspage", "migrate", "structure", - "--from", "atlassian", - "--source-page-id", "src-1", - "--api-key", "x", - ) - if err != nil { - t.Fatalf("execCommand: %v", err) - } - - var payload map[string]any - if err := json.Unmarshal([]byte(out), &payload); err != nil { - t.Fatalf("output is not JSON: %v\n%s", err, out) - } - if payload["type"] != "structure" { - t.Errorf("type = %v, want structure", payload["type"]) - } - if payload["source"] != "atlassian" { - t.Errorf("source = %v, want atlassian", payload["source"]) - } - if payload["source_page_id"] != "src-1" { - t.Errorf("source_page_id = %v, want src-1", payload["source_page_id"]) - } - if payload["job_id"] != "job-1" { - t.Errorf("job_id = %v, want job-1", payload["job_id"]) - } - if next, _ := payload["next_command"].(string); !strings.Contains(next, "job-1") { - t.Errorf("next_command missing job id: %v", payload["next_command"]) - } -} - -// TestCommandStatusPageMigrateEmailSubscribersSendsSDKInput asserts the -// email-subscribers command POSTs to /status-page/migrate-email-subscribers -// with the target_page_id wire field and renders the returned job id. -func TestCommandStatusPageMigrateEmailSubscribersSendsSDKInput(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{"job_id": "sub-1"} - - out, err := execCommand("statuspage", "migrate", "email-subscribers", - "--from", "atlassian", - "--source-page-id", "src-1", - "--target-page-id", "2048", - "--api-key", "atlassian-secret", - ) - if err != nil { - t.Fatalf("execCommand: %v", err) - } - - if stub.lastPath != "/status-page/migrate-email-subscribers" { - t.Fatalf("expected /status-page/migrate-email-subscribers, got %q", stub.lastPath) - } - if stub.lastBody["api_key"] != "atlassian-secret" { - t.Errorf("api_key = %v, want atlassian-secret", stub.lastBody["api_key"]) - } - if stub.lastBody["source_page_id"] != "src-1" { - t.Errorf("source_page_id = %v, want src-1", stub.lastBody["source_page_id"]) - } - // JSON numbers decode to float64 through the stub. - if got, _ := stub.lastBody["target_page_id"].(float64); got != 2048 { - t.Errorf("target_page_id = %v, want 2048", stub.lastBody["target_page_id"]) - } - if !strings.Contains(out, "Target page ID: 2048") { - t.Errorf("missing target page id line in output:\n%s", out) - } - if !strings.Contains(out, "Job ID: sub-1") { - t.Errorf("missing job id in output:\n%s", out) - } -} - -func TestCommandStatusPageMigrateStatusRendersJobFields(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{ - "job_id": "job-9", - "source_page_id": "src-9", - "target_page_id": 1024, - "phase": "history", - "status": "running", - "progress": map[string]any{ - "total_steps": 5, - "completed_steps": 3, - "components_imported": 2, - "sections_imported": 1, - "incidents_imported": 4, - "maintenances_imported": 1, - "subscribers_imported": 0, - "subscribers_skipped": 0, - "templates_imported": 2, - "warnings": []string{"missing field X"}, - }, - } - - out, err := execCommand("statuspage", "migrate", "status", "--job-id", "job-9") - if err != nil { - t.Fatalf("execCommand: %v", err) - } - - // migration-status is a GET: job_id rides in the query string, so the - // decoded body is empty. Assert the endpoint path instead. - if stub.lastPath != "/status-page/migration/status" { - t.Errorf("expected /status-page/migration/status, got %q", stub.lastPath) - } - for _, want := range []string{ - "Job ID: job-9", - "Source page: src-9", - "Target page ID: 1024", - "Phase: history", - "Status: running", - "Progress: 3/5", - "Incidents imported: 4", - "Templates imported: 2", - "Warnings:", - "- missing field X", - } { - if !strings.Contains(out, want) { - t.Errorf("missing %q in output:\n%s", want, out) - } - } -} - -func TestCommandStatusPageMigrateStatusJSON(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - stub.data = map[string]any{ - "job_id": "job-j", - "phase": "completed", - "status": "completed", - } - - out, err := execCommand("--json", "statuspage", "migrate", "status", "--job-id", "job-j") - if err != nil { - t.Fatalf("execCommand: %v", err) - } - - var payload map[string]any - if err := json.Unmarshal([]byte(out), &payload); err != nil { - t.Fatalf("output is not JSON: %v\n%s", err, out) - } - if payload["job_id"] != "job-j" { - t.Errorf("job_id = %v, want job-j", payload["job_id"]) - } - if payload["status"] != "completed" { - t.Errorf("status = %v, want completed", payload["status"]) - } -} - -func TestCommandStatusPageMigrateCancelIssuesCancelAndHint(t *testing.T) { - saveAndResetGlobals(t) - stub := newGFStub(t) - - out, err := execCommand("statuspage", "migrate", "cancel", "--job-id", "job-c") - if err != nil { - t.Fatalf("execCommand: %v", err) - } - - if stub.lastPath != "/status-page/migration/cancel" { - t.Fatalf("expected /status-page/migration/cancel, got %q", stub.lastPath) - } - if stub.lastBody["job_id"] != "job-c" { - t.Errorf("job_id = %v, want job-c", stub.lastBody["job_id"]) - } - if !strings.Contains(out, "Cancellation requested.") { - t.Errorf("missing confirmation in output:\n%s", out) - } - if !strings.Contains(out, "flashduty statuspage migrate status --job-id job-c") { - t.Errorf("missing status hint in output:\n%s", out) - } -} - -func TestCommandStatusPageMigrateCancelJSON(t *testing.T) { - saveAndResetGlobals(t) - newGFStub(t) - - out, err := execCommand("--json", "statuspage", "migrate", "cancel", "--job-id", "job-c") - if err != nil { - t.Fatalf("execCommand: %v", err) - } - - var payload map[string]any - if err := json.Unmarshal([]byte(out), &payload); err != nil { - t.Fatalf("output is not JSON: %v\n%s", err, out) - } - if payload["job_id"] != "job-c" { - t.Errorf("job_id = %v, want job-c", payload["job_id"]) - } - if payload["status"] != "cancel_requested" { - t.Errorf("status = %v, want cancel_requested", payload["status"]) - } - if command, _ := payload["command"].(string); !strings.Contains(command, "job-c") { - t.Errorf("command missing job id: %v", payload["command"]) - } - if next, _ := payload["next_command"].(string); !strings.Contains(next, "job-c") { - t.Errorf("next_command missing job id: %v", payload["next_command"]) - } -} - -func TestCommandStatusPageMigrateStatusPropagatesSDKError(t *testing.T) { - saveAndResetGlobals(t) - - // gfStub always replies with a success ("OK") envelope, so to exercise the - // error path we stand up a tiny server that returns a failure envelope and - // wire newClientFn at it directly. The client surfaces the envelope's - // error.code/message in the returned error. - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(map[string]any{ - "request_id": "test-request-id", - "error": map[string]any{"code": "not_found", "message": "job missing"}, - }) - })) - t.Cleanup(srv.Close) - newClientFn = func() (*flashduty.Client, error) { - return flashduty.NewClient("test-key", flashduty.WithBaseURL(srv.URL)) - } - - _, err := execCommand("statuspage", "migrate", "status", "--job-id", "nope") - if err == nil { - t.Fatal("expected SDK error to propagate") - } - if !strings.Contains(err.Error(), "not_found") || !strings.Contains(err.Error(), "job missing") { - t.Errorf("unexpected error: %v", err) - } -} diff --git a/internal/cli/team.go b/internal/cli/team.go index bfce530..1d82ac2 100644 --- a/internal/cli/team.go +++ b/internal/cli/team.go @@ -39,7 +39,7 @@ func newTeamListCmd() *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "List teams", - Long: `List teams in your account. + Long: curatedLong(`List teams in your account. Use --name to search by team name substring. Use --person-id to filter teams containing a specific member. @@ -49,7 +49,7 @@ Examples: flashduty team list flashduty team list --name "SRE" flashduty team list --person-id 12345 --limit 50 - flashduty team list --orderby team_name --asc`, + flashduty team list --orderby team_name --asc`, "Teams", "ReadList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { result, _, err := ctx.Client.Teams.ReadList(cmdContext(ctx.Cmd), &flashduty.TeamListRequest{ @@ -94,7 +94,7 @@ func newTeamGetCmd() *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "Get team detail", - Long: `Get detailed information about a specific team. + Long: curatedLong(`Get detailed information about a specific team. Specify the team by exactly one of: --id, --name, or --ref-id. The output includes team metadata, member list, and audit information. @@ -103,7 +103,7 @@ Examples: flashduty team get --id 123 flashduty team get --name "SRE Team" flashduty team get --ref-id "hr-dept-42" - flashduty team get --id 123 --json`, + flashduty team get --id 123 --json`, "Teams", "ReadInfo"), PreRunE: func(cmd *cobra.Command, args []string) error { return requireExactlyOneFlag(cmd, "id", "name", "ref-id") }, diff --git a/internal/cli/template.go b/internal/cli/template.go index 2acac10..8e1784f 100644 --- a/internal/cli/template.go +++ b/internal/cli/template.go @@ -67,6 +67,7 @@ func newTemplateGetPresetCmd() *cobra.Command { cmd := &cobra.Command{ Use: "get-preset", Short: "Get the preset template for a channel", + Long: curatedLong("Get the preset notification template code for a channel.", "NotificationTemplates", "ReadInfo"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { fieldName, ok := templateChannels[channel] @@ -116,6 +117,7 @@ func newTemplateValidateCmd() *cobra.Command { cmd := &cobra.Command{ Use: "validate", Short: "Validate and preview a template", + Long: curatedLong("Validate a template file and render a preview for a channel.", "NotificationTemplates", "ReadPreview"), RunE: func(cmd *cobra.Command, args []string) error { templateCode, err := os.ReadFile(file) if err != nil { diff --git a/internal/cli/zz_generated_a2a_agents.go b/internal/cli/zz_generated_a2a_agents.go new file mode 100644 index 0000000..ed0d779 --- /dev/null +++ b/internal/cli/zz_generated_a2a_agents.go @@ -0,0 +1,494 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genA2aAgentsReadGetCmd() *cobra.Command { + var dataJSON string + var fAgentID string + cmd := &cobra.Command{ + Use: "a2a-agent-get", + Short: "Get A2A agent detail", + Long: `Get A2A agent detail. + +Return the full configuration of a single A2A agent by ID. + +API: POST /safari/a2a-agent/get (remote-agent-read-get) + +Request fields: + --agent-id string (required) — Identifier of the target agent. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account. + - agent_card_name (string) — Name resolved from the fetched agent card. + - agent_card_skills (array) — Skills advertised on the fetched agent card. + - agent_id (string) (required) — Unique identifier of the A2A agent. + - agent_name (string) (required) — Display name of the agent. + - auth_config (object) — Authentication parameters keyed by name. + - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth. + - auth_type (string) (required) — Authentication scheme used when calling the agent. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - card_resolve_timeout (integer) (required) — Timeout for fetching the agent card, in seconds. + - card_url (string) (required) — URL of the agent's published A2A agent card. + - created_at (integer) (required) — Creation time as a Unix timestamp in seconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What this agent does and when to delegate to it. + - oauth_metadata (string) — OAuth metadata JSON. + - secret_schema (string) — JSON schema of the per-user secret. + - status (string) (required) — Whether the agent is active and reachable. [enabled, disabled] + - streaming (boolean) (required) — Whether the agent supports streaming responses. + - task_timeout (integer) (required) — Timeout for a single delegated task, in seconds. + - team_id (integer) (required) — Owning team; 0 means account scope. + - updated_at (integer) (required) — Last-update time as a Unix timestamp in seconds. +`, + Example: ` flashduty safari a2a-agent-get --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("agent-id") { + body["agent_id"] = fAgentID + } + }) + if err != nil { + return err + } + req := new(flashduty.A2aAgentIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.A2aAgents.ReadGet(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genA2aAgentsReadListCmd() *cobra.Command { + var dataJSON string + var fIncludeAccount bool + var fLimit int64 + var fOffset int64 + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "a2a-agent-list", + Short: "List A2A agents", + Long: `List A2A agents. + +List registered A2A agents visible to the caller across account and team scopes, with pagination. + +API: POST /safari/a2a-agent/list (remote-agent-read-list) + +Request fields: + --include-account bool — Include account-scoped rows alongside team-scoped ones; defaults to true. + --limit int — Maximum number of rows to return; defaults to 20. + --offset int — Number of rows to skip for pagination. + --team-ids []int — Restrict results to resources owned by these teams; intersected with the caller's visible set. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — A2A agents on the current page. + - account_id (integer) (required) — Owning account. + - agent_card_name (string) — Name resolved from the fetched agent card. + - agent_card_skills (array) — Skills advertised on the fetched agent card. + - agent_id (string) (required) — Unique identifier of the A2A agent. + - agent_name (string) (required) — Display name of the agent. + - auth_config (object) — Authentication parameters keyed by name. + - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth. + - auth_type (string) (required) — Authentication scheme used when calling the agent. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - card_resolve_timeout (integer) (required) — Timeout for fetching the agent card, in seconds. + - card_url (string) (required) — URL of the agent's published A2A agent card. + - created_at (integer) (required) — Creation time as a Unix timestamp in seconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What this agent does and when to delegate to it. + - oauth_metadata (string) — OAuth metadata JSON. + - secret_schema (string) — JSON schema of the per-user secret. + - status (string) (required) — Whether the agent is active and reachable. [enabled, disabled] + - streaming (boolean) (required) — Whether the agent supports streaming responses. + - task_timeout (integer) (required) — Timeout for a single delegated task, in seconds. + - team_id (integer) (required) — Owning team; 0 means account scope. + - updated_at (integer) (required) — Last-update time as a Unix timestamp in seconds. + - total (integer) (required) — Total number of agents matching the filters. +`, + Example: ` flashduty safari a2a-agent-list --data '{"include_account":true,"limit":20,"offset":0}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("include-account") { + body["include_account"] = fIncludeAccount + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("offset") { + body["offset"] = fOffset + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.A2aAgentListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.A2aAgents.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped rows alongside team-scoped ones; defaults to true.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Maximum number of rows to return; defaults to 20.") + cmd.Flags().Int64Var(&fOffset, "offset", 0, "Number of rows to skip for pagination.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Restrict results to resources owned by these teams; intersected with the caller's visible set.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genA2aAgentsWriteCreateCmd() *cobra.Command { + var dataJSON string + var fAgentName string + var fAuthMode string + var fAuthType string + var fCardURL string + var fDescription string + var fOauthMetadata string + var fSecretSchema string + var fStreaming bool + var fTeamID int64 + cmd := &cobra.Command{ + Use: "a2a-agent-create", + Short: "Create A2A agent", + Long: `Create A2A agent. + +Register a new A2A remote agent the SRE agent can delegate tasks to. + +API: POST /safari/a2a-agent/create (remote-agent-write-create) + +Request fields: + --agent-name string (required) — Display name of the agent. (≤128 chars) + --auth-mode string — Credential model; defaults to shared. + --auth-type string — Authentication scheme used when calling the agent. + --card-url string (required) — URL of the agent's published A2A agent card. + --description string — What this agent does and when to delegate to it. + --oauth-metadata string — OAuth metadata JSON; reserved for OAuth-based auth. + --secret-schema string — JSON schema of the per-user secret; required when auth_mode is per_user_secret. + --streaming bool — Whether the agent supports streaming responses. + --team-id int — Owning team for the new agent; 0 for account scope. + auth_config (object, via --data) — Authentication parameters keyed by name. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - agent_id (string) (required) — Identifier of the created agent. +`, + Example: ` flashduty safari a2a-agent-create --data '{"agent_name":"Network Diagnostics Agent","auth_config":{"token":"secret"},"auth_type":"bearer","card_url":"https://agents.example.com/network-diag/.well-known/agent.json","description":"Runs traceroute and BGP-path analysis for network incidents.","streaming":true,"team_id":0}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("agent-name") { + body["agent_name"] = fAgentName + } + if cmd.Flags().Changed("auth-mode") { + body["auth_mode"] = fAuthMode + } + if cmd.Flags().Changed("auth-type") { + body["auth_type"] = fAuthType + } + if cmd.Flags().Changed("card-url") { + body["card_url"] = fCardURL + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("oauth-metadata") { + body["oauth_metadata"] = fOauthMetadata + } + if cmd.Flags().Changed("secret-schema") { + body["secret_schema"] = fSecretSchema + } + if cmd.Flags().Changed("streaming") { + body["streaming"] = fStreaming + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.A2aAgentCreateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.A2aAgents.WriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAgentName, "agent-name", "", "Display name of the agent. (required) (≤128 chars)") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "Credential model; defaults to shared.") + cmd.Flags().StringVar(&fAuthType, "auth-type", "", "Authentication scheme used when calling the agent.") + cmd.Flags().StringVar(&fCardURL, "card-url", "", "URL of the agent's published A2A agent card. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "What this agent does and when to delegate to it.") + cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "OAuth metadata JSON; reserved for OAuth-based auth.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON schema of the per-user secret; required when auth_mode is per_user_secret.") + cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Whether the agent supports streaming responses.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team for the new agent; 0 for account scope.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genA2aAgentsWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fAgentID string + cmd := &cobra.Command{ + Use: "a2a-agent-delete", + Short: "Delete A2A agent", + Long: `Delete A2A agent. + +Soft-delete an A2A agent registration so it can no longer be used. + +API: POST /safari/a2a-agent/delete (remote-agent-write-delete) + +Request fields: + --agent-id string (required) — Identifier of the target agent. +`, + Example: ` flashduty safari a2a-agent-delete --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("agent-id") { + body["agent_id"] = fAgentID + } + }) + if err != nil { + return err + } + req := new(flashduty.A2aAgentIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.A2aAgents.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genA2aAgentsWriteDisableCmd() *cobra.Command { + var dataJSON string + var fAgentID string + cmd := &cobra.Command{ + Use: "a2a-agent-disable", + Short: "Disable A2A agent", + Long: `Disable A2A agent. + +Deactivate an A2A agent so the SRE agent stops delegating to it. + +API: POST /safari/a2a-agent/disable (remote-agent-write-disable) + +Request fields: + --agent-id string (required) — Identifier of the target agent. +`, + Example: ` flashduty safari a2a-agent-disable --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("agent-id") { + body["agent_id"] = fAgentID + } + }) + if err != nil { + return err + } + req := new(flashduty.A2aAgentIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.A2aAgents.WriteDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genA2aAgentsWriteEnableCmd() *cobra.Command { + var dataJSON string + var fAgentID string + cmd := &cobra.Command{ + Use: "a2a-agent-enable", + Short: "Enable A2A agent", + Long: `Enable A2A agent. + +Activate a disabled A2A agent so the SRE agent can delegate to it. + +API: POST /safari/a2a-agent/enable (remote-agent-write-enable) + +Request fields: + --agent-id string (required) — Identifier of the target agent. +`, + Example: ` flashduty safari a2a-agent-enable --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("agent-id") { + body["agent_id"] = fAgentID + } + }) + if err != nil { + return err + } + req := new(flashduty.A2aAgentIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.A2aAgents.WriteEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genA2aAgentsWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fAgentID string + var fAgentName string + var fAuthMode string + var fAuthType string + var fCardURL string + var fDescription string + var fOauthMetadata string + var fSecretSchema string + var fStreaming bool + var fTeamID int64 + cmd := &cobra.Command{ + Use: "a2a-agent-update", + Short: "Update A2A agent", + Long: `Update A2A agent. + +Edit an A2A agent's card URL, auth, streaming flag, or owning team. + +API: POST /safari/a2a-agent/update (remote-agent-write-update) + +Request fields: + --agent-id string (required) — Identifier of the agent to update. + --agent-name string — New display name. (≤128 chars) + --auth-mode string — New credential model. + --auth-type string — New authentication scheme. + --card-url string — New agent card URL. + --description string — New description. + --oauth-metadata string — New OAuth metadata JSON. + --secret-schema string — New per-user secret JSON schema. + --streaming bool — Toggle streaming-response support. + --team-id int — Reassign the agent to this team; omit to leave unchanged, 0 for account scope. + auth_config (object, via --data) — New authentication parameters. +`, + Example: ` flashduty safari a2a-agent-update --data '{"agent_id":"a2a_9d4c1f60b3a2","description":"Runs traceroute, BGP, and DNS analysis for network incidents."}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("agent-id") { + body["agent_id"] = fAgentID + } + if cmd.Flags().Changed("agent-name") { + body["agent_name"] = fAgentName + } + if cmd.Flags().Changed("auth-mode") { + body["auth_mode"] = fAuthMode + } + if cmd.Flags().Changed("auth-type") { + body["auth_type"] = fAuthType + } + if cmd.Flags().Changed("card-url") { + body["card_url"] = fCardURL + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("oauth-metadata") { + body["oauth_metadata"] = fOauthMetadata + } + if cmd.Flags().Changed("secret-schema") { + body["secret_schema"] = fSecretSchema + } + if cmd.Flags().Changed("streaming") { + body["streaming"] = fStreaming + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.A2aAgentUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.A2aAgents.WriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the agent to update. (required)") + cmd.Flags().StringVar(&fAgentName, "agent-name", "", "New display name. (≤128 chars)") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "New credential model.") + cmd.Flags().StringVar(&fAuthType, "auth-type", "", "New authentication scheme.") + cmd.Flags().StringVar(&fCardURL, "card-url", "", "New agent card URL.") + cmd.Flags().StringVar(&fDescription, "description", "", "New description.") + cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "New OAuth metadata JSON.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "New per-user secret JSON schema.") + cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Toggle streaming-response support.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign the agent to this team; omit to leave unchanged, 0 for account scope.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedA2aAgents(root *cobra.Command) { + gSafari := genGroup(root, "safari", "AI SRE API") + genAddLeaf(gSafari, genA2aAgentsReadGetCmd()) + genAddLeaf(gSafari, genA2aAgentsReadListCmd()) + genAddLeaf(gSafari, genA2aAgentsWriteCreateCmd()) + genAddLeaf(gSafari, genA2aAgentsWriteDeleteCmd()) + genAddLeaf(gSafari, genA2aAgentsWriteDisableCmd()) + genAddLeaf(gSafari, genA2aAgentsWriteEnableCmd()) + genAddLeaf(gSafari, genA2aAgentsWriteUpdateCmd()) +} diff --git a/internal/cli/zz_generated_account.go b/internal/cli/zz_generated_account.go new file mode 100644 index 0000000..5c97831 --- /dev/null +++ b/internal/cli/zz_generated_account.go @@ -0,0 +1,61 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import "github.com/spf13/cobra" + +func genAccountInfoCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "info", + Short: "Get account detail", + Long: `Get account detail. + +Return the current account's profile and settings. + +API: POST /account/info (account-read-info) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) — Account identifier. + - account_name (string) — Account name. + - avatar (string) — Account avatar URL. + - country_code (string) — Calling country code for the contact phone. + - created_at (integer) — Account creation time, Unix timestamp in seconds. + - domain (string) — Primary account domain (login subdomain). + - email (string) — Account contact email. + - extra_domains (array) — Additional account domains. + - locale (string) — Account language preference (e.g. zh-CN, en-US). + - mp_account_id (string) — Account identifier on the cloud marketplace platform (present only for marketplace accounts). + - mp_plat (string) — Cloud marketplace platform the account was provisioned from (present only for marketplace accounts). + - phone (string) — Account contact phone, masked for privacy. + - restrictions (object) — Account access restrictions (present only when configured). + - allow_subdomain (boolean) — Whether subdomains of the allowed email domains are also accepted. + - email_domains (array) — Allowed login email domains. + - ips (array) — Allowed source IP/CIDR whitelist. + - time_zone (string) — Account default timezone (IANA name, e.g. Asia/Shanghai). +`, + Example: ` flashduty account info --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.Account.Info(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedAccount(root *cobra.Command) { + gAccount := genGroup(root, "account", "Platform/Account API") + genAddLeaf(gAccount, genAccountInfoCmd()) +} diff --git a/internal/cli/zz_generated_alert_enrichment.go b/internal/cli/zz_generated_alert_enrichment.go new file mode 100644 index 0000000..7a08659 --- /dev/null +++ b/internal/cli/zz_generated_alert_enrichment.go @@ -0,0 +1,1482 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genAlertEnrichmentEnrichmentReadInfoCmd() *cobra.Command { + var dataJSON string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "info", + Short: "Get enrichment rules", + Long: `Get enrichment rules. + +Return the enrichment rule set configured for a specific integration. + +API: POST /enrichment/info (enrichment-read-info) + +Request fields: + --integration-id int (required) — Integration ID to query enrichment rules for. Must be greater than 0. (min 1) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - created_at (integer) (required) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - integration_id (integer) (required) — Integration ID. + - rules (array) (required) — Ordered enrichment rules. + - if (array) — Optional AND-filter list. The rule is skipped if the condition does not match. + - key (string) (required) — Alert label key. + - oper (string) (required) — Match operator. 'IN' matches when any value matches; 'NOTIN' matches when none of the values match. [IN, NOTIN] + - vals (array) (required) — Values to match against. + - kind (string) (required) — Rule type. 'extraction' extracts a label via regex or GJson. 'composition' builds a label from a template. 'mapping' looks up values from a schema or API. 'drop' removes labels. [extraction, composition, mapping, drop] + - settings (any) (required) — Rule-kind–specific settings. The shape depends on 'kind'. + - status (string) (required) — Rule set status. + - updated_at (integer) (required) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. +`, + Example: ` flashduty enrichment info --data '{"integration_id":5001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.EnrichmentInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.EnrichmentReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID to query enrichment rules for. Must be greater than 0. (required) (min 1)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentEnrichmentReadListCmd() *cobra.Command { + var dataJSON string + var fIntegrationIDs []int + cmd := &cobra.Command{ + Use: "list", + Short: "List enrichment rules", + Long: `List enrichment rules. + +Return the enrichment rule sets for a list of integration IDs. + +API: POST /enrichment/list (enrichment-read-list) + +Request fields: + --integration-ids []int (required) — List of integration IDs to query. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Enrichment rule sets. + - created_at (integer) (required) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - integration_id (integer) (required) — Integration ID. + - rules (array) (required) — Ordered enrichment rules. + - if (array) — Optional AND-filter list. The rule is skipped if the condition does not match. + - key (string) (required) — Alert label key. + - oper (string) (required) — Match operator. 'IN' matches when any value matches; 'NOTIN' matches when none of the values match. [IN, NOTIN] + - vals (array) (required) — Values to match against. + - kind (string) (required) — Rule type. 'extraction' extracts a label via regex or GJson. 'composition' builds a label from a template. 'mapping' looks up values from a schema or API. 'drop' removes labels. [extraction, composition, mapping, drop] + - settings (any) (required) — Rule-kind–specific settings. The shape depends on 'kind'. + - status (string) (required) — Rule set status. + - updated_at (integer) (required) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. +`, + Example: ` flashduty enrichment list --data '{"integration_ids":[5001,5002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-ids") { + body["integration_ids"] = fIntegrationIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.EnrichmentListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.EnrichmentReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "List of integration IDs to query. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentEnrichmentWriteUpsertCmd() *cobra.Command { + var dataJSON string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "upsert", + Short: "Upsert enrichment rules", + Long: `Upsert enrichment rules. + +Create or fully replace the enrichment rule set for an integration. The entire 'rules' array is replaced atomically. + +API: POST /enrichment/upsert (enrichment-write-upsert) + +Request fields: + --integration-id int (required) — Integration ID to configure enrichment rules for. + rules (array, via --data) (required) — Ordered list of enrichment rules. Replaces all existing rules. + - if (array) — Optional AND-filter list. The rule is skipped if the condition does not match. + - key (string) (required) — Alert label key. + - oper (string) (required) — Match operator. 'IN' matches when any value matches; 'NOTIN' matches when none of the values match. [IN, NOTIN] + - vals (array) (required) — Values to match against. + - kind (string) (required) — Rule type. 'extraction' extracts a label via regex or GJson. 'composition' builds a label from a template. 'mapping' looks up values from a schema or API. 'drop' removes labels. [extraction, composition, mapping, drop] + - settings (any) (required) — Rule-kind–specific settings. The shape depends on 'kind'. +`, + Example: ` flashduty enrichment upsert --data '{"integration_id":5001,"rules":[{"kind":"extraction","settings":{"override":true,"pattern":"(?P\u003cresult\u003eprod|staging|dev)","result_label":"environment","source_field":"labels.env"}},{"kind":"composition","settings":{"override":false,"result_label":"full_env","template":"{{.labels.region}}-{{.labels.environment}}"}}]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.EnrichmentUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.EnrichmentWriteUpsert(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/upsert") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID to configure enrichment rules for. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentFieldReadInfoCmd() *cobra.Command { + var dataJSON string + var fFieldID string + cmd := &cobra.Command{ + Use: "info", + Short: "Get field detail", + Long: `Get field detail. + +Return the configuration of a single incident custom field by ID. + +API: POST /field/info (field-read-info) + +Request fields: + --field-id string (required) — Field ID — 24-character hex ObjectID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account ID. + - created_at (integer) (required) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - default_value (any) — Default value. Type depends on 'field_type': 'bool' for checkbox; 'string' for single_select/text; 'string[]' for multi_select; may be 'null' if no default. + - deleted_at (integer) — Deletion timestamp, Unix seconds. Only present for soft-deleted fields. + - description (string) — Optional free-text description. (≤499 chars) + - display_name (string) (required) — Human-readable name shown in the UI. (≤39 chars) + - field_id (string) (required) — Field ID — 24-character hex ObjectID. + - field_name (string) (required) — Machine name used in incident payloads under 'fields.'. Immutable. (≤39 chars) + - field_type (string) (required) — Field input type. [checkbox, multi_select, single_select, text] + - options (any) — Allowed choices for 'single_select'/'multi_select' (non-empty unique string array). 'null' or empty for 'checkbox'/'text'. + - status (string) (required) — Field status (e.g. 'enabled', 'deleted'). + - updated_at (integer) (required) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. + - value_type (string) (required) — Stored value type. 'checkbox' is always 'bool'; 'single_select'/'multi_select'/'text' are always 'string'. [string, bool, float] +`, + Example: ` flashduty field info --data '{"field_id":"66e9d3a4f7c2b04a1c8a91b3"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("field-id") { + body["field_id"] = fFieldID + } + }) + if err != nil { + return err + } + req := new(flashduty.FieldInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.FieldReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fFieldID, "field-id", "", "Field ID — 24-character hex ObjectID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentFieldReadListCmd() *cobra.Command { + var dataJSON string + var fAsc bool + var fCreatorID int64 + var fOrderby string + var fQuery string + cmd := &cobra.Command{ + Use: "list", + Short: "List fields", + Long: `List fields. + +Return all incident custom fields configured for the account. + +API: POST /field/list (field-read-list) + +Request fields: + --asc bool — Sort ascending when 'true'; descending otherwise. + --creator-id int — Filter by creator member ID. Omit or send 'null' to skip. + --orderby string — Sort key. Defaults to backend ordering when omitted. [created_at, updated_at] + --query string — Regex filter against 'field_name' and 'display_name'. Invalid regex is auto-escaped to literal substring match. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — All non-deleted custom fields for the account. No pagination. + - account_id (integer) (required) — Owning account ID. + - created_at (integer) (required) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - default_value (any) — Default value. Type depends on 'field_type': 'bool' for checkbox; 'string' for single_select/text; 'string[]' for multi_select; may be 'null' if no default. + - deleted_at (integer) — Deletion timestamp, Unix seconds. Only present for soft-deleted fields. + - description (string) — Optional free-text description. (≤499 chars) + - display_name (string) (required) — Human-readable name shown in the UI. (≤39 chars) + - field_id (string) (required) — Field ID — 24-character hex ObjectID. + - field_name (string) (required) — Machine name used in incident payloads under 'fields.'. Immutable. (≤39 chars) + - field_type (string) (required) — Field input type. [checkbox, multi_select, single_select, text] + - options (any) — Allowed choices for 'single_select'/'multi_select' (non-empty unique string array). 'null' or empty for 'checkbox'/'text'. + - status (string) (required) — Field status (e.g. 'enabled', 'deleted'). + - updated_at (integer) (required) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. + - value_type (string) (required) — Stored value type. 'checkbox' is always 'bool'; 'single_select'/'multi_select'/'text' are always 'string'. [string, bool, float] +`, + Example: ` flashduty field list --data '{"asc":false,"orderby":"updated_at","query":"severity"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("creator-id") { + body["creator_id"] = fCreatorID + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + }) + if err != nil { + return err + } + req := new(flashduty.FieldListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.FieldReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true'; descending otherwise.") + cmd.Flags().Int64Var(&fCreatorID, "creator-id", 0, "Filter by creator member ID. Omit or send 'null' to skip.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort key. Defaults to backend ordering when omitted. [created_at, updated_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Regex filter against 'field_name' and 'display_name'. Invalid regex is auto-escaped to literal substring match.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentFieldWriteCreateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fDisplayName string + var fFieldName string + var fFieldType string + var fOptions []string + var fValueType string + cmd := &cobra.Command{ + Use: "create", + Short: "Create field", + Long: `Create field. + +Create a new incident custom field on the account. + +API: POST /field/create (field-write-create) + +Request fields: + --description string — Optional free-text description. (≤499 chars) + --display-name string (required) — Human-readable name. Must be unique within the account. (≤39 chars) + --field-name string (required) — Machine name. Must start with a letter or underscore; 1–40 chars of '[a-zA-Z0-9_]'. Immutable after creation. (≤39 chars) + --field-type string (required) — Field input type. Immutable after creation. [checkbox, multi_select, single_select, text] + --options []string — Required and non-empty for 'single_select'/'multi_select' (unique strings, each 1–200 chars). Must be omitted or empty for 'checkbox'/'text'. + --value-type string (required) — Stored value type. 'checkbox' requires 'bool'; 'single_select'/'multi_select'/'text' require 'string'. Immutable after creation. [string, bool, float] + default_value (any, via --data) — Optional default value. Type must match 'field_type': 'bool' for checkbox; one of 'options' for single_select; subset of 'options' for multi_select; string ≤3000 chars for text. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - field_id (string) (required) — Newly assigned field ID — 24-character hex ObjectID. + - field_name (string) (required) — Echo of the submitted 'field_name'. +`, + Example: ` flashduty field create --data '{"default_value":"Medium","description":"Business severity tier.","display_name":"Severity Class","field_name":"severity_class","field_type":"single_select","options":["Critical","High","Medium","Low"],"value_type":"string"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("display-name") { + body["display_name"] = fDisplayName + } + if cmd.Flags().Changed("field-name") { + body["field_name"] = fFieldName + } + if cmd.Flags().Changed("field-type") { + body["field_type"] = fFieldType + } + if cmd.Flags().Changed("options") { + body["options"] = fOptions + } + if cmd.Flags().Changed("value-type") { + body["value_type"] = fValueType + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateFieldRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.FieldWriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Optional free-text description. (≤499 chars)") + cmd.Flags().StringVar(&fDisplayName, "display-name", "", "Human-readable name. Must be unique within the account. (required) (≤39 chars)") + cmd.Flags().StringVar(&fFieldName, "field-name", "", "Machine name. Must start with a letter or underscore; 1–40 chars of '[a-zA-Z0-9_]'. Immutable after creation. (required) (≤39 chars)") + cmd.Flags().StringVar(&fFieldType, "field-type", "", "Field input type. Immutable after creation. (required) [checkbox, multi_select, single_select, text]") + cmd.Flags().StringSliceVar(&fOptions, "options", nil, "Required and non-empty for 'single_select'/'multi_select' (unique strings, each 1–200 chars). Must be omitted or empty for 'checkbox'/'text'.") + cmd.Flags().StringVar(&fValueType, "value-type", "", "Stored value type. 'checkbox' requires 'bool'; 'single_select'/'multi_select'/'text' require 'string'. Immutable after creation. (required) [string, bool, float]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentFieldWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fFieldID string + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete field", + Long: `Delete field. + +Delete an incident custom field and asynchronously strip it from existing incidents. + +API: POST /field/delete (field-write-delete) + +Request fields: + --field-id string (required) — Field ID — 24-character hex ObjectID. +`, + Example: ` flashduty field delete --data '{"field_id":"66e9d3a4f7c2b04a1c8a91b3"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("field-id") { + body["field_id"] = fFieldID + } + }) + if err != nil { + return err + } + req := new(flashduty.DeleteFieldRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.FieldWriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /field/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fFieldID, "field-id", "", "Field ID — 24-character hex ObjectID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentFieldWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fDisplayName string + var fFieldID string + var fOptions []string + cmd := &cobra.Command{ + Use: "update", + Short: "Update field", + Long: `Update field. + +Update mutable attributes of an existing incident custom field. + +API: POST /field/update (field-write-update) + +Request fields: + --description string — New description. + --display-name string — New display name. Must remain unique within the account. (≤39 chars) + --field-id string (required) — Field ID — 24-character hex ObjectID. + --options []string — Replacement options list. Must obey the same per-type rules as create. + default_value (any, via --data) — Replacement default value. Type must match the field's existing 'field_type'. +`, + Example: ` flashduty field update --data '{"default_value":"Medium","description":"Business severity tier.","display_name":"Severity Class","field_id":"66e9d3a4f7c2b04a1c8a91b3","options":["Critical","High","Medium","Low"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("display-name") { + body["display_name"] = fDisplayName + } + if cmd.Flags().Changed("field-id") { + body["field_id"] = fFieldID + } + if cmd.Flags().Changed("options") { + body["options"] = fOptions + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateFieldRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.FieldWriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /field/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "New description.") + cmd.Flags().StringVar(&fDisplayName, "display-name", "", "New display name. Must remain unique within the account. (≤39 chars)") + cmd.Flags().StringVar(&fFieldID, "field-id", "", "Field ID — 24-character hex ObjectID. (required)") + cmd.Flags().StringSliceVar(&fOptions, "options", nil, "Replacement options list. Must obey the same per-type rules as create.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingAPIReadInfoCmd() *cobra.Command { + var dataJSON string + var fAPIID string + cmd := &cobra.Command{ + Use: "mapping-api-info", + Short: "Get mapping API detail", + Long: `Get mapping API detail. + +Return detail of a single mapping API by its ID. + +API: POST /enrichment/mapping/api/info (mapping-api-read-info) + +Request fields: + --api-id string (required) — Mapping API ID (MongoDB ObjectID hex). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - api_id (string) (required) — API ID (MongoDB ObjectID hex). + - api_name (string) (required) — API name. + - created_at (integer) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - description (string) (required) — Description. + - headers (object) (required) — Custom request headers. + - insecure_skip_verify (boolean) (required) — Whether TLS verification is skipped. + - retry_count (integer) (required) — Retry count. + - status (string) (required) — API status. + - team_id (integer) (required) — Owning team ID. + - timeout (integer) (required) — Request timeout in seconds. + - updated_at (integer) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. + - url (string) (required) — Endpoint URL. +`, + Example: ` flashduty enrichment mapping-api-info --data '{"api_id":"665f1a2b3c4d5e6f7a8b9c02"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("api-id") { + body["api_id"] = fAPIID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingApiidRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.MappingAPIReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAPIID, "api-id", "", "Mapping API ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingAPIReadListCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "mapping-api-list", + Short: "List mapping APIs", + Long: `List mapping APIs. + +Return all mapping APIs configured for the account. + +API: POST /enrichment/mapping/api/list (mapping-api-read-list) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Mapping APIs. + - api_id (string) (required) — API ID (MongoDB ObjectID hex). + - api_name (string) (required) — API name. + - created_at (integer) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - description (string) (required) — Description. + - headers (object) (required) — Custom request headers. + - insecure_skip_verify (boolean) (required) — Whether TLS verification is skipped. + - retry_count (integer) (required) — Retry count. + - status (string) (required) — API status. + - team_id (integer) (required) — Owning team ID. + - timeout (integer) (required) — Request timeout in seconds. + - updated_at (integer) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. + - url (string) (required) — Endpoint URL. + - total (integer) (required) — Total API count. +`, + Example: ` flashduty enrichment mapping-api-list --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AlertEnrichment.MappingAPIReadList(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingAPIWriteCreateCmd() *cobra.Command { + var dataJSON string + var fAPIName string + var fDescription string + var fInsecureSkipVerify bool + var fRetryCount int64 + var fTeamID int64 + var fTimeout int64 + var fURL string + cmd := &cobra.Command{ + Use: "mapping-api-create", + Short: "Create mapping API", + Long: `Create mapping API. + +Create a new external HTTP API endpoint used to enrich alerts via HTTP lookup. + +API: POST /enrichment/mapping/api/create (mapping-api-write-create) + +Request fields: + --api-name string (required) — Unique API name (max 199 chars). (≤199 chars) + --description string — Optional description. + --insecure-skip-verify bool — Skip TLS certificate verification. Default 'false'. + --retry-count int — Number of retries on failure (0–1). Default 0. + --team-id int — Owning team ID. + --timeout int — Request timeout in seconds (1–3). Default 2. + --url string (required) — HTTP/HTTPS endpoint URL (max 500 chars). (≤500 chars) + headers (object, via --data) — Custom HTTP request headers. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - api_id (string) (required) — Created API ID (MongoDB ObjectID hex). + - api_name (string) (required) — API name. +`, + Example: ` flashduty enrichment mapping-api-create --data '{"api_name":"CMDB API","description":"Query CMDB for host metadata","headers":{"X-Token":"mytoken"},"insecure_skip_verify":false,"retry_count":1,"timeout":2,"url":"https://cmdb.example.com/api/lookup"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("api-name") { + body["api_name"] = fAPIName + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("insecure-skip-verify") { + body["insecure_skip_verify"] = fInsecureSkipVerify + } + if cmd.Flags().Changed("retry-count") { + body["retry_count"] = fRetryCount + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("timeout") { + body["timeout"] = fTimeout + } + if cmd.Flags().Changed("url") { + body["url"] = fURL + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingAPICreateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.MappingAPIWriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAPIName, "api-name", "", "Unique API name (max 199 chars). (required) (≤199 chars)") + cmd.Flags().StringVar(&fDescription, "description", "", "Optional description.") + cmd.Flags().BoolVar(&fInsecureSkipVerify, "insecure-skip-verify", false, "Skip TLS certificate verification. Default 'false'.") + cmd.Flags().Int64Var(&fRetryCount, "retry-count", 0, "Number of retries on failure (0–1). Default 0.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") + cmd.Flags().Int64Var(&fTimeout, "timeout", 0, "Request timeout in seconds (1–3). Default 2.") + cmd.Flags().StringVar(&fURL, "url", "", "HTTP/HTTPS endpoint URL (max 500 chars). (required) (≤500 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingAPIWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fAPIID string + cmd := &cobra.Command{ + Use: "mapping-api-delete", + Short: "Delete mapping API", + Long: `Delete mapping API. + +Delete a mapping API. Deletion is blocked if the API is referenced by any enrichment rule. + +API: POST /enrichment/mapping/api/delete (mapping-api-write-delete) + +Request fields: + --api-id string (required) — Mapping API ID (MongoDB ObjectID hex). +`, + Example: ` flashduty enrichment mapping-api-delete --data '{"api_id":"665f1a2b3c4d5e6f7a8b9c02"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("api-id") { + body["api_id"] = fAPIID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingApiidRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.MappingAPIWriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/mapping/api/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fAPIID, "api-id", "", "Mapping API ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingAPIWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fAPIID string + var fAPIName string + var fDescription string + var fInsecureSkipVerify bool + var fRetryCount int64 + var fTeamID int64 + var fTimeout int64 + var fURL string + cmd := &cobra.Command{ + Use: "mapping-api-update", + Short: "Update mapping API", + Long: `Update mapping API. + +Update configuration of an existing mapping API. + +API: POST /enrichment/mapping/api/update (mapping-api-write-update) + +Request fields: + --api-id string (required) — Mapping API ID (MongoDB ObjectID hex). + --api-name string — New API name (max 199 chars). (≤199 chars) + --description string — New description. + --insecure-skip-verify bool — New TLS skip-verify setting. + --retry-count int — New retry count. + --team-id int — New owning team ID. + --timeout int — New timeout in seconds. + --url string — New endpoint URL (max 500 chars). (≤500 chars) + headers (object, via --data) — New headers map (replaces existing). +`, + Example: ` flashduty enrichment mapping-api-update --data '{"api_id":"665f1a2b3c4d5e6f7a8b9c02","retry_count":1,"timeout":3}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("api-id") { + body["api_id"] = fAPIID + } + if cmd.Flags().Changed("api-name") { + body["api_name"] = fAPIName + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("insecure-skip-verify") { + body["insecure_skip_verify"] = fInsecureSkipVerify + } + if cmd.Flags().Changed("retry-count") { + body["retry_count"] = fRetryCount + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("timeout") { + body["timeout"] = fTimeout + } + if cmd.Flags().Changed("url") { + body["url"] = fURL + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingAPIUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.MappingAPIWriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/mapping/api/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fAPIID, "api-id", "", "Mapping API ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&fAPIName, "api-name", "", "New API name (max 199 chars). (≤199 chars)") + cmd.Flags().StringVar(&fDescription, "description", "", "New description.") + cmd.Flags().BoolVar(&fInsecureSkipVerify, "insecure-skip-verify", false, "New TLS skip-verify setting.") + cmd.Flags().Int64Var(&fRetryCount, "retry-count", 0, "New retry count.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID.") + cmd.Flags().Int64Var(&fTimeout, "timeout", 0, "New timeout in seconds.") + cmd.Flags().StringVar(&fURL, "url", "", "New endpoint URL (max 500 chars). (≤500 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingDataReadDownloadCmd() *cobra.Command { + var dataJSON string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-data-download", + Short: "Download mapping data as CSV", + Long: `Download mapping data as CSV. + +Export all data rows of a mapping schema as a CSV file download. + +API: POST /enrichment/mapping/data/download (mapping-data-read-download) + +Request fields: + --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). +`, + Example: ` flashduty enrichment mapping-data-download --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingSchemaIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.MappingDataReadDownload(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingDataReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fOrderby string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-data-list", + Short: "List mapping data", + Long: `List mapping data. + +Return paginated mapping data rows for a schema, with optional exact-match filtering on source label values. + +API: POST /enrichment/mapping/data/list (mapping-data-read-list) + +Request fields: + --page int — Page number (1-based). Used for offset-based pagination. + --limit int — Page size (1–100, default 20). + --search-after-ctx string — Opaque cursor token for cursor-based pagination. + --asc bool — Sort ascending when 'true'. + --orderby string — Sort field. [created_at, updated_at] + --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). + query (object, via --data) — Exact-match filter on source label values. All source labels must be provided if any are specified. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — Whether more pages exist. + - items (array) (required) — Data rows. + - created_at (integer) — Creation timestamp, Unix seconds. + - fields (object) — All label key-value pairs for this row. + - key (string) — Composite key derived from source label values. + - updated_at (integer) — Last update timestamp, Unix seconds. + - search_after_ctx (string) — Cursor token for the next page. + - total (integer) (required) — Total matching rows. +`, + Example: ` flashduty enrichment mapping-data-list --data '{"asc":false,"limit":20,"orderby":"updated_at","p":1,"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingDataListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.MappingDataReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number (1-based). Used for offset-based pagination.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size (1–100, default 20).") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque cursor token for cursor-based pagination.") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true'.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingDataWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fKeys []string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-data-delete", + Short: "Delete mapping data rows", + Long: `Delete mapping data rows. + +Delete up to 100 mapping data rows by their keys. + +API: POST /enrichment/mapping/data/delete (mapping-data-write-delete) + +Request fields: + --keys []string (required) — Keys of rows to delete. + --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). +`, + Example: ` flashduty enrichment mapping-data-delete --data '{"keys":["server01","server02"],"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("keys") { + body["keys"] = fKeys + } + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingDataDeleteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.MappingDataWriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/mapping/data/delete") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fKeys, "keys", nil, "Keys of rows to delete. (required)") + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingDataWriteTruncateCmd() *cobra.Command { + var dataJSON string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-data-truncate", + Short: "Truncate mapping data", + Long: `Truncate mapping data. + +Delete all data rows in a mapping schema. + +API: POST /enrichment/mapping/data/truncate (mapping-data-write-truncate) + +Request fields: + --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). +`, + Example: ` flashduty enrichment mapping-data-truncate --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingSchemaIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.MappingDataWriteTruncate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/mapping/data/truncate") + return nil + }) + }, + } + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingDataWriteUploadCmd() *cobra.Command { + var dataJSON string + var fFile string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-data-upload", + Short: "Upload mapping data via CSV", + Long: `Upload mapping data via CSV. + +Upload a CSV file to bulk-load mapping data. By default the existing data is truncated before loading the new rows. + +API: POST /enrichment/mapping/data/upload (mapping-data-write-upload) + +Request fields: + --file string — CSV file to upload. + --schema-id string — Mapping schema ID (query parameter). +`, + Example: ` flashduty enrichment mapping-data-upload --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("file") { + body["file"] = fFile + } + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingDataUploadRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.MappingDataWriteUpload(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/mapping/data/upload") + return nil + }) + }, + } + cmd.Flags().StringVar(&fFile, "file", "", "CSV file to upload.") + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (query parameter).") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingDataWriteUpsertCmd() *cobra.Command { + var dataJSON string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-data-upsert", + Short: "Upsert mapping data rows", + Long: `Upsert mapping data rows. + +Insert or update up to 1000 data rows in a mapping schema. Each row must contain all source and result labels. + +API: POST /enrichment/mapping/data/upsert (mapping-data-write-upsert) + +Request fields: + --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). + docs (array, via --data) (required) — Rows to insert or update. Each row must include all source and result labels. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - keys (array) (required) — Composite keys of upserted rows. +`, + Example: ` flashduty enrichment mapping-data-upsert --data '{"docs":[{"host":"server01","owner":"alice","service":"api","team":"sre"},{"host":"server02","owner":"bob","service":"gateway","team":"platform"}],"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingDataUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.MappingDataWriteUpsert(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingSchemaReadInfoCmd() *cobra.Command { + var dataJSON string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-schema-info", + Short: "Get mapping schema detail", + Long: `Get mapping schema detail. + +Return detail of a single mapping schema by its ID. + +API: POST /enrichment/mapping/schema/info (mapping-schema-read-info) + +Request fields: + --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - created_at (integer) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - description (string) (required) — Schema description. + - result_labels (array) (required) — Output label names. + - schema_id (string) (required) — Schema ID (MongoDB ObjectID hex). + - schema_name (string) (required) — Schema name. + - source_labels (array) (required) — Lookup key label names. + - status (string) (required) — Schema status. + - team_id (integer) (required) — Owning team ID. + - updated_at (integer) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. +`, + Example: ` flashduty enrichment mapping-schema-info --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingSchemaIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.MappingSchemaReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingSchemaReadListCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "mapping-schema-list", + Short: "List mapping schemas", + Long: `List mapping schemas. + +Return all mapping schemas for the account, sorted by creation time ascending. + +API: POST /enrichment/mapping/schema/list (mapping-schema-read-list) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Mapping schemas. + - created_at (integer) — Creation timestamp, Unix seconds. + - creator_id (integer) (required) — Creator member ID. + - description (string) (required) — Schema description. + - result_labels (array) (required) — Output label names. + - schema_id (string) (required) — Schema ID (MongoDB ObjectID hex). + - schema_name (string) (required) — Schema name. + - source_labels (array) (required) — Lookup key label names. + - status (string) (required) — Schema status. + - team_id (integer) (required) — Owning team ID. + - updated_at (integer) — Last update timestamp, Unix seconds. + - updated_by (integer) (required) — Last updater member ID. + - total (integer) (required) — Total schema count. +`, + Example: ` flashduty enrichment mapping-schema-list --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AlertEnrichment.MappingSchemaReadList(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingSchemaWriteCreateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fResultLabels []string + var fSchemaName string + var fSourceLabels []string + var fTeamID int64 + cmd := &cobra.Command{ + Use: "mapping-schema-create", + Short: "Create mapping schema", + Long: `Create mapping schema. + +Create a new mapping schema defining source lookup labels and the result labels to populate. Requires a Pro plan. + +API: POST /enrichment/mapping/schema/create (mapping-schema-write-create) + +Request fields: + --description string — Optional description (max 500 chars). (≤500 chars) + --result-labels []string (required) — Output label names (1–10). Must not overlap with 'source_labels'. + --schema-name string (required) — Unique schema name (max 39 chars). (≤39 chars) + --source-labels []string (required) — Lookup key label names (1–3). Must not overlap with 'result_labels'. + --team-id int — Owning team ID. '0' means no team. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - schema_id (string) (required) — Created schema ID (MongoDB ObjectID hex). + - schema_name (string) (required) — Schema name. +`, + Example: ` flashduty enrichment mapping-schema-create --data '{"description":"Enrich alerts with CMDB data","result_labels":["owner","team","service"],"schema_name":"CMDB Lookup","source_labels":["host"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("result-labels") { + body["result_labels"] = fResultLabels + } + if cmd.Flags().Changed("schema-name") { + body["schema_name"] = fSchemaName + } + if cmd.Flags().Changed("source-labels") { + body["source_labels"] = fSourceLabels + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingSchemaCreateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertEnrichment.MappingSchemaWriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Optional description (max 500 chars). (≤500 chars)") + cmd.Flags().StringSliceVar(&fResultLabels, "result-labels", nil, "Output label names (1–10). Must not overlap with 'source_labels'. (required)") + cmd.Flags().StringVar(&fSchemaName, "schema-name", "", "Unique schema name (max 39 chars). (required) (≤39 chars)") + cmd.Flags().StringSliceVar(&fSourceLabels, "source-labels", nil, "Lookup key label names (1–3). Must not overlap with 'result_labels'. (required)") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. '0' means no team.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingSchemaWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fSchemaID string + cmd := &cobra.Command{ + Use: "mapping-schema-delete", + Short: "Delete mapping schema", + Long: `Delete mapping schema. + +Delete a mapping schema and all its associated data. Deletion is blocked if the schema is referenced by any enrichment rule or webhook. + +API: POST /enrichment/mapping/schema/delete (mapping-schema-write-delete) + +Request fields: + --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). +`, + Example: ` flashduty enrichment mapping-schema-delete --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingSchemaIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.MappingSchemaWriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/mapping/schema/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertEnrichmentMappingSchemaWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fSchemaID string + var fSchemaName string + var fTeamID int64 + cmd := &cobra.Command{ + Use: "mapping-schema-update", + Short: "Update mapping schema", + Long: `Update mapping schema. + +Update the name, description, or owning team of a mapping schema. Source and result labels cannot be changed after creation. + +API: POST /enrichment/mapping/schema/update (mapping-schema-write-update) + +Request fields: + --description string — New description (max 500 chars). (≤500 chars) + --schema-id string (required) — Schema ID (MongoDB ObjectID hex). + --schema-name string — New schema name (max 39 chars). (≤39 chars) + --team-id int — New owning team ID. '0' removes the team association. +`, + Example: ` flashduty enrichment mapping-schema-update --data '{"description":"Updated description","schema_id":"665f1a2b3c4d5e6f7a8b9c01","schema_name":"CMDB Lookup v2"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("schema-id") { + body["schema_id"] = fSchemaID + } + if cmd.Flags().Changed("schema-name") { + body["schema_name"] = fSchemaName + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.MappingSchemaUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertEnrichment.MappingSchemaWriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /enrichment/mapping/schema/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "New description (max 500 chars). (≤500 chars)") + cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Schema ID (MongoDB ObjectID hex). (required)") + cmd.Flags().StringVar(&fSchemaName, "schema-name", "", "New schema name (max 39 chars). (≤39 chars)") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID. '0' removes the team association.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedAlertEnrichment(root *cobra.Command) { + gEnrichment := genGroup(root, "enrichment", "On-call/Alert enrichment API") + genAddLeaf(gEnrichment, genAlertEnrichmentEnrichmentReadInfoCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentEnrichmentReadListCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentEnrichmentWriteUpsertCmd()) + gField := genGroup(root, "field", "On-call/Alert enrichment API") + genAddLeaf(gField, genAlertEnrichmentFieldReadInfoCmd()) + genAddLeaf(gField, genAlertEnrichmentFieldReadListCmd()) + genAddLeaf(gField, genAlertEnrichmentFieldWriteCreateCmd()) + genAddLeaf(gField, genAlertEnrichmentFieldWriteDeleteCmd()) + genAddLeaf(gField, genAlertEnrichmentFieldWriteUpdateCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingAPIReadInfoCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingAPIReadListCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingAPIWriteCreateCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingAPIWriteDeleteCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingAPIWriteUpdateCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingDataReadDownloadCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingDataReadListCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingDataWriteDeleteCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingDataWriteTruncateCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingDataWriteUploadCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingDataWriteUpsertCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingSchemaReadInfoCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingSchemaReadListCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingSchemaWriteCreateCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingSchemaWriteDeleteCmd()) + genAddLeaf(gEnrichment, genAlertEnrichmentMappingSchemaWriteUpdateCmd()) +} diff --git a/internal/cli/zz_generated_alert_rules.go b/internal/cli/zz_generated_alert_rules.go new file mode 100644 index 0000000..7d2b8d5 --- /dev/null +++ b/internal/cli/zz_generated_alert_rules.go @@ -0,0 +1,1518 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genAlertRulesReadAuditDetailCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "rule-audit-detail", + Short: "Get rule audit snapshot", + Long: `Get rule audit snapshot. + +Return the audit record (including the 'content' field, a JSON string of the rule snapshot at that point in time). + +API: POST /monit/rule/audit/detail (monit-rule-read-audit-detail) + +Request fields: + --id int (required) — Audit record ID — the 'id' of an audit row returned by 'POST /monit/rule/audits', NOT the rule ID. Passing a rule ID returns HTTP 400. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) + - action (string) (required) — Action performed, e.g. 'create', 'update'. + - alert_rule_id (integer) (required) — ID of the alert rule this record belongs to. + - content (string) — JSON string of the full rule snapshot at audit time. Populated on '/monit/rule/audit/detail', omitted on list responses. + - created_at (integer) (required) + - creator_id (integer) (required) + - creator_name (string) (required) + - id (integer) (required) — Audit record ID. +`, + Example: ` flashduty monit rule-audit-detail --data '{"id":9001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.AuditRecordIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.ReadAuditDetail(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Audit record ID — the 'id' of an audit row returned by 'POST /monit/rule/audits', NOT the rule ID. Passing a rule ID returns HTTP 400. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadAuditsCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "rule-audits", + Short: "List rule change history", + Long: `List rule change history. + +Return the change history (audit records) for an alert rule. + +API: POST /monit/rule/audits (monit-rule-read-audits) + +Request fields: + --id int (required) — Rule ID. + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - account_id (integer) (required) + - action (string) (required) — Action performed, e.g. 'create', 'update'. + - alert_rule_id (integer) (required) — ID of the alert rule this record belongs to. + - content (string) — JSON string of the full rule snapshot at audit time. Populated on '/monit/rule/audit/detail', omitted on list responses. + - created_at (integer) (required) + - creator_id (integer) (required) + - creator_name (string) (required) + - id (integer) (required) — Audit record ID. +`, + Example: ` flashduty monit rule-audits --data '{"id":50001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.ReadAudits(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Rule ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadCounterChannelCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "rule-counter-channel", + Short: "Get rule counts by channel", + Long: `Get rule counts by channel. + +Return an object mapping channel name to the number of rules routing alerts to that channel. If a channel name cannot be resolved, the channel ID (as a string) is used as the key. + +API: POST /monit/rule/counter/channel (monit-rule-read-counter-channel) +`, + Example: ` flashduty monit rule-counter-channel --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AlertRules.ReadCounterChannel(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadCounterNodeCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "rule-counter-node", + Short: "Get rule counts by folder node", + Long: `Get rule counts by folder node. + +Return an object mapping top-level folder name to the total number of rules under that folder and all its descendants. + +API: POST /monit/rule/counter/node (monit-rule-read-counter-node) +`, + Example: ` flashduty monit rule-counter-node --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AlertRules.ReadCounterNode(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadCounterStatusCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "rule-counter-status", + Short: "Get rule status counters for top-level folders", + Long: `Get rule status counters for top-level folders. + +Return trigger status summary for all top-level folder nodes — used for the overview dashboard. + +API: POST /monit/rule/counter/status (monit-rule-read-counter-status) + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - folder_id (integer) (required) + - folder_name (string) + - rule_total (integer) (required) — Total rules in the folder family. + - triggered_rule_count (integer) (required) — Rules with active alerts. +`, + Example: ` flashduty monit rule-counter-status --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AlertRules.ReadCounterStatus(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadCounterTotalCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "rule-counter-total", + Short: "Get rule counter time series", + Long: `Get rule counter time series. + +Return the stored time series of the total rule count across the account — one sample per 'clock' timestamp. + +API: POST /monit/rule/counter/total (monit-rule-read-counter-total) + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - account_id (integer) (required) + - clock (integer) (required) — Sample timestamp, Unix epoch seconds. + - id (integer) (required) + - num (integer) (required) — Rule count at the sample time. +`, + Example: ` flashduty monit rule-counter-total --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AlertRules.ReadCounterTotal(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadDstypesCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "rule-dstypes", + Short: "List available datasource types", + Long: `List available datasource types. + +Return the list of datasource types ('DSType' records) that the current account can use when authoring alert rules — combines global types and account-scoped types. + +API: POST /monit/rule/dstypes (monit-rule-read-dstypes) + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - account_id (integer) (required) — Owning account ID. '0' for global types. + - id (integer) (required) + - ident (string) (required) — Identifier used as the 'ds_type' of rules, e.g. 'prometheus'. + - name (string) (required) — Display name, e.g. 'Prometheus'. + - weight (integer) (required) — Display order weight; higher appears first. +`, + Example: ` flashduty monit rule-dstypes --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AlertRules.ReadDstypes(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadExportCmd() *cobra.Command { + var dataJSON string + var fIDs []int + cmd := &cobra.Command{ + Use: "rule-export", + Short: "Export alert rules", + Long: `Export alert rules. + +Export the configuration of selected alert rules as a portable JSON array, compatible with 'POST /monit/rule/import'. + +API: POST /monit/rule/export (monit-rule-read-export) + +Request fields: + --ids []int (required) — Rule IDs. + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - annotations (object) + - cron_pattern (string) (required) + - debug_log_enabled (boolean) (required) + - delay_seconds (integer) + - description (string) + - description_type (string) [text, markdown] + - ds_ids (array) + - ds_list (array) + - ds_type (string) (required) + - enabled (boolean) (required) + - enabled_times (array) + - days (array) — Days of week, 0 = Sunday. + - etime (string) — End time, e.g. '18:00'. + - stime (string) — Start time, e.g. '09:00'. + - labels (object) + - name (string) (required) + - repeat_interval (integer) + - repeat_total (integer) + - rule_configs (object) — Rule evaluation configuration. + - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery (object) — Recovery condition for any-data check. If omitted or 'mode' is empty, treated as 'nodata'. + - args (object) + - condition (string) — Recovery expression. Required when 'mode' is 'ql'. + - mode (string) — 'nodata' = recover when the query returns no data; 'ql' = recover when the 'condition' expression evaluates to true. When 'mode' is 'ql', only a single query ('name=A') is permitted. [nodata, ql] + - recovery_check_times (integer) + - severity (string) [Critical, Warning, Info] + - check_nodata (object) — No-data check configuration. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery_check_times (integer) + - resolve_timeout (integer) — Auto-resolve after N seconds. + - severity (string) [Critical, Warning, Info] + - check_threshold (object) — Threshold check configuration. + - alerting_check_times (integer) + - critical (string) + - enabled (boolean) + - info (string) + - push_recovery_event (boolean) + - recovery (object) + - condition (string) + - mode (string) [invert, threshold, ql] + - recovery_check_times (integer) + - warning (string) + - queries (array) + - args (object) + - expr (string) — Query expression. + - label_fields (array) + - name (string) — Query identifier (letter, e.g. 'A'). The name 'R' is reserved and must not be used. + - value_fields (array) + - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique 'name' (not duplicating any query name) and a non-empty 'expr'. + - args (object) + - expr (string) — Query expression. + - name (string) — Relate-query identifier. +`, + Example: ` flashduty monit rule-export --data '{"ids":[50001]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("ids") { + body["ids"] = fIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleIDsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.ReadExport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadInfoCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "rule-info", + Short: "Get alert rule detail", + Long: `Get alert rule detail. + +Return the full configuration of an alert rule by its ID, including rule queries, thresholds, and notification settings. + +API: POST /monit/rule/info (monit-rule-read-info) + +Request fields: + --id int (required) — Rule ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) + - annotations (object) + - channel_ids (array) — Channel IDs to send alerts to. + - created_at (integer) (required) + - creator_id (integer) (required) + - creator_name (string) (required) + - cron_pattern (string) (required) — 5-field cron schedule. + - debug_log_enabled (boolean) (required) + - delay_seconds (integer) (required) + - description (string) + - description_type (string) [text, markdown] + - ds_ids (array) — Specific data source IDs. + - ds_list (array) — Data source name patterns (supports wildcards). + - ds_type (string) (required) — Data source type. + - enabled (boolean) (required) + - enabled_times (array) — Time windows when the rule is active. + - days (array) — Days of week (0=Sunday). + - etime (string) — End time, e.g. '18:00'. + - stime (string) — Start time, e.g. '09:00'. + - folder_id (integer) (required) — Folder the rule belongs to. + - id (integer) (required) + - labels (object) — Custom labels. + - name (string) (required) — Rule name. + - repeat_interval (integer) — Notification repeat interval in seconds. + - repeat_total (integer) — Max number of repeat notifications. + - rule_configs (object) — Rule evaluation configuration. + - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery (object) — Recovery condition for any-data check. If omitted or 'mode' is empty, treated as 'nodata'. + - args (object) + - condition (string) — Recovery expression. Required when 'mode' is 'ql'. + - mode (string) — 'nodata' = recover when the query returns no data; 'ql' = recover when the 'condition' expression evaluates to true. When 'mode' is 'ql', only a single query ('name=A') is permitted. [nodata, ql] + - recovery_check_times (integer) + - severity (string) [Critical, Warning, Info] + - check_nodata (object) — No-data check configuration. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery_check_times (integer) + - resolve_timeout (integer) — Auto-resolve after N seconds. + - severity (string) [Critical, Warning, Info] + - check_threshold (object) — Threshold check configuration. + - alerting_check_times (integer) + - critical (string) + - enabled (boolean) + - info (string) + - push_recovery_event (boolean) + - recovery (object) + - condition (string) + - mode (string) [invert, threshold, ql] + - recovery_check_times (integer) + - warning (string) + - queries (array) + - args (object) + - expr (string) — Query expression. + - label_fields (array) + - name (string) — Query identifier (letter, e.g. 'A'). The name 'R' is reserved and must not be used. + - value_fields (array) + - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique 'name' (not duplicating any query name) and a non-empty 'expr'. + - args (object) + - expr (string) — Query expression. + - name (string) — Relate-query identifier. + - updated_at (integer) (required) + - updater_id (integer) (required) + - updater_name (string) (required) +`, + Example: ` flashduty monit rule-info --data '{"id":50001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Rule ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesReadListCmd() *cobra.Command { + var dataJSON string + var fFolderID int64 + cmd := &cobra.Command{ + Use: "rule-list-basic", + Short: "List alert rules", + Long: `List alert rules. + +Return the basic information of all alert rules in a folder. For full rule details, call 'POST /monit/rule/info'. + +API: POST /monit/rule/list/basic (monit-rule-read-list) + +Request fields: + --folder-id int — Folder ID. 0 to list all accessible rules. + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - account_id (integer) (required) — Account ID. + - created_at (integer) (required) + - creator_id (integer) (required) + - creator_name (string) (required) + - cron_pattern (string) (required) — 5-field cron schedule, e.g. '* * * * *'. + - debug_log_enabled (boolean) (required) — Whether debug logging is enabled. + - delay_seconds (integer) (required) — Evaluation delay in seconds. + - ds_type (string) (required) — Data source type, e.g. 'prometheus'. + - enabled (boolean) (required) — Whether the rule is enabled. + - folder_id (integer) (required) — Folder ID. + - id (integer) (required) — Unique rule ID. + - labels (object) — Custom labels. + - name (string) (required) — Rule name. + - triggered (boolean) (required) — True if the rule currently has active alerts. + - updated_at (integer) (required) + - updater_id (integer) (required) + - updater_name (string) (required) +`, + Example: ` flashduty monit rule-list-basic --data '{"folder_id":100}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("folder-id") { + body["folder_id"] = fFolderID + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fFolderID, "folder-id", 0, "Folder ID. 0 to list all accessible rules.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteCreateCmd() *cobra.Command { + var dataJSON string + var fAccountID int64 + var fChannelIDs []int + var fCreatedAt int64 + var fCreatorID int64 + var fCreatorName string + var fCronPattern string + var fDebugLogEnabled bool + var fDelaySeconds int64 + var fDescription string + var fDescriptionType string + var fDsIDs []int + var fDsList []string + var fDsType string + var fEnabled bool + var fFolderID int64 + var fID int64 + var fName string + var fRepeatInterval int64 + var fRepeatTotal int64 + var fUpdatedAt int64 + var fUpdaterID int64 + var fUpdaterName string + cmd := &cobra.Command{ + Use: "rule-create", + Short: "Create alert rule", + Long: `Create alert rule. + +Create a new alert rule. Returns the created rule with its assigned ID. + +API: POST /monit/rule/create (monit-rule-write-create) + +Request fields: + --account-id int + --channel-ids []int — Channel IDs to send alerts to. + --created-at int + --creator-id int + --creator-name string + --cron-pattern string — 5-field cron schedule. + --debug-log-enabled bool + --delay-seconds int + --description string + --description-type string [text, markdown] + --ds-ids []int — Specific data source IDs. + --ds-list []string — Data source name patterns (supports wildcards). + --ds-type string — Data source type. + --enabled bool + --folder-id int — Folder the rule belongs to. + --id int + --name string — Rule name. + --repeat-interval int — Notification repeat interval in seconds. + --repeat-total int — Max number of repeat notifications. + --updated-at int + --updater-id int + --updater-name string + annotations (object, via --data) + enabled_times (array, via --data) — Time windows when the rule is active. + - days (array) — Days of week (0=Sunday). + - etime (string) — End time, e.g. '18:00'. + - stime (string) — Start time, e.g. '09:00'. + labels (object, via --data) — Custom labels. + rule_configs (object, via --data) — Rule evaluation configuration. + - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery (object) — Recovery condition for any-data check. If omitted or 'mode' is empty, treated as 'nodata'. + - args (object) + - condition (string) — Recovery expression. Required when 'mode' is 'ql'. + - mode (string) — 'nodata' = recover when the query returns no data; 'ql' = recover when the 'condition' expression evaluates to true. When 'mode' is 'ql', only a single query ('name=A') is permitted. [nodata, ql] + - recovery_check_times (integer) + - severity (string) [Critical, Warning, Info] + - check_nodata (object) — No-data check configuration. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery_check_times (integer) + - resolve_timeout (integer) — Auto-resolve after N seconds. + - severity (string) [Critical, Warning, Info] + - check_threshold (object) — Threshold check configuration. + - alerting_check_times (integer) + - critical (string) + - enabled (boolean) + - info (string) + - push_recovery_event (boolean) + - recovery (object) + - condition (string) + - mode (string) [invert, threshold, ql] + - recovery_check_times (integer) + - warning (string) + - queries (array) + - args (object) + - expr (string) — Query expression. + - label_fields (array) + - name (string) — Query identifier (letter, e.g. 'A'). The name 'R' is reserved and must not be used. + - value_fields (array) + - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique 'name' (not duplicating any query name) and a non-empty 'expr'. + - args (object) + - expr (string) — Query expression. + - name (string) — Relate-query identifier. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) + - annotations (object) + - channel_ids (array) — Channel IDs to send alerts to. + - created_at (integer) + - creator_id (integer) + - creator_name (string) + - cron_pattern (string) — 5-field cron schedule. + - debug_log_enabled (boolean) + - delay_seconds (integer) + - description (string) + - description_type (string) [text, markdown] + - ds_ids (array) — Specific data source IDs. + - ds_list (array) — Data source name patterns (supports wildcards). + - ds_type (string) — Data source type. + - enabled (boolean) + - enabled_times (array) — Time windows when the rule is active. + - days (array) — Days of week (0=Sunday). + - etime (string) — End time, e.g. '18:00'. + - stime (string) — Start time, e.g. '09:00'. + - folder_id (integer) — Folder the rule belongs to. + - id (integer) + - labels (object) — Custom labels. + - name (string) — Rule name. + - repeat_interval (integer) — Notification repeat interval in seconds. + - repeat_total (integer) — Max number of repeat notifications. + - rule_configs (object) — Rule evaluation configuration. + - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery (object) — Recovery condition for any-data check. If omitted or 'mode' is empty, treated as 'nodata'. + - args (object) + - condition (string) — Recovery expression. Required when 'mode' is 'ql'. + - mode (string) — 'nodata' = recover when the query returns no data; 'ql' = recover when the 'condition' expression evaluates to true. When 'mode' is 'ql', only a single query ('name=A') is permitted. [nodata, ql] + - recovery_check_times (integer) + - severity (string) [Critical, Warning, Info] + - check_nodata (object) — No-data check configuration. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery_check_times (integer) + - resolve_timeout (integer) — Auto-resolve after N seconds. + - severity (string) [Critical, Warning, Info] + - check_threshold (object) — Threshold check configuration. + - alerting_check_times (integer) + - critical (string) + - enabled (boolean) + - info (string) + - push_recovery_event (boolean) + - recovery (object) + - condition (string) + - mode (string) [invert, threshold, ql] + - recovery_check_times (integer) + - warning (string) + - queries (array) + - args (object) + - expr (string) — Query expression. + - label_fields (array) + - name (string) — Query identifier (letter, e.g. 'A'). The name 'R' is reserved and must not be used. + - value_fields (array) + - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique 'name' (not duplicating any query name) and a non-empty 'expr'. + - args (object) + - expr (string) — Query expression. + - name (string) — Relate-query identifier. + - updated_at (integer) + - updater_id (integer) + - updater_name (string) +`, + Example: ` flashduty monit rule-create --data '{"channel_ids":[20001],"cron_pattern":"* * * * *","ds_list":["prometheus*"],"ds_type":"prometheus","enabled":true,"folder_id":100,"name":"CPU High","rule_configs":{"check_threshold":{"alerting_check_times":1,"critical":"A","enabled":true,"push_recovery_event":true,"recovery":{"mode":"invert"},"recovery_check_times":1},"queries":[{"expr":"avg(cpu_usage_idle) \u003c 10","name":"A"}]}}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("account-id") { + body["account_id"] = fAccountID + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("created-at") { + body["created_at"] = fCreatedAt + } + if cmd.Flags().Changed("creator-id") { + body["creator_id"] = fCreatorID + } + if cmd.Flags().Changed("creator-name") { + body["creator_name"] = fCreatorName + } + if cmd.Flags().Changed("cron-pattern") { + body["cron_pattern"] = fCronPattern + } + if cmd.Flags().Changed("debug-log-enabled") { + body["debug_log_enabled"] = fDebugLogEnabled + } + if cmd.Flags().Changed("delay-seconds") { + body["delay_seconds"] = fDelaySeconds + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("description-type") { + body["description_type"] = fDescriptionType + } + if cmd.Flags().Changed("ds-ids") { + body["ds_ids"] = fDsIDs + } + if cmd.Flags().Changed("ds-list") { + body["ds_list"] = fDsList + } + if cmd.Flags().Changed("ds-type") { + body["ds_type"] = fDsType + } + if cmd.Flags().Changed("enabled") { + body["enabled"] = fEnabled + } + if cmd.Flags().Changed("folder-id") { + body["folder_id"] = fFolderID + } + if cmd.Flags().Changed("id") { + body["id"] = fID + } + if cmd.Flags().Changed("name") { + body["name"] = fName + } + if cmd.Flags().Changed("repeat-interval") { + body["repeat_interval"] = fRepeatInterval + } + if cmd.Flags().Changed("repeat-total") { + body["repeat_total"] = fRepeatTotal + } + if cmd.Flags().Changed("updated-at") { + body["updated_at"] = fUpdatedAt + } + if cmd.Flags().Changed("updater-id") { + body["updater_id"] = fUpdaterID + } + if cmd.Flags().Changed("updater-name") { + body["updater_name"] = fUpdaterName + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertRule) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.WriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Request field account_id") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Channel IDs to send alerts to.") + cmd.Flags().Int64Var(&fCreatedAt, "created-at", 0, "Request field created_at") + cmd.Flags().Int64Var(&fCreatorID, "creator-id", 0, "Request field creator_id") + cmd.Flags().StringVar(&fCreatorName, "creator-name", "", "Request field creator_name") + cmd.Flags().StringVar(&fCronPattern, "cron-pattern", "", "5-field cron schedule.") + cmd.Flags().BoolVar(&fDebugLogEnabled, "debug-log-enabled", false, "Request field debug_log_enabled") + cmd.Flags().Int64Var(&fDelaySeconds, "delay-seconds", 0, "Request field delay_seconds") + cmd.Flags().StringVar(&fDescription, "description", "", "Request field description") + cmd.Flags().StringVar(&fDescriptionType, "description-type", "", "Request field description_type [text, markdown]") + cmd.Flags().IntSliceVar(&fDsIDs, "ds-ids", nil, "Specific data source IDs.") + cmd.Flags().StringSliceVar(&fDsList, "ds-list", nil, "Data source name patterns (supports wildcards).") + cmd.Flags().StringVar(&fDsType, "ds-type", "", "Data source type.") + cmd.Flags().BoolVar(&fEnabled, "enabled", false, "Request field enabled") + cmd.Flags().Int64Var(&fFolderID, "folder-id", 0, "Folder the rule belongs to.") + cmd.Flags().Int64Var(&fID, "id", 0, "Request field id") + cmd.Flags().StringVar(&fName, "name", "", "Rule name.") + cmd.Flags().Int64Var(&fRepeatInterval, "repeat-interval", 0, "Notification repeat interval in seconds.") + cmd.Flags().Int64Var(&fRepeatTotal, "repeat-total", 0, "Max number of repeat notifications.") + cmd.Flags().Int64Var(&fUpdatedAt, "updated-at", 0, "Request field updated_at") + cmd.Flags().Int64Var(&fUpdaterID, "updater-id", 0, "Request field updater_id") + cmd.Flags().StringVar(&fUpdaterName, "updater-name", "", "Request field updater_name") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "rule-delete", + Short: "Delete alert rule", + Long: `Delete alert rule. + +Delete a single alert rule by its ID. + +API: POST /monit/rule/delete (monit-rule-write-delete) + +Request fields: + --id int (required) — Rule ID. +`, + Example: ` flashduty monit rule-delete --data '{"id":50001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertRules.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /monit/rule/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Rule ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteDeleteBatchCmd() *cobra.Command { + var dataJSON string + var fIDs []int + cmd := &cobra.Command{ + Use: "rule-delete-batch", + Short: "Batch delete alert rules", + Long: `Batch delete alert rules. + +Delete multiple alert rules in a single request. + +API: POST /monit/rule/delete/batch (monit-rule-write-delete-batch) + +Request fields: + --ids []int (required) — Rule IDs. +`, + Example: ` flashduty monit rule-delete-batch --data '{"ids":[50001,50002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("ids") { + body["ids"] = fIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleIDsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.AlertRules.WriteDeleteBatch(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /monit/rule/delete/batch") + return nil + }) + }, + } + cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteFieldsUpdateCmd() *cobra.Command { + var dataJSON string + var fChannelIDs []int + var fCronPattern string + var fDebugLogEnabled bool + var fDelaySeconds int64 + var fDescription string + var fDsIDs []int + var fDsList []string + var fDsType string + var fEnabled bool + var fFields []string + var fIDs []int + var fRepeatInterval int64 + var fRepeatTotal int64 + cmd := &cobra.Command{ + Use: "rule-update-fields", + Short: "Batch update rule fields", + Long: `Batch update rule fields. + +Update specific fields across multiple alert rules at once. Only the fields listed in 'fields' are applied. + +API: POST /monit/rule/update/fields (monit-rule-write-fields-update) + +Request fields: + --channel-ids []int + --cron-pattern string + --debug-log-enabled bool + --delay-seconds int + --description string + --ds-ids []int + --ds-list []string + --ds-type string + --enabled bool + --fields []string (required) — Field names to update. + --ids []int (required) — Rule IDs to update. + --repeat-interval int + --repeat-total int + annotations (object, via --data) + enabled_times (array, via --data) + - days (array) — Days of week, 0 = Sunday. + - etime (string) — End time, e.g. '18:00'. + - stime (string) — Start time, e.g. '09:00'. + labels (object, via --data) + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - message (string) (required) — Empty on success, error message on failure. + - name (string) (required) — Rule name. +`, + Example: ` flashduty monit rule-update-fields --data '{"enabled":false,"fields":["enabled"],"ids":[50001,50002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("cron-pattern") { + body["cron_pattern"] = fCronPattern + } + if cmd.Flags().Changed("debug-log-enabled") { + body["debug_log_enabled"] = fDebugLogEnabled + } + if cmd.Flags().Changed("delay-seconds") { + body["delay_seconds"] = fDelaySeconds + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("ds-ids") { + body["ds_ids"] = fDsIDs + } + if cmd.Flags().Changed("ds-list") { + body["ds_list"] = fDsList + } + if cmd.Flags().Changed("ds-type") { + body["ds_type"] = fDsType + } + if cmd.Flags().Changed("enabled") { + body["enabled"] = fEnabled + } + if cmd.Flags().Changed("fields") { + body["fields"] = fFields + } + if cmd.Flags().Changed("ids") { + body["ids"] = fIDs + } + if cmd.Flags().Changed("repeat-interval") { + body["repeat_interval"] = fRepeatInterval + } + if cmd.Flags().Changed("repeat-total") { + body["repeat_total"] = fRepeatTotal + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleFieldsUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.WriteFieldsUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Request field channel_ids") + cmd.Flags().StringVar(&fCronPattern, "cron-pattern", "", "Request field cron_pattern") + cmd.Flags().BoolVar(&fDebugLogEnabled, "debug-log-enabled", false, "Request field debug_log_enabled") + cmd.Flags().Int64Var(&fDelaySeconds, "delay-seconds", 0, "Request field delay_seconds") + cmd.Flags().StringVar(&fDescription, "description", "", "Request field description") + cmd.Flags().IntSliceVar(&fDsIDs, "ds-ids", nil, "Request field ds_ids") + cmd.Flags().StringSliceVar(&fDsList, "ds-list", nil, "Request field ds_list") + cmd.Flags().StringVar(&fDsType, "ds-type", "", "Request field ds_type") + cmd.Flags().BoolVar(&fEnabled, "enabled", false, "Request field enabled") + cmd.Flags().StringSliceVar(&fFields, "fields", nil, "Field names to update. (required)") + cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs to update. (required)") + cmd.Flags().Int64Var(&fRepeatInterval, "repeat-interval", 0, "Request field repeat_interval") + cmd.Flags().Int64Var(&fRepeatTotal, "repeat-total", 0, "Request field repeat_total") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteImportCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "rule-import", + Short: "Import alert rules", + Long: `Import alert rules. + +Import one or more alert rules from a JSON array. Returns the result for each rule, indicating success or failure. + +API: POST /monit/rule/import (monit-rule-write-import) + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - message (string) (required) — Empty on success, error message on failure. + - name (string) (required) — Rule name. +`, + Example: ` flashduty monit rule-import --data '[{"cron_pattern":"* * * * *","ds_list":["prometheus*"],"ds_type":"prometheus","enabled":true,"folder_id":100,"name":"CPU High","rule_configs":{"queries":[{"expr":"avg(cpu_usage_idle) \u003c 10","name":"A"}]}}]'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + req := new(flashduty.RuleImportRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.WriteImport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteMoveCmd() *cobra.Command { + var dataJSON string + var fDestFolderID int64 + var fIDs []int + cmd := &cobra.Command{ + Use: "rule-move", + Short: "Move alert rules to folder", + Long: `Move alert rules to folder. + +Move one or more alert rules to a different folder. + +API: POST /monit/rule/move (monit-rule-write-move) + +Request fields: + --dest-folder-id int (required) — Destination folder ID. + --ids []int (required) — Rule IDs to move. + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - message (string) (required) — Empty on success, error message on failure. + - name (string) (required) — Rule name. +`, + Example: ` flashduty monit rule-move --data '{"dest_folder_id":200,"ids":[50001,50002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("dest-folder-id") { + body["dest_folder_id"] = fDestFolderID + } + if cmd.Flags().Changed("ids") { + body["ids"] = fIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleMoveRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.WriteMove(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fDestFolderID, "dest-folder-id", 0, "Destination folder ID. (required)") + cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs to move. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteStatusCmd() *cobra.Command { + var dataJSON string + var fFolderID int64 + cmd := &cobra.Command{ + Use: "rule-status", + Short: "Get rule trigger status under folder", + Long: `Get rule trigger status under folder. + +Return the rule trigger summary for all rules under a folder node and its descendants. + +API: POST /monit/rule/status (monit-rule-write-status) + +Request fields: + --folder-id int — Folder ID. 0 for all. + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - folder_id (integer) (required) + - folder_name (string) + - rule_total (integer) (required) — Total rules in the folder family. + - triggered_rule_count (integer) (required) — Rules with active alerts. +`, + Example: ` flashduty monit rule-status --data '{"folder_id":100}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("folder-id") { + body["folder_id"] = fFolderID + } + }) + if err != nil { + return err + } + req := new(flashduty.RuleFolderIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.WriteStatus(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fFolderID, "folder-id", 0, "Folder ID. 0 for all.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertRulesWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fAccountID int64 + var fChannelIDs []int + var fCreatedAt int64 + var fCreatorID int64 + var fCreatorName string + var fCronPattern string + var fDebugLogEnabled bool + var fDelaySeconds int64 + var fDescription string + var fDescriptionType string + var fDsIDs []int + var fDsList []string + var fDsType string + var fEnabled bool + var fFolderID int64 + var fID int64 + var fName string + var fRepeatInterval int64 + var fRepeatTotal int64 + var fUpdatedAt int64 + var fUpdaterID int64 + var fUpdaterName string + cmd := &cobra.Command{ + Use: "rule-update", + Short: "Update alert rule", + Long: `Update alert rule. + +Replace the full configuration of an existing alert rule. All fields are overwritten. + +API: POST /monit/rule/update (monit-rule-write-update) + +Request fields: + --account-id int + --channel-ids []int — Channel IDs to send alerts to. + --created-at int + --creator-id int + --creator-name string + --cron-pattern string — 5-field cron schedule. + --debug-log-enabled bool + --delay-seconds int + --description string + --description-type string [text, markdown] + --ds-ids []int — Specific data source IDs. + --ds-list []string — Data source name patterns (supports wildcards). + --ds-type string — Data source type. + --enabled bool + --folder-id int — Folder the rule belongs to. + --id int + --name string — Rule name. + --repeat-interval int — Notification repeat interval in seconds. + --repeat-total int — Max number of repeat notifications. + --updated-at int + --updater-id int + --updater-name string + annotations (object, via --data) + enabled_times (array, via --data) — Time windows when the rule is active. + - days (array) — Days of week (0=Sunday). + - etime (string) — End time, e.g. '18:00'. + - stime (string) — Start time, e.g. '09:00'. + labels (object, via --data) — Custom labels. + rule_configs (object, via --data) — Rule evaluation configuration. + - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery (object) — Recovery condition for any-data check. If omitted or 'mode' is empty, treated as 'nodata'. + - args (object) + - condition (string) — Recovery expression. Required when 'mode' is 'ql'. + - mode (string) — 'nodata' = recover when the query returns no data; 'ql' = recover when the 'condition' expression evaluates to true. When 'mode' is 'ql', only a single query ('name=A') is permitted. [nodata, ql] + - recovery_check_times (integer) + - severity (string) [Critical, Warning, Info] + - check_nodata (object) — No-data check configuration. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery_check_times (integer) + - resolve_timeout (integer) — Auto-resolve after N seconds. + - severity (string) [Critical, Warning, Info] + - check_threshold (object) — Threshold check configuration. + - alerting_check_times (integer) + - critical (string) + - enabled (boolean) + - info (string) + - push_recovery_event (boolean) + - recovery (object) + - condition (string) + - mode (string) [invert, threshold, ql] + - recovery_check_times (integer) + - warning (string) + - queries (array) + - args (object) + - expr (string) — Query expression. + - label_fields (array) + - name (string) — Query identifier (letter, e.g. 'A'). The name 'R' is reserved and must not be used. + - value_fields (array) + - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique 'name' (not duplicating any query name) and a non-empty 'expr'. + - args (object) + - expr (string) — Query expression. + - name (string) — Relate-query identifier. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) + - annotations (object) + - channel_ids (array) — Channel IDs to send alerts to. + - created_at (integer) + - creator_id (integer) + - creator_name (string) + - cron_pattern (string) — 5-field cron schedule. + - debug_log_enabled (boolean) + - delay_seconds (integer) + - description (string) + - description_type (string) [text, markdown] + - ds_ids (array) — Specific data source IDs. + - ds_list (array) — Data source name patterns (supports wildcards). + - ds_type (string) — Data source type. + - enabled (boolean) + - enabled_times (array) — Time windows when the rule is active. + - days (array) — Days of week (0=Sunday). + - etime (string) — End time, e.g. '18:00'. + - stime (string) — Start time, e.g. '09:00'. + - folder_id (integer) — Folder the rule belongs to. + - id (integer) + - labels (object) — Custom labels. + - name (string) — Rule name. + - repeat_interval (integer) — Notification repeat interval in seconds. + - repeat_total (integer) — Max number of repeat notifications. + - rule_configs (object) — Rule evaluation configuration. + - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery (object) — Recovery condition for any-data check. If omitted or 'mode' is empty, treated as 'nodata'. + - args (object) + - condition (string) — Recovery expression. Required when 'mode' is 'ql'. + - mode (string) — 'nodata' = recover when the query returns no data; 'ql' = recover when the 'condition' expression evaluates to true. When 'mode' is 'ql', only a single query ('name=A') is permitted. [nodata, ql] + - recovery_check_times (integer) + - severity (string) [Critical, Warning, Info] + - check_nodata (object) — No-data check configuration. + - alerting_check_times (integer) + - enabled (boolean) + - push_recovery_event (boolean) + - recovery_check_times (integer) + - resolve_timeout (integer) — Auto-resolve after N seconds. + - severity (string) [Critical, Warning, Info] + - check_threshold (object) — Threshold check configuration. + - alerting_check_times (integer) + - critical (string) + - enabled (boolean) + - info (string) + - push_recovery_event (boolean) + - recovery (object) + - condition (string) + - mode (string) [invert, threshold, ql] + - recovery_check_times (integer) + - warning (string) + - queries (array) + - args (object) + - expr (string) — Query expression. + - label_fields (array) + - name (string) — Query identifier (letter, e.g. 'A'). The name 'R' is reserved and must not be used. + - value_fields (array) + - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique 'name' (not duplicating any query name) and a non-empty 'expr'. + - args (object) + - expr (string) — Query expression. + - name (string) — Relate-query identifier. + - updated_at (integer) + - updater_id (integer) + - updater_name (string) +`, + Example: ` flashduty monit rule-update --data '{"cron_pattern":"* * * * *","ds_list":["prometheus*"],"ds_type":"prometheus","enabled":true,"folder_id":100,"id":50001,"name":"CPU High v2","rule_configs":{"queries":[{"expr":"avg(cpu_usage_idle) \u003c 5","name":"A"}]}}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("account-id") { + body["account_id"] = fAccountID + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("created-at") { + body["created_at"] = fCreatedAt + } + if cmd.Flags().Changed("creator-id") { + body["creator_id"] = fCreatorID + } + if cmd.Flags().Changed("creator-name") { + body["creator_name"] = fCreatorName + } + if cmd.Flags().Changed("cron-pattern") { + body["cron_pattern"] = fCronPattern + } + if cmd.Flags().Changed("debug-log-enabled") { + body["debug_log_enabled"] = fDebugLogEnabled + } + if cmd.Flags().Changed("delay-seconds") { + body["delay_seconds"] = fDelaySeconds + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("description-type") { + body["description_type"] = fDescriptionType + } + if cmd.Flags().Changed("ds-ids") { + body["ds_ids"] = fDsIDs + } + if cmd.Flags().Changed("ds-list") { + body["ds_list"] = fDsList + } + if cmd.Flags().Changed("ds-type") { + body["ds_type"] = fDsType + } + if cmd.Flags().Changed("enabled") { + body["enabled"] = fEnabled + } + if cmd.Flags().Changed("folder-id") { + body["folder_id"] = fFolderID + } + if cmd.Flags().Changed("id") { + body["id"] = fID + } + if cmd.Flags().Changed("name") { + body["name"] = fName + } + if cmd.Flags().Changed("repeat-interval") { + body["repeat_interval"] = fRepeatInterval + } + if cmd.Flags().Changed("repeat-total") { + body["repeat_total"] = fRepeatTotal + } + if cmd.Flags().Changed("updated-at") { + body["updated_at"] = fUpdatedAt + } + if cmd.Flags().Changed("updater-id") { + body["updater_id"] = fUpdaterID + } + if cmd.Flags().Changed("updater-name") { + body["updater_name"] = fUpdaterName + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertRule) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AlertRules.WriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Request field account_id") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Channel IDs to send alerts to.") + cmd.Flags().Int64Var(&fCreatedAt, "created-at", 0, "Request field created_at") + cmd.Flags().Int64Var(&fCreatorID, "creator-id", 0, "Request field creator_id") + cmd.Flags().StringVar(&fCreatorName, "creator-name", "", "Request field creator_name") + cmd.Flags().StringVar(&fCronPattern, "cron-pattern", "", "5-field cron schedule.") + cmd.Flags().BoolVar(&fDebugLogEnabled, "debug-log-enabled", false, "Request field debug_log_enabled") + cmd.Flags().Int64Var(&fDelaySeconds, "delay-seconds", 0, "Request field delay_seconds") + cmd.Flags().StringVar(&fDescription, "description", "", "Request field description") + cmd.Flags().StringVar(&fDescriptionType, "description-type", "", "Request field description_type [text, markdown]") + cmd.Flags().IntSliceVar(&fDsIDs, "ds-ids", nil, "Specific data source IDs.") + cmd.Flags().StringSliceVar(&fDsList, "ds-list", nil, "Data source name patterns (supports wildcards).") + cmd.Flags().StringVar(&fDsType, "ds-type", "", "Data source type.") + cmd.Flags().BoolVar(&fEnabled, "enabled", false, "Request field enabled") + cmd.Flags().Int64Var(&fFolderID, "folder-id", 0, "Folder the rule belongs to.") + cmd.Flags().Int64Var(&fID, "id", 0, "Request field id") + cmd.Flags().StringVar(&fName, "name", "", "Rule name.") + cmd.Flags().Int64Var(&fRepeatInterval, "repeat-interval", 0, "Notification repeat interval in seconds.") + cmd.Flags().Int64Var(&fRepeatTotal, "repeat-total", 0, "Max number of repeat notifications.") + cmd.Flags().Int64Var(&fUpdatedAt, "updated-at", 0, "Request field updated_at") + cmd.Flags().Int64Var(&fUpdaterID, "updater-id", 0, "Request field updater_id") + cmd.Flags().StringVar(&fUpdaterName, "updater-name", "", "Request field updater_name") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedAlertRules(root *cobra.Command) { + gMonit := genGroup(root, "monit", "Monitors API") + genAddLeaf(gMonit, genAlertRulesReadAuditDetailCmd()) + genAddLeaf(gMonit, genAlertRulesReadAuditsCmd()) + genAddLeaf(gMonit, genAlertRulesReadCounterChannelCmd()) + genAddLeaf(gMonit, genAlertRulesReadCounterNodeCmd()) + genAddLeaf(gMonit, genAlertRulesReadCounterStatusCmd()) + genAddLeaf(gMonit, genAlertRulesReadCounterTotalCmd()) + genAddLeaf(gMonit, genAlertRulesReadDstypesCmd()) + genAddLeaf(gMonit, genAlertRulesReadExportCmd()) + genAddLeaf(gMonit, genAlertRulesReadInfoCmd()) + genAddLeaf(gMonit, genAlertRulesReadListCmd()) + genAddLeaf(gMonit, genAlertRulesWriteCreateCmd()) + genAddLeaf(gMonit, genAlertRulesWriteDeleteCmd()) + genAddLeaf(gMonit, genAlertRulesWriteDeleteBatchCmd()) + genAddLeaf(gMonit, genAlertRulesWriteFieldsUpdateCmd()) + genAddLeaf(gMonit, genAlertRulesWriteImportCmd()) + genAddLeaf(gMonit, genAlertRulesWriteMoveCmd()) + genAddLeaf(gMonit, genAlertRulesWriteStatusCmd()) + genAddLeaf(gMonit, genAlertRulesWriteUpdateCmd()) +} diff --git a/internal/cli/zz_generated_alerts.go b/internal/cli/zz_generated_alerts.go new file mode 100644 index 0000000..c04557c --- /dev/null +++ b/internal/cli/zz_generated_alerts.go @@ -0,0 +1,947 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genAlertsEventReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fChannelIDs []int + var fEndTime int64 + var fIntegrationIDs []int + var fIntegrationTypes []string + var fOrderby string + var fSeverities string + var fStartTime int64 + cmd := &cobra.Command{ + Use: "list", + Short: "List raw alert events", + Long: `List raw alert events. + +Return a cursor-paginated list of raw alert events across all alerts, with filtering by integration, channel, time range, and severity. + +API: POST /alert-event/list (alert-event-read-list) + +Request fields: + --page int — Page number, starting at 1. Used when 'search_after_ctx' is not provided. + --limit int — Page size, max 100, default 20. + --search-after-ctx string — Opaque cursor for the next page. + --asc bool — Sort ascending when 'true'. + --channel-ids []int — Filter by channel IDs. Max 100. + --end-time int — End of search window, Unix epoch seconds. + --integration-ids []int — Filter by integration IDs. + --integration-types []string — Filter by integration types (plugin keys). + --orderby string — Sort field (ES field name). [event_time] + --severities string — Comma-separated severity filter, e.g. 'Critical,Warning'. + --start-time int — Start of search window, Unix epoch seconds. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) + - items (array) + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - search_after_ctx (string) + - total (integer) +`, + Example: ` flashduty alert-event list --data '{"end_time":1712707200,"limit":20,"severities":"Critical","start_time":1712620800}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("integration-ids") { + body["integration_ids"] = fIntegrationIDs + } + if cmd.Flags().Changed("integration-types") { + body["integration_types"] = fIntegrationTypes + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertEventGlobalListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.EventReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1. Used when 'search_after_ctx' is not provided.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, max 100, default 20.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque cursor for the next page.") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true'.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. Max 100.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End of search window, Unix epoch seconds.") + cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "Filter by integration IDs.") + cmd.Flags().StringSliceVar(&fIntegrationTypes, "integration-types", nil, "Filter by integration types (plugin keys).") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field (ES field name). [event_time]") + cmd.Flags().StringVar(&fSeverities, "severities", "", "Comma-separated severity filter, e.g. 'Critical,Warning'.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start of search window, Unix epoch seconds.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsReadEventListCmd() *cobra.Command { + var dataJSON string + var fAlertID string + cmd := &cobra.Command{ + Use: "event-list", + Short: "List events for an alert", + Long: `List events for an alert. + +Return all raw events that have been ingested into a specific alert, in chronological order. + +API: POST /alert/event/list (alert-read-event-list) + +Request fields: + --alert-id string (required) — Alert ID (ObjectID hex string). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. +`, + Example: ` flashduty alert event-list --data '{"alert_id":"663a1b2c3d4e5f6789abcdef"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("alert-id") { + body["alert_id"] = fAlertID + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertEventListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.ReadEventList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAlertID, "alert-id", "", "Alert ID (ObjectID hex string). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsReadFeedCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAlertID string + var fAsc bool + var fTypes []string + cmd := &cobra.Command{ + Use: "feed", + Short: "List alert activity feed", + Long: `List alert activity feed. + +Return the activity feed (comments, state changes, merges, silence events) for a single alert, with page-based pagination. + +API: POST /alert/feed (alert-read-feed) + +Request fields: + --page int — Page number, starting at 1. + --limit int — Page size, max 100, default 20. + --search-after-ctx string + --alert-id string (required) — Alert ID. + --asc bool — Sort ascending. + --types []string — Filter by feed types. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) + - items (array) + - account_id (integer) (required) — Account ID. + - created_at (integer) (required) — Creation timestamp in Unix epoch milliseconds. + - creator_id (integer) (required) — Member ID of the creator. 0 for system-generated entries. + - detail (any) (required) — Type-specific payload. The concrete shape is determined by 'type'. + - ref_id (string) (required) — ObjectID of the alert this entry references. + - type (string) (required) — Alert activity feed entry type. Each value identifies one alert lifecycle event; the matching 'detail' payload shape is determined by this field. | Type | Meaning | |---|---| | 'a_new' | Alert triggered. | | 'a_comm' | Comment added on the alert. | | 'a_close' | Alert closed. | [a_new, a_comm, a_close] + - updated_at (integer) (required) — Last update timestamp in Unix epoch milliseconds. +`, + Example: ` flashduty alert feed --data '{"alert_id":"663a1b2c3d4e5f6789abcdef","asc":false,"limit":20}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("alert-id") { + body["alert_id"] = fAlertID + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("types") { + body["types"] = fTypes + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertFeedRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.ReadFeed(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, max 100, default 20.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().StringVar(&fAlertID, "alert-id", "", "Alert ID. (required)") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending.") + cmd.Flags().StringSliceVar(&fTypes, "types", nil, "Filter by feed types.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsReadInfoCmd() *cobra.Command { + var dataJSON string + var fAlertID string + cmd := &cobra.Command{ + Use: "info", + Short: "Get alert detail", + Long: `Get alert detail. + +Return the full details of a single alert by its ID, including its associated incident and event count. + +API: POST /alert/info (alert-read-info) + +Request fields: + --alert-id string (required) — Alert ID (ObjectID hex string). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) — Account ID. + - alert_id (string) — Unique alert ID (ObjectID hex string). + - alert_key (string) — Deduplication key. + - alert_severity (string) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) — ID of the channel the alert belongs to. + - channel_name (string) — Display name of the channel. + - channel_status (string) — Status of the channel (e.g. 'enabled', 'disabled'). + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. Deprecated: use 'integration_id' instead. + - data_source_name (string) — Deprecated. Use 'integration_name' instead. + - data_source_ref_id (string) — Deprecated. Use 'integration_ref_id' instead. + - data_source_type (string) — Deprecated. Use 'integration_type' instead. + - description (string) — Alert description. + - end_time (integer) — Resolution time, Unix epoch seconds. 0 if still active. + - event_cnt (integer) — Total number of raw events received by this alert. + - events (array) — Recent raw events attached to this alert. Populated only by some endpoints. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) — True if this alert has ever been silenced. + - images (array) — Images attached to the alert. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) — ID of the integration that produced this alert. + - integration_name (string) — Display name of the integration. + - integration_ref_id (string) — External reference ID of the integration. + - integration_type (string) — Type/plugin key of the integration. + - labels (object) — Label key-value pairs. + - last_time (integer) — Last-event time, Unix epoch seconds. + - responder_email (string) — Email of the current responder (from the associated incident). + - responder_name (string) — Display name of the current responder (from the associated incident). + - start_time (integer) — First-seen time, Unix epoch seconds. + - title (string) — Alert title. + - title_rule (string) — Title template used to derive 'title' from the event labels (e.g. '$service::$cluster'). + - updated_at (integer) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty alert info --data '{"alert_id":"663a1b2c3d4e5f6789abcdef"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("alert-id") { + body["alert_id"] = fAlertID + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAlertID, "alert-id", "", "Alert ID (ObjectID hex string). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAlertIDs []string + var fAlertKeys []string + var fAlertSeverity string + var fAsc bool + var fByUpdatedAt bool + var fChannelIDs []int + var fEndTime int64 + var fEverMuted bool + var fIntegrationIDs []int + var fIsActive bool + var fOrderby string + var fStartTime int64 + cmd := &cobra.Command{ + Use: "list", + Short: "List alerts", + Long: `List alerts. + +Return a cursor-paginated list of alerts matching the given filters. + +API: POST /alert/list (alert-read-list) + +Request fields: + --page int — Page number, starting at 1. Used when 'search_after_ctx' is not provided. + --limit int — Page size. Max 100, default 20. + --search-after-ctx string — Opaque cursor from the previous response for the next page. + --alert-ids []string — Filter to specific alert IDs (ObjectID hex strings). + --alert-keys []string — Filter by alert deduplication keys. + --alert-severity string — Comma-separated severity filter, e.g. 'Critical,Warning'. Allowed values: 'Critical', 'Warning', 'Info', 'Ok'. + --asc bool — Sort ascending when 'true'. Default descending. + --by-updated-at bool — When 'true', the time range filter is applied on 'updated_at' rather than 'start_time'. + --channel-ids []int — Filter by channel IDs. + --end-time int (required) — End of the search window, Unix epoch seconds. Max span 31 days. + --ever-muted bool — Filter by whether the alert has ever been silenced. + --integration-ids []int — Filter by integration IDs. + --is-active bool — Filter by active ('true') or resolved ('false') status. + --orderby string — Sort field. [created_at, updated_at] + --start-time int (required) — Start of the search window, Unix epoch seconds. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) — True if more pages are available. + - items (array) + - account_id (integer) — Account ID. + - alert_id (string) — Unique alert ID (ObjectID hex string). + - alert_key (string) — Deduplication key. + - alert_severity (string) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) — ID of the channel the alert belongs to. + - channel_name (string) — Display name of the channel. + - channel_status (string) — Status of the channel (e.g. 'enabled', 'disabled'). + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. Deprecated: use 'integration_id' instead. + - data_source_name (string) — Deprecated. Use 'integration_name' instead. + - data_source_ref_id (string) — Deprecated. Use 'integration_ref_id' instead. + - data_source_type (string) — Deprecated. Use 'integration_type' instead. + - description (string) — Alert description. + - end_time (integer) — Resolution time, Unix epoch seconds. 0 if still active. + - event_cnt (integer) — Total number of raw events received by this alert. + - events (array) — Recent raw events attached to this alert. Populated only by some endpoints. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) — True if this alert has ever been silenced. + - images (array) — Images attached to the alert. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) — ID of the integration that produced this alert. + - integration_name (string) — Display name of the integration. + - integration_ref_id (string) — External reference ID of the integration. + - integration_type (string) — Type/plugin key of the integration. + - labels (object) — Label key-value pairs. + - last_time (integer) — Last-event time, Unix epoch seconds. + - responder_email (string) — Email of the current responder (from the associated incident). + - responder_name (string) — Display name of the current responder (from the associated incident). + - start_time (integer) — First-seen time, Unix epoch seconds. + - title (string) — Alert title. + - title_rule (string) — Title template used to derive 'title' from the event labels (e.g. '$service::$cluster'). + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - search_after_ctx (string) — Cursor for the next page. + - total (integer) — Total matching alerts. +`, + Example: ` flashduty alert list --data '{"end_time":1712707200,"is_active":true,"limit":20,"start_time":1712620800}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("alert-ids") { + body["alert_ids"] = fAlertIDs + } + if cmd.Flags().Changed("alert-keys") { + body["alert_keys"] = fAlertKeys + } + if cmd.Flags().Changed("alert-severity") { + body["alert_severity"] = fAlertSeverity + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("by-updated-at") { + body["by_updated_at"] = fByUpdatedAt + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("ever-muted") { + body["ever_muted"] = fEverMuted + } + if cmd.Flags().Changed("integration-ids") { + body["integration_ids"] = fIntegrationIDs + } + if cmd.Flags().Changed("is-active") { + body["is_active"] = fIsActive + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1. Used when 'search_after_ctx' is not provided.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Max 100, default 20.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque cursor from the previous response for the next page.") + cmd.Flags().StringSliceVar(&fAlertIDs, "alert-ids", nil, "Filter to specific alert IDs (ObjectID hex strings).") + cmd.Flags().StringSliceVar(&fAlertKeys, "alert-keys", nil, "Filter by alert deduplication keys.") + cmd.Flags().StringVar(&fAlertSeverity, "alert-severity", "", "Comma-separated severity filter, e.g. 'Critical,Warning'. Allowed values: 'Critical', 'Warning', 'Info', 'Ok'.") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true'. Default descending.") + cmd.Flags().BoolVar(&fByUpdatedAt, "by-updated-at", false, "When 'true', the time range filter is applied on 'updated_at' rather than 'start_time'.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End of the search window, Unix epoch seconds. Max span 31 days. (required)") + cmd.Flags().BoolVar(&fEverMuted, "ever-muted", false, "Filter by whether the alert has ever been silenced.") + cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "Filter by integration IDs.") + cmd.Flags().BoolVar(&fIsActive, "is-active", false, "Filter by active ('true') or resolved ('false') status.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start of the search window, Unix epoch seconds. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsReadListByIDsCmd() *cobra.Command { + var dataJSON string + var fAlertIDs []string + cmd := &cobra.Command{ + Use: "list-by-ids", + Short: "List alerts by IDs", + Long: `List alerts by IDs. + +Return the details of multiple alerts by their IDs in a single request. + +API: POST /alert/list-by-ids (alert-read-list-by-ids) + +Request fields: + --alert-ids []string (required) — List of alert IDs (ObjectID hex strings). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) — True if more pages are available. + - items (array) + - account_id (integer) — Account ID. + - alert_id (string) — Unique alert ID (ObjectID hex string). + - alert_key (string) — Deduplication key. + - alert_severity (string) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) — ID of the channel the alert belongs to. + - channel_name (string) — Display name of the channel. + - channel_status (string) — Status of the channel (e.g. 'enabled', 'disabled'). + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. Deprecated: use 'integration_id' instead. + - data_source_name (string) — Deprecated. Use 'integration_name' instead. + - data_source_ref_id (string) — Deprecated. Use 'integration_ref_id' instead. + - data_source_type (string) — Deprecated. Use 'integration_type' instead. + - description (string) — Alert description. + - end_time (integer) — Resolution time, Unix epoch seconds. 0 if still active. + - event_cnt (integer) — Total number of raw events received by this alert. + - events (array) — Recent raw events attached to this alert. Populated only by some endpoints. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) — True if this alert has ever been silenced. + - images (array) — Images attached to the alert. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) — ID of the integration that produced this alert. + - integration_name (string) — Display name of the integration. + - integration_ref_id (string) — External reference ID of the integration. + - integration_type (string) — Type/plugin key of the integration. + - labels (object) — Label key-value pairs. + - last_time (integer) — Last-event time, Unix epoch seconds. + - responder_email (string) — Email of the current responder (from the associated incident). + - responder_name (string) — Display name of the current responder (from the associated incident). + - start_time (integer) — First-seen time, Unix epoch seconds. + - title (string) — Alert title. + - title_rule (string) — Title template used to derive 'title' from the event labels (e.g. '$service::$cluster'). + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - search_after_ctx (string) — Cursor for the next page. + - total (integer) — Total matching alerts. +`, + Example: ` flashduty alert list-by-ids --data '{"alert_ids":["663a1b2c3d4e5f6789abcdef"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("alert-ids") { + body["alert_ids"] = fAlertIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertListByIDsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.ReadListByIDs(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringSliceVar(&fAlertIDs, "alert-ids", nil, "List of alert IDs (ObjectID hex strings). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsReadPipelineInfoCmd() *cobra.Command { + var dataJSON string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "pipeline-info", + Short: "Get alert pipeline", + Long: `Get alert pipeline. + +Return the alert processing pipeline configured for a specific integration. + +API: POST /alert/pipeline/info (alert-read-pipeline-info) + +Request fields: + --integration-id int (required) — Integration ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - creator_id (integer) — Member ID who created the pipeline. + - integration_id (integer) — Integration ID this pipeline applies to. + - rules (array) — Ordered list of processing rules. + - if (array) — OR-of-AND filter tree. Outer array is a list of AND groups; the condition passes if **any** AND group matches. Within each AND group, **all** conditions must match. + - kind (string) — Rule type. [title_reset, description_reset, severity_reset, alert_drop, alert_inhibit] + - settings (object) — Kind-specific settings. Shape depends on 'kind': - 'title_reset': '{ "title": "" }' - 'description_reset': '{ "description": "" }' - 'severity_reset': '{ "severity": "Critical"|"Warning"|"Info" }' - 'alert_drop': '{}' (empty object) - 'alert_inhibit': '{ "equals": ["", ...], "source_filters": }' + - status (string) — Pipeline status. Possible values: 'enabled', 'disabled'. + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - updated_by (integer) — Member ID who last updated the pipeline. +`, + Example: ` flashduty alert pipeline-info --data '{"integration_id":10001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertPipelineInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.ReadPipelineInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsReadPipelineListCmd() *cobra.Command { + var dataJSON string + var fIntegrationIDs []int + cmd := &cobra.Command{ + Use: "pipeline-list", + Short: "List alert pipelines", + Long: `List alert pipelines. + +Return the alert processing pipelines configured for multiple integrations. + +API: POST /alert/pipeline/list (alert-read-pipeline-list) + +Request fields: + --integration-ids []int (required) — Integration IDs. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - creator_id (integer) — Member ID who created the pipeline. + - integration_id (integer) — Integration ID this pipeline applies to. + - rules (array) — Ordered list of processing rules. + - if (array) — OR-of-AND filter tree. Outer array is a list of AND groups; the condition passes if **any** AND group matches. Within each AND group, **all** conditions must match. + - kind (string) — Rule type. [title_reset, description_reset, severity_reset, alert_drop, alert_inhibit] + - settings (object) — Kind-specific settings. Shape depends on 'kind': - 'title_reset': '{ "title": "" }' - 'description_reset': '{ "description": "" }' - 'severity_reset': '{ "severity": "Critical"|"Warning"|"Info" }' - 'alert_drop': '{}' (empty object) - 'alert_inhibit': '{ "equals": ["", ...], "source_filters": }' + - status (string) — Pipeline status. Possible values: 'enabled', 'disabled'. + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - updated_by (integer) — Member ID who last updated the pipeline. +`, + Example: ` flashduty alert pipeline-list --data '{"integration_ids":[10001,10002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-ids") { + body["integration_ids"] = fIntegrationIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertPipelineListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Alerts.ReadPipelineList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "Integration IDs. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsWriteMergeCmd() *cobra.Command { + var dataJSON string + var fAlertIDs []string + var fComment string + var fIncidentID string + var fOwnerID int64 + var fTitle string + cmd := &cobra.Command{ + Use: "merge", + Short: "Merge alerts into an incident", + Long: `Merge alerts into an incident. + +Associate one or more alerts with an existing incident. If a source alert previously belonged to a different incident and that incident becomes empty after the merge, it will be automatically closed. + +API: POST /alert/merge (alert-write-merge) + +Request fields: + --alert-ids []string (required) — Alert IDs to merge. + --comment string — Optional comment on the merge action. + --incident-id string (required) — Target incident ID. + --owner-id int — Optional new owner for the target incident. + --title string — Optional new title for the target incident. +`, + Example: ` flashduty alert merge --data '{"alert_ids":["663a1b2c3d4e5f6789abcdef"],"incident_id":"663a000000000000deadbeef"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("alert-ids") { + body["alert_ids"] = fAlertIDs + } + if cmd.Flags().Changed("comment") { + body["comment"] = fComment + } + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("owner-id") { + body["owner_id"] = fOwnerID + } + if cmd.Flags().Changed("title") { + body["title"] = fTitle + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertMergeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Alerts.WriteMerge(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /alert/merge") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fAlertIDs, "alert-ids", nil, "Alert IDs to merge. (required)") + cmd.Flags().StringVar(&fComment, "comment", "", "Optional comment on the merge action.") + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Target incident ID. (required)") + cmd.Flags().Int64Var(&fOwnerID, "owner-id", 0, "Optional new owner for the target incident.") + cmd.Flags().StringVar(&fTitle, "title", "", "Optional new title for the target incident.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAlertsWritePipelineUpsertCmd() *cobra.Command { + var dataJSON string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "pipeline-upsert", + Short: "Create or update alert pipeline", + Long: `Create or update alert pipeline. + +Set the alert processing pipeline for an integration. Replaces the existing configuration entirely. + +API: POST /alert/pipeline/upsert (alert-write-pipeline-upsert) + +Request fields: + --integration-id int (required) — Integration ID to configure. + rules (array, via --data) (required) — Rules to apply. Max 50. + - if (array) — OR-of-AND filter tree. Outer array is a list of AND groups; the condition passes if **any** AND group matches. Within each AND group, **all** conditions must match. + - kind (string) — Rule type. [title_reset, description_reset, severity_reset, alert_drop, alert_inhibit] + - settings (object) — Kind-specific settings. Shape depends on 'kind': - 'title_reset': '{ "title": "" }' - 'description_reset': '{ "description": "" }' - 'severity_reset': '{ "severity": "Critical"|"Warning"|"Info" }' - 'alert_drop': '{}' (empty object) - 'alert_inhibit': '{ "equals": ["", ...], "source_filters": }' +`, + Example: ` flashduty alert pipeline-upsert --data '{"integration_id":10001,"rules":[{"if":null,"kind":"severity_reset","settings":{"severity":"Warning"}}]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.AlertPipelineUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Alerts.WritePipelineUpsert(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /alert/pipeline/upsert") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID to configure. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedAlerts(root *cobra.Command) { + gAlertEvent := genGroup(root, "alert-event", "On-call/Alerts API") + genAddLeaf(gAlertEvent, genAlertsEventReadListCmd()) + gAlert := genGroup(root, "alert", "On-call/Alerts API") + genAddLeaf(gAlert, genAlertsReadEventListCmd()) + genAddLeaf(gAlert, genAlertsReadFeedCmd()) + genAddLeaf(gAlert, genAlertsReadInfoCmd()) + genAddLeaf(gAlert, genAlertsReadListCmd()) + genAddLeaf(gAlert, genAlertsReadListByIDsCmd()) + genAddLeaf(gAlert, genAlertsReadPipelineInfoCmd()) + genAddLeaf(gAlert, genAlertsReadPipelineListCmd()) + genAddLeaf(gAlert, genAlertsWriteMergeCmd()) + genAddLeaf(gAlert, genAlertsWritePipelineUpsertCmd()) +} diff --git a/internal/cli/zz_generated_analytics.go b/internal/cli/zz_generated_analytics.go new file mode 100644 index 0000000..3b93736 --- /dev/null +++ b/internal/cli/zz_generated_analytics.go @@ -0,0 +1,1807 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genAnalyticsByAccountCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "account", + Short: "Get account-level insight", + Long: `Get account-level insight. + +Return aggregated incident insight metrics for the entire account. + +API: POST /insight/account (insightByAccount) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - account_id (integer) + - acknowledgement_pct (number) + - channel_id (integer) + - channel_name (string) + - hours (string) — Hour bucket when 'split_hours' is enabled. [work, sleep, off] + - mean_seconds_to_ack (number) + - mean_seconds_to_close (number) + - noise_reduction_pct (number) + - responder_id (integer) + - responder_name (string) + - team_id (integer) + - team_name (string) + - total_alert_cnt (integer) + - total_alert_event_cnt (integer) + - total_engaged_seconds (integer) + - total_incident_cnt (integer) + - total_incidents_acknowledged (integer) + - total_incidents_auto_closed (integer) + - total_incidents_closed (integer) + - total_incidents_escalated (integer) + - total_incidents_manually_closed (integer) + - total_incidents_manually_escalated (integer) + - total_incidents_reassigned (integer) + - total_incidents_timeout_closed (integer) + - total_incidents_timeout_escalated (integer) + - total_interruptions (integer) + - total_notifications (integer) + - total_seconds_to_ack (integer) + - total_seconds_to_close (integer) + - ts (integer) — Aggregation bucket start time, Unix seconds. Present when 'aggregate_unit' is used. +`, + Example: ` flashduty insight account --data '{"aggregate_unit":"day","end_time":1712604800,"severities":["Critical","Warning"],"start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightQueryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Analytics.ByAccount(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsByChannelCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "channel", + Short: "Get channel insight", + Long: `Get channel insight. + +Return insight metrics aggregated by channel. + +API: POST /insight/channel (insightByChannel) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - account_id (integer) + - acknowledgement_pct (number) + - channel_id (integer) + - channel_name (string) + - hours (string) — Hour bucket when 'split_hours' is enabled. [work, sleep, off] + - mean_seconds_to_ack (number) + - mean_seconds_to_close (number) + - noise_reduction_pct (number) + - responder_id (integer) + - responder_name (string) + - team_id (integer) + - team_name (string) + - total_alert_cnt (integer) + - total_alert_event_cnt (integer) + - total_engaged_seconds (integer) + - total_incident_cnt (integer) + - total_incidents_acknowledged (integer) + - total_incidents_auto_closed (integer) + - total_incidents_closed (integer) + - total_incidents_escalated (integer) + - total_incidents_manually_closed (integer) + - total_incidents_manually_escalated (integer) + - total_incidents_reassigned (integer) + - total_incidents_timeout_closed (integer) + - total_incidents_timeout_escalated (integer) + - total_interruptions (integer) + - total_notifications (integer) + - total_seconds_to_ack (integer) + - total_seconds_to_close (integer) + - ts (integer) — Aggregation bucket start time, Unix seconds. Present when 'aggregate_unit' is used. +`, + Example: ` flashduty insight channel --data '{"aggregate_unit":"day","channel_ids":[4321322010131],"end_time":1712604800,"start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightQueryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Analytics.ByChannel(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsByResponderCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "responder", + Short: "Get responder insight", + Long: `Get responder insight. + +Return insight metrics aggregated by responder. + +API: POST /insight/responder (insightByResponder) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - account_id (integer) + - acknowledgement_pct (number) + - channel_id (integer) + - channel_name (string) + - hours (string) — Hour bucket when 'split_hours' is enabled. [work, sleep, off] + - mean_seconds_to_ack (number) + - responder_id (integer) + - responder_name (string) + - team_id (integer) + - team_name (string) + - total_engaged_seconds (integer) + - total_incident_cnt (integer) + - total_incidents_acknowledged (integer) + - total_incidents_escalated (integer) + - total_incidents_manually_escalated (integer) + - total_incidents_reassigned (integer) + - total_incidents_timeout_escalated (integer) + - total_interruptions (integer) + - total_notifications (integer) + - total_seconds_to_ack (integer) + - ts (integer) — Aggregation bucket start time, Unix seconds. Present when 'aggregate_unit' is used. +`, + Example: ` flashduty insight responder --data '{"aggregate_unit":"day","end_time":1712604800,"responder_ids":[3790925372131],"start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightQueryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Analytics.ByResponder(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsByTeamCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "team", + Short: "Get team insight", + Long: `Get team insight. + +Return insight metrics aggregated by team. + +API: POST /insight/team (insightByTeam) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - account_id (integer) + - acknowledgement_pct (number) + - channel_id (integer) + - channel_name (string) + - hours (string) — Hour bucket when 'split_hours' is enabled. [work, sleep, off] + - mean_seconds_to_ack (number) + - mean_seconds_to_close (number) + - noise_reduction_pct (number) + - responder_id (integer) + - responder_name (string) + - team_id (integer) + - team_name (string) + - total_alert_cnt (integer) + - total_alert_event_cnt (integer) + - total_engaged_seconds (integer) + - total_incident_cnt (integer) + - total_incidents_acknowledged (integer) + - total_incidents_auto_closed (integer) + - total_incidents_closed (integer) + - total_incidents_escalated (integer) + - total_incidents_manually_closed (integer) + - total_incidents_manually_escalated (integer) + - total_incidents_reassigned (integer) + - total_incidents_timeout_closed (integer) + - total_incidents_timeout_escalated (integer) + - total_interruptions (integer) + - total_notifications (integer) + - total_seconds_to_ack (integer) + - total_seconds_to_close (integer) + - ts (integer) — Aggregation bucket start time, Unix seconds. Present when 'aggregate_unit' is used. +`, + Example: ` flashduty insight team --data '{"aggregate_unit":"day","end_time":1712604800,"start_time":1712000000,"team_ids":[4295771902131]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightQueryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Analytics.ByTeam(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsChannelExportCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "channel-export", + Short: "Export channel insight", + Long: `Export channel insight. + +Export channel insight metrics as a CSV file. The response is a CSV stream delivered with 'Content-Disposition: attachment' — it is not a JSON envelope. + +API: POST /insight/channel/export (insightChannelExport) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). +`, + Example: ` flashduty insight channel-export --data '{"channel_ids":[4321322010131],"end_time":1712604800,"severities":["Critical","Warning"],"start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightQueryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Analytics.ChannelExport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /insight/channel/export") + return nil + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsIncidentExportCmd() *cobra.Command { + var dataJSON string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "incident-export", + Short: "Export insight incidents", + Long: `Export insight incidents. + +Export the filtered incident analytics list as a CSV file. The response is a CSV stream delivered with 'Content-Disposition: attachment' — it is not a JSON envelope. + +API: POST /insight/incident/export (insightIncidentExport) + +Request fields: + --asc bool + --channel-ids []int + --description-html-to-text bool + --end-time int + --export-fields []string + --incident-ids []string + --is-my-team bool + --orderby string + --query string + --responder-ids []int + --seconds-to-ack-from int + --seconds-to-ack-to int + --seconds-to-close-from int + --seconds-to-close-to int + --severities []string + --start-time int + --team-ids []int + --time-zone string + fields (JSON, via --data) + labels (JSON, via --data) +`, + Example: ` flashduty insight incident-export --data '{"description_html_to_text":true,"end_time":1712604800,"export_fields":["incident_id","title","severity","created_at","seconds_to_close"],"severities":["Critical","Warning"],"start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightFilter) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Analytics.IncidentExport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /insight/incident/export") + return nil + }) + }, + } + cmd.Flags().BoolVar(&fAsc, "asc", false, "Request field ") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Request field ") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Request field ") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "Request field ") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Request field ") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Request field ") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Request field ") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Request field ") + cmd.Flags().StringVar(&fQuery, "query", "", "Request field ") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Request field ") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Request field ") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Request field ") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Request field ") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Request field ") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Request field ") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Request field ") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Request field ") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "Request field ") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsIncidentListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "incident-list", + Short: "List insight incidents", + Long: `List insight incidents. + +Return a paged list of incidents with per-incident handling metrics used by the analytics dashboard. + +API: POST /insight/incident/list (insightIncidentList) + +Request fields: + --page int — Page number, starting at 1. Defaults to 1. (min 1) + --limit int — Page size, between 1 and 100. Defaults to 20. (1-100) + --search-after-ctx string — Cursor token returned by a previous page. Pass it back to fetch the next page. + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) + - items (array) + - acknowledgements (integer) + - assigned_to (object) — Current assignment target for the incident. + - assigned_at (integer) — Unix timestamp (seconds) when this assignment was made. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) driving the assignment. + - escalate_rule_name (string) — Display name of the escalation rule. + - id (string) — Internal assignment record ID. + - layer_idx (integer) — Current level index within the escalation rule. + - person_ids (array) — Member IDs assigned directly to this incident. + - type (string) — Assignment type. [assign, reassign, escalate, reopen] + - assignments (integer) + - channel_id (integer) + - channel_name (string) + - closed_by (string) [auto, timeout, manually] + - created_at (integer) + - creator_id (integer) + - creator_name (string) + - description (string) + - engaged_seconds (integer) + - escalations (integer) + - fields (object) + - hours (string) + - incident_id (string) + - interruptions (integer) + - labels (object) + - manual_escalations (integer) + - notifications (integer) + - progress (string) — Incident progress state — one of 'Triggered', 'Processing', 'Closed'. + - reassignments (integer) + - responders (array) + - seconds_to_ack (integer) + - seconds_to_close (integer) + - severity (string) [Critical, Warning, Info, Ok] + - team_id (integer) + - team_name (string) + - timeout_escalations (integer) + - title (string) + - search_after_ctx (string) — Cursor token to fetch the next page. Pass it back in the next request's 'search_after_ctx'. + - total (integer) — Total matching incidents. +`, + Example: ` flashduty insight incident-list --data '{"end_time":1712604800,"limit":20,"p":1,"severities":["Critical"],"start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightIncidentListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Analytics.IncidentList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1. Defaults to 1. (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, between 1 and 100. Defaults to 20. (1-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Cursor token returned by a previous page. Pass it back to fetch the next page.") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsResponderExportCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "responder-export", + Short: "Export responder insight", + Long: `Export responder insight. + +Export responder insight metrics as a CSV file. The response is a CSV stream delivered with 'Content-Disposition: attachment' — it is not a JSON envelope. + +API: POST /insight/responder/export (insightResponderExport) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). +`, + Example: ` flashduty insight responder-export --data '{"end_time":1712604800,"responder_ids":[3790925372131],"severities":["Critical","Warning"],"start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightQueryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Analytics.ResponderExport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /insight/responder/export") + return nil + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsTeamExportCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "team-export", + Short: "Export team insight", + Long: `Export team insight. + +Export team insight metrics as a CSV file. The response is a CSV stream delivered with 'Content-Disposition: attachment' — it is not a JSON envelope. + +API: POST /insight/team/export (insightTeamExport) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --orderby string — Field to sort the underlying incident set by. [created_at] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). +`, + Example: ` flashduty insight team-export --data '{"end_time":1712604800,"severities":["Critical","Warning"],"start_time":1712000000,"team_ids":[4295771902131]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightQueryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Analytics.TeamExport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /insight/team/export") + return nil + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the underlying incident set by. [created_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAnalyticsTopkAlertsByLabelCmd() *cobra.Command { + var dataJSON string + var fAggregateUnit string + var fAsc bool + var fChannelIDs []int + var fDescriptionHTMLToText bool + var fEndTime int64 + var fExportFields []string + var fIncidentIDs []string + var fIsMyTeam bool + var fK int64 + var fLabel string + var fOrderby string + var fQuery string + var fResponderIDs []int + var fSecondsToAckFrom int64 + var fSecondsToAckTo int64 + var fSecondsToCloseFrom int64 + var fSecondsToCloseTo int64 + var fSeverities []string + var fSplitHours bool + var fStartTime int64 + var fTeamIDs []int + var fTimeZone string + cmd := &cobra.Command{ + Use: "alert-topk-by-label", + Short: "Get top-K alerts grouped by check or resource", + Long: `Get top-K alerts grouped by check or resource. + +Return the top-K alert groups aggregated either by 'check' or by 'resource' label over the specified time range. + +API: POST /insight/alert/topk-by-label (insightTopkAlertsByLabel) + +Request fields: + --aggregate-unit string — Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month] + --asc bool — Sort ascending when 'true', descending otherwise. + --channel-ids []int — Filter by channel IDs. At most 100 entries. + --description-html-to-text bool — Strip HTML markup from the description column when exporting. + --end-time int (required) — End time, Unix seconds. Must be greater than 'start_time'. + --export-fields []string — Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name] + --incident-ids []string — Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries. + --is-my-team bool — Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty. + --k int — Number of top entries to return, between 1 and 100. + --label string (required) — Dimension to aggregate by. [check, resource] + --orderby string — Field to sort results by. [total_alert_cnt, total_alert_event_cnt] + --query string — Full-text query applied to incident title and description. + --responder-ids []int — Filter by responder person IDs. At most 100 entries. + --seconds-to-ack-from int — Lower bound (inclusive) on time-to-acknowledge, in seconds. + --seconds-to-ack-to int — Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set. + --seconds-to-close-from int — Lower bound (inclusive) on time-to-close, in seconds. + --seconds-to-close-to int — Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set. + --severities []string — Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok] + --split-hours bool — When true, metrics are split into 'work'/'sleep'/'off' hour buckets. + --start-time int (required) — Start time, Unix seconds. Must be greater than 0. + --team-ids []int — Filter by team IDs. At most 100 entries. + --time-zone string — IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone. + fields (object, via --data) — Custom-field filters (exact match). + labels (object, via --data) — Label filters (exact match). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - hours (string) — Hour bucket when 'split_hours' is enabled. + - label (string) — Aggregation key value (check name or resource identifier). + - total_alert_cnt (integer) + - total_alert_event_cnt (integer) +`, + Example: ` flashduty insight alert-topk-by-label --data '{"end_time":1712604800,"k":10,"label":"check","orderby":"total_alert_cnt","start_time":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggregate-unit") { + body["aggregate_unit"] = fAggregateUnit + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("description-html-to-text") { + body["description_html_to_text"] = fDescriptionHTMLToText + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("export-fields") { + body["export_fields"] = fExportFields + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("k") { + body["k"] = fK + } + if cmd.Flags().Changed("label") { + body["label"] = fLabel + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("seconds-to-ack-from") { + body["seconds_to_ack_from"] = fSecondsToAckFrom + } + if cmd.Flags().Changed("seconds-to-ack-to") { + body["seconds_to_ack_to"] = fSecondsToAckTo + } + if cmd.Flags().Changed("seconds-to-close-from") { + body["seconds_to_close_from"] = fSecondsToCloseFrom + } + if cmd.Flags().Changed("seconds-to-close-to") { + body["seconds_to_close_to"] = fSecondsToCloseTo + } + if cmd.Flags().Changed("severities") { + body["severities"] = fSeverities + } + if cmd.Flags().Changed("split-hours") { + body["split_hours"] = fSplitHours + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.InsightTopkAlertByLabelRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Analytics.TopkAlertsByLabel(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAggregateUnit, "aggregate-unit", "", "Aggregate metrics into time buckets. When set, the time range must cover at least 24 hours; 'day' additionally caps the range at 31 days. [day, week, month]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true', descending otherwise.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by channel IDs. At most 100 entries.") + cmd.Flags().BoolVar(&fDescriptionHTMLToText, "description-html-to-text", false, "Strip HTML markup from the description column when exporting.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End time, Unix seconds. Must be greater than 'start_time'. (required)") + cmd.Flags().StringSliceVar(&fExportFields, "export-fields", nil, "Subset of CSV column keys to include in the export. At most 50 entries. Only used by the export endpoints. [incident_id, title, severity, progress, channel_id, channel_name, team_id, team_name, created_at, seconds_to_ack, seconds_to_close, closed_by, engaged_seconds, hours, notifications, interruptions, acknowledgements, assignments, reassignments, escalations, manual_escalations, timeout_escalations, assigned_to, responders, description, labels, fields, creator_id, creator_name]") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Filter by incident IDs (MongoDB ObjectIDs). At most 100 entries.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Restrict results to teams the caller belongs to. When true and the caller has no teams, the result set is empty.") + cmd.Flags().Int64Var(&fK, "k", 0, "Number of top entries to return, between 1 and 100.") + cmd.Flags().StringVar(&fLabel, "label", "", "Dimension to aggregate by. (required) [check, resource]") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort results by. [total_alert_cnt, total_alert_event_cnt]") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text query applied to incident title and description.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Filter by responder person IDs. At most 100 entries.") + cmd.Flags().Int64Var(&fSecondsToAckFrom, "seconds-to-ack-from", 0, "Lower bound (inclusive) on time-to-acknowledge, in seconds.") + cmd.Flags().Int64Var(&fSecondsToAckTo, "seconds-to-ack-to", 0, "Upper bound (exclusive) on time-to-acknowledge, in seconds. Must be greater than 'seconds_to_ack_from' when both are set.") + cmd.Flags().Int64Var(&fSecondsToCloseFrom, "seconds-to-close-from", 0, "Lower bound (inclusive) on time-to-close, in seconds.") + cmd.Flags().Int64Var(&fSecondsToCloseTo, "seconds-to-close-to", 0, "Upper bound (exclusive) on time-to-close, in seconds. Must be greater than 'seconds_to_close_from' when both are set.") + cmd.Flags().StringSliceVar(&fSeverities, "severities", nil, "Filter by severity. At most 3 entries. [Critical, Warning, Info, Ok]") + cmd.Flags().BoolVar(&fSplitHours, "split-hours", false, "When true, metrics are split into 'work'/'sleep'/'off' hour buckets.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start time, Unix seconds. Must be greater than 0. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedAnalytics(root *cobra.Command) { + gInsight := genGroup(root, "insight", "On-call/Analytics API") + genAddLeaf(gInsight, genAnalyticsByAccountCmd()) + genAddLeaf(gInsight, genAnalyticsByChannelCmd()) + genAddLeaf(gInsight, genAnalyticsByResponderCmd()) + genAddLeaf(gInsight, genAnalyticsByTeamCmd()) + genAddLeaf(gInsight, genAnalyticsChannelExportCmd()) + genAddLeaf(gInsight, genAnalyticsIncidentExportCmd()) + genAddLeaf(gInsight, genAnalyticsIncidentListCmd()) + genAddLeaf(gInsight, genAnalyticsResponderExportCmd()) + genAddLeaf(gInsight, genAnalyticsTeamExportCmd()) + genAddLeaf(gInsight, genAnalyticsTopkAlertsByLabelCmd()) +} diff --git a/internal/cli/zz_generated_applications.go b/internal/cli/zz_generated_applications.go new file mode 100644 index 0000000..216026f --- /dev/null +++ b/internal/cli/zz_generated_applications.go @@ -0,0 +1,490 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genApplicationsReadInfoCmd() *cobra.Command { + var dataJSON string + var fApplicationID string + cmd := &cobra.Command{ + Use: "application-info", + Short: "Get application detail", + Long: `Get application detail. + +Retrieve full details of a single RUM application by 'application_id'. + +API: POST /rum/application/info (rum-application-read-info) + +Request fields: + --application-id string (required) — RUM application ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) — Account ID. + - alerting (object) — Alert settings for the application. + - channel_ids (array) — Channel IDs to send alerts to. + - enabled (boolean) — Whether alerting is enabled. + - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned). + - application_id (string) — Unique application ID. + - application_name (string) — Application display name. + - client_token (string) — Token used to initialize the RUM SDK. + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - created_by (integer) — Creator member ID. + - is_private (boolean) — If 'true', the application is only accessible to team members. + - no_geo (boolean) — If 'true', geographic location is not inferred from IP. + - no_ip (boolean) — If 'true', IP addresses are not collected. + - status (string) — Application status. [enabled, disabled, deleted] + - team_id (integer) — Owning team ID. + - tracing (object) — APM tracing integration settings. + - enabled (boolean) — Whether tracing integration is enabled. + - endpoint (string) — Trace endpoint URL (http or https). + - open_type (string) — How to open the trace link. [popup, tab] + - type (string) — Application type. [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity] + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - updated_by (integer) — Last updater member ID. +`, + Example: ` flashduty rum application-info --data '{"application_id":"WoyQQ3BohkdtPivubEvE8o"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("application-id") { + body["application_id"] = fApplicationID + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMApplicationIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Applications.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fApplicationID, "application-id", "", "RUM application ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genApplicationsReadInfosCmd() *cobra.Command { + var dataJSON string + var fApplicationIDs []string + cmd := &cobra.Command{ + Use: "application-infos", + Short: "Batch get applications", + Long: `Batch get applications. + +Retrieve details for multiple RUM applications by their IDs in one request. + +API: POST /rum/application/infos (rum-application-read-infos) + +Request fields: + --application-ids []string (required) — Up to 200 application IDs. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - account_id (integer) — Account ID. + - alerting (object) — Alert settings for the application. + - channel_ids (array) — Channel IDs to send alerts to. + - enabled (boolean) — Whether alerting is enabled. + - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned). + - application_id (string) — Unique application ID. + - application_name (string) — Application display name. + - client_token (string) — Token used to initialize the RUM SDK. + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - created_by (integer) — Creator member ID. + - is_private (boolean) — If 'true', the application is only accessible to team members. + - no_geo (boolean) — If 'true', geographic location is not inferred from IP. + - no_ip (boolean) — If 'true', IP addresses are not collected. + - status (string) — Application status. [enabled, disabled, deleted] + - team_id (integer) — Owning team ID. + - tracing (object) — APM tracing integration settings. + - enabled (boolean) — Whether tracing integration is enabled. + - endpoint (string) — Trace endpoint URL (http or https). + - open_type (string) — How to open the trace link. [popup, tab] + - type (string) — Application type. [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity] + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - updated_by (integer) — Last updater member ID. +`, + Example: ` flashduty rum application-infos --data '{"application_ids":["eWbr4xk3ZRnLabRa6unqwD","WoyQQ3BohkdtPivubEvE8o"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("application-ids") { + body["application_ids"] = fApplicationIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMApplicationInfosRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Applications.ReadInfos(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringSliceVar(&fApplicationIDs, "application-ids", nil, "Up to 200 application IDs. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genApplicationsReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fTeamID int64 + cmd := &cobra.Command{ + Use: "application-list", + Short: "List applications", + Long: `List applications. + +Return a paginated list of RUM applications accessible to the current user. + +API: POST /rum/application/list (rum-application-read-list) + +Request fields: + --page int — Page number (1-based). Default: 1. + --limit int — Page size. Range: 1–100. Default: 20. + --search-after-ctx string + --asc bool — Sort ascending if 'true'. + --is-my-team bool — If 'true', return only applications belonging to the current user's teams. + --orderby string — Sort field. [created_at, updated_at] + --query string — Search query to filter by application name. + --team-id int — Filter by team ID. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) + - items (array) + - account_id (integer) — Account ID. + - alerting (object) — Alert settings for the application. + - channel_ids (array) — Channel IDs to send alerts to. + - enabled (boolean) — Whether alerting is enabled. + - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned). + - application_id (string) — Unique application ID. + - application_name (string) — Application display name. + - client_token (string) — Token used to initialize the RUM SDK. + - created_at (integer) — Creation timestamp, Unix epoch seconds. + - created_by (integer) — Creator member ID. + - is_private (boolean) — If 'true', the application is only accessible to team members. + - no_geo (boolean) — If 'true', geographic location is not inferred from IP. + - no_ip (boolean) — If 'true', IP addresses are not collected. + - status (string) — Application status. [enabled, disabled, deleted] + - team_id (integer) — Owning team ID. + - tracing (object) — APM tracing integration settings. + - enabled (boolean) — Whether tracing integration is enabled. + - endpoint (string) — Trace endpoint URL (http or https). + - open_type (string) — How to open the trace link. [popup, tab] + - type (string) — Application type. [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity] + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - updated_by (integer) — Last updater member ID. + - total (integer) +`, + Example: ` flashduty rum application-list --data '{"is_my_team":false,"limit":20,"p":1,"query":""}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMApplicationListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Applications.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number (1-based). Default: 1.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Range: 1–100. Default: 20.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending if 'true'.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "If 'true', return only applications belonging to the current user's teams.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Search query to filter by application name.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Filter by team ID.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genApplicationsWriteCreateCmd() *cobra.Command { + var dataJSON string + var fApplicationName string + var fIsPrivate bool + var fNoGeo bool + var fNoIP bool + var fTeamID int64 + var fType string + cmd := &cobra.Command{ + Use: "application-create", + Short: "Create application", + Long: `Create application. + +Create a new RUM application. Returns the generated 'application_id' and 'client_token'. + +API: POST /rum/application/create (rum-application-write-create) + +Request fields: + --application-name string (required) — Application name. 1–40 characters. + --is-private bool — Restrict access to team members only. + --no-geo bool — Do not infer geographic location. + --no-ip bool — Do not collect IP addresses. + --team-id int (required) — Owning team ID. + --type string (required) — Application type. [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity] + alerting (object, via --data) — Alert settings for the application. + - channel_ids (array) — Channel IDs to send alerts to. + - enabled (boolean) — Whether alerting is enabled. + - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned). + tracing (object, via --data) — APM tracing integration settings. + - enabled (boolean) — Whether tracing integration is enabled. + - endpoint (string) — Trace endpoint URL (http or https). + - open_type (string) — How to open the trace link. [popup, tab] + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - application_id (string) — Auto-generated unique application ID. + - application_name (string) — Application display name. + - client_token (string) — Token for RUM SDK initialization. +`, + Example: ` flashduty rum application-create --data '{"application_name":"My Web App","is_private":false,"team_id":2477033058131,"type":"browser"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("application-name") { + body["application_name"] = fApplicationName + } + if cmd.Flags().Changed("is-private") { + body["is_private"] = fIsPrivate + } + if cmd.Flags().Changed("no-geo") { + body["no_geo"] = fNoGeo + } + if cmd.Flags().Changed("no-ip") { + body["no_ip"] = fNoIP + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMApplicationCreateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Applications.WriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fApplicationName, "application-name", "", "Application name. 1–40 characters. (required)") + cmd.Flags().BoolVar(&fIsPrivate, "is-private", false, "Restrict access to team members only.") + cmd.Flags().BoolVar(&fNoGeo, "no-geo", false, "Do not infer geographic location.") + cmd.Flags().BoolVar(&fNoIP, "no-ip", false, "Do not collect IP addresses.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. (required)") + cmd.Flags().StringVar(&fType, "type", "", "Application type. (required) [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genApplicationsWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fApplicationID string + cmd := &cobra.Command{ + Use: "application-delete", + Short: "Delete application", + Long: `Delete application. + +Delete a RUM application by 'application_id'. + +API: POST /rum/application/delete (rum-application-write-delete) + +Request fields: + --application-id string (required) — RUM application ID. +`, + Example: ` flashduty rum application-delete --data '{"application_id":"qLpu24Dz4CAzWsESPbJYWA"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("application-id") { + body["application_id"] = fApplicationID + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMApplicationIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Applications.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /rum/application/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fApplicationID, "application-id", "", "RUM application ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genApplicationsWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fApplicationID string + var fApplicationName string + var fIsPrivate bool + var fNoGeo bool + var fNoIP bool + var fTeamID int64 + var fType string + cmd := &cobra.Command{ + Use: "application-update", + Short: "Update application", + Long: `Update application. + +Update an existing RUM application. All fields except 'application_id' are optional — only provided fields are updated. + +API: POST /rum/application/update (rum-application-write-update) + +Request fields: + --application-id string (required) — Application ID to update. + --application-name string — New application name. + --is-private bool + --no-geo bool + --no-ip bool + --team-id int + --type string [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity] + alerting (object, via --data) — Alert settings for the application. + - channel_ids (array) — Channel IDs to send alerts to. + - enabled (boolean) — Whether alerting is enabled. + - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned). + tracing (object, via --data) — APM tracing integration settings. + - enabled (boolean) — Whether tracing integration is enabled. + - endpoint (string) — Trace endpoint URL (http or https). + - open_type (string) — How to open the trace link. [popup, tab] +`, + Example: ` flashduty rum application-update --data '{"alerting":{"channel_ids":[2490121812131],"enabled":true},"application_id":"WoyQQ3BohkdtPivubEvE8o","application_name":"My Web App v2"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("application-id") { + body["application_id"] = fApplicationID + } + if cmd.Flags().Changed("application-name") { + body["application_name"] = fApplicationName + } + if cmd.Flags().Changed("is-private") { + body["is_private"] = fIsPrivate + } + if cmd.Flags().Changed("no-geo") { + body["no_geo"] = fNoGeo + } + if cmd.Flags().Changed("no-ip") { + body["no_ip"] = fNoIP + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMApplicationUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Applications.WriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /rum/application/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fApplicationID, "application-id", "", "Application ID to update. (required)") + cmd.Flags().StringVar(&fApplicationName, "application-name", "", "New application name.") + cmd.Flags().BoolVar(&fIsPrivate, "is-private", false, "Request field is_private") + cmd.Flags().BoolVar(&fNoGeo, "no-geo", false, "Request field no_geo") + cmd.Flags().BoolVar(&fNoIP, "no-ip", false, "Request field no_ip") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Request field team_id") + cmd.Flags().StringVar(&fType, "type", "", "Request field type [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedApplications(root *cobra.Command) { + gRUM := genGroup(root, "rum", "RUM API") + genAddLeaf(gRUM, genApplicationsReadInfoCmd()) + genAddLeaf(gRUM, genApplicationsReadInfosCmd()) + genAddLeaf(gRUM, genApplicationsReadListCmd()) + genAddLeaf(gRUM, genApplicationsWriteCreateCmd()) + genAddLeaf(gRUM, genApplicationsWriteDeleteCmd()) + genAddLeaf(gRUM, genApplicationsWriteUpdateCmd()) +} diff --git a/internal/cli/zz_generated_audit_logs.go b/internal/cli/zz_generated_audit_logs.go new file mode 100644 index 0000000..9beed37 --- /dev/null +++ b/internal/cli/zz_generated_audit_logs.go @@ -0,0 +1,162 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genAuditLogsOperationListCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "operation-list", + Short: "List auditable operation types", + Long: `List auditable operation types. + +Return all operation names that are recorded in the audit log, for use as 'operations' filter values. + +API: POST /audit/operation/list (audit-read-operation-list) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - name (string) (required) — Stable machine-readable operation name for use as a filter. + - name_cn (string) (required) — Human-readable Chinese label shown in the console. +`, + Example: ` flashduty audit operation-list --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.AuditLogs.OperationList(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genAuditLogsSearchCmd() *cobra.Command { + var dataJSON string + var fEndTime int64 + var fIsDangerous bool + var fIsWrite bool + var fLimit int64 + var fOperations []string + var fPersonID int64 + var fRequestID string + var fSearchAfterCtx string + var fStartTime int64 + cmd := &cobra.Command{ + Use: "search", + Short: "Search audit logs", + Long: `Search audit logs. + +Return a cursor-paginated list of audit log entries within a time range. + +API: POST /audit/search (audit-read-search) + +Request fields: + --end-time int (required) — End of the search window, Unix epoch seconds. Must be after 'start_time'. Maximum span 90 days. + --is-dangerous bool — When true, return only high-risk (dangerous) operations. + --is-write bool — When true, return only write operations; when false, return only read operations. + --limit int — Page size. Minimum 0, maximum 99. (0-99) + --operations []string — Filter to specific operation names. Use 'POST /audit/operation/list' to get the valid set. + --person-id int — Filter by the member who performed the action. + --request-id string — Filter to a single request by its unique request ID. + --search-after-ctx string — Opaque pagination cursor returned by the previous response. Leave empty for the first page. + --start-time int (required) — Start of the search window, Unix epoch seconds. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - docs (array) — Audit log entries for this page. + - account_id (integer) (required) — ID of the account. + - body (string) (required) — JSON-encoded request body (may be truncated at 10 KB). + - created_at (integer) (required) — Timestamp of the operation in Unix epoch milliseconds. + - ip (string) (required) — Client IP address of the caller. + - is_dangerous (boolean) (required) — True if this is flagged as a high-risk operation. + - is_write (boolean) (required) — True for mutating operations; false for read-only ones. + - member_id (integer) (required) — ID of the member who performed the action. + - member_name (string) (required) — Display name of the member. + - operation (string) (required) — Stable machine-readable operation name, e.g. 'template:write:create'. + - operation_name (string) (required) — Human-readable operation label in the account's locale. + - params (array) (required) — URL path parameters as an array of key-value pairs, or an empty array when none. + - Key (string) + - Value (string) + - request_id (string) (required) — Unique request ID for correlation. + - search_after_ctx (string) (required) — Opaque cursor for the next page. Empty string when there are no more results. + - total (integer) (required) — Total matching entries in the search window. +`, + Example: ` flashduty audit search --data '{"end_time":1712707200,"limit":20,"operations":["template:write:create","template:write:delete"],"start_time":1712620800}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("is-dangerous") { + body["is_dangerous"] = fIsDangerous + } + if cmd.Flags().Changed("is-write") { + body["is_write"] = fIsWrite + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("operations") { + body["operations"] = fOperations + } + if cmd.Flags().Changed("person-id") { + body["person_id"] = fPersonID + } + if cmd.Flags().Changed("request-id") { + body["request_id"] = fRequestID + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + }) + if err != nil { + return err + } + req := new(flashduty.AuditSearchRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.AuditLogs.Search(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End of the search window, Unix epoch seconds. Must be after 'start_time'. Maximum span 90 days. (required)") + cmd.Flags().BoolVar(&fIsDangerous, "is-dangerous", false, "When true, return only high-risk (dangerous) operations.") + cmd.Flags().BoolVar(&fIsWrite, "is-write", false, "When true, return only write operations; when false, return only read operations.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Minimum 0, maximum 99. (0-99)") + cmd.Flags().StringSliceVar(&fOperations, "operations", nil, "Filter to specific operation names. Use 'POST /audit/operation/list' to get the valid set.") + cmd.Flags().Int64Var(&fPersonID, "person-id", 0, "Filter by the member who performed the action.") + cmd.Flags().StringVar(&fRequestID, "request-id", "", "Filter to a single request by its unique request ID.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque pagination cursor returned by the previous response. Leave empty for the first page.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start of the search window, Unix epoch seconds. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedAuditLogs(root *cobra.Command) { + gAudit := genGroup(root, "audit", "Platform/Audit logs API") + genAddLeaf(gAudit, genAuditLogsOperationListCmd()) + genAddLeaf(gAudit, genAuditLogsSearchCmd()) +} diff --git a/internal/cli/zz_generated_calendars.go b/internal/cli/zz_generated_calendars.go new file mode 100644 index 0000000..912e84e --- /dev/null +++ b/internal/cli/zz_generated_calendars.go @@ -0,0 +1,567 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genCalendarsCalEventDeleteCmd() *cobra.Command { + var dataJSON string + var fCalID string + var fEventID string + cmd := &cobra.Command{ + Use: "event-delete", + Short: "Delete calendar event", + Long: `Delete calendar event. + +Delete a calendar event by calendar ID and event ID. + +API: POST /calendar/event/delete (calEventDelete) + +Request fields: + --cal-id string (required) — Calendar ID. + --event-id string (required) — Event ID. +`, + Example: ` flashduty calendar event-delete --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","event_id":"cale.KyG9XWTCU5CucbwukEVBQ4"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("cal-id") { + body["cal_id"] = fCalID + } + if cmd.Flags().Changed("event-id") { + body["event_id"] = fEventID + } + }) + if err != nil { + return err + } + req := new(flashduty.CalEventIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Calendars.CalEventDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /calendar/event/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") + cmd.Flags().StringVar(&fEventID, "event-id", "", "Event ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genCalendarsCalEventListCmd() *cobra.Command { + var dataJSON string + var fCalID string + var fDay int64 + var fMonth int64 + var fYear int64 + cmd := &cobra.Command{ + Use: "event-list", + Short: "List calendar events", + Long: `List calendar events. + +Return events for a personal calendar within a year/month/day scope. When month and day are both omitted the whole year is returned. + +API: POST /calendar/event/list (calEventList) + +Request fields: + --cal-id string (required) — Calendar ID. + --day int — Day (1-31). 0 means no day filter. (0-31) + --month int — Month (1-12). 0 means no month filter. (0-12) + --year int — Year. Defaults to the current year when omitted. (min 2023) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Calendar events sorted by start_at. + - account_id (integer) — Account ID. Only present for private events. + - cal_id (string) (required) — Calendar ID. For public events this is a locale key such as zh-cn.china.official. + - created_at (integer) (required) — Creation timestamp (Unix seconds). + - creator_id (integer) — Creator person ID. Only present for private events. + - description (string) (required) — Event description. + - end_at (string) (required) — Event end date (YYYY-MM-DD, exclusive). + - event_id (string) (required) — Event ID. + - is_off (boolean) (required) — Whether the event marks a non-working day. + - start_at (string) (required) — Event start date (YYYY-MM-DD). + - summary (string) (required) — Event summary. + - updated_at (integer) (required) — Last update timestamp (Unix seconds). + - total (integer) (required) — Total number of events returned. +`, + Example: ` flashduty calendar event-list --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","month":5,"year":2024}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("cal-id") { + body["cal_id"] = fCalID + } + if cmd.Flags().Changed("day") { + body["day"] = fDay + } + if cmd.Flags().Changed("month") { + body["month"] = fMonth + } + if cmd.Flags().Changed("year") { + body["year"] = fYear + } + }) + if err != nil { + return err + } + req := new(flashduty.CalEventListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Calendars.CalEventList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") + cmd.Flags().Int64Var(&fDay, "day", 0, "Day (1-31). 0 means no day filter. (0-31)") + cmd.Flags().Int64Var(&fMonth, "month", 0, "Month (1-12). 0 means no month filter. (0-12)") + cmd.Flags().Int64Var(&fYear, "year", 0, "Year. Defaults to the current year when omitted. (min 2023)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genCalendarsCalEventUpsertCmd() *cobra.Command { + var dataJSON string + var fCalID string + var fDescription string + var fEndAt string + var fEventID string + var fIsOff bool + var fStartAt string + var fSummary string + cmd := &cobra.Command{ + Use: "event-upsert", + Short: "Upsert calendar event", + Long: `Upsert calendar event. + +Create or update a calendar event (holiday or workday override). Omit event_id to create a new event. + +API: POST /calendar/event/upsert (calEventUpsert) + +Request fields: + --cal-id string (required) — Calendar ID. + --description string — Event description. (≤499 chars) + --end-at string (required) — Event end date in YYYY-MM-DD (exclusive). + --event-id string — Event ID. Omit when creating. (≤63 chars) + --is-off bool (required) — Whether the event marks a non-working day. true = day off, false = working day override. + --start-at string (required) — Event start date in YYYY-MM-DD. + --summary string (required) — Event summary. (1-39 chars) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - cal_id (string) (required) — Calendar ID. + - event_id (string) (required) — Event ID (existing or newly generated). + - summary (string) (required) — Event summary. +`, + Example: ` flashduty calendar event-upsert --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","description":"International Workers Day holiday","end_at":"2024-05-06","is_off":true,"start_at":"2024-05-01","summary":"Labour Day"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("cal-id") { + body["cal_id"] = fCalID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("end-at") { + body["end_at"] = fEndAt + } + if cmd.Flags().Changed("event-id") { + body["event_id"] = fEventID + } + if cmd.Flags().Changed("is-off") { + body["is_off"] = fIsOff + } + if cmd.Flags().Changed("start-at") { + body["start_at"] = fStartAt + } + if cmd.Flags().Changed("summary") { + body["summary"] = fSummary + } + }) + if err != nil { + return err + } + req := new(flashduty.CalEventUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Calendars.CalEventUpsert(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Event description. (≤499 chars)") + cmd.Flags().StringVar(&fEndAt, "end-at", "", "Event end date in YYYY-MM-DD (exclusive). (required)") + cmd.Flags().StringVar(&fEventID, "event-id", "", "Event ID. Omit when creating. (≤63 chars)") + cmd.Flags().BoolVar(&fIsOff, "is-off", false, "Whether the event marks a non-working day. true = day off, false = working day override. (required)") + cmd.Flags().StringVar(&fStartAt, "start-at", "", "Event start date in YYYY-MM-DD. (required)") + cmd.Flags().StringVar(&fSummary, "summary", "", "Event summary. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genCalendarsCalendarCreateCmd() *cobra.Command { + var dataJSON string + var fCalName string + var fDescription string + var fExtraCalIDs []string + var fTeamID int64 + var fTimezone string + var fWorkdays []int + cmd := &cobra.Command{ + Use: "create", + Short: "Create calendar", + Long: `Create calendar. + +Create a personal service calendar. Each account is limited to 5 calendars unless the Flashcat-Break-Cal-Limit header is set. + +API: POST /calendar/create (calendarCreate) + +Request fields: + --cal-name string (required) — Calendar display name. (1-39 chars) + --description string — Calendar description. (≤499 chars) + --extra-cal-ids []string — Additional public-holiday calendar IDs to inherit events from (for example zh-cn.china.official). + --team-id int — Owning team ID. 0 means no team. + --timezone string — IANA timezone. Defaults to Asia/Shanghai when empty. + --workdays []int — Workday numbers (0 = Sunday, 6 = Saturday). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - cal_id (string) (required) — ID of the newly created calendar (format cal.). + - cal_name (string) (required) — Calendar display name. +`, + Example: ` flashduty calendar create --data '{"cal_name":"Production On-Call Calendar","description":"Calendar for production on-call team","timezone":"Asia/Shanghai","workdays":[1,2,3,4,5]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("cal-name") { + body["cal_name"] = fCalName + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("extra-cal-ids") { + body["extra_cal_ids"] = fExtraCalIDs + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("timezone") { + body["timezone"] = fTimezone + } + if cmd.Flags().Changed("workdays") { + body["workdays"] = fWorkdays + } + }) + if err != nil { + return err + } + req := new(flashduty.CalendarCreateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Calendars.CalendarCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fCalName, "cal-name", "", "Calendar display name. (required) (1-39 chars)") + cmd.Flags().StringVar(&fDescription, "description", "", "Calendar description. (≤499 chars)") + cmd.Flags().StringSliceVar(&fExtraCalIDs, "extra-cal-ids", nil, "Additional public-holiday calendar IDs to inherit events from (for example zh-cn.china.official).") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. 0 means no team.") + cmd.Flags().StringVar(&fTimezone, "timezone", "", "IANA timezone. Defaults to Asia/Shanghai when empty.") + cmd.Flags().IntSliceVar(&fWorkdays, "workdays", nil, "Workday numbers (0 = Sunday, 6 = Saturday).") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genCalendarsCalendarDeleteCmd() *cobra.Command { + var dataJSON string + var fCalID string + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete calendar", + Long: `Delete calendar. + +Delete a personal service calendar. The call fails when referenced by escalation or silence rules. + +API: POST /calendar/delete (calendarDelete) + +Request fields: + --cal-id string (required) — Calendar ID. +`, + Example: ` flashduty calendar delete --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("cal-id") { + body["cal_id"] = fCalID + } + }) + if err != nil { + return err + } + req := new(flashduty.CalendarIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Calendars.CalendarDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /calendar/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genCalendarsCalendarInfoCmd() *cobra.Command { + var dataJSON string + var fCalID string + cmd := &cobra.Command{ + Use: "info", + Short: "Get calendar info", + Long: `Get calendar info. + +Return details of a service calendar. + +API: POST /calendar/info (calendarInfo) + +Request fields: + --cal-id string (required) — Calendar ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Account ID. + - cal_id (string) (required) — Calendar ID. + - cal_name (string) (required) — Calendar display name. + - created_at (integer) (required) — Creation timestamp (Unix seconds). + - creator_id (integer) (required) — Creator person ID. + - description (string) (required) — Calendar description. + - extra_cal_ids (array) — Inherited public-holiday calendar IDs. + - kind (string) (required) — Calendar kind. [region.official.holiday, religion.holiday, personal] + - status (string) (required) — Calendar status. [enabled, deleted] + - team_id (integer) (required) — Owning team ID (0 when not assigned). + - timezone (string) (required) — IANA timezone. + - updated_at (integer) (required) — Last update timestamp (Unix seconds). + - updated_by (integer) (required) — Last updater person ID. + - workdays (array) — Workday numbers (0 = Sunday, 6 = Saturday). +`, + Example: ` flashduty calendar info --data '{"cal_id":"cal.eh9gvPtWeH3xXgKeVSRxRg"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("cal-id") { + body["cal_id"] = fCalID + } + }) + if err != nil { + return err + } + req := new(flashduty.CalendarIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Calendars.CalendarInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genCalendarsCalendarListCmd() *cobra.Command { + var dataJSON string + var fKind string + var fNoLocale bool + cmd := &cobra.Command{ + Use: "list", + Short: "List calendars", + Long: `List calendars. + +Return the list of service calendars visible to the current account. + +API: POST /calendar/list (calendarList) + +Request fields: + --kind string — Calendar kind filter. Defaults to personal when empty. [region.official.holiday, personal] + --no-locale bool — Disable locale filtering when listing public-holiday calendars. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Calendar items. + - account_id (integer) (required) — Account ID. + - cal_id (string) (required) — Calendar ID. + - cal_name (string) (required) — Calendar display name. + - created_at (integer) (required) — Creation timestamp (Unix seconds). + - creator_id (integer) (required) — Creator person ID. + - description (string) (required) — Calendar description. + - extra_cal_ids (array) — Inherited public-holiday calendar IDs. + - kind (string) (required) — Calendar kind. [region.official.holiday, religion.holiday, personal] + - status (string) (required) — Calendar status. [enabled, deleted] + - team_id (integer) (required) — Owning team ID (0 when not assigned). + - timezone (string) (required) — IANA timezone. + - updated_at (integer) (required) — Last update timestamp (Unix seconds). + - updated_by (integer) (required) — Last updater person ID. + - workdays (array) — Workday numbers (0 = Sunday, 6 = Saturday). + - total (integer) (required) — Total number of calendars returned. +`, + Example: ` flashduty calendar list --data '{"kind":"personal"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("kind") { + body["kind"] = fKind + } + if cmd.Flags().Changed("no-locale") { + body["no_locale"] = fNoLocale + } + }) + if err != nil { + return err + } + req := new(flashduty.CalendarListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Calendars.CalendarList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fKind, "kind", "", "Calendar kind filter. Defaults to personal when empty. [region.official.holiday, personal]") + cmd.Flags().BoolVar(&fNoLocale, "no-locale", false, "Disable locale filtering when listing public-holiday calendars.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genCalendarsCalendarUpdateCmd() *cobra.Command { + var dataJSON string + var fCalID string + var fCalName string + var fDescription string + var fExtraCalIDs []string + var fTeamID int64 + var fTimezone string + var fWorkdays []int + cmd := &cobra.Command{ + Use: "update", + Short: "Update calendar", + Long: `Update calendar. + +Update a personal service calendar. Only non-null fields are updated. + +API: POST /calendar/update (calendarUpdate) + +Request fields: + --cal-id string (required) — Calendar ID. + --cal-name string — New calendar name. (1-39 chars) + --description string — New description. (≤499 chars) + --extra-cal-ids []string — Additional public-holiday calendar IDs to inherit events from. + --team-id int — New owning team ID. + --timezone string — New IANA timezone. + --workdays []int — Workday numbers (0 = Sunday, 6 = Saturday). +`, + Example: ` flashduty calendar update --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","cal_name":"Production On-Call Calendar (Updated)","timezone":"America/New_York","workdays":[1,2,3,4,5]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("cal-id") { + body["cal_id"] = fCalID + } + if cmd.Flags().Changed("cal-name") { + body["cal_name"] = fCalName + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("extra-cal-ids") { + body["extra_cal_ids"] = fExtraCalIDs + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("timezone") { + body["timezone"] = fTimezone + } + if cmd.Flags().Changed("workdays") { + body["workdays"] = fWorkdays + } + }) + if err != nil { + return err + } + req := new(flashduty.CalendarUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Calendars.CalendarUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /calendar/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") + cmd.Flags().StringVar(&fCalName, "cal-name", "", "New calendar name. (1-39 chars)") + cmd.Flags().StringVar(&fDescription, "description", "", "New description. (≤499 chars)") + cmd.Flags().StringSliceVar(&fExtraCalIDs, "extra-cal-ids", nil, "Additional public-holiday calendar IDs to inherit events from.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID.") + cmd.Flags().StringVar(&fTimezone, "timezone", "", "New IANA timezone.") + cmd.Flags().IntSliceVar(&fWorkdays, "workdays", nil, "Workday numbers (0 = Sunday, 6 = Saturday).") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedCalendars(root *cobra.Command) { + gCalendar := genGroup(root, "calendar", "On-call/Calendars API") + genAddLeaf(gCalendar, genCalendarsCalEventDeleteCmd()) + genAddLeaf(gCalendar, genCalendarsCalEventListCmd()) + genAddLeaf(gCalendar, genCalendarsCalEventUpsertCmd()) + genAddLeaf(gCalendar, genCalendarsCalendarCreateCmd()) + genAddLeaf(gCalendar, genCalendarsCalendarDeleteCmd()) + genAddLeaf(gCalendar, genCalendarsCalendarInfoCmd()) + genAddLeaf(gCalendar, genCalendarsCalendarListCmd()) + genAddLeaf(gCalendar, genCalendarsCalendarUpdateCmd()) +} diff --git a/internal/cli/zz_generated_changes.go b/internal/cli/zz_generated_changes.go new file mode 100644 index 0000000..d3c1aa3 --- /dev/null +++ b/internal/cli/zz_generated_changes.go @@ -0,0 +1,153 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genChangesListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fChannelIDs []int + var fEndTime int64 + var fIncludeEvents bool + var fIntegrationIDs []int + var fOrderby string + var fQuery string + var fStartTime int64 + cmd := &cobra.Command{ + Use: "list", + Short: "List changes", + Long: `List changes. + +Query change records within a time window, with filtering, search, and pagination. + +API: POST /change/list (change-read-list) + +Request fields: + --page int — Page number, starting at 1. (min 1) + --limit int — Number of items per page. (1-100) + --search-after-ctx string + --asc bool — Sort in ascending order when true. + --channel-ids []int — Filter by collaboration channel IDs. + --end-time int — Unix timestamp in seconds for the end of the query window. + --include-events bool — Include the underlying change events for each change when true. + --integration-ids []int — Filter by reporting integration IDs. + --orderby string — Field to sort the result by. [start_time, last_time] + --query string — Free-text or regular-expression search over change fields. + --start-time int — Unix timestamp in seconds for the start of the query window. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) — Whether more pages are available after this one. + - items (array) — Changes on the current page. + - account_id (integer) — Account this change belongs to. + - change_id (string) — Change ID, a MongoDB ObjectID hex string. + - change_key (string) — Stable key that groups events belonging to the same change. + - change_status (string) — Current lifecycle status of the change. + - channel_id (integer) — Collaboration channel this change is routed to. + - channel_name (string) — Name of the collaboration channel. + - channel_status (string) — Status of the collaboration channel. + - description (string) — Change description. + - end_time (integer) — Unix timestamp in seconds when the change ended. + - events (array) — Underlying change events, returned only when include_events is true. + - account_id (integer) — Account this change event belongs to. + - change_key (string) — Stable key that groups events belonging to the same change. + - change_status (string) — Lifecycle status of the change event. [Planned, Ready, Processing, Canceled, Done] + - channel_id (integer) — Collaboration channel this change event is routed to. + - created_at (integer) — Unix timestamp in seconds when the change event was created. + - deleted_at (integer) — Unix timestamp in seconds when the change event was deleted. + - description (string) — Change event description. + - event_id (string) — Change event ID, a MongoDB ObjectID hex string. + - event_time (integer) — Unix timestamp in seconds when the change event occurred. + - integration_id (integer) — Integration that reported this change event. + - labels (object) — Key-value labels attached to the change event. + - link (string) — External link to the source change record. + - title (string) — Change event title. + - updated_at (integer) — Unix timestamp in seconds when the change event was last updated. + - integration_id (integer) — Integration that reported this change. + - integration_name (string) — Name of the reporting integration. + - labels (object) — Key-value labels attached to the change. + - last_time (integer) — Unix timestamp in seconds of the most recent change activity. + - link (string) — External link to the source change record. + - start_time (integer) — Unix timestamp in seconds when the change started. + - title (string) — Change title. + - total (integer) — Total number of matching changes. +`, + Example: ` flashduty change list --data '{"asc":false,"end_time":1717046400,"include_events":false,"integration_ids":[362],"limit":10,"orderby":"start_time","p":1,"start_time":1716960000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("include-events") { + body["include_events"] = fIncludeEvents + } + if cmd.Flags().Changed("integration-ids") { + body["integration_ids"] = fIntegrationIDs + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + }) + if err != nil { + return err + } + req := new(flashduty.ListChangeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Changes.List(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1. (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Number of items per page. (1-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort in ascending order when true.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by collaboration channel IDs.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "Unix timestamp in seconds for the end of the query window.") + cmd.Flags().BoolVar(&fIncludeEvents, "include-events", false, "Include the underlying change events for each change when true.") + cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "Filter by reporting integration IDs.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the result by. [start_time, last_time]") + cmd.Flags().StringVar(&fQuery, "query", "", "Free-text or regular-expression search over change fields.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Unix timestamp in seconds for the start of the query window.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedChanges(root *cobra.Command) { + gChange := genGroup(root, "change", "On-call/Changes API") + genAddLeaf(gChange, genChangesListCmd()) +} diff --git a/internal/cli/zz_generated_channels.go b/internal/cli/zz_generated_channels.go new file mode 100644 index 0000000..ad83e1b --- /dev/null +++ b/internal/cli/zz_generated_channels.go @@ -0,0 +1,2864 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genChannelsChannelCreateCmd() *cobra.Command { + var dataJSON string + var fAutoResolveMode string + var fAutoResolveTimeout int64 + var fChannelName string + var fDescription string + var fDisableAutoClose bool + var fDisableOutlierDetection bool + var fIsExternalReportEnabled bool + var fIsPrivate bool + var fManagingTeamIDs []int + var fPluginIDs []int + var fTeamID int64 + cmd := &cobra.Command{ + Use: "create", + Short: "Create channel", + Long: `Create channel. + +Create a new channel for incident management. + +API: POST /channel/create (channelCreate) + +Request fields: + --auto-resolve-mode string — Auto-resolve timer reset mode. [trigger, update] + --auto-resolve-timeout int — Auto-resolve timeout in seconds. 0 disables auto-resolve. Max 30 days. (0-2592000) + --channel-name string (required) — Channel name. 1 to 59 characters. (1-59 chars) + --description string — Free-form description. Up to 500 characters. (≤500 chars) + --disable-auto-close bool — Disable automatic incident closing. + --disable-outlier-detection bool — Disable outlier incident detection. + --is-external-report-enabled bool — Allow external reporters to file incidents into this channel. + --is-private bool — When true, the channel is visible only to its managing teams. + --managing-team-ids []int — Additional teams that can manage the channel. Up to 3 entries. + --plugin-ids []int — IDs of plugins (integrations) subscribed to this channel. + --team-id int (required) — Owning team ID. + escalate_rule (object, via --data) — Default escalation rule applied to the channel. Omit to skip default escalation. + - aggr_window (integer) — Aggregation window in seconds. 0 disables aggregation. (0-3600) + - target (object) (required) — Notification target. At least one of 'person_ids', 'team_ids', 'schedule_to_role_ids', or 'emails' must be set, together with either 'by' or 'webhooks'. + - by (object) — Per-severity personal notification channels. Required unless 'webhooks' is provided. + - critical (array) — Channels for Critical events (e.g. 'voice', 'sms', 'email', 'feishu'). + - follow_preference (boolean) — When true, use each responder's personal preference instead of the lists below. + - info (array) — Channels for Info events. + - warning (array) — Channels for Warning events. + - emails (array) — Email addresses to notify (push-only scenarios). + - person_ids (array) — Member IDs to notify directly. + - schedule_to_role_ids (object) — Map of schedule ID to the role IDs on that schedule to notify. + - team_ids (array) — Team IDs to notify. + - webhooks (array) — Group chat / webhook targets. Required unless 'by' is provided. + - settings (object) (required) — Type-specific settings (chat IDs, URLs, etc.). + - type (string) (required) — Webhook type (e.g. 'feishu', 'dingtalk_app', 'wecom_app', 'slack', 'teams', 'custom'). + - template_id (string) (required) — Notification template ID (MongoDB ObjectID). + flapping (object, via --data) — Flapping detection configuration. + - in_mins (integer) — Observation window in minutes. (1-1440) + - is_disabled (boolean) — Disable flapping detection. + - max_changes (integer) — Max state changes allowed within 'in_mins'. (2-100) + - mute_mins (integer) — Mute duration in minutes after flapping is detected. (0-1440) + group (object, via --data) — Alert grouping configuration. + - all_equals_required (boolean) — When true, all listed keys must be present for grouping. + - cases (array) — Per-filter grouping overrides. + - equals (array) — Groups of label keys whose equality defines a bucket. + - i_keys (array) — Label keys used for intelligent grouping embeddings. + - i_score_threshold (number) — Intelligent grouping similarity threshold. (0.5-1) + - method (string) (required) — Grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - storm_threshold (integer) — Alert storm threshold. (0-10000) + - storm_thresholds (array) — Multi-level storm thresholds. + - time_window (integer) — Grouping time window in seconds. (min 0) + - window_type (string) — Window type. Defaults to 'tumbling'. [tumbling, sliding] + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - channel_id (integer) (required) — Newly created channel ID. + - channel_name (string) (required) — Channel name echoed back from the request. + - external_report_token (string) — External report token. Emitted only when external reporting is enabled. +`, + Example: ` flashduty channel create --data '{"auto_resolve_mode":"trigger","auto_resolve_timeout":86400,"channel_name":"Production Alerts","description":"Handles all production environment alerts","group":{"method":"p","time_window":10,"window_type":"tumbling"},"team_id":3521074710131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("auto-resolve-mode") { + body["auto_resolve_mode"] = fAutoResolveMode + } + if cmd.Flags().Changed("auto-resolve-timeout") { + body["auto_resolve_timeout"] = fAutoResolveTimeout + } + if cmd.Flags().Changed("channel-name") { + body["channel_name"] = fChannelName + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("disable-auto-close") { + body["disable_auto_close"] = fDisableAutoClose + } + if cmd.Flags().Changed("disable-outlier-detection") { + body["disable_outlier_detection"] = fDisableOutlierDetection + } + if cmd.Flags().Changed("is-external-report-enabled") { + body["is_external_report_enabled"] = fIsExternalReportEnabled + } + if cmd.Flags().Changed("is-private") { + body["is_private"] = fIsPrivate + } + if cmd.Flags().Changed("managing-team-ids") { + body["managing_team_ids"] = fManagingTeamIDs + } + if cmd.Flags().Changed("plugin-ids") { + body["plugin_ids"] = fPluginIDs + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateChannelRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAutoResolveMode, "auto-resolve-mode", "", "Auto-resolve timer reset mode. [trigger, update]") + cmd.Flags().Int64Var(&fAutoResolveTimeout, "auto-resolve-timeout", 0, "Auto-resolve timeout in seconds. 0 disables auto-resolve. Max 30 days. (0-2592000)") + cmd.Flags().StringVar(&fChannelName, "channel-name", "", "Channel name. 1 to 59 characters. (required) (1-59 chars)") + cmd.Flags().StringVar(&fDescription, "description", "", "Free-form description. Up to 500 characters. (≤500 chars)") + cmd.Flags().BoolVar(&fDisableAutoClose, "disable-auto-close", false, "Disable automatic incident closing.") + cmd.Flags().BoolVar(&fDisableOutlierDetection, "disable-outlier-detection", false, "Disable outlier incident detection.") + cmd.Flags().BoolVar(&fIsExternalReportEnabled, "is-external-report-enabled", false, "Allow external reporters to file incidents into this channel.") + cmd.Flags().BoolVar(&fIsPrivate, "is-private", false, "When true, the channel is visible only to its managing teams.") + cmd.Flags().IntSliceVar(&fManagingTeamIDs, "managing-team-ids", nil, "Additional teams that can manage the channel. Up to 3 entries.") + cmd.Flags().IntSliceVar(&fPluginIDs, "plugin-ids", nil, "IDs of plugins (integrations) subscribed to this channel.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelDeleteCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete channel", + Long: `Delete channel. + +Delete a channel and all associated configuration. + +API: POST /channel/delete (channelDelete) + +Request fields: + --channel-id int (required) — Channel ID. +`, + Example: ` flashduty channel delete --data '{"channel_id":3521074710131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelDisableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "disable", + Short: "Disable channel", + Long: `Disable channel. + +Disable a channel to stop incident routing without deleting it. + +API: POST /channel/disable (channelDisable) + +Request fields: + --channel-id int (required) — Channel ID. +`, + Example: ` flashduty channel disable --data '{"channel_id":3521074710131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/disable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEnableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "enable", + Short: "Enable channel", + Long: `Enable channel. + +Enable a disabled channel to resume incident routing. + +API: POST /channel/enable (channelEnable) + +Request fields: + --channel-id int (required) — Channel ID. +`, + Example: ` flashduty channel enable --data '{"channel_id":3521074710131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/enable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEscalateRuleCreateCmd() *cobra.Command { + var dataJSON string + var fAggrWindow int64 + var fChannelID int64 + var fDescription string + var fPriority int64 + var fRuleName string + var fTemplateID string + cmd := &cobra.Command{ + Use: "escalate-rule-create", + Short: "Create escalation rule", + Long: `Create escalation rule. + +Create an escalation rule defining who gets notified and when during an incident. + +API: POST /channel/escalate/rule/create (channelEscalateRuleCreate) + +Request fields: + --aggr-window int — Aggregation window in seconds. 0 disables aggregation. (0-3600) + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --priority int — Evaluation priority. Lower runs first. (0-200) + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + --template-id string (required) — Notification template ID (MongoDB ObjectID). + filters (array, via --data) — Or-of-and filter tree. Each outer element is an AND group; within each group, all conditions must match. + layers (array, via --data) (required) — Escalation levels in order. At least one level is required. + - escalate_window (integer) — Wait before moving to the next level, in minutes. (0-720) + - force_escalate (boolean) — When true, always escalate regardless of acknowledgement. + - max_times (integer) — Max repeat notifications within the level. (0-6) + - notify_step (number) — Repeat interval in minutes. (0.5-120) + - target (object) (required) — Notification target. At least one of 'person_ids', 'team_ids', 'schedule_to_role_ids', or 'emails' must be set, together with either 'by' or 'webhooks'. + - by (object) — Per-severity personal notification channels. Required unless 'webhooks' is provided. + - critical (array) — Channels for Critical events (e.g. 'voice', 'sms', 'email', 'feishu'). + - follow_preference (boolean) — When true, use each responder's personal preference instead of the lists below. + - info (array) — Channels for Info events. + - warning (array) — Channels for Warning events. + - emails (array) — Email addresses to notify (push-only scenarios). + - person_ids (array) — Member IDs to notify directly. + - schedule_to_role_ids (object) — Map of schedule ID to the role IDs on that schedule to notify. + - team_ids (array) — Team IDs to notify. + - webhooks (array) — Group chat / webhook targets. Required unless 'by' is provided. + - settings (object) (required) — Type-specific settings (chat IDs, URLs, etc.). + - type (string) (required) — Webhook type (e.g. 'feishu', 'dingtalk_app', 'wecom_app', 'slack', 'teams', 'custom'). + time_filters (array, via --data) — Optional recurring time windows during which the rule applies. + - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar. + - end (string) — End of the window in 'HH:MM'. + - is_off (boolean) — When true, match days marked as days-off in the calendar. + - repeat (array) — Days of the week this window repeats on. Empty means every day. + - start (string) — Start of the window in 'HH:MM'. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID). + - rule_name (string) (required) — Rule name echoed back from the request. +`, + Example: ` flashduty channel escalate-rule-create --data '{"channel_id":3521074710131,"description":"Notify primary on-call, then escalate to secondary after 30 minutes","layers":[{"escalate_window":30,"force_escalate":false,"max_times":3,"notify_step":10,"target":{"by":{"follow_preference":true},"person_ids":[3790925372131]}}],"rule_name":"On-call escalation","template_id":"6321aad26c12104586a88916"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggr-window") { + body["aggr_window"] = fAggrWindow + } + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + if cmd.Flags().Changed("template-id") { + body["template_id"] = fTemplateID + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateEscalationRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelEscalateRuleCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAggrWindow, "aggr-window", 0, "Aggregation window in seconds. 0 disables aggregation. (0-3600)") + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first. (0-200)") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Notification template ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEscalateRuleDeleteCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "escalate-rule-delete", + Short: "Delete escalation rule", + Long: `Delete escalation rule. + +Delete an escalation rule. + +API: POST /channel/escalate/rule/delete (channelEscalateRuleDelete) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel escalate-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelEscalateRuleDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/escalate/rule/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEscalateRuleDisableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "escalate-rule-disable", + Short: "Disable escalation rule", + Long: `Disable escalation rule. + +Disable an escalation rule without deleting it. + +API: POST /channel/escalate/rule/disable (channelEscalateRuleDisable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel escalate-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelEscalateRuleDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/escalate/rule/disable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEscalateRuleEnableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "escalate-rule-enable", + Short: "Enable escalation rule", + Long: `Enable escalation rule. + +Enable a disabled escalation rule. + +API: POST /channel/escalate/rule/enable (channelEscalateRuleEnable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel escalate-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelEscalateRuleEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/escalate/rule/enable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEscalateRuleInfoCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "escalate-rule-info", + Short: "Get escalation rule detail", + Long: `Get escalation rule detail. + +Retrieve detailed information for a specific escalation rule. + +API: POST /channel/escalate/rule/info (channelEscalateRuleInfo) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account ID. + - aggr_window (integer) (required) — Aggregation window in seconds. + - channel_id (integer) (required) — Channel the rule belongs to. + - channel_name (string) — Channel name, populated for cross-channel listing responses. + - created_at (integer) (required) — Creation timestamp (unix seconds). + - deleted_at (integer) — Deletion timestamp (unix seconds). Emitted only for soft-deleted rules. + - description (string) (required) — Rule description. + - filters (object) (required) + - layers (array) (required) — Escalation levels in order. + - escalate_window (integer) — Wait before moving to the next level, in minutes. (0-720) + - force_escalate (boolean) — When true, always escalate regardless of acknowledgement. + - max_times (integer) — Max repeat notifications within the level. (0-6) + - notify_step (number) — Repeat interval in minutes. (0.5-120) + - target (object) (required) — Notification target. At least one of 'person_ids', 'team_ids', 'schedule_to_role_ids', or 'emails' must be set, together with either 'by' or 'webhooks'. + - by (object) — Per-severity personal notification channels. Required unless 'webhooks' is provided. + - critical (array) — Channels for Critical events (e.g. 'voice', 'sms', 'email', 'feishu'). + - follow_preference (boolean) — When true, use each responder's personal preference instead of the lists below. + - info (array) — Channels for Info events. + - warning (array) — Channels for Warning events. + - emails (array) — Email addresses to notify (push-only scenarios). + - person_ids (array) — Member IDs to notify directly. + - schedule_to_role_ids (object) — Map of schedule ID to the role IDs on that schedule to notify. + - team_ids (array) — Team IDs to notify. + - webhooks (array) — Group chat / webhook targets. Required unless 'by' is provided. + - settings (object) (required) — Type-specific settings (chat IDs, URLs, etc.). + - type (string) (required) — Webhook type (e.g. 'feishu', 'dingtalk_app', 'wecom_app', 'slack', 'teams', 'custom'). + - priority (integer) (required) — Evaluation priority. Lower runs first. + - rule_id (string) (required) — Escalation rule ID (MongoDB ObjectID). + - rule_name (string) (required) — Rule name. + - status (string) (required) — Rule status. [enabled, disabled] + - template_id (string) (required) — Notification template ID (MongoDB ObjectID). + - time_filters (array) (required) — Recurring time windows during which the rule applies. + - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar. + - end (string) — End of the window in 'HH:MM'. + - is_off (boolean) — When true, match days marked as days-off in the calendar. + - repeat (array) — Days of the week this window repeats on. Empty means every day. + - start (string) — Start of the window in 'HH:MM'. + - updated_at (integer) (required) — Last update timestamp (unix seconds). + - updated_by (integer) (required) — Member ID that last updated the rule. +`, + Example: ` flashduty channel escalate-rule-info --data '{"channel_id":1001,"rule_id":"6621b23f4a2c5e0012ab34d0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelEscalateRuleInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEscalateRuleListCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "escalate-rule-list", + Short: "List escalation rules", + Long: `List escalation rules. + +List all escalation rules for a channel. + +API: POST /channel/escalate/rule/list (channelEscalateRuleList) + +Request fields: + --channel-id int (required) — Channel to list rules for. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - account_id (integer) (required) — Owning account ID. + - aggr_window (integer) (required) — Aggregation window in seconds. + - channel_id (integer) (required) — Channel the rule belongs to. + - channel_name (string) — Channel name, populated for cross-channel listing responses. + - created_at (integer) (required) — Creation timestamp (unix seconds). + - deleted_at (integer) — Deletion timestamp (unix seconds). Emitted only for soft-deleted rules. + - description (string) (required) — Rule description. + - filters (object) (required) + - layers (array) (required) — Escalation levels in order. + - escalate_window (integer) — Wait before moving to the next level, in minutes. (0-720) + - force_escalate (boolean) — When true, always escalate regardless of acknowledgement. + - max_times (integer) — Max repeat notifications within the level. (0-6) + - notify_step (number) — Repeat interval in minutes. (0.5-120) + - target (object) (required) — Notification target. At least one of 'person_ids', 'team_ids', 'schedule_to_role_ids', or 'emails' must be set, together with either 'by' or 'webhooks'. + - by (object) — Per-severity personal notification channels. Required unless 'webhooks' is provided. + - emails (array) — Email addresses to notify (push-only scenarios). + - person_ids (array) — Member IDs to notify directly. + - schedule_to_role_ids (object) — Map of schedule ID to the role IDs on that schedule to notify. + - team_ids (array) — Team IDs to notify. + - webhooks (array) — Group chat / webhook targets. Required unless 'by' is provided. + - priority (integer) (required) — Evaluation priority. Lower runs first. + - rule_id (string) (required) — Escalation rule ID (MongoDB ObjectID). + - rule_name (string) (required) — Rule name. + - status (string) (required) — Rule status. [enabled, disabled] + - template_id (string) (required) — Notification template ID (MongoDB ObjectID). + - time_filters (array) (required) — Recurring time windows during which the rule applies. + - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar. + - end (string) — End of the window in 'HH:MM'. + - is_off (boolean) — When true, match days marked as days-off in the calendar. + - repeat (array) — Days of the week this window repeats on. Empty means every day. + - start (string) — Start of the window in 'HH:MM'. + - updated_at (integer) (required) — Last update timestamp (unix seconds). + - updated_by (integer) (required) — Member ID that last updated the rule. +`, + Example: ` flashduty channel escalate-rule-list --data '{"channel_id":1001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelScopedListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelEscalateRuleList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelEscalateRuleUpdateCmd() *cobra.Command { + var dataJSON string + var fAggrWindow int64 + var fChannelID int64 + var fDescription string + var fPriority int64 + var fRuleID string + var fRuleName string + var fTemplateID string + cmd := &cobra.Command{ + Use: "escalate-rule-update", + Short: "Update escalation rule", + Long: `Update escalation rule. + +Update an existing escalation rule configuration. + +API: POST /channel/escalate/rule/update (channelEscalateRuleUpdate) + +Request fields: + --aggr-window int — Aggregation window in seconds. 0 disables aggregation. + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --priority int — Evaluation priority. Lower runs first. + --rule-id string (required) — Escalation rule ID (MongoDB ObjectID). + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + --template-id string (required) — Notification template ID (MongoDB ObjectID). + filters (object, via --data) + layers (array, via --data) (required) — Escalation levels in order. At least one level is required. + - escalate_window (integer) — Wait before moving to the next level, in minutes. (0-720) + - force_escalate (boolean) — When true, always escalate regardless of acknowledgement. + - max_times (integer) — Max repeat notifications within the level. (0-6) + - notify_step (number) — Repeat interval in minutes. (0.5-120) + - target (object) (required) — Notification target. At least one of 'person_ids', 'team_ids', 'schedule_to_role_ids', or 'emails' must be set, together with either 'by' or 'webhooks'. + - by (object) — Per-severity personal notification channels. Required unless 'webhooks' is provided. + - critical (array) — Channels for Critical events (e.g. 'voice', 'sms', 'email', 'feishu'). + - follow_preference (boolean) — When true, use each responder's personal preference instead of the lists below. + - info (array) — Channels for Info events. + - warning (array) — Channels for Warning events. + - emails (array) — Email addresses to notify (push-only scenarios). + - person_ids (array) — Member IDs to notify directly. + - schedule_to_role_ids (object) — Map of schedule ID to the role IDs on that schedule to notify. + - team_ids (array) — Team IDs to notify. + - webhooks (array) — Group chat / webhook targets. Required unless 'by' is provided. + - settings (object) (required) — Type-specific settings (chat IDs, URLs, etc.). + - type (string) (required) — Webhook type (e.g. 'feishu', 'dingtalk_app', 'wecom_app', 'slack', 'teams', 'custom'). + time_filters (array, via --data) — Optional recurring time windows during which the rule applies. + - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar. + - end (string) — End of the window in 'HH:MM'. + - is_off (boolean) — When true, match days marked as days-off in the calendar. + - repeat (array) — Days of the week this window repeats on. Empty means every day. + - start (string) — Start of the window in 'HH:MM'. +`, + Example: ` flashduty channel escalate-rule-update --data '{"channel_id":1001,"layers":[{"target":{"by":{"critical":["voice"],"warning":["sms"]},"person_ids":[42]}}],"rule_id":"6621b23f4a2c5e0012ab34d0","rule_name":"Default escalation","template_id":"6621b23f4a2c5e0012ab34d1"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("aggr-window") { + body["aggr_window"] = fAggrWindow + } + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + if cmd.Flags().Changed("template-id") { + body["template_id"] = fTemplateID + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateEscalationRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelEscalateRuleUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/escalate/rule/update") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fAggrWindow, "aggr-window", 0, "Aggregation window in seconds. 0 disables aggregation.") + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Escalation rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Notification template ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInfoCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "info", + Short: "Get channel detail", + Long: `Get channel detail. + +Retrieve detailed information for a specific channel. + +API: POST /channel/info (channelInfo) + +Request fields: + --channel-id int (required) — Channel ID to fetch. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) — Owning account ID. + - active_incident_highest_severity (string) — Highest severity among active incidents in the channel. + - auto_resolve_mode (string) — Auto-resolve timer reset mode. [trigger, update] + - auto_resolve_timeout (integer) — Auto-resolve timeout in seconds. 0 disables auto-resolve. + - channel_id (integer) — Channel ID. + - channel_name (string) — Channel name. + - created_at (integer) — Creation timestamp (unix seconds). + - creator_id (integer) — Member ID who created the channel. + - deleted_at (integer) — Deletion timestamp (unix seconds). Non-zero only for soft-deleted channels. + - description (string) — Free-form description. + - disable_auto_close (boolean) — When true, automatic incident closing is disabled. + - disable_outlier_detection (boolean) — When true, outlier incident detection is disabled. + - external_report_token (string) — Token granted to external reporters when external reporting is enabled. + - flapping (object) — Flapping detection configuration. + - in_mins (integer) — Observation window in minutes. (1-1440) + - is_disabled (boolean) — Disable flapping detection. + - max_changes (integer) — Max state changes allowed within 'in_mins'. (2-100) + - mute_mins (integer) — Mute duration in minutes after flapping is detected. (0-1440) + - group (object) — Alert grouping configuration. + - all_equals_required (boolean) — When true, all listed keys must be present for grouping. + - cases (array) — Per-filter grouping overrides. + - equals (array) — Groups of label keys whose equality defines a bucket. + - i_keys (array) — Label keys used for intelligent grouping embeddings. + - i_score_threshold (number) — Intelligent grouping similarity threshold. (0.5-1) + - method (string) (required) — Grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - storm_threshold (integer) — Alert storm threshold. (0-10000) + - storm_thresholds (array) — Multi-level storm thresholds. + - time_window (integer) — Grouping time window in minutes. Default max is 1440 minutes (24 h); extended accounts may allow up to 43200 minutes (30 days). (min 0) + - window_type (string) — Window type. Defaults to 'tumbling'. [tumbling, sliding] + - is_external_report_enabled (boolean) — Whether external reporters can file incidents into this channel. + - is_private (boolean) — When true, the channel is visible only to its managing teams. + - is_starred (boolean) — Whether the current user has starred this channel. + - last_incident_at (integer) — Timestamp of the most recent incident (unix seconds). + - managing_team_ids (array) — Additional teams that can manage the channel. + - progress_to_incident_cnts (object) + - Processing (integer) (required) — Count of processing incidents in the last 30 days. + - Triggered (integer) (required) — Count of triggered incidents in the last 30 days. + - status (string) — Channel status. [enabled, disabled, deleted] + - team_id (integer) — Owning team ID. + - updated_at (integer) — Last update timestamp (unix seconds). +`, + Example: ` flashduty channel info --data '{"channel_id":1001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID to fetch. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInfosCmd() *cobra.Command { + var dataJSON string + var fChannelIDs []int + cmd := &cobra.Command{ + Use: "infos", + Short: "Batch get channels", + Long: `Batch get channels. + +Retrieve multiple channels by their IDs. + +API: POST /channel/infos (channelInfos) + +Request fields: + --channel-ids []int (required) — Channel IDs to look up. Up to 1000. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - channel_id (integer) (required) — Channel ID. + - channel_name (string) (required) — Channel name. + - status (string) — Channel status. [enabled, disabled] +`, + Example: ` flashduty channel infos --data '{"channel_ids":[1001,1002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelInfosRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelInfos(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Channel IDs to look up. Up to 1000. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInhibitRuleCreateCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fDescription string + var fEquals []string + var fIsDirectlyDiscard bool + var fPriority int64 + var fRuleName string + cmd := &cobra.Command{ + Use: "inhibit-rule-create", + Short: "Create inhibit rule", + Long: `Create inhibit rule. + +Create an inhibit rule to suppress lower-priority alerts when higher-priority ones are firing. + +API: POST /channel/inhibit/rule/create (channelInhibitRuleCreate) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --equals []string (required) — Label keys used to pair source and target alerts. + --is-directly-discard bool — When true, suppressed target alerts are dropped instead of merged. + --priority int — Evaluation priority. Lower runs first. + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + source_filters (array, via --data) — Or-of-and filter tree. Each outer element is an AND group; within each group, all conditions must match. + target_filters (array, via --data) — Or-of-and filter tree. Each outer element is an AND group; within each group, all conditions must match. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID). + - rule_name (string) (required) — Rule name echoed back from the request. +`, + Example: ` flashduty channel inhibit-rule-create --data '{"channel_id":3521074710131,"description":"When a Critical alert fires, suppress matching Info alerts","equals":["labels.cluster","labels.service"],"is_directly_discard":false,"rule_name":"Suppress Info when Critical fires","source_filters":[[{"key":"severity","oper":"IN","vals":["Critical"]}]],"target_filters":[[{"key":"severity","oper":"IN","vals":["Info"]}]]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("equals") { + body["equals"] = fEquals + } + if cmd.Flags().Changed("is-directly-discard") { + body["is_directly_discard"] = fIsDirectlyDiscard + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateInhibitRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelInhibitRuleCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().StringSliceVar(&fEquals, "equals", nil, "Label keys used to pair source and target alerts. (required)") + cmd.Flags().BoolVar(&fIsDirectlyDiscard, "is-directly-discard", false, "When true, suppressed target alerts are dropped instead of merged.") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInhibitRuleDeleteCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "inhibit-rule-delete", + Short: "Delete inhibit rule", + Long: `Delete inhibit rule. + +Delete an inhibit rule. + +API: POST /channel/inhibit/rule/delete (channelInhibitRuleDelete) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel inhibit-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelInhibitRuleDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/inhibit/rule/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInhibitRuleDisableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "inhibit-rule-disable", + Short: "Disable inhibit rule", + Long: `Disable inhibit rule. + +Disable an inhibit rule without deleting it. + +API: POST /channel/inhibit/rule/disable (channelInhibitRuleDisable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel inhibit-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelInhibitRuleDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/inhibit/rule/disable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInhibitRuleEnableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "inhibit-rule-enable", + Short: "Enable inhibit rule", + Long: `Enable inhibit rule. + +Enable a disabled inhibit rule. + +API: POST /channel/inhibit/rule/enable (channelInhibitRuleEnable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel inhibit-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelInhibitRuleEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/inhibit/rule/enable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInhibitRuleListCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "inhibit-rule-list", + Short: "List inhibit rules", + Long: `List inhibit rules. + +List all inhibit rules configured for a channel. + +API: POST /channel/inhibit/rule/list (channelInhibitRuleList) + +Request fields: + --channel-id int (required) — Channel to list rules for. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - account_id (integer) (required) + - channel_id (integer) (required) + - created_at (integer) (required) + - deleted_at (integer) + - description (string) (required) + - equals (array) (required) — Label keys used to pair source and target alerts. + - is_directly_discard (boolean) (required) + - priority (integer) (required) + - rule_id (string) (required) + - rule_name (string) (required) + - source_filters (object) (required) + - status (string) (required) [enabled, disabled] + - target_filters (object) (required) + - updated_at (integer) (required) + - updated_by (integer) (required) +`, + Example: ` flashduty channel inhibit-rule-list --data '{"channel_id":1001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelScopedListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelInhibitRuleList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelInhibitRuleUpdateCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fDescription string + var fEquals []string + var fIsDirectlyDiscard bool + var fPriority int64 + var fRuleID string + var fRuleName string + cmd := &cobra.Command{ + Use: "inhibit-rule-update", + Short: "Update inhibit rule", + Long: `Update inhibit rule. + +Update an existing inhibit rule configuration. + +API: POST /channel/inhibit/rule/update (channelInhibitRuleUpdate) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --equals []string (required) — Label keys used to pair source and target alerts. + --is-directly-discard bool — When true, suppressed target alerts are dropped instead of merged. + --priority int — Evaluation priority. Lower runs first. + --rule-id string (required) — Inhibit rule ID (MongoDB ObjectID). + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + source_filters (object, via --data) + target_filters (object, via --data) +`, + Example: ` flashduty channel inhibit-rule-update --data '{"channel_id":1001,"equals":["labels.cluster"],"rule_id":"6621b23f4a2c5e0012ab34ce","rule_name":"Suppress downstream"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("equals") { + body["equals"] = fEquals + } + if cmd.Flags().Changed("is-directly-discard") { + body["is_directly_discard"] = fIsDirectlyDiscard + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateInhibitRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelInhibitRuleUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/inhibit/rule/update") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().StringSliceVar(&fEquals, "equals", nil, "Label keys used to pair source and target alerts. (required)") + cmd.Flags().BoolVar(&fIsDirectlyDiscard, "is-directly-discard", false, "When true, suppressed target alerts are dropped instead of merged.") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Inhibit rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fChannelIDs []int + var fChannelName string + var fIsBrief bool + var fIsMyManaged bool + var fIsMyStarred bool + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "list", + Short: "List channels", + Long: `List channels. + +List channels accessible to the current user with optional filters. + +API: POST /channel/list (channelList) + +Request fields: + --page int — Page number (1-based). (min 1) + --limit int — Page size. Defaults to 100 when omitted. (1-100) + --search-after-ctx string + --asc bool — When true, sort ascending. + --channel-ids []int — Filter by explicit channel IDs. + --channel-name string — Exact-match filter on channel name. Takes priority over 'query' for name filtering. + --is-brief bool — When true, return only brief fields ('channel_id', 'channel_name', 'description', 'status'). + --is-my-managed bool — When true, return only channels the caller manages. + --is-my-starred bool — When true, return only channels the caller has starred. Mutually exclusive with 'is_my_team'. + --is-my-team bool — When true, return channels owned by the caller's teams. Mutually exclusive with 'is_my_starred'. + --orderby string — Field used to order results. [ranking, created_at, updated_at, channel_name, last_incident_at] + --query string — Free-text query against channel name/description. + --team-ids []int — Filter by team IDs. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — Whether more pages are available. + - items (array) (required) + - account_id (integer) — Owning account ID. + - active_incident_highest_severity (string) — Highest severity among active incidents in the channel. + - auto_resolve_mode (string) — Auto-resolve timer reset mode. [trigger, update] + - auto_resolve_timeout (integer) — Auto-resolve timeout in seconds. 0 disables auto-resolve. + - channel_id (integer) — Channel ID. + - channel_name (string) — Channel name. + - created_at (integer) — Creation timestamp (unix seconds). + - creator_id (integer) — Member ID who created the channel. + - deleted_at (integer) — Deletion timestamp (unix seconds). Non-zero only for soft-deleted channels. + - description (string) — Free-form description. + - disable_auto_close (boolean) — When true, automatic incident closing is disabled. + - disable_outlier_detection (boolean) — When true, outlier incident detection is disabled. + - external_report_token (string) — Token granted to external reporters when external reporting is enabled. + - flapping (object) — Flapping detection configuration. + - in_mins (integer) — Observation window in minutes. (1-1440) + - is_disabled (boolean) — Disable flapping detection. + - max_changes (integer) — Max state changes allowed within 'in_mins'. (2-100) + - mute_mins (integer) — Mute duration in minutes after flapping is detected. (0-1440) + - group (object) — Alert grouping configuration. + - all_equals_required (boolean) — When true, all listed keys must be present for grouping. + - cases (array) — Per-filter grouping overrides. + - equals (array) — Groups of label keys whose equality defines a bucket. + - i_keys (array) — Label keys used for intelligent grouping embeddings. + - i_score_threshold (number) — Intelligent grouping similarity threshold. (0.5-1) + - method (string) (required) — Grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - storm_threshold (integer) — Alert storm threshold. (0-10000) + - storm_thresholds (array) — Multi-level storm thresholds. + - time_window (integer) — Grouping time window in minutes. Default max is 1440 minutes (24 h); extended accounts may allow up to 43200 minutes (30 days). (min 0) + - window_type (string) — Window type. Defaults to 'tumbling'. [tumbling, sliding] + - is_external_report_enabled (boolean) — Whether external reporters can file incidents into this channel. + - is_private (boolean) — When true, the channel is visible only to its managing teams. + - is_starred (boolean) — Whether the current user has starred this channel. + - last_incident_at (integer) — Timestamp of the most recent incident (unix seconds). + - managing_team_ids (array) — Additional teams that can manage the channel. + - progress_to_incident_cnts (object) + - Processing (integer) (required) — Count of processing incidents in the last 30 days. + - Triggered (integer) (required) — Count of triggered incidents in the last 30 days. + - status (string) — Channel status. [enabled, disabled, deleted] + - team_id (integer) — Owning team ID. + - updated_at (integer) — Last update timestamp (unix seconds). + - total (integer) (required) — Total matching channels. +`, + Example: ` flashduty channel list --data '{"asc":false,"limit":20,"orderby":"created_at","p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("channel-name") { + body["channel_name"] = fChannelName + } + if cmd.Flags().Changed("is-brief") { + body["is_brief"] = fIsBrief + } + if cmd.Flags().Changed("is-my-managed") { + body["is_my_managed"] = fIsMyManaged + } + if cmd.Flags().Changed("is-my-starred") { + body["is_my_starred"] = fIsMyStarred + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ListChannelsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number (1-based). (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Defaults to 100 when omitted. (1-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "When true, sort ascending.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Filter by explicit channel IDs.") + cmd.Flags().StringVar(&fChannelName, "channel-name", "", "Exact-match filter on channel name. Takes priority over 'query' for name filtering.") + cmd.Flags().BoolVar(&fIsBrief, "is-brief", false, "When true, return only brief fields ('channel_id', 'channel_name', 'description', 'status').") + cmd.Flags().BoolVar(&fIsMyManaged, "is-my-managed", false, "When true, return only channels the caller manages.") + cmd.Flags().BoolVar(&fIsMyStarred, "is-my-starred", false, "When true, return only channels the caller has starred. Mutually exclusive with 'is_my_team'.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "When true, return channels owned by the caller's teams. Mutually exclusive with 'is_my_starred'.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field used to order results. [ranking, created_at, updated_at, channel_name, last_incident_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Free-text query against channel name/description.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelNotifyRuleCreateCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "notify-rule-create", + Short: "Create channel notification rule", + Long: `Create channel notification rule. + +Create a notification rule for a channel. + +API: POST /channel/notify/rule/create (channelNotifyRuleCreate) +`, + Example: ` flashduty channel notify-rule-create --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + resp, err := ctx.Client.Channels.ChannelNotifyRuleCreate(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/notify/rule/create") + return nil + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelNotifyRuleDeleteCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "notify-rule-delete", + Short: "Delete channel notification rule", + Long: `Delete channel notification rule. + +Delete a channel notification rule. + +API: POST /channel/notify/rule/delete (channelNotifyRuleDelete) +`, + Example: ` flashduty channel notify-rule-delete --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + resp, err := ctx.Client.Channels.ChannelNotifyRuleDelete(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/notify/rule/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelNotifyRuleDisableCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "notify-rule-disable", + Short: "Disable channel notification rule", + Long: `Disable channel notification rule. + +Disable a channel notification rule without deleting it. + +API: POST /channel/notify/rule/disable (channelNotifyRuleDisable) +`, + Example: ` flashduty channel notify-rule-disable --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + resp, err := ctx.Client.Channels.ChannelNotifyRuleDisable(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/notify/rule/disable") + return nil + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelNotifyRuleEnableCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "notify-rule-enable", + Short: "Enable channel notification rule", + Long: `Enable channel notification rule. + +Enable a disabled channel notification rule. + +API: POST /channel/notify/rule/enable (channelNotifyRuleEnable) +`, + Example: ` flashduty channel notify-rule-enable --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + resp, err := ctx.Client.Channels.ChannelNotifyRuleEnable(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/notify/rule/enable") + return nil + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelNotifyRuleListCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "notify-rule-list", + Short: "List channel notification rules", + Long: `List channel notification rules. + +List all notification rules configured for a channel. + +API: POST /channel/notify/rule/list (channelNotifyRuleList) +`, + Example: ` flashduty channel notify-rule-list --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + resp, err := ctx.Client.Channels.ChannelNotifyRuleList(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/notify/rule/list") + return nil + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelNotifyRuleUpdateCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "notify-rule-update", + Short: "Update channel notification rule", + Long: `Update channel notification rule. + +Update an existing channel notification rule. + +API: POST /channel/notify/rule/update (channelNotifyRuleUpdate) +`, + Example: ` flashduty channel notify-rule-update --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + resp, err := ctx.Client.Channels.ChannelNotifyRuleUpdate(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/notify/rule/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelSilenceRuleCreateCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fDescription string + var fFromIncidentID string + var fIsAutoDelete bool + var fIsDirectlyDiscard bool + var fPriority int64 + var fRuleName string + cmd := &cobra.Command{ + Use: "silence-rule-create", + Short: "Create silence rule", + Long: `Create silence rule. + +Create a silence rule to suppress notifications matching specified conditions. + +API: POST /channel/silence/rule/create (channelSilenceRuleCreate) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --from-incident-id string — Source incident ID when the silence was created from an incident. + --is-auto-delete bool — When true, the silence rule is automatically deleted after its time window expires. Defaults to false. + --is-directly-discard bool — When true, silenced alerts are dropped instead of suppressed into incidents. + --priority int — Evaluation priority. Lower runs first. + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + filters (array, via --data) — Or-of-and filter tree. Each outer element is an AND group; within each group, all conditions must match. + time_filter (object, via --data) — One-off time window defined by unix seconds. + - end_time (integer) (required) — Window end (unix seconds). + - start_time (integer) (required) — Window start (unix seconds). Must be less than 'end_time'. + time_filters (array, via --data) — Recurring time windows during which silencing applies. Mutually exclusive with 'time_filter'. + - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar. + - end (string) — End of the window in 'HH:MM'. + - is_off (boolean) — When true, match days marked as days-off in the calendar. + - repeat (array) — Days of the week this window repeats on. Empty means every day. + - start (string) — Start of the window in 'HH:MM'. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID). + - rule_name (string) (required) — Rule name echoed back from the request. +`, + Example: ` flashduty channel silence-rule-create --data '{"channel_id":3521074710131,"description":"Silence all Info alerts during planned maintenance","filters":[[{"key":"severity","oper":"IN","vals":["Info"]}]],"is_directly_discard":false,"rule_name":"Maintenance window silence","time_filter":{"end_time":1773414000,"start_time":1773388800}}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("from-incident-id") { + body["from_incident_id"] = fFromIncidentID + } + if cmd.Flags().Changed("is-auto-delete") { + body["is_auto_delete"] = fIsAutoDelete + } + if cmd.Flags().Changed("is-directly-discard") { + body["is_directly_discard"] = fIsDirectlyDiscard + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateSilenceRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelSilenceRuleCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().StringVar(&fFromIncidentID, "from-incident-id", "", "Source incident ID when the silence was created from an incident.") + cmd.Flags().BoolVar(&fIsAutoDelete, "is-auto-delete", false, "When true, the silence rule is automatically deleted after its time window expires. Defaults to false.") + cmd.Flags().BoolVar(&fIsDirectlyDiscard, "is-directly-discard", false, "When true, silenced alerts are dropped instead of suppressed into incidents.") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelSilenceRuleDeleteCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "silence-rule-delete", + Short: "Delete silence rule", + Long: `Delete silence rule. + +Delete a silence rule. + +API: POST /channel/silence/rule/delete (channelSilenceRuleDelete) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel silence-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelSilenceRuleDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/silence/rule/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelSilenceRuleDisableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "silence-rule-disable", + Short: "Disable silence rule", + Long: `Disable silence rule. + +Disable a silence rule without deleting it. + +API: POST /channel/silence/rule/disable (channelSilenceRuleDisable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel silence-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelSilenceRuleDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/silence/rule/disable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelSilenceRuleEnableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "silence-rule-enable", + Short: "Enable silence rule", + Long: `Enable silence rule. + +Enable a disabled silence rule. + +API: POST /channel/silence/rule/enable (channelSilenceRuleEnable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel silence-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelSilenceRuleEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/silence/rule/enable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelSilenceRuleListCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "silence-rule-list", + Short: "List silence rules", + Long: `List silence rules. + +List all silence rules configured for a channel. + +API: POST /channel/silence/rule/list (channelSilenceRuleList) + +Request fields: + --channel-id int (required) — Channel to list rules for. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - account_id (integer) (required) + - channel_id (integer) (required) + - created_at (integer) (required) + - deleted_at (integer) + - description (string) (required) + - filters (object) (required) + - from_incident_id (string) — Source incident ID when the silence was created from an incident. + - is_auto_delete (boolean) — When true, the silence rule is automatically deleted after its time window expires. Defaults to false. + - is_directly_discard (boolean) (required) — When true, silenced alerts are dropped instead of suppressed into incidents. + - is_effective (boolean) (required) — Whether the rule is currently in effect. + - priority (integer) (required) — Evaluation priority. Lower runs first. + - rule_id (string) (required) + - rule_name (string) (required) + - status (string) (required) [enabled, disabled] + - time_filter (object) (required) — One-off time window defined by unix seconds. + - end_time (integer) (required) — Window end (unix seconds). Must be > 0. + - start_time (integer) (required) — Window start (unix seconds). Must be > 0 and less than 'end_time'. + - time_filters (array) (required) — Recurring time windows. + - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar. + - end (string) — End of the window in 'HH:MM'. + - is_off (boolean) — When true, match days marked as days-off in the calendar. + - repeat (array) — Days of the week this window repeats on. Empty means every day. + - start (string) — Start of the window in 'HH:MM'. + - updated_at (integer) (required) + - updated_by (integer) (required) +`, + Example: ` flashduty channel silence-rule-list --data '{"channel_id":1001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelScopedListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelSilenceRuleList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelSilenceRuleUpdateCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fDescription string + var fIsAutoDelete bool + var fIsDirectlyDiscard bool + var fPriority int64 + var fRuleID string + var fRuleName string + cmd := &cobra.Command{ + Use: "silence-rule-update", + Short: "Update silence rule", + Long: `Update silence rule. + +Update an existing silence rule configuration. + +API: POST /channel/silence/rule/update (channelSilenceRuleUpdate) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --is-auto-delete bool — When true, the silence rule is automatically deleted after its time window expires. Defaults to false. + --is-directly-discard bool — When true, silenced alerts are dropped instead of suppressed into incidents. + --priority int — Evaluation priority. Lower runs first. + --rule-id string (required) — Silence rule ID (MongoDB ObjectID). + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + filters (object, via --data) + time_filter (object, via --data) — One-off time window defined by unix seconds. + - end_time (integer) (required) — Window end (unix seconds). Must be > 0. + - start_time (integer) (required) — Window start (unix seconds). Must be > 0 and less than 'end_time'. + time_filters (array, via --data) — Recurring time windows. Mutually exclusive with 'time_filter'. + - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar. + - end (string) — End of the window in 'HH:MM'. + - is_off (boolean) — When true, match days marked as days-off in the calendar. + - repeat (array) — Days of the week this window repeats on. Empty means every day. + - start (string) — Start of the window in 'HH:MM'. +`, + Example: ` flashduty channel silence-rule-update --data '{"channel_id":1001,"filters":[[{"key":"labels.service","oper":"IN","vals":["billing"]}]],"rule_id":"6621b23f4a2c5e0012ab34cd","rule_name":"Mute during maintenance","time_filter":{"end_time":1710086400,"start_time":1710000000}}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("is-auto-delete") { + body["is_auto_delete"] = fIsAutoDelete + } + if cmd.Flags().Changed("is-directly-discard") { + body["is_directly_discard"] = fIsDirectlyDiscard + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateSilenceRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelSilenceRuleUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/silence/rule/update") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().BoolVar(&fIsAutoDelete, "is-auto-delete", false, "When true, the silence rule is automatically deleted after its time window expires. Defaults to false.") + cmd.Flags().BoolVar(&fIsDirectlyDiscard, "is-directly-discard", false, "When true, silenced alerts are dropped instead of suppressed into incidents.") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Silence rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelUnsubscribeRuleCreateCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fDescription string + var fPriority int64 + var fRuleName string + cmd := &cobra.Command{ + Use: "unsubscribe-rule-create", + Short: "Create drop rule", + Long: `Create drop rule. + +Create a drop rule to filter out unwanted alerts before they become incidents. + +API: POST /channel/unsubscribe/rule/create (channelUnsubscribeRuleCreate) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --priority int — Evaluation priority. Lower runs first. + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + filters (array, via --data) — Or-of-and filter tree. Each outer element is an AND group; within each group, all conditions must match. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID). + - rule_name (string) (required) — Rule name echoed back from the request. +`, + Example: ` flashduty channel unsubscribe-rule-create --data '{"channel_id":3521074710131,"description":"Discard all alerts from the test environment before they create incidents","filters":[[{"key":"labels.env","oper":"IN","vals":["test","dev"]}]],"rule_name":"Drop test environment alerts"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateDropRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelUnsubscribeRuleCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelUnsubscribeRuleDeleteCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "unsubscribe-rule-delete", + Short: "Delete drop rule", + Long: `Delete drop rule. + +Delete a drop rule. + +API: POST /channel/unsubscribe/rule/delete (channelUnsubscribeRuleDelete) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel unsubscribe-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelUnsubscribeRuleDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/unsubscribe/rule/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelUnsubscribeRuleDisableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "unsubscribe-rule-disable", + Short: "Disable drop rule", + Long: `Disable drop rule. + +Disable a drop rule without deleting it. + +API: POST /channel/unsubscribe/rule/disable (channelUnsubscribeRuleDisable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel unsubscribe-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelUnsubscribeRuleDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/unsubscribe/rule/disable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelUnsubscribeRuleEnableCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fRuleID string + cmd := &cobra.Command{ + Use: "unsubscribe-rule-enable", + Short: "Enable drop rule", + Long: `Enable drop rule. + +Enable a disabled drop rule. + +API: POST /channel/unsubscribe/rule/enable (channelUnsubscribeRuleEnable) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --rule-id string (required) — Rule ID (MongoDB ObjectID). +`, + Example: ` flashduty channel unsubscribe-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelRuleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelUnsubscribeRuleEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/unsubscribe/rule/enable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelUnsubscribeRuleListCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + cmd := &cobra.Command{ + Use: "unsubscribe-rule-list", + Short: "List drop rules", + Long: `List drop rules. + +List drop rules for a channel. + +API: POST /channel/unsubscribe/rule/list (channelUnsubscribeRuleList) + +Request fields: + --channel-id int (required) — Channel to list rules for. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - account_id (integer) (required) + - channel_id (integer) (required) + - created_at (integer) (required) + - deleted_at (integer) + - description (string) (required) + - filters (object) (required) + - priority (integer) (required) + - rule_id (string) (required) + - rule_name (string) (required) + - status (string) (required) [enabled, disabled] + - updated_at (integer) (required) + - updated_by (integer) (required) +`, + Example: ` flashduty channel unsubscribe-rule-list --data '{"channel_id":1001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + }) + if err != nil { + return err + } + req := new(flashduty.ChannelScopedListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelUnsubscribeRuleList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelUnsubscribeRuleUpdateCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fDescription string + var fPriority int64 + var fRuleID string + var fRuleName string + cmd := &cobra.Command{ + Use: "unsubscribe-rule-update", + Short: "Update drop rule", + Long: `Update drop rule. + +Update an existing drop rule configuration. + +API: POST /channel/unsubscribe/rule/update (channelUnsubscribeRuleUpdate) + +Request fields: + --channel-id int (required) — Channel the rule belongs to. + --description string — Rule description, up to 500 characters. (≤500 chars) + --priority int — Evaluation priority. Lower runs first. + --rule-id string (required) — Drop rule ID (MongoDB ObjectID). + --rule-name string (required) — Rule name, 1 to 39 characters. (1-39 chars) + filters (object, via --data) +`, + Example: ` flashduty channel unsubscribe-rule-update --data '{"channel_id":1001,"filters":[[{"key":"labels.env","oper":"IN","vals":["test"]}]],"rule_id":"6621b23f4a2c5e0012ab34cf","rule_name":"Drop test alerts"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("priority") { + body["priority"] = fPriority + } + if cmd.Flags().Changed("rule-id") { + body["rule_id"] = fRuleID + } + if cmd.Flags().Changed("rule-name") { + body["rule_name"] = fRuleName + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateDropRuleRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.ChannelUnsubscribeRuleUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /channel/unsubscribe/rule/update") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") + cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") + cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Drop rule ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsChannelUpdateCmd() *cobra.Command { + var dataJSON string + var fAutoResolveMode string + var fAutoResolveTimeout int64 + var fChannelID int64 + var fChannelName string + var fDescription string + var fDisableAutoClose bool + var fDisableOutlierDetection bool + var fIsExternalReportEnabled bool + var fIsPrivate bool + var fManagingTeamIDs []int + var fTeamID int64 + cmd := &cobra.Command{ + Use: "update", + Short: "Update channel", + Long: `Update channel. + +Update an existing channel's configuration and settings. + +API: POST /channel/update (channelUpdate) + +Request fields: + --auto-resolve-mode string — Auto-resolve timer reset mode. [trigger, update] + --auto-resolve-timeout int — Auto-resolve timeout in seconds. 0 disables auto-resolve. Max 30 days. (0-2592000) + --channel-id int (required) — Channel ID to update. + --channel-name string — New channel name. 1 to 59 characters. (1-59 chars) + --description string — New description. Up to 500 characters. (≤500 chars) + --disable-auto-close bool — Disable automatic incident closing. + --disable-outlier-detection bool — Disable outlier incident detection. + --is-external-report-enabled bool — Allow external reporters to file incidents into this channel. + --is-private bool — When true, the channel is visible only to its managing teams. + --managing-team-ids []int — Additional teams that can manage the channel. Up to 3 entries. + --team-id int — New owning team ID. + flapping (object, via --data) — Flapping detection configuration. + - in_mins (integer) — Observation window in minutes. (1-1440) + - is_disabled (boolean) — Disable flapping detection. + - max_changes (integer) — Max state changes allowed within 'in_mins'. (2-100) + - mute_mins (integer) — Mute duration in minutes after flapping is detected. (0-1440) + group (object, via --data) — Alert grouping configuration. + - all_equals_required (boolean) — When true, all listed keys must be present for grouping. + - cases (array) — Per-filter grouping overrides. + - equals (array) — Groups of label keys whose equality defines a bucket. + - i_keys (array) — Label keys used for intelligent grouping embeddings. + - i_score_threshold (number) — Intelligent grouping similarity threshold. (0.5-1) + - method (string) (required) — Grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - storm_threshold (integer) — Alert storm threshold. (0-10000) + - storm_thresholds (array) — Multi-level storm thresholds. + - time_window (integer) — Grouping time window in minutes. Default max is 1440 minutes (24 h); extended accounts may allow up to 43200 minutes (30 days). (min 0) + - window_type (string) — Window type. Defaults to 'tumbling'. [tumbling, sliding] + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - external_report_token (string) — Newly generated token for external reporters. Only returned when 'is_external_report_enabled' is set to 'true' in the request. Callers should store this value; it cannot be retrieved afterwards. +`, + Example: ` flashduty channel update --data '{"channel_id":1001,"channel_name":"Production Alerts (v2)","description":"Updated description"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("auto-resolve-mode") { + body["auto_resolve_mode"] = fAutoResolveMode + } + if cmd.Flags().Changed("auto-resolve-timeout") { + body["auto_resolve_timeout"] = fAutoResolveTimeout + } + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("channel-name") { + body["channel_name"] = fChannelName + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("disable-auto-close") { + body["disable_auto_close"] = fDisableAutoClose + } + if cmd.Flags().Changed("disable-outlier-detection") { + body["disable_outlier_detection"] = fDisableOutlierDetection + } + if cmd.Flags().Changed("is-external-report-enabled") { + body["is_external_report_enabled"] = fIsExternalReportEnabled + } + if cmd.Flags().Changed("is-private") { + body["is_private"] = fIsPrivate + } + if cmd.Flags().Changed("managing-team-ids") { + body["managing_team_ids"] = fManagingTeamIDs + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateChannelRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAutoResolveMode, "auto-resolve-mode", "", "Auto-resolve timer reset mode. [trigger, update]") + cmd.Flags().Int64Var(&fAutoResolveTimeout, "auto-resolve-timeout", 0, "Auto-resolve timeout in seconds. 0 disables auto-resolve. Max 30 days. (0-2592000)") + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID to update. (required)") + cmd.Flags().StringVar(&fChannelName, "channel-name", "", "New channel name. 1 to 59 characters. (1-59 chars)") + cmd.Flags().StringVar(&fDescription, "description", "", "New description. Up to 500 characters. (≤500 chars)") + cmd.Flags().BoolVar(&fDisableAutoClose, "disable-auto-close", false, "Disable automatic incident closing.") + cmd.Flags().BoolVar(&fDisableOutlierDetection, "disable-outlier-detection", false, "Disable outlier incident detection.") + cmd.Flags().BoolVar(&fIsExternalReportEnabled, "is-external-report-enabled", false, "Allow external reporters to file incidents into this channel.") + cmd.Flags().BoolVar(&fIsPrivate, "is-private", false, "When true, the channel is visible only to its managing teams.") + cmd.Flags().IntSliceVar(&fManagingTeamIDs, "managing-team-ids", nil, "Additional teams that can manage the channel. Up to 3 entries.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsRouteInfoCmd() *cobra.Command { + var dataJSON string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "info", + Short: "Get routing rule detail", + Long: `Get routing rule detail. + +Retrieve the routing rule configuration for a specific integration. Returns null when the integration has no routing rule configured. + +API: POST /route/info (routeInfo) + +Request fields: + --integration-id int (required) — Integration ID. Must be greater than 0. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - cases (array) — Ordered list of case branches. + - channel_ids (array) (required) — Target channel IDs. Required when 'routing_mode' is 'standard' (or empty). + - fallthrough (boolean) (required) — If 'true', evaluation continues to the next case after this one matches; otherwise matching stops at the first hit. + - if (array) (required) — List of match conditions that are AND-ed together. + - key (string) (required) — Field key to match against the alert event (e.g. 'alert_severity', 'labels.service'). + - oper (string) (required) — Match operator. 'IN' matches when the field value is one of 'vals'; 'NOTIN' matches when it is not. [IN, NOTIN] + - vals (array) (required) — Values to compare against. Each value may be a literal string, a wildcard ('*', '?'), a regular expression wrapped in slashes ('/pattern/'), a CIDR ('cidr:10.0.0.0/8'), or a numeric comparison ('num:lt:100'). + - name_mapping_label (string) — Label key whose value is used as the target channel name. Required when 'routing_mode' is 'name_mapping'. + - routing_mode (string) — Routing mode. 'standard' (default, also used when left empty) routes to the fixed channel IDs; 'name_mapping' resolves channels by reading a label value from the alert event. [standard, name_mapping] + - created_at (integer) — Creation time, Unix timestamp in seconds. + - creator_id (integer) (required) — ID of the person who created the rule. + - default (object) — Default branch used when no case matches (or all matched cases yield no valid channels). + - channel_ids (array) — Channel IDs to fall back to. + - deleted_at (integer) — Soft-delete timestamp, Unix seconds. Omitted when the rule is active. + - integration_id (integer) — Integration the rule belongs to. + - sections (array) — Optional sections that visually group cases. + - name (string) (required) — Section name. Must be unique within the rule. + - position (integer) (required) — Index in 'cases' where this section starts. Must be between 0 and the length of 'cases'. + - status (string) — Rule status. [enabled, deleted] + - updated_at (integer) — Last update time, Unix timestamp in seconds. + - updated_by (integer) (required) — ID of the person who performed the last update. + - version (integer) (required) — Monotonic version number, incremented on each update. Use it for optimistic concurrency control. +`, + Example: ` flashduty route info --data '{"integration_id":6113996590131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.RouteInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.RouteInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID. Must be greater than 0. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsRouteListCmd() *cobra.Command { + var dataJSON string + var fIntegrationIDs []int + cmd := &cobra.Command{ + Use: "list", + Short: "List routing rules", + Long: `List routing rules. + +Return routing rules for the specified integrations. Integrations without a configured rule are omitted from the response. + +API: POST /route/list (routeList) + +Request fields: + --integration-ids []int (required) — Integration IDs to fetch routing rules for. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Routing rules of the requested integrations. Integrations without a configured rule are omitted. + - cases (array) — Ordered list of case branches. + - channel_ids (array) (required) — Target channel IDs. Required when 'routing_mode' is 'standard' (or empty). + - fallthrough (boolean) (required) — If 'true', evaluation continues to the next case after this one matches; otherwise matching stops at the first hit. + - if (array) (required) — List of match conditions that are AND-ed together. + - key (string) (required) — Field key to match against the alert event (e.g. 'alert_severity', 'labels.service'). + - oper (string) (required) — Match operator. 'IN' matches when the field value is one of 'vals'; 'NOTIN' matches when it is not. [IN, NOTIN] + - vals (array) (required) — Values to compare against. Each value may be a literal string, a wildcard ('*', '?'), a regular expression wrapped in slashes ('/pattern/'), a CIDR ('cidr:10.0.0.0/8'), or a numeric comparison ('num:lt:100'). + - name_mapping_label (string) — Label key whose value is used as the target channel name. Required when 'routing_mode' is 'name_mapping'. + - routing_mode (string) — Routing mode. 'standard' (default, also used when left empty) routes to the fixed channel IDs; 'name_mapping' resolves channels by reading a label value from the alert event. [standard, name_mapping] + - created_at (integer) — Creation time, Unix timestamp in seconds. + - creator_id (integer) (required) — ID of the person who created the rule. + - default (object) — Default branch used when no case matches (or all matched cases yield no valid channels). + - channel_ids (array) — Channel IDs to fall back to. + - deleted_at (integer) — Soft-delete timestamp, Unix seconds. Omitted when the rule is active. + - integration_id (integer) — Integration the rule belongs to. + - sections (array) — Optional sections that visually group cases. + - name (string) (required) — Section name. Must be unique within the rule. + - position (integer) (required) — Index in 'cases' where this section starts. Must be between 0 and the length of 'cases'. + - status (string) — Rule status. [enabled, deleted] + - updated_at (integer) — Last update time, Unix timestamp in seconds. + - updated_by (integer) (required) — ID of the person who performed the last update. + - version (integer) (required) — Monotonic version number, incremented on each update. Use it for optimistic concurrency control. +`, + Example: ` flashduty route list --data '{"integration_ids":[6113996590131,6113996590132]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-ids") { + body["integration_ids"] = fIntegrationIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ListRoutesRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.RouteList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "Integration IDs to fetch routing rules for. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genChannelsRouteUpsertCmd() *cobra.Command { + var dataJSON string + var fIntegrationID int64 + var fVersion int64 + cmd := &cobra.Command{ + Use: "upsert", + Short: "Upsert routing rule", + Long: `Upsert routing rule. + +Create or update routing rules for an integration to direct alerts to specific channels. At least one of 'cases' or 'default' must be provided. + +API: POST /route/upsert (routeUpsert) + +Request fields: + --integration-id int (required) — Integration the rule belongs to. + --version int — Expected current version for optimistic concurrency control. Pass the value returned by the latest read. + cases (array, via --data) — Ordered list of case branches. Cases are evaluated top to bottom. + - channel_ids (array) (required) — Target channel IDs. Required when 'routing_mode' is 'standard' (or empty). + - fallthrough (boolean) (required) — If 'true', evaluation continues to the next case after this one matches; otherwise matching stops at the first hit. + - if (array) (required) — List of match conditions that are AND-ed together. + - key (string) (required) — Field key to match against the alert event (e.g. 'alert_severity', 'labels.service'). + - oper (string) (required) — Match operator. 'IN' matches when the field value is one of 'vals'; 'NOTIN' matches when it is not. [IN, NOTIN] + - vals (array) (required) — Values to compare against. Each value may be a literal string, a wildcard ('*', '?'), a regular expression wrapped in slashes ('/pattern/'), a CIDR ('cidr:10.0.0.0/8'), or a numeric comparison ('num:lt:100'). + - name_mapping_label (string) — Label key whose value is used as the target channel name. Required when 'routing_mode' is 'name_mapping'. + - routing_mode (string) — Routing mode. 'standard' (default, also used when left empty) routes to the fixed channel IDs; 'name_mapping' resolves channels by reading a label value from the alert event. [standard, name_mapping] + default (object, via --data) — Default branch used when no case matches (or all matched cases yield no valid channels). + - channel_ids (array) — Channel IDs to fall back to. + sections (array, via --data) — Optional sections that group consecutive cases for display. + - name (string) (required) — Section name. Must be unique within the rule. + - position (integer) (required) — Index in 'cases' where this section starts. Must be between 0 and the length of 'cases'. +`, + Example: ` flashduty route upsert --data '{"cases":[{"channel_ids":[3521074710131],"fallthrough":false,"if":[{"key":"severity","oper":"IN","vals":["Critical"]}],"routing_mode":"standard"}],"default":{"channel_ids":[3521074710131]},"integration_id":6113996590131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + if cmd.Flags().Changed("version") { + body["version"] = fVersion + } + }) + if err != nil { + return err + } + req := new(flashduty.UpsertRouteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Channels.RouteUpsert(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /route/upsert") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration the rule belongs to. (required)") + cmd.Flags().Int64Var(&fVersion, "version", 0, "Expected current version for optimistic concurrency control. Pass the value returned by the latest read.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedChannels(root *cobra.Command) { + gChannel := genGroup(root, "channel", "On-call/Channels API") + genAddLeaf(gChannel, genChannelsChannelCreateCmd()) + genAddLeaf(gChannel, genChannelsChannelDeleteCmd()) + genAddLeaf(gChannel, genChannelsChannelDisableCmd()) + genAddLeaf(gChannel, genChannelsChannelEnableCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateRuleCreateCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateRuleDeleteCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateRuleDisableCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateRuleEnableCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateRuleInfoCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateRuleListCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateRuleUpdateCmd()) + genAddLeaf(gChannel, genChannelsChannelInfoCmd()) + genAddLeaf(gChannel, genChannelsChannelInfosCmd()) + genAddLeaf(gChannel, genChannelsChannelInhibitRuleCreateCmd()) + genAddLeaf(gChannel, genChannelsChannelInhibitRuleDeleteCmd()) + genAddLeaf(gChannel, genChannelsChannelInhibitRuleDisableCmd()) + genAddLeaf(gChannel, genChannelsChannelInhibitRuleEnableCmd()) + genAddLeaf(gChannel, genChannelsChannelInhibitRuleListCmd()) + genAddLeaf(gChannel, genChannelsChannelInhibitRuleUpdateCmd()) + genAddLeaf(gChannel, genChannelsChannelListCmd()) + genAddLeaf(gChannel, genChannelsChannelNotifyRuleCreateCmd()) + genAddLeaf(gChannel, genChannelsChannelNotifyRuleDeleteCmd()) + genAddLeaf(gChannel, genChannelsChannelNotifyRuleDisableCmd()) + genAddLeaf(gChannel, genChannelsChannelNotifyRuleEnableCmd()) + genAddLeaf(gChannel, genChannelsChannelNotifyRuleListCmd()) + genAddLeaf(gChannel, genChannelsChannelNotifyRuleUpdateCmd()) + genAddLeaf(gChannel, genChannelsChannelSilenceRuleCreateCmd()) + genAddLeaf(gChannel, genChannelsChannelSilenceRuleDeleteCmd()) + genAddLeaf(gChannel, genChannelsChannelSilenceRuleDisableCmd()) + genAddLeaf(gChannel, genChannelsChannelSilenceRuleEnableCmd()) + genAddLeaf(gChannel, genChannelsChannelSilenceRuleListCmd()) + genAddLeaf(gChannel, genChannelsChannelSilenceRuleUpdateCmd()) + genAddLeaf(gChannel, genChannelsChannelUnsubscribeRuleCreateCmd()) + genAddLeaf(gChannel, genChannelsChannelUnsubscribeRuleDeleteCmd()) + genAddLeaf(gChannel, genChannelsChannelUnsubscribeRuleDisableCmd()) + genAddLeaf(gChannel, genChannelsChannelUnsubscribeRuleEnableCmd()) + genAddLeaf(gChannel, genChannelsChannelUnsubscribeRuleListCmd()) + genAddLeaf(gChannel, genChannelsChannelUnsubscribeRuleUpdateCmd()) + genAddLeaf(gChannel, genChannelsChannelUpdateCmd()) + gRoute := genGroup(root, "route", "On-call/Channels API") + genAddLeaf(gRoute, genChannelsRouteInfoCmd()) + genAddLeaf(gRoute, genChannelsRouteListCmd()) + genAddLeaf(gRoute, genChannelsRouteUpsertCmd()) +} diff --git a/internal/cli/zz_generated_data_sources.go b/internal/cli/zz_generated_data_sources.go new file mode 100644 index 0000000..77ae3d8 --- /dev/null +++ b/internal/cli/zz_generated_data_sources.go @@ -0,0 +1,1125 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genDataSourcesReadInfoCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "datasource-info", + Short: "Get datasource detail", + Long: `Get datasource detail. + +Retrieve full details of a single data source by its ID, including the 'payload' configuration. + +API: POST /monit/datasource/info (monit-datasource-read-info) + +Request fields: + --id int (required) — Resource ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Account ID. + - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. + - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. + - enabled (boolean) (required) — Whether the datasource is active. + - id (integer) (required) — Unique datasource ID. + - name (string) (required) — Datasource display name. + - note (string) (required) — Optional description. + - payload (object) — Type-specific datasource configuration. Include only the block matching 'type_ident'. + - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig. + - database (string) — Default database for authentication. + - dial_timeout_mills (integer) — Dial timeout in milliseconds. + - idle_conns (integer) + - lifetime_seconds (integer) + - max_execution_seconds (integer) — Max query execution time in seconds. + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_enabled (boolean) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - elasticsearch (object) — Elasticsearch datasource configuration. + - api_key (string) — Elastic Cloud API key. Only for 'cloud' deployment. + - certificate_fingerprint (string) + - cloud_id (string) — Elastic Cloud deployment ID. Only for 'cloud' deployment. + - deployment (string) — Deployment type. 'cloud' uses Elastic Cloud; 'self-managed' uses a self-hosted cluster. [cloud, self-managed] + - headers (array) + - password (string) + - service_token (string) — Service token; overrides username/password if set. + - timeout_mills (integer) + - tls_ca (string) + - username (string) — Username for 'self-managed' deployment. + - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig. + - idle_conns (integer) — Maximum idle connections. + - lifetime_seconds (integer) — Connection maximum lifetime in seconds. + - open_conns (integer) — Maximum open connections. + - password (string) + - timeout_mills (integer) — Query timeout in milliseconds. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - oracle (object) — Oracle datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - options (object) — Extra connection options as key-value pairs. + - password (string) + - timeout_mills (integer) + - username (string) + - postgres (object) — PostgreSQL datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - username (string) + - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) — Enable HTTP Basic Auth. + - basic_auth_password (string) — Basic auth password. + - basic_auth_username (string) — Basic auth username. + - headers (array) — Custom HTTP headers in 'Key: Value' format. + - params (array) — Custom query parameters in 'key=value' format. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - sls (object) — Alibaba Cloud SLS datasource configuration. + - access_key_id (string) — Alibaba Cloud Access Key ID. + - access_key_secret (string) — Alibaba Cloud Access Key Secret. + - headers (array) — Custom HTTP headers. + - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - type_ident (string) (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit datasource-info --data '{"id":10}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.IDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.DataSources.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDataSourcesReadListCmd() *cobra.Command { + var dataJSON string + var fType string + cmd := &cobra.Command{ + Use: "datasource-list", + Short: "List datasources", + Long: `List datasources. + +Return all data sources for the current account. Optionally filter by 'type_ident'. + +API: POST /monit/datasource/list (monit-datasource-read-list) + +Request fields: + --type string — Filter by datasource type identifier. Omit to return all types. Allowed values: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - account_id (integer) (required) — Account ID. + - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. + - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. + - enabled (boolean) (required) — Whether the datasource is active. + - id (integer) (required) — Unique datasource ID. + - name (string) (required) — Datasource display name. + - note (string) (required) — Optional description. + - payload (object) — Type-specific datasource configuration. Include only the block matching 'type_ident'. + - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig. + - database (string) — Default database for authentication. + - dial_timeout_mills (integer) — Dial timeout in milliseconds. + - idle_conns (integer) + - lifetime_seconds (integer) + - max_execution_seconds (integer) — Max query execution time in seconds. + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_enabled (boolean) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - elasticsearch (object) — Elasticsearch datasource configuration. + - api_key (string) — Elastic Cloud API key. Only for 'cloud' deployment. + - certificate_fingerprint (string) + - cloud_id (string) — Elastic Cloud deployment ID. Only for 'cloud' deployment. + - deployment (string) — Deployment type. 'cloud' uses Elastic Cloud; 'self-managed' uses a self-hosted cluster. [cloud, self-managed] + - headers (array) + - password (string) + - service_token (string) — Service token; overrides username/password if set. + - timeout_mills (integer) + - tls_ca (string) + - username (string) — Username for 'self-managed' deployment. + - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig. + - idle_conns (integer) — Maximum idle connections. + - lifetime_seconds (integer) — Connection maximum lifetime in seconds. + - open_conns (integer) — Maximum open connections. + - password (string) + - timeout_mills (integer) — Query timeout in milliseconds. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - oracle (object) — Oracle datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - options (object) — Extra connection options as key-value pairs. + - password (string) + - timeout_mills (integer) + - username (string) + - postgres (object) — PostgreSQL datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - username (string) + - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) — Enable HTTP Basic Auth. + - basic_auth_password (string) — Basic auth password. + - basic_auth_username (string) — Basic auth username. + - headers (array) — Custom HTTP headers in 'Key: Value' format. + - params (array) — Custom query parameters in 'key=value' format. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - sls (object) — Alibaba Cloud SLS datasource configuration. + - access_key_id (string) — Alibaba Cloud Access Key ID. + - access_key_secret (string) — Alibaba Cloud Access Key Secret. + - headers (array) — Custom HTTP headers. + - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - type_ident (string) (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit datasource-list --data '{"type":"prometheus"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("type") { + body["type"] = fType + } + }) + if err != nil { + return err + } + req := new(flashduty.DataSourceListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.DataSources.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fType, "type", "", "Filter by datasource type identifier. Omit to return all types. Allowed values: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDataSourcesReadSLSLogstoresCmd() *cobra.Command { + var dataJSON string + var fID int64 + var fOffset int64 + var fProject string + var fSize int64 + cmd := &cobra.Command{ + Use: "datasource-sls-logstores", + Short: "List SLS logstores", + Long: `List SLS logstores. + +List logstores within an SLS project for the specified SLS datasource. + +API: POST /monit/datasource/sls/logstores (monit-datasource-read-sls-logstores) + +Request fields: + --id int — SLS datasource ID. + --offset int — Pagination offset. + --project string — SLS project name. + --size int — Page size. +`, + Example: ` flashduty monit datasource-sls-logstores --data '{"id":10,"offset":0,"project":"project-a","size":50}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + if cmd.Flags().Changed("offset") { + body["offset"] = fOffset + } + if cmd.Flags().Changed("project") { + body["project"] = fProject + } + if cmd.Flags().Changed("size") { + body["size"] = fSize + } + }) + if err != nil { + return err + } + req := new(flashduty.SLSLogstoresRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.DataSources.ReadSLSLogstores(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "SLS datasource ID.") + cmd.Flags().Int64Var(&fOffset, "offset", 0, "Pagination offset.") + cmd.Flags().StringVar(&fProject, "project", "", "SLS project name.") + cmd.Flags().Int64Var(&fSize, "size", 0, "Page size.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDataSourcesReadSLSProjectsCmd() *cobra.Command { + var dataJSON string + var fID int64 + var fOffset int64 + var fQuery string + var fSize int64 + cmd := &cobra.Command{ + Use: "datasource-sls-projects", + Short: "List SLS projects", + Long: `List SLS projects. + +List Alibaba Cloud SLS (Simple Log Service) projects available in the specified SLS datasource. + +API: POST /monit/datasource/sls/projects (monit-datasource-read-sls-projects) + +Request fields: + --id int — SLS datasource ID. + --offset int — Pagination offset. + --query string — Name prefix filter. + --size int — Page size. +`, + Example: ` flashduty monit datasource-sls-projects --data '{"id":10,"offset":0,"query":"","size":50}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + if cmd.Flags().Changed("offset") { + body["offset"] = fOffset + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("size") { + body["size"] = fSize + } + }) + if err != nil { + return err + } + req := new(flashduty.SLSProjectsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.DataSources.ReadSLSProjects(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "SLS datasource ID.") + cmd.Flags().Int64Var(&fOffset, "offset", 0, "Pagination offset.") + cmd.Flags().StringVar(&fQuery, "query", "", "Name prefix filter.") + cmd.Flags().Int64Var(&fSize, "size", 0, "Page size.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDataSourcesWriteCreateCmd() *cobra.Command { + var dataJSON string + var fAddress string + var fEdgeClusterName string + var fID int64 + var fName string + var fNote string + var fTypeIdent string + cmd := &cobra.Command{ + Use: "datasource-create", + Short: "Create datasource", + Long: `Create datasource. + +Create a new monitoring data source. The 'payload' must include the type-specific configuration block. + +API: POST /monit/datasource/create (monit-datasource-write-create) + +Request fields: + --address string — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. Not required for Elasticsearch cloud deployment. + --edge-cluster-name string (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. + --id int — Datasource ID. Required for update; omit for create. + --name string (required) — Datasource display name. + --note string — Optional description. + --type-ident string (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. + payload (object, via --data) (required) — Type-specific datasource configuration. Include only the block matching 'type_ident'. + - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig. + - database (string) — Default database for authentication. + - dial_timeout_mills (integer) — Dial timeout in milliseconds. + - idle_conns (integer) + - lifetime_seconds (integer) + - max_execution_seconds (integer) — Max query execution time in seconds. + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_enabled (boolean) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - elasticsearch (object) — Elasticsearch datasource configuration. + - api_key (string) — Elastic Cloud API key. Only for 'cloud' deployment. + - certificate_fingerprint (string) + - cloud_id (string) — Elastic Cloud deployment ID. Only for 'cloud' deployment. + - deployment (string) — Deployment type. 'cloud' uses Elastic Cloud; 'self-managed' uses a self-hosted cluster. [cloud, self-managed] + - headers (array) + - password (string) + - service_token (string) — Service token; overrides username/password if set. + - timeout_mills (integer) + - tls_ca (string) + - username (string) — Username for 'self-managed' deployment. + - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig. + - idle_conns (integer) — Maximum idle connections. + - lifetime_seconds (integer) — Connection maximum lifetime in seconds. + - open_conns (integer) — Maximum open connections. + - password (string) + - timeout_mills (integer) — Query timeout in milliseconds. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - oracle (object) — Oracle datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - options (object) — Extra connection options as key-value pairs. + - password (string) + - timeout_mills (integer) + - username (string) + - postgres (object) — PostgreSQL datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - username (string) + - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) — Enable HTTP Basic Auth. + - basic_auth_password (string) — Basic auth password. + - basic_auth_username (string) — Basic auth username. + - headers (array) — Custom HTTP headers in 'Key: Value' format. + - params (array) — Custom query parameters in 'key=value' format. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - sls (object) — Alibaba Cloud SLS datasource configuration. + - access_key_id (string) — Alibaba Cloud Access Key ID. + - access_key_secret (string) — Alibaba Cloud Access Key Secret. + - headers (array) — Custom HTTP headers. + - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Account ID. + - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. + - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. + - enabled (boolean) (required) — Whether the datasource is active. + - id (integer) (required) — Unique datasource ID. + - name (string) (required) — Datasource display name. + - note (string) (required) — Optional description. + - payload (object) — Type-specific datasource configuration. Include only the block matching 'type_ident'. + - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig. + - database (string) — Default database for authentication. + - dial_timeout_mills (integer) — Dial timeout in milliseconds. + - idle_conns (integer) + - lifetime_seconds (integer) + - max_execution_seconds (integer) — Max query execution time in seconds. + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_enabled (boolean) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - elasticsearch (object) — Elasticsearch datasource configuration. + - api_key (string) — Elastic Cloud API key. Only for 'cloud' deployment. + - certificate_fingerprint (string) + - cloud_id (string) — Elastic Cloud deployment ID. Only for 'cloud' deployment. + - deployment (string) — Deployment type. 'cloud' uses Elastic Cloud; 'self-managed' uses a self-hosted cluster. [cloud, self-managed] + - headers (array) + - password (string) + - service_token (string) — Service token; overrides username/password if set. + - timeout_mills (integer) + - tls_ca (string) + - username (string) — Username for 'self-managed' deployment. + - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig. + - idle_conns (integer) — Maximum idle connections. + - lifetime_seconds (integer) — Connection maximum lifetime in seconds. + - open_conns (integer) — Maximum open connections. + - password (string) + - timeout_mills (integer) — Query timeout in milliseconds. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - oracle (object) — Oracle datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - options (object) — Extra connection options as key-value pairs. + - password (string) + - timeout_mills (integer) + - username (string) + - postgres (object) — PostgreSQL datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - username (string) + - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) — Enable HTTP Basic Auth. + - basic_auth_password (string) — Basic auth password. + - basic_auth_username (string) — Basic auth username. + - headers (array) — Custom HTTP headers in 'Key: Value' format. + - params (array) — Custom query parameters in 'key=value' format. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - sls (object) — Alibaba Cloud SLS datasource configuration. + - access_key_id (string) — Alibaba Cloud Access Key ID. + - access_key_secret (string) — Alibaba Cloud Access Key Secret. + - headers (array) — Custom HTTP headers. + - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - type_ident (string) (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit datasource-create --data '{"address":"http://prometheus.example.com:9090","edge_cluster_name":"default","name":"Prometheus Prod","note":"Production Prometheus","payload":{"prometheus":{"basic_auth_enabled":false}},"type_ident":"prometheus"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("address") { + body["address"] = fAddress + } + if cmd.Flags().Changed("edge-cluster-name") { + body["edge_cluster_name"] = fEdgeClusterName + } + if cmd.Flags().Changed("id") { + body["id"] = fID + } + if cmd.Flags().Changed("name") { + body["name"] = fName + } + if cmd.Flags().Changed("note") { + body["note"] = fNote + } + if cmd.Flags().Changed("type-ident") { + body["type_ident"] = fTypeIdent + } + }) + if err != nil { + return err + } + req := new(flashduty.DataSourceUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.DataSources.WriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAddress, "address", "", "Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. Not required for Elasticsearch cloud deployment.") + cmd.Flags().StringVar(&fEdgeClusterName, "edge-cluster-name", "", "Monitors edge cluster name responsible for evaluating rules using this datasource. (required)") + cmd.Flags().Int64Var(&fID, "id", 0, "Datasource ID. Required for update; omit for create.") + cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. (required)") + cmd.Flags().StringVar(&fNote, "note", "", "Optional description.") + cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDataSourcesWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "datasource-delete", + Short: "Delete datasource", + Long: `Delete datasource. + +Delete a data source by ID. Alert rules referencing this datasource must be updated or deleted first. + +API: POST /monit/datasource/delete (monit-datasource-write-delete) + +Request fields: + --id int (required) — Resource ID. +`, + Example: ` flashduty monit datasource-delete --data '{"id":10}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.IDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.DataSources.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /monit/datasource/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDataSourcesWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fAddress string + var fEdgeClusterName string + var fID int64 + var fName string + var fNote string + var fTypeIdent string + cmd := &cobra.Command{ + Use: "datasource-update", + Short: "Update datasource", + Long: `Update datasource. + +Update an existing data source. Supply 'id' plus the fields to change. + +API: POST /monit/datasource/update (monit-datasource-write-update) + +Request fields: + --address string — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. Not required for Elasticsearch cloud deployment. + --edge-cluster-name string (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. + --id int — Datasource ID. Required for update; omit for create. + --name string (required) — Datasource display name. + --note string — Optional description. + --type-ident string (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. + payload (object, via --data) (required) — Type-specific datasource configuration. Include only the block matching 'type_ident'. + - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig. + - database (string) — Default database for authentication. + - dial_timeout_mills (integer) — Dial timeout in milliseconds. + - idle_conns (integer) + - lifetime_seconds (integer) + - max_execution_seconds (integer) — Max query execution time in seconds. + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_enabled (boolean) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - elasticsearch (object) — Elasticsearch datasource configuration. + - api_key (string) — Elastic Cloud API key. Only for 'cloud' deployment. + - certificate_fingerprint (string) + - cloud_id (string) — Elastic Cloud deployment ID. Only for 'cloud' deployment. + - deployment (string) — Deployment type. 'cloud' uses Elastic Cloud; 'self-managed' uses a self-hosted cluster. [cloud, self-managed] + - headers (array) + - password (string) + - service_token (string) — Service token; overrides username/password if set. + - timeout_mills (integer) + - tls_ca (string) + - username (string) — Username for 'self-managed' deployment. + - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig. + - idle_conns (integer) — Maximum idle connections. + - lifetime_seconds (integer) — Connection maximum lifetime in seconds. + - open_conns (integer) — Maximum open connections. + - password (string) + - timeout_mills (integer) — Query timeout in milliseconds. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - oracle (object) — Oracle datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - options (object) — Extra connection options as key-value pairs. + - password (string) + - timeout_mills (integer) + - username (string) + - postgres (object) — PostgreSQL datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - username (string) + - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) — Enable HTTP Basic Auth. + - basic_auth_password (string) — Basic auth password. + - basic_auth_username (string) — Basic auth username. + - headers (array) — Custom HTTP headers in 'Key: Value' format. + - params (array) — Custom query parameters in 'key=value' format. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - sls (object) — Alibaba Cloud SLS datasource configuration. + - access_key_id (string) — Alibaba Cloud Access Key ID. + - access_key_secret (string) — Alibaba Cloud Access Key Secret. + - headers (array) — Custom HTTP headers. + - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Account ID. + - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. + - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource. + - enabled (boolean) (required) — Whether the datasource is active. + - id (integer) (required) — Unique datasource ID. + - name (string) (required) — Datasource display name. + - note (string) (required) — Optional description. + - payload (object) — Type-specific datasource configuration. Include only the block matching 'type_ident'. + - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig. + - database (string) — Default database for authentication. + - dial_timeout_mills (integer) — Dial timeout in milliseconds. + - idle_conns (integer) + - lifetime_seconds (integer) + - max_execution_seconds (integer) — Max query execution time in seconds. + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_enabled (boolean) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - elasticsearch (object) — Elasticsearch datasource configuration. + - api_key (string) — Elastic Cloud API key. Only for 'cloud' deployment. + - certificate_fingerprint (string) + - cloud_id (string) — Elastic Cloud deployment ID. Only for 'cloud' deployment. + - deployment (string) — Deployment type. 'cloud' uses Elastic Cloud; 'self-managed' uses a self-hosted cluster. [cloud, self-managed] + - headers (array) + - password (string) + - service_token (string) — Service token; overrides username/password if set. + - timeout_mills (integer) + - tls_ca (string) + - username (string) — Username for 'self-managed' deployment. + - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig. + - idle_conns (integer) — Maximum idle connections. + - lifetime_seconds (integer) — Connection maximum lifetime in seconds. + - open_conns (integer) — Maximum open connections. + - password (string) + - timeout_mills (integer) — Query timeout in milliseconds. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - username (string) + - oracle (object) — Oracle datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - options (object) — Extra connection options as key-value pairs. + - password (string) + - timeout_mills (integer) + - username (string) + - postgres (object) — PostgreSQL datasource configuration. + - idle_conns (integer) + - lifetime_seconds (integer) + - open_conns (integer) + - password (string) + - timeout_mills (integer) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - username (string) + - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) — Enable HTTP Basic Auth. + - basic_auth_password (string) — Basic auth password. + - basic_auth_username (string) — Basic auth username. + - headers (array) — Custom HTTP headers in 'Key: Value' format. + - params (array) — Custom query parameters in 'key=value' format. + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - sls (object) — Alibaba Cloud SLS datasource configuration. + - access_key_id (string) — Alibaba Cloud Access Key ID. + - access_key_secret (string) — Alibaba Cloud Access Key Secret. + - headers (array) — Custom HTTP headers. + - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig. + - basic_auth_enabled (boolean) + - basic_auth_password (string) + - basic_auth_username (string) + - headers (array) + - params (array) + - tls_ca (string) + - tls_cert (string) + - tls_key (string) + - tls_key_pwd (string) + - tls_max_version (string) + - tls_min_version (string) + - tls_server_name (string) + - tls_skip_verify (boolean) + - type_ident (string) (required) — Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit datasource-update --data '{"address":"http://prometheus-v2.example.com:9090","edge_cluster_name":"default","id":10,"name":"Prometheus Prod v2","note":"Updated","payload":{"prometheus":{"basic_auth_enabled":false}},"type_ident":"prometheus"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("address") { + body["address"] = fAddress + } + if cmd.Flags().Changed("edge-cluster-name") { + body["edge_cluster_name"] = fEdgeClusterName + } + if cmd.Flags().Changed("id") { + body["id"] = fID + } + if cmd.Flags().Changed("name") { + body["name"] = fName + } + if cmd.Flags().Changed("note") { + body["note"] = fNote + } + if cmd.Flags().Changed("type-ident") { + body["type_ident"] = fTypeIdent + } + }) + if err != nil { + return err + } + req := new(flashduty.DataSourceUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.DataSources.WriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAddress, "address", "", "Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: 'host:port'. For SLS: endpoint without http/https prefix. Not required for Elasticsearch cloud deployment.") + cmd.Flags().StringVar(&fEdgeClusterName, "edge-cluster-name", "", "Monitors edge cluster name responsible for evaluating rules using this datasource. (required)") + cmd.Flags().Int64Var(&fID, "id", 0, "Datasource ID. Required for update; omit for create.") + cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. (required)") + cmd.Flags().StringVar(&fNote, "note", "", "Optional description.") + cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedDataSources(root *cobra.Command) { + gMonit := genGroup(root, "monit", "Monitors API") + genAddLeaf(gMonit, genDataSourcesReadInfoCmd()) + genAddLeaf(gMonit, genDataSourcesReadListCmd()) + genAddLeaf(gMonit, genDataSourcesReadSLSLogstoresCmd()) + genAddLeaf(gMonit, genDataSourcesReadSLSProjectsCmd()) + genAddLeaf(gMonit, genDataSourcesWriteCreateCmd()) + genAddLeaf(gMonit, genDataSourcesWriteDeleteCmd()) + genAddLeaf(gMonit, genDataSourcesWriteUpdateCmd()) +} diff --git a/internal/cli/zz_generated_diagnostics.go b/internal/cli/zz_generated_diagnostics.go new file mode 100644 index 0000000..42e493f --- /dev/null +++ b/internal/cli/zz_generated_diagnostics.go @@ -0,0 +1,413 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genDiagnosticsQueryDiagnoseCmd() *cobra.Command { + var dataJSON string + var fAccountID int64 + var fDsName string + var fDsType string + var fOperation string + cmd := &cobra.Command{ + Use: "query-diagnose", + Short: "Diagnose data source", + Long: `Diagnose data source. + +Run a synchronous diagnostic query ('log_patterns' for Loki/VictoriaLogs, 'metric_trends' for Prometheus). Used by Flashduty AI SRE for log-pattern clustering and time-series trend analysis. Long-running — up to 35 s. + +API: POST /monit/query/diagnose (monit-read-query-diagnose) + +Request fields: + --account-id int — Optional consistency check. Must equal the authenticated account when supplied. + --ds-name string (required) — Data source name configured under the tenant. + --ds-type string (required) — Data source type. 'log_patterns' supports 'loki' and 'victorialogs'; 'metric_trends' supports 'prometheus'. + --operation string — Diagnostic operation. When omitted, inferred from 'ds_type' (loki / victorialogs → 'log_patterns', prometheus → 'metric_trends'). Other sources must specify explicitly. [log_patterns, metric_trends] + input (object, via --data) (required) + - query (string) (required) — Query expression. LogQL / VictoriaLogs query syntax for 'log_patterns'; PromQL for 'metric_trends'. + methods (array, via --data) — Diagnostic methods to run. When omitted, 'log_patterns' defaults to 'pattern_snapshot + pattern_compare(previous_window)' and 'metric_trends' defaults to 'single_window_shape + window_compare(previous_window)'. + - baseline (string) — Only meaningful for compare-style methods. Defaults to 'previous_window'. [previous_window, same_window_yesterday, same_window_last_week] + - name (string) — 'log_patterns' supports 'pattern_snapshot', 'pattern_compare'. 'metric_trends' supports 'single_window_shape', 'window_compare'. + options (object, via --data) — Execution options, all upper-bounded by monit-edge. + - examples_per_pattern (integer) — Max redacted examples per pattern. Default 2, hard max 3. + - max_logs_scanned (integer) — Per-window log scan cap. Default 10 000, hard max 50 000. + - max_patterns (integer) — Max patterns returned. Default 20, hard max 50. + - max_series (integer) — 'metric_trends' max series considered. Default 50, hard max 200. + - step_seconds (integer) — 'metric_trends' query_range step. Default 60, range [15, 300]. + - timeout_seconds (integer) — Edge-side diagnostic timeout in seconds. Default 25, hard max 30. + - topk (integer) — 'metric_trends' max notable series returned. Default 10, hard max 50. + time_range (object, via --data) — Diagnostic window in Unix seconds. Defaults to the last 15 minutes when missing or invalid; windows wider than 6 hours are rejected. + - end (integer) — Window end, Unix seconds. + - start (integer) — Window start, Unix seconds. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - ds_name (string) + - ds_type (string) + - operation (string) [log_patterns, metric_trends] + - query (string) — Query string echoed back from the request. + - results (array) — One entry per 'methods[]' in the request, in the same order. + - baseline (string) — Only present for compare-style methods. + - baseline_window (object) — Only present for compare-style methods. + - end (integer) + - start (integer) + - method (string) — 'pattern_snapshot' / 'pattern_compare' for 'log_patterns'; 'single_window_shape' / 'window_compare' for 'metric_trends'. + - patterns (array) — 'log_patterns' only. Sorted RCA-first; each item carries pattern_hash, template, count, severity, sources, examples, and (for compare) baseline_count / change_ratio / is_new / is_gone. + - series (array) — 'metric_trends' only. Notable series with current / baseline / change / notable_period. + - summary (object) — Aggregate summary for this method. Shape differs between 'log_patterns' (logs_scanned, patterns_total, surging_threshold, …) and 'metric_trends' (series_total, data_quality, observations, …). + - warnings (array) — Per-method advisory messages (e.g. 'examples redacted', sampling notices). + - window (object) + - end (integer) + - start (integer) + - window (object) + - end (integer) + - start (integer) +`, + Example: ` flashduty monit query-diagnose --data '{"account_id":10001,"ds_name":"vmlogs-read","ds_type":"victorialogs","input":{"query":"_stream:{status='500'}"},"methods":[{"name":"pattern_snapshot"},{"baseline":"same_window_yesterday","name":"pattern_compare"}],"operation":"log_patterns","options":{"examples_per_pattern":2,"max_logs_scanned":10000,"max_patterns":20,"timeout_seconds":25},"time_range":{"end":1776849344,"start":1776847544}}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("account-id") { + body["account_id"] = fAccountID + } + if cmd.Flags().Changed("ds-name") { + body["ds_name"] = fDsName + } + if cmd.Flags().Changed("ds-type") { + body["ds_type"] = fDsType + } + if cmd.Flags().Changed("operation") { + body["operation"] = fOperation + } + }) + if err != nil { + return err + } + req := new(flashduty.DiagnoseRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Diagnostics.QueryDiagnose(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Optional consistency check. Must equal the authenticated account when supplied.") + cmd.Flags().StringVar(&fDsName, "ds-name", "", "Data source name configured under the tenant. (required)") + cmd.Flags().StringVar(&fDsType, "ds-type", "", "Data source type. 'log_patterns' supports 'loki' and 'victorialogs'; 'metric_trends' supports 'prometheus'. (required)") + cmd.Flags().StringVar(&fOperation, "operation", "", "Diagnostic operation. When omitted, inferred from 'ds_type' (loki / victorialogs → 'log_patterns', prometheus → 'metric_trends'). Other sources must specify explicitly. [log_patterns, metric_trends]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDiagnosticsQueryRowsCmd() *cobra.Command { + var dataJSON string + var fAccountID int64 + var fDelaySeconds int64 + var fDsName string + var fDsType string + var fExpr string + cmd := &cobra.Command{ + Use: "query-rows", + Short: "Query data source rows", + Long: `Query data source rows. + +Run a synchronous ad-hoc query against a configured data source and get back its raw rows. Used by Flashduty AI SRE and by UI preview. The request is forwarded over WebSocket to monit-edge, which executes the query against the underlying source (Prometheus / Loki / VictoriaLogs / SLS / MySQL / Postgres / Oracle / ClickHouse / Elasticsearch). + +API: POST /monit/query/rows (monit-read-query-rows) + +Request fields: + --account-id int — Optional consistency check. Must equal the authenticated account when supplied; mismatched values are rejected. Business execution always uses the authenticated account. + --delay-seconds int — Look-back offset in seconds applied to point-in-time queries (Prometheus, Loki stats, VictoriaLogs stats). Ignored for raw / detail queries. + --ds-name string (required) — Data source name; must match a configured data source under the tenant. + --ds-type string (required) — Data source type; must match a configured data source under the tenant. Examples: 'prometheus', 'loki', 'victorialogs', 'sls', 'elasticsearch', 'mysql', 'postgres', 'oracle', 'clickhouse'. + --expr string (required) — Query expression. Syntax depends on 'ds_type' and is interpreted by the corresponding monit-edge client (PromQL for Prometheus, LogQL for Loki, SQL for SQL sources, etc.). + args (object, via --data) — Polymorphic key/value extension parameters forwarded verbatim to monit-edge. All values must be strings. Semantics depend on 'ds_type': SLS requires 'sls.project' + 'sls.logstore'; Loki / VictoriaLogs raw mode requires a time range via '.start'/'.end' or '.timespan.value' + '.timespan.unit'; Prometheus and SQL sources ignore it. Always namespace keys by source (e.g. 'sls.project', 'loki.type'). + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - fields (object) — String-valued fields (labels, log fields, SQL columns). + - values (object) — Numeric fields. For metric queries the canonical key is '__value__'. May be 'null' for detail-oriented sources. +`, + Example: ` flashduty monit query-rows --data '{"account_id":10001,"delay_seconds":30,"ds_name":"prod-prom","ds_type":"prometheus","expr":"up"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("account-id") { + body["account_id"] = fAccountID + } + if cmd.Flags().Changed("delay-seconds") { + body["delay_seconds"] = fDelaySeconds + } + if cmd.Flags().Changed("ds-name") { + body["ds_name"] = fDsName + } + if cmd.Flags().Changed("ds-type") { + body["ds_type"] = fDsType + } + if cmd.Flags().Changed("expr") { + body["expr"] = fExpr + } + }) + if err != nil { + return err + } + req := new(flashduty.QueryRowsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Diagnostics.QueryRows(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Optional consistency check. Must equal the authenticated account when supplied; mismatched values are rejected. Business execution always uses the authenticated account.") + cmd.Flags().Int64Var(&fDelaySeconds, "delay-seconds", 0, "Look-back offset in seconds applied to point-in-time queries (Prometheus, Loki stats, VictoriaLogs stats). Ignored for raw / detail queries.") + cmd.Flags().StringVar(&fDsName, "ds-name", "", "Data source name; must match a configured data source under the tenant. (required)") + cmd.Flags().StringVar(&fDsType, "ds-type", "", "Data source type; must match a configured data source under the tenant. Examples: 'prometheus', 'loki', 'victorialogs', 'sls', 'elasticsearch', 'mysql', 'postgres', 'oracle', 'clickhouse'. (required)") + cmd.Flags().StringVar(&fExpr, "expr", "", "Query expression. Syntax depends on 'ds_type' and is interpreted by the corresponding monit-edge client (PromQL for Prometheus, LogQL for Loki, SQL for SQL sources, etc.). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDiagnosticsTargetsListCmd() *cobra.Command { + var dataJSON string + var fAccountID int64 + var fCursor string + var fKeyword string + var fLimit int64 + cmd := &cobra.Command{ + Use: "targets", + Short: "List monitored targets", + Long: `List monitored targets. + +List the targets observed under the current tenant by the monit-agent route projection. Supports 'target_locator' prefix search and cursor pagination. Use this to drive 'target_locator' selection for '/monit/tools/catalog' and '/monit/tools/invoke'. + +API: POST /monit/targets (monit-read-targets-list) + +Request fields: + --account-id int — Optional consistency check. Must equal the authenticated account when supplied. + --cursor string — Opaque pagination cursor from the previous response's 'next_cursor'. Omit / pass empty string for the first page. Reset whenever 'keyword', 'limit', or tenant changes. + --keyword string — Prefix match against 'target_locator'. ASCII only, no whitespace, no '|', max 256 bytes. Substring search is not supported. + --limit int — Page size. Default 50, max 200. (max 200) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) + - agent_version (string) — Most recently observed Agent version. + - cluster_name (string) — Edge cluster name. + - edge_ipport (string) — Edge instance address ('ip:port'), surfaced for diagnostics. + - target_kind (string) — Target kind, e.g. 'host', 'mysql'. Filtering by kind is not supported in v1. + - target_locator (string) — Target identifier; the list is sorted by this field ascending. + - updated_at (integer) — Last route-projection upsert time, Unix seconds. Treat as 'most recently observed', not a live-online indicator. + - next_cursor (string) — Opaque cursor for the next page. Absent / empty means this is the last page. + - total (integer) — Total matches for the current '(account_id, keyword)' pair, independent of 'cursor'. +`, + Example: ` flashduty monit targets --data '{"keyword":"db-prod","limit":50}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("account-id") { + body["account_id"] = fAccountID + } + if cmd.Flags().Changed("cursor") { + body["cursor"] = fCursor + } + if cmd.Flags().Changed("keyword") { + body["keyword"] = fKeyword + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + }) + if err != nil { + return err + } + req := new(flashduty.TargetsListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Diagnostics.TargetsList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Optional consistency check. Must equal the authenticated account when supplied.") + cmd.Flags().StringVar(&fCursor, "cursor", "", "Opaque pagination cursor from the previous response's 'next_cursor'. Omit / pass empty string for the first page. Reset whenever 'keyword', 'limit', or tenant changes.") + cmd.Flags().StringVar(&fKeyword, "keyword", "", "Prefix match against 'target_locator'. ASCII only, no whitespace, no '|', max 256 bytes. Substring search is not supported.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Default 50, max 200. (max 200)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDiagnosticsToolsCatalogCmd() *cobra.Command { + var dataJSON string + var fAccountID int64 + var fIncludeOutputShape bool + var fTargetKind string + var fTargetLocator string + cmd := &cobra.Command{ + Use: "tools-catalog", + Short: "List target tool catalog", + Long: `List target tool catalog. + +Look up the tools that the per-target monit-agent currently exposes for a given 'target_locator' (host, mysql, …). Returns each tool's name, description, and JSON-Schema 'input_schema'. Pair with '/monit/tools/invoke' to drive AI-SRE tool calls. + +API: POST /monit/tools/catalog (monit-read-tools-catalog) + +Request fields: + --account-id int — Optional consistency check. Must equal the authenticated account when supplied. + --include-output-shape bool — When true, each tool entry includes its 'output_shape' JSON Schema. Defaults to false to keep responses small for LLM consumption. + --target-kind string — Optional target kind. When omitted webapi auto-infers across currently known kinds. Built-in kinds: 'host', 'mysql'. Required on retry when the previous call returned 'ambiguous_target_kind'. + --target-locator string (required) — Target identifier (host name, MySQL address, …). Max 256 bytes; no whitespace, control characters, or '|'. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - error (object) — Business error. 'null' on success. + - code (string) [target_unavailable, unknown_toolset_hash, ambiguous_target_kind] + - message (string) + - target_kinds (array) — Returned for 'ambiguous_target_kind'; lists the candidate kinds. + - target (object) — Resolved target. 'null' when locator could not be uniquely resolved. + - kind (string) + - locator (string) + - tools (array) — Tool catalog entries. Empty when 'error' is non-null. + - description (string) — Tool capability description for UI / AI-SRE consumption. + - input_schema (object) — JSON Schema for 'tools[].params'. + - name (string) — Tool name; pass into '/monit/tools/invoke' as 'tools[].tool'. + - output_shape (object) — Optional output JSON Schema; only returned when 'include_output_shape=true'. + - target_kind (string) — Target kind this tool applies to. +`, + Example: ` flashduty monit tools-catalog --data '{"account_id":10001,"include_output_shape":true,"target_locator":"web-01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("account-id") { + body["account_id"] = fAccountID + } + if cmd.Flags().Changed("include-output-shape") { + body["include_output_shape"] = fIncludeOutputShape + } + if cmd.Flags().Changed("target-kind") { + body["target_kind"] = fTargetKind + } + if cmd.Flags().Changed("target-locator") { + body["target_locator"] = fTargetLocator + } + }) + if err != nil { + return err + } + req := new(flashduty.ToolCatalogRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Diagnostics.ToolsCatalog(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Optional consistency check. Must equal the authenticated account when supplied.") + cmd.Flags().BoolVar(&fIncludeOutputShape, "include-output-shape", false, "When true, each tool entry includes its 'output_shape' JSON Schema. Defaults to false to keep responses small for LLM consumption.") + cmd.Flags().StringVar(&fTargetKind, "target-kind", "", "Optional target kind. When omitted webapi auto-infers across currently known kinds. Built-in kinds: 'host', 'mysql'. Required on retry when the previous call returned 'ambiguous_target_kind'.") + cmd.Flags().StringVar(&fTargetLocator, "target-locator", "", "Target identifier (host name, MySQL address, …). Max 256 bytes; no whitespace, control characters, or '|'. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genDiagnosticsToolsInvokeCmd() *cobra.Command { + var dataJSON string + var fAccountID int64 + var fTargetKind string + var fTargetLocator string + cmd := &cobra.Command{ + Use: "tools-invoke", + Short: "Invoke target tools", + Long: `Invoke target tools. + +Invoke up to 8 monit-agent tools concurrently on a single target. Results come back in the order of the input 'tools' array. Long-running — individual tools have per-tool timeouts on the agent and the whole request may take tens of seconds. + +API: POST /monit/tools/invoke (monit-read-tools-invoke) + +Request fields: + --account-id int — Optional consistency check. Must equal the authenticated account when supplied. + --target-kind string — Optional target kind; auto-inferred when omitted. + --target-locator string (required) — Target identifier. Same validation rules as '/monit/tools/catalog'. + tools (array, via --data) (required) — Up to 8 tool calls; webapi executes them concurrently and returns results in input order. + - params (object) — Tool parameters matching the catalog 'input_schema'. For no-arg tools pass '{}' explicitly. + - tool (string) (required) — Tool name, typically from '/monit/tools/catalog'. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - error (object) — Request-level business error. 'null' on success. + - code (string) [target_unavailable, unknown_toolset_hash, forward_failed, invalid_tool_result, ambiguous_target_kind] + - message (string) + - target_kinds (array) + - results (array) — Per-tool results aligned with the request 'tools[]' order. Empty when 'error' is non-null. + - agent_elapsed_ms (integer) — Agent-self-reported tool execution time in milliseconds, excludes network. May be 0 when the failure occurred before the agent started executing. + - data (object) — Successful tool payload — passthrough of monit-agent 'ToolResultPayload.data' (typically 'data' / 'summary' / 'truncated'). 'null' when the per-tool 'error' is set. + - e2e_elapsed_ms (integer) — Webapi-observed end-to-end time in milliseconds (webapi → ws → edge → agent → ws → webapi). A large gap vs 'agent_elapsed_ms' indicates network / edge slowness. + - error (object) — Per-tool error. Mutually exclusive with 'data'. + - code (string) — Common values: 'timeout', 'target_unavailable', 'edge_unsupported', 'invalid_tool_result', 'internal', 'invalid_args', 'unknown_tool', 'unknown_tool_version', 'unknown_toolset_hash', 'target_not_owned', 'wrong_agent', 'overloaded', 'denied', 'permission_denied', 'credential_unavailable', 'target_unreachable'. + - message (string) + - tool (string) + - tool_version (string) — Agent-executed tool version. Empty when execution failed before the agent picked a version. + - target (object) — Resolved target. + - kind (string) + - locator (string) +`, + Example: ` flashduty monit tools-invoke --data '{"account_id":10001,"target_locator":"web-01","tools":[{"params":{},"tool":"os.overview"},{"params":{"host":"10.0.0.10","port":3306},"tool":"net.tcp_ping"}]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("account-id") { + body["account_id"] = fAccountID + } + if cmd.Flags().Changed("target-kind") { + body["target_kind"] = fTargetKind + } + if cmd.Flags().Changed("target-locator") { + body["target_locator"] = fTargetLocator + } + }) + if err != nil { + return err + } + req := new(flashduty.ToolInvokeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Diagnostics.ToolsInvoke(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Optional consistency check. Must equal the authenticated account when supplied.") + cmd.Flags().StringVar(&fTargetKind, "target-kind", "", "Optional target kind; auto-inferred when omitted.") + cmd.Flags().StringVar(&fTargetLocator, "target-locator", "", "Target identifier. Same validation rules as '/monit/tools/catalog'. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedDiagnostics(root *cobra.Command) { + gMonit := genGroup(root, "monit", "Monitors API") + genAddLeaf(gMonit, genDiagnosticsQueryDiagnoseCmd()) + genAddLeaf(gMonit, genDiagnosticsQueryRowsCmd()) + genAddLeaf(gMonit, genDiagnosticsTargetsListCmd()) + genAddLeaf(gMonit, genDiagnosticsToolsCatalogCmd()) + genAddLeaf(gMonit, genDiagnosticsToolsInvokeCmd()) +} diff --git a/internal/cli/zz_generated_im_integrations.go b/internal/cli/zz_generated_im_integrations.go new file mode 100644 index 0000000..cf220ec --- /dev/null +++ b/internal/cli/zz_generated_im_integrations.go @@ -0,0 +1,66 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import "github.com/spf13/cobra" + +func genImIntegrationsListCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "im-war-room-enabled-list", + Short: "List war-room-enabled IM integrations", + Long: `List war-room-enabled IM integrations. + +List IM integrations that have the war-room feature enabled for the account. + +API: POST /datasource/im/war-room-enabled/list (im-war-room-enabled-list) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) — IM integrations with the war-room feature enabled. + - account_id (integer) — Account this integration belongs to. + - category (string) — Category of the integration plugin. + - created_at (integer) — Unix timestamp in seconds when the integration was created. + - creator_id (integer) — Person who created the integration. + - data_source_id (integer) — Integration ID. + - description (string) — Integration description. + - exclusive_data_source_id (integer) — Exclusive integration ID associated with this integration. + - integration_id (integer) — Integration ID, alias of data_source_id. + - integration_key (string) — Push key used by alert sources to send to this integration. + - last_time (integer) — Unix timestamp in seconds of the most recent activity on the integration. + - name (string) — Integration name. + - no_editable (boolean) — Whether the integration is read-only. + - plugin_id (integer) — Plugin ID backing this integration. + - plugin_type (string) — Type identifier of the integration plugin. + - plugin_type_name (string) — Localized display name of the integration plugin type. + - ref_id (string) — External reference ID of the integration. + - settings (object) — Plugin-specific configuration of the integration. + - status (string) — Current status of the integration. + - team_id (integer) — Team that owns this integration. + - updated_at (integer) — Unix timestamp in seconds when the integration was last updated. + - updated_by (integer) — Person who last updated the integration. +`, + Example: ` flashduty datasource im-war-room-enabled-list --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.ImIntegrations.List(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedImIntegrations(root *cobra.Command) { + gDatasource := genGroup(root, "datasource", "On-call/IM integrations API") + genAddLeaf(gDatasource, genImIntegrationsListCmd()) +} diff --git a/internal/cli/zz_generated_incidents.go b/internal/cli/zz_generated_incidents.go new file mode 100644 index 0000000..c719d96 --- /dev/null +++ b/internal/cli/zz_generated_incidents.go @@ -0,0 +1,2697 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genIncidentsReadGetWarRoomDefaultObserversCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + cmd := &cobra.Command{ + Use: "war-room-default-observers", + Short: "Get war-room default observers", + Long: `Get war-room default observers. + +Return historical responders suggested as default observers when opening a war room. + +API: POST /incident/war-room/default-observers (incident-read-get-war-room-default-observers) + +Request fields: + --incident-id string (required) — Incident ID, a MongoDB ObjectID hex string. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - observers (array) — Historical responders suggested as default war-room observers. + - account_id (integer) — Account this person belongs to. + - as (string) — Role the person holds in the related context. + - avatar (string) — URL of the person's avatar image. + - email (string) — Email address of the person. + - locale (string) — Preferred language locale of the person. + - person_id (integer) — Person ID. + - person_name (string) — Display name of the person. + - phone (string) — Phone number of the person. + - status (string) — Current status of the person. + - time_zone (string) — Time zone of the person. +`, + Example: ` flashduty incident war-room-default-observers --data '{"incident_id":"664a1b2c3d4e5f6a7b8c9d0e"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + }) + if err != nil { + return err + } + req := new(flashduty.GetWarRoomDefaultObserversRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.ReadGetWarRoomDefaultObservers(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID, a MongoDB ObjectID hex string. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsWriteAddWarRoomMemberCmd() *cobra.Command { + var dataJSON string + var fChatID string + var fIntegrationID int64 + var fMemberIDs []int + cmd := &cobra.Command{ + Use: "war-room-add-member", + Short: "Add war-room member", + Long: `Add war-room member. + +Add one or more members to the IM war room bound to an incident integration. + +API: POST /incident/war-room/add-member (incident-write-add-war-room-member) + +Request fields: + --chat-id string (required) — Chat ID of the war room within the IM platform. + --integration-id int (required) — IM integration that hosts the war room. + --member-ids []int (required) — Person IDs to add to the war room. +`, + Example: ` flashduty incident war-room-add-member --data '{"chat_id":"oc_5ce6d572455d361153b7cb51da133945","integration_id":362,"member_ids":[20001,20002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("chat-id") { + body["chat_id"] = fChatID + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + if cmd.Flags().Changed("member-ids") { + body["member_ids"] = fMemberIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.AddWarRoomMemberRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.WriteAddWarRoomMember(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fChatID, "chat-id", "", "Chat ID of the war room within the IM platform. (required)") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration that hosts the war room. (required)") + cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Person IDs to add to the war room. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsAckCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + cmd := &cobra.Command{ + Use: "ack", + Short: "Acknowledge incident", + Long: `Acknowledge incident. + +Acknowledge an incident to indicate you are actively working on it. + +API: POST /incident/ack (incidentAck) + +Request fields: + --incident-ids []string (required) — Incident IDs to acknowledge. At most 100 per call. +`, + Example: ` flashduty incident ack --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.AckIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Ack(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/ack") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to acknowledge. At most 100 per call. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsAlertListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fIncidentID string + var fIncludeEvents bool + var fIsActive bool + cmd := &cobra.Command{ + Use: "alert-list", + Short: "List alerts of incident", + Long: `List alerts of incident. + +List all alerts merged into a specific incident. + +API: POST /incident/alert/list (incidentAlertList) + +Request fields: + --page int — Page number starting at 1. (min 0) + --limit int — Page size, at most 1000. (0-1000) + --search-after-ctx string + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --include-events bool — When true, include raw alert events in each alert item. + --is-active bool — When true return only active alerts (Critical/Warning/Info); when false return only recovered alerts (Ok). Omit to include all. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Alert list. + - account_id (integer) (required) — Account ID. + - alert_id (string) (required) — Alert ID (MongoDB ObjectID). + - alert_key (string) (required) — Deduplication key used to merge events into the alert. + - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) (required) — Channel ID. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - created_at (integer) (required) — Creation timestamp (seconds). + - data_source_id (integer) (required) — Deprecated. Use 'integration_id' instead. + - data_source_name (string) (required) — Deprecated. Use 'integration_name'. + - data_source_ref_id (string) (required) — Deprecated. Use 'integration_ref_id'. + - data_source_type (string) — Deprecated. Use 'integration_type'. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Alert description. + - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active. + - event_cnt (integer) (required) — Total number of raw events merged into this alert. + - events (array) — Raw alert events, populated when the caller opts in. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) (required) — Whether this alert has ever been silenced. + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) (required) — Integration ID that produced the alert. + - integration_name (string) (required) — Integration display name. + - integration_ref_id (string) (required) — Integration reference ID. + - integration_type (string) (required) — Integration type string. + - labels (object) (required) — Alert labels. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event. + - responder_email (string) (required) — Primary responder email, if any. + - responder_name (string) (required) — Primary responder name, if any. + - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired. + - title (string) (required) — Alert title. + - title_rule (string) (required) — Title rendering rule. + - updated_at (integer) (required) — Last update timestamp (seconds). + - total (integer) (required) — Total matching alerts. +`, + Example: ` flashduty incident alert-list --data '{"incident_id":"69da451ef77b1b51f40e83ee","is_active":true,"limit":100,"p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("include-events") { + body["include_events"] = fIncludeEvents + } + if cmd.Flags().Changed("is-active") { + body["is_active"] = fIsActive + } + }) + if err != nil { + return err + } + req := new(flashduty.ListIncidentAlertsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.AlertList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number starting at 1. (min 0)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, at most 1000. (0-1000)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().BoolVar(&fIncludeEvents, "include-events", false, "When true, include raw alert events in each alert item.") + cmd.Flags().BoolVar(&fIsActive, "is-active", false, "When true return only active alerts (Critical/Warning/Info); when false return only recovered alerts (Ok). Omit to include all.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsAssignCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + var fIncidentIDs []string + cmd := &cobra.Command{ + Use: "assign", + Short: "Assign incident", + Long: `Assign incident. + +Dispatch an incident to a specific escalation level or responder. + +API: POST /incident/assign (incidentAssign) + +Request fields: + --incident-id string — Single incident ID. Ignored when 'incident_ids' is also provided. + --incident-ids []string — Batch incident IDs. + assigned_to (object, via --data) (required) — Incident assignment target. Either 'person_ids' or 'escalate_rule_id' must be provided. + - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made. + - emails (array) — Email recipients, used by integrations such as ServiceNow. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment. + - escalate_rule_name (string) — Escalation rule display name, filled by the server. + - id (string) — Opaque assignment ID generated by the server. + - layer_idx (integer) — Current level index within the escalation rule. + - person_ids (array) — Member IDs to assign directly. + - type (string) — Assignment type: 'assign' direct assignment, 'reassign' reassignment, 'escalate' escalation-rule driven, 'reopen' automatic reassignment on reopen. [assign, reassign, escalate, reopen] +`, + Example: ` flashduty incident assign --data '{"assigned_to":{"person_ids":[2476444212131],"type":"assign"},"incident_id":"69da451ef77b1b51f40e83ee"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.AssignIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Assign(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/assign") + return nil + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Single incident ID. Ignored when 'incident_ids' is also provided.") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Batch incident IDs.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsCommentCmd() *cobra.Command { + var dataJSON string + var fComment string + var fIncidentIDs []string + var fMuteReply bool + cmd := &cobra.Command{ + Use: "comment", + Short: "Add comment to incident", + Long: `Add comment to incident. + +Add a text comment to the incident timeline. + +API: POST /incident/comment (incidentComment) + +Request fields: + --comment string — Comment body. (≤1024 chars) + --incident-ids []string (required) — Incident IDs to comment on. At most 100 per call. + --mute-reply bool — When true, do not trigger webhook reply actions for this comment. +`, + Example: ` flashduty incident comment --data '{"comment":"Identified the root cause. Rolling back the deployment now.","incident_ids":["69da451ef77b1b51f40e83ee"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("comment") { + body["comment"] = fComment + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("mute-reply") { + body["mute_reply"] = fMuteReply + } + }) + if err != nil { + return err + } + req := new(flashduty.CommentIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Comment(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/comment") + return nil + }) + }, + } + cmd.Flags().StringVar(&fComment, "comment", "", "Comment body. (≤1024 chars)") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to comment on. At most 100 per call. (required)") + cmd.Flags().BoolVar(&fMuteReply, "mute-reply", false, "When true, do not trigger webhook reply actions for this comment.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsCreateCmd() *cobra.Command { + var dataJSON string + var fChannelID int64 + var fDescription string + var fIncidentSeverity string + var fTitle string + cmd := &cobra.Command{ + Use: "create", + Short: "Create incident", + Long: `Create incident. + +Manually create a new incident and assign responders. + +API: POST /incident/create (incidentCreate) + +Request fields: + --channel-id int — Channel to file the incident into. Optional; leave unset for a standalone incident. + --description string — Incident description, up to 1024 characters. (≤1024 chars) + --incident-severity string (required) — Incident severity. [Info, Warning, Critical] + --title string — Incident title, up to 512 characters. (≤512 chars) + assigned_to (object, via --data) — Incident assignment target. Either 'person_ids' or 'escalate_rule_id' must be provided. + - emails (array) — Email recipients, used for ServiceNow-style integrations. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment. + - layer_idx (integer) — Starting layer index when using an escalation rule. + - notify (object) — Override the notification channels used for this assignment. + - follow_preference (boolean) — When true, fall back to each responder's personal preference. + - personal_channels (array) — Channels to use (e.g. 'voice', 'sms', 'email'). + - template_id (string) — Notification template ID (MongoDB ObjectID). + - person_ids (array) — Member IDs to assign directly. + - type (string) — Assignment type. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - incident_id (string) (required) — Newly created incident ID (MongoDB ObjectID). + - title (string) (required) — Echoes the incident title from the request. +`, + Example: ` flashduty incident create --data '{"assigned_to":{"person_ids":[2476444212131]},"channel_id":2551105804131,"incident_severity":"Critical","title":"Database connection timeout on prod-db-01"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("channel-id") { + body["channel_id"] = fChannelID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("incident-severity") { + body["incident_severity"] = fIncidentSeverity + } + if cmd.Flags().Changed("title") { + body["title"] = fTitle + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.Create(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to file the incident into. Optional; leave unset for a standalone incident.") + cmd.Flags().StringVar(&fDescription, "description", "", "Incident description, up to 1024 characters. (≤1024 chars)") + cmd.Flags().StringVar(&fIncidentSeverity, "incident-severity", "", "Incident severity. (required) [Info, Warning, Critical]") + cmd.Flags().StringVar(&fTitle, "title", "", "Incident title, up to 512 characters. (≤512 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsCustomActionDoCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "custom-action-do", + Short: "Execute custom action", + Long: `Execute custom action. + +Execute a custom action configured for an incident. + +API: POST /incident/custom-action/do (incidentCustomActionDo) + +Request fields: + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --integration-id int (required) — Custom action integration ID. Must be enabled and associated with the incident's channel. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - message (string) — Error message if the action's HTTP call failed; omitted on success. +`, + Example: ` flashduty incident custom-action-do --data '{"incident_id":"69da451ef77b1b51f40e83ee","integration_id":2490562293131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.DoIncidentCustomActionRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.CustomActionDo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Custom action integration ID. Must be enabled and associated with the incident's channel. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsDisableMergeCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + cmd := &cobra.Command{ + Use: "disable-merge", + Short: "Disable incident merge", + Long: `Disable incident merge. + +Disable automatic merging for a specific incident. + +API: POST /incident/disable-merge (incidentDisableMerge) + +Request fields: + --incident-ids []string (required) — Incident IDs whose automatic merge should be disabled. +`, + Example: ` flashduty incident disable-merge --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.DisableIncidentMergeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.DisableMerge(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/disable-merge") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs whose automatic merge should be disabled. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsFeedCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fIncidentID string + var fTypes []string + cmd := &cobra.Command{ + Use: "feed", + Short: "Get incident timeline", + Long: `Get incident timeline. + +Retrieve the timeline feed for a specific incident, including state changes, comments and system events. + +API: POST /incident/feed (incidentFeed) + +Request fields: + --page int — Page number starting at 1. (min 1) + --limit int — Page size, at most 100. (1-100) + --search-after-ctx string + --asc bool — Ascending chronological order when true. + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --types []string — Optional filter restricting the returned entries to specific types. [i_new, i_assign, i_a_rspd, i_notify, i_storm, i_snooze, i_wake, i_ack, i_unack, i_comm, i_rslv, i_reopen, i_merge, i_r_title, i_r_desc, i_r_impact, i_r_rc, i_r_rsltn, i_r_severity, i_r_field, i_m_flapping, i_m_reply, i_custom, i_wr_create, i_wr_delete, i_auto_refresh, a_merge] + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — True when more entries are available. + - items (array) (required) — Timeline entries for the current page. + - account_id (integer) (required) — Account ID. + - created_at (integer) (required) — Creation timestamp in milliseconds. + - creator_id (integer) (required) — User ID of the actor. '0' means system-generated. + - deleted_at (integer) — Soft-delete timestamp (ms). Zero if not deleted. + - detail (any) (required) — Type-specific payload. The concrete shape is determined by 'type'. + - ref_id (string) (required) — ObjectID of the source alert or incident this entry references. + - type (string) (required) — Incident timeline entry type. Each value identifies one lifecycle event; the matching 'detail' payload shape is determined by this field. Incident types are prefixed with 'i_'. | Type | Meaning | |---|---| | 'i_new' | Incident Created: A new incident was created automatically or manually. | | 'i_assign' | Assigned: Incident was assigned to responders. | | 'i_a_rspd' | Responder Added: Additional responders joined the incident. | | 'i_notify' | Notification dispatched through a channel at a specific escalation level. | | 'i_storm' | Alert storm threshold reached on the incident. | | 'i_snooze' | Notifications snoozed for a given duration. | | 'i_wake' | Snooze cancelled and notifications resumed. | | 'i_ack' | Acknowledged: Responder confirmed they are working on the incident. | | 'i_unack' | Acknowledgement removed. | | 'i_comm' | Comment: Responder logged progress or key information. | | 'i_rslv' | Resolved: Incident was marked as resolved. | | 'i_reopen' | Reopened: Resolved incident was reopened, possibly due to recurrence. | | 'i_merge' | Merged: Multiple related incidents were merged into one. | | 'i_r_title' | Title updated. | | 'i_r_desc' | Description updated. | | 'i_r_impact' | Impact updated. | | 'i_r_rc' | Root cause updated. | | 'i_r_rsltn' | Resolution updated. | | 'i_r_severity' | Severity Changed: Incident severity level was adjusted. | | 'i_r_field' | Custom field value updated. | | 'i_m_flapping' | Incident muted by flapping detection. | | 'i_m_reply' | Mute reply marker on a comment. | | 'i_custom' | Action: Automated action or script was triggered. | | 'i_wr_create' | War Room Created: Chat group was created for collaborative response. | | 'i_wr_delete' | War room chat group deleted. | | 'i_auto_refresh' | Card auto-refresh event posted back to the timeline. | | 'a_merge' | Alert Merged: An alert was merged into an existing incident. | [i_new, i_assign, i_a_rspd, i_notify, i_storm, i_snooze, i_wake, i_ack, i_unack, i_comm, i_rslv, i_reopen, i_merge, i_r_title, i_r_desc, i_r_impact, i_r_rc, i_r_rsltn, i_r_severity, i_r_field, i_m_flapping, i_m_reply, i_custom, i_wr_create, i_wr_delete, i_auto_refresh, a_merge] + - updated_at (integer) (required) — Last update timestamp in milliseconds. +`, + Example: ` flashduty incident feed --data '{"incident_id":"69da451ef77b1b51f40e83ee","limit":20,"p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("types") { + body["types"] = fTypes + } + }) + if err != nil { + return err + } + req := new(flashduty.ListIncidentFeedRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.Feed(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number starting at 1. (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, at most 100. (1-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending chronological order when true.") + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().StringSliceVar(&fTypes, "types", nil, "Optional filter restricting the returned entries to specific types. [i_new, i_assign, i_a_rspd, i_notify, i_storm, i_snooze, i_wake, i_ack, i_unack, i_comm, i_rslv, i_reopen, i_merge, i_r_title, i_r_desc, i_r_impact, i_r_rc, i_r_rsltn, i_r_severity, i_r_field, i_m_flapping, i_m_reply, i_custom, i_wr_create, i_wr_delete, i_auto_refresh, a_merge]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsFieldResetCmd() *cobra.Command { + var dataJSON string + var fFieldName string + var fIncidentID string + cmd := &cobra.Command{ + Use: "field-reset", + Short: "Update incident custom field", + Long: `Update incident custom field. + +Update a custom field value on an incident. + +API: POST /incident/field/reset (incidentFieldReset) + +Request fields: + --field-name string (required) — Custom field name; must match a field defined on the account. + --incident-id string (required) — Incident ID (MongoDB ObjectID). + field_value (any, via --data) — New field value. Type must match the field definition. +`, + Example: ` flashduty incident field-reset --data '{"field_name":"affected_service","field_value":"payment-service","incident_id":"69da451ef77b1b51f40e83ee"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("field-name") { + body["field_name"] = fFieldName + } + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + }) + if err != nil { + return err + } + req := new(flashduty.ResetIncidentFieldRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.FieldReset(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/field/reset") + return nil + }) + }, + } + cmd.Flags().StringVar(&fFieldName, "field-name", "", "Custom field name; must match a field defined on the account. (required)") + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsInfoCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + cmd := &cobra.Command{ + Use: "info", + Short: "Get incident detail", + Long: `Get incident detail. + +Retrieve detailed information for a single incident including timeline, alerts, responders and custom fields. + +API: POST /incident/info (incidentInfo) + +Request fields: + --incident-id string (required) — Incident ID (MongoDB ObjectID). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Account ID that owns the incident. + - account_locale (string) (required) — Account locale. + - account_name (string) (required) — Account name. + - account_time_zone (string) (required) — Account time zone. + - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged. + - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state. + - ai_summary (string) (required) — AI-generated summary of the incident. + - alert_cnt (integer) (required) — Total count of alerts merged into this incident. + - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts. + - alerts (array) — Embedded alerts, only populated for notification templates and custom actions. + - account_id (integer) (required) — Account ID. + - alert_id (string) (required) — Alert ID (MongoDB ObjectID). + - alert_key (string) (required) — Deduplication key used to merge events into the alert. + - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) (required) — Channel ID. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - created_at (integer) (required) — Creation timestamp (seconds). + - data_source_id (integer) (required) — Deprecated. Use 'integration_id' instead. + - data_source_name (string) (required) — Deprecated. Use 'integration_name'. + - data_source_ref_id (string) (required) — Deprecated. Use 'integration_ref_id'. + - data_source_type (string) — Deprecated. Use 'integration_type'. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Alert description. + - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active. + - event_cnt (integer) (required) — Total number of raw events merged into this alert. + - events (array) — Raw alert events, populated when the caller opts in. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - alt (string) — Alt text. + - href (string) — Optional link URL when the image is clicked. + - src (string) (required) — Image source URL or internal image reference (starts with 'img_' or 'http'). + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) (required) — Whether this alert has ever been silenced. + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) (required) — Integration ID that produced the alert. + - integration_name (string) (required) — Integration display name. + - integration_ref_id (string) (required) — Integration reference ID. + - integration_type (string) (required) — Integration type string. + - labels (object) (required) — Alert labels. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event. + - responder_email (string) (required) — Primary responder email, if any. + - responder_name (string) (required) — Primary responder name, if any. + - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired. + - title (string) (required) — Alert title. + - title_rule (string) (required) — Title rendering rule. + - updated_at (integer) (required) — Last update timestamp (seconds). + - assigned_to (object) (required) — Incident assignment target. Either 'person_ids' or 'escalate_rule_id' must be provided. + - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made. + - emails (array) — Email recipients, used by integrations such as ServiceNow. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment. + - escalate_rule_name (string) — Escalation rule display name, filled by the server. + - id (string) — Opaque assignment ID generated by the server. + - layer_idx (integer) — Current level index within the escalation rule. + - person_ids (array) — Member IDs to assign directly. + - type (string) — Assignment type: 'assign' direct assignment, 'reassign' reassignment, 'escalate' escalation-rule driven, 'reopen' automatic reassignment on reopen. [assign, reassign, escalate, reopen] + - channel_id (integer) (required) — Channel ID. 0 for standalone incidents. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open. + - closer (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed. + - created_at (integer) (required) — Creation timestamp (seconds). + - creator (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - data_source_ids (array) — Deprecated. Use 'integration_ids' instead. + - data_source_type (string) — Deprecated. Use 'integration_type' instead. + - data_source_types (array) — Deprecated. Use 'integration_types' instead. + - dedup_key (string) (required) — Deduplication key used to coalesce alerts. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Incident description. + - detail_url (string) (required) — Web console URL for the incident. + - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active. + - equals_md5 (string) (required) — MD5 hash used for content-equality checks. + - ever_muted (boolean) (required) — Whether the incident has ever been silenced. + - fields (object) (required) — Custom field values keyed by field name. + - frequency (string) — Frequency bucket for recurrence analysis: 'frequent' or 'rare'. [frequent, rare] + - group_method (string) (required) — Alert grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - impact (string) (required) — Impact description. + - incident_id (string) (required) — Incident ID (MongoDB ObjectID). + - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok] + - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok] + - integration_id (integer) (required) — First integration associated with the incident. + - integration_ids (array) (required) — All integration IDs contributing alerts to this incident. + - integration_type (string) — First alert's integration type string, used by the detail page for label mappings. + - integration_types (array) (required) — Integration type strings for all contributing integrations. + - labels (object) (required) — Labels propagated from alerts. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update. + - links (array) — Channel-level link integrations rendered for this incident. + - endpoint (string) (required) — Rendered URL for the link. + - name (string) (required) — Display name of the link. + - open_type (string) (required) — How the link should be opened. [popup, tab] + - manual_overrides (array) (required) — Fields that were manually overridden after auto-population. + - num (string) (required) — Short display identifier; not guaranteed unique. + - owner (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - owner_id (integer) (required) — Primary owner member ID. 0 if none. + - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem. + - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed] + - reporter_email (string) — Reporter email for manually created incidents. + - resolution (string) (required) — Resolution notes. + - responders (array) (required) — Current responders with assignment/acknowledgement state. + - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged. + - as (string) — Role label of this responder. + - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned. + - email (string) — Member email, filled by the server. + - person_id (integer) (required) — Responder member ID. + - person_name (string) — Member display name, filled by the server. + - root_cause (string) (required) — Root cause analysis. + - silence_url (string) (required) — Quick-silence URL for this incident. + - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed. + - start_time (integer) (required) — Unix timestamp (seconds) when the incident started. + - title (string) (required) — Incident title. + - updated_at (integer) (required) — Last update timestamp (seconds). +`, + Example: ` flashduty incident info --data '{"incident_id":"69da451ef77b1b51f40e83ee"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + }) + if err != nil { + return err + } + req := new(flashduty.IncidentInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.Info(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAckerIDs []int + var fAsc bool + var fChannelIDs []int + var fCloserIDs []int + var fCreatorIDs []int + var fEndTime int64 + var fEverMuted bool + var fIncidentIDs []string + var fIncidentSeverity string + var fIsMyChannel bool + var fIsMyTeam bool + var fIsRare bool + var fIsSnoozed bool + var fNums []string + var fProgress string + var fQuery string + var fResponderIDs []int + var fStartTime int64 + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "list", + Short: "List incidents", + Long: `List incidents. + +Query a paginated list of incidents with filters by channel, severity, status, responder, and time range. + +API: POST /incident/list (incidentList) + +Request fields: + --page int — Page number starting at 1. (min 0) + --limit int — Page size, at most 100. (0-100) + --search-after-ctx string — Cursor from a previous response for forward pagination. + --acker-ids []int — Acknowledger member IDs. + --asc bool — Ascending order when true. + --channel-ids []int — Channel IDs to filter by. Use 0 for standalone (global) incidents. + --closer-ids []int — Closer member IDs. Use 0 for automatically closed incidents. + --creator-ids []int — Creator member IDs. Use 0 for automatically created incidents. + --end-time int (required) — Window end, Unix seconds. Must be greater than 'start_time' and within 31 days. + --ever-muted bool — When true, include only incidents that were ever silenced. + --incident-ids []string — Restrict to the given incident IDs. + --incident-severity string — Comma-separated list of severities ('Critical,Warning,Info'). + --is-my-channel bool — When true, restrict to incidents in channels the user personally owns. + --is-my-team bool — When true, restrict to incidents in channels owned by the user's teams. + --is-rare bool — When true, include only outlier (rare) incidents. + --is-snoozed bool — When true, include only snoozed incidents. + --nums []string — Restrict to the given short display identifiers. + --progress string — Comma-separated list of progress states to match (e.g. 'Triggered,Processing'). + --query string — Full-text search query. + --responder-ids []int — Responder member IDs. + --start-time int (required) — Window start, Unix seconds. + --team-ids []int — Team IDs; resolved to channels via channel ownership. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — True when more results are available beyond this page. + - items (array) (required) — Incident list for the current page. + - account_id (integer) (required) — Account ID that owns the incident. + - account_locale (string) (required) — Account locale. + - account_name (string) (required) — Account name. + - account_time_zone (string) (required) — Account time zone. + - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged. + - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state. + - ai_summary (string) (required) — AI-generated summary of the incident. + - alert_cnt (integer) (required) — Total count of alerts merged into this incident. + - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts. + - alerts (array) — Embedded alerts, only populated for notification templates and custom actions. + - account_id (integer) (required) — Account ID. + - alert_id (string) (required) — Alert ID (MongoDB ObjectID). + - alert_key (string) (required) — Deduplication key used to merge events into the alert. + - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) (required) — Channel ID. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - created_at (integer) (required) — Creation timestamp (seconds). + - data_source_id (integer) (required) — Deprecated. Use 'integration_id' instead. + - data_source_name (string) (required) — Deprecated. Use 'integration_name'. + - data_source_ref_id (string) (required) — Deprecated. Use 'integration_ref_id'. + - data_source_type (string) — Deprecated. Use 'integration_type'. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Alert description. + - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active. + - event_cnt (integer) (required) — Total number of raw events merged into this alert. + - events (array) — Raw alert events, populated when the caller opts in. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) (required) — Whether this alert has ever been silenced. + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) (required) — Integration ID that produced the alert. + - integration_name (string) (required) — Integration display name. + - integration_ref_id (string) (required) — Integration reference ID. + - integration_type (string) (required) — Integration type string. + - labels (object) (required) — Alert labels. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event. + - responder_email (string) (required) — Primary responder email, if any. + - responder_name (string) (required) — Primary responder name, if any. + - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired. + - title (string) (required) — Alert title. + - title_rule (string) (required) — Title rendering rule. + - updated_at (integer) (required) — Last update timestamp (seconds). + - assigned_to (object) (required) — Incident assignment target. Either 'person_ids' or 'escalate_rule_id' must be provided. + - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made. + - emails (array) — Email recipients, used by integrations such as ServiceNow. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment. + - escalate_rule_name (string) — Escalation rule display name, filled by the server. + - id (string) — Opaque assignment ID generated by the server. + - layer_idx (integer) — Current level index within the escalation rule. + - person_ids (array) — Member IDs to assign directly. + - type (string) — Assignment type: 'assign' direct assignment, 'reassign' reassignment, 'escalate' escalation-rule driven, 'reopen' automatic reassignment on reopen. [assign, reassign, escalate, reopen] + - channel_id (integer) (required) — Channel ID. 0 for standalone incidents. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open. + - closer (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed. + - created_at (integer) (required) — Creation timestamp (seconds). + - creator (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - data_source_ids (array) — Deprecated. Use 'integration_ids' instead. + - data_source_type (string) — Deprecated. Use 'integration_type' instead. + - data_source_types (array) — Deprecated. Use 'integration_types' instead. + - dedup_key (string) (required) — Deduplication key used to coalesce alerts. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Incident description. + - detail_url (string) (required) — Web console URL for the incident. + - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active. + - equals_md5 (string) (required) — MD5 hash used for content-equality checks. + - ever_muted (boolean) (required) — Whether the incident has ever been silenced. + - fields (object) (required) — Custom field values keyed by field name. + - frequency (string) — Frequency bucket for recurrence analysis: 'frequent' or 'rare'. [frequent, rare] + - group_method (string) (required) — Alert grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - impact (string) (required) — Impact description. + - incident_id (string) (required) — Incident ID (MongoDB ObjectID). + - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok] + - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok] + - integration_id (integer) (required) — First integration associated with the incident. + - integration_ids (array) (required) — All integration IDs contributing alerts to this incident. + - integration_type (string) — First alert's integration type string, used by the detail page for label mappings. + - integration_types (array) (required) — Integration type strings for all contributing integrations. + - labels (object) (required) — Labels propagated from alerts. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update. + - links (array) — Channel-level link integrations rendered for this incident. + - endpoint (string) (required) — Rendered URL for the link. + - name (string) (required) — Display name of the link. + - open_type (string) (required) — How the link should be opened. [popup, tab] + - manual_overrides (array) (required) — Fields that were manually overridden after auto-population. + - num (string) (required) — Short display identifier; not guaranteed unique. + - owner (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - owner_id (integer) (required) — Primary owner member ID. 0 if none. + - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem. + - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed] + - reporter_email (string) — Reporter email for manually created incidents. + - resolution (string) (required) — Resolution notes. + - responders (array) (required) — Current responders with assignment/acknowledgement state. + - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged. + - as (string) — Role label of this responder. + - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned. + - email (string) — Member email, filled by the server. + - person_id (integer) (required) — Responder member ID. + - person_name (string) — Member display name, filled by the server. + - root_cause (string) (required) — Root cause analysis. + - silence_url (string) (required) — Quick-silence URL for this incident. + - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed. + - start_time (integer) (required) — Unix timestamp (seconds) when the incident started. + - title (string) (required) — Incident title. + - updated_at (integer) (required) — Last update timestamp (seconds). + - search_after_ctx (string) — Opaque cursor to pass as 'search_after_ctx' on the next request. + - total (integer) (required) — Total number of matching incidents. +`, + Example: ` flashduty incident list --data '{"channel_ids":[2551105804131],"end_time":1712000000,"incident_severity":"Critical,Warning","limit":20,"p":1,"progress":"Triggered,Processing","start_time":1711900800}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("acker-ids") { + body["acker_ids"] = fAckerIDs + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("closer-ids") { + body["closer_ids"] = fCloserIDs + } + if cmd.Flags().Changed("creator-ids") { + body["creator_ids"] = fCreatorIDs + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("ever-muted") { + body["ever_muted"] = fEverMuted + } + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("incident-severity") { + body["incident_severity"] = fIncidentSeverity + } + if cmd.Flags().Changed("is-my-channel") { + body["is_my_channel"] = fIsMyChannel + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("is-rare") { + body["is_rare"] = fIsRare + } + if cmd.Flags().Changed("is-snoozed") { + body["is_snoozed"] = fIsSnoozed + } + if cmd.Flags().Changed("nums") { + body["nums"] = fNums + } + if cmd.Flags().Changed("progress") { + body["progress"] = fProgress + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("responder-ids") { + body["responder_ids"] = fResponderIDs + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ListIncidentsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.List(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number starting at 1. (min 0)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, at most 100. (0-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Cursor from a previous response for forward pagination.") + cmd.Flags().IntSliceVar(&fAckerIDs, "acker-ids", nil, "Acknowledger member IDs.") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending order when true.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Channel IDs to filter by. Use 0 for standalone (global) incidents.") + cmd.Flags().IntSliceVar(&fCloserIDs, "closer-ids", nil, "Closer member IDs. Use 0 for automatically closed incidents.") + cmd.Flags().IntSliceVar(&fCreatorIDs, "creator-ids", nil, "Creator member IDs. Use 0 for automatically created incidents.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "Window end, Unix seconds. Must be greater than 'start_time' and within 31 days. (required)") + cmd.Flags().BoolVar(&fEverMuted, "ever-muted", false, "When true, include only incidents that were ever silenced.") + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Restrict to the given incident IDs.") + cmd.Flags().StringVar(&fIncidentSeverity, "incident-severity", "", "Comma-separated list of severities ('Critical,Warning,Info').") + cmd.Flags().BoolVar(&fIsMyChannel, "is-my-channel", false, "When true, restrict to incidents in channels the user personally owns.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "When true, restrict to incidents in channels owned by the user's teams.") + cmd.Flags().BoolVar(&fIsRare, "is-rare", false, "When true, include only outlier (rare) incidents.") + cmd.Flags().BoolVar(&fIsSnoozed, "is-snoozed", false, "When true, include only snoozed incidents.") + cmd.Flags().StringSliceVar(&fNums, "nums", nil, "Restrict to the given short display identifiers.") + cmd.Flags().StringVar(&fProgress, "progress", "", "Comma-separated list of progress states to match (e.g. 'Triggered,Processing').") + cmd.Flags().StringVar(&fQuery, "query", "", "Full-text search query.") + cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Responder member IDs.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Window start, Unix seconds. (required)") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Team IDs; resolved to channels via channel ownership.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsListByIDsCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + cmd := &cobra.Command{ + Use: "list-by-ids", + Short: "List incidents by IDs", + Long: `List incidents by IDs. + +Retrieve multiple incidents by their IDs in a single request. + +API: POST /incident/list-by-ids (incidentListByIds) + +Request fields: + --incident-ids []string (required) — Incident IDs to fetch. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — True when more results are available beyond this page. + - items (array) (required) — Incident list for the current page. + - account_id (integer) (required) — Account ID that owns the incident. + - account_locale (string) (required) — Account locale. + - account_name (string) (required) — Account name. + - account_time_zone (string) (required) — Account time zone. + - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged. + - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state. + - ai_summary (string) (required) — AI-generated summary of the incident. + - alert_cnt (integer) (required) — Total count of alerts merged into this incident. + - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts. + - alerts (array) — Embedded alerts, only populated for notification templates and custom actions. + - account_id (integer) (required) — Account ID. + - alert_id (string) (required) — Alert ID (MongoDB ObjectID). + - alert_key (string) (required) — Deduplication key used to merge events into the alert. + - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) (required) — Channel ID. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - created_at (integer) (required) — Creation timestamp (seconds). + - data_source_id (integer) (required) — Deprecated. Use 'integration_id' instead. + - data_source_name (string) (required) — Deprecated. Use 'integration_name'. + - data_source_ref_id (string) (required) — Deprecated. Use 'integration_ref_id'. + - data_source_type (string) — Deprecated. Use 'integration_type'. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Alert description. + - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active. + - event_cnt (integer) (required) — Total number of raw events merged into this alert. + - events (array) — Raw alert events, populated when the caller opts in. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) (required) — Whether this alert has ever been silenced. + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) (required) — Integration ID that produced the alert. + - integration_name (string) (required) — Integration display name. + - integration_ref_id (string) (required) — Integration reference ID. + - integration_type (string) (required) — Integration type string. + - labels (object) (required) — Alert labels. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event. + - responder_email (string) (required) — Primary responder email, if any. + - responder_name (string) (required) — Primary responder name, if any. + - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired. + - title (string) (required) — Alert title. + - title_rule (string) (required) — Title rendering rule. + - updated_at (integer) (required) — Last update timestamp (seconds). + - assigned_to (object) (required) — Incident assignment target. Either 'person_ids' or 'escalate_rule_id' must be provided. + - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made. + - emails (array) — Email recipients, used by integrations such as ServiceNow. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment. + - escalate_rule_name (string) — Escalation rule display name, filled by the server. + - id (string) — Opaque assignment ID generated by the server. + - layer_idx (integer) — Current level index within the escalation rule. + - person_ids (array) — Member IDs to assign directly. + - type (string) — Assignment type: 'assign' direct assignment, 'reassign' reassignment, 'escalate' escalation-rule driven, 'reopen' automatic reassignment on reopen. [assign, reassign, escalate, reopen] + - channel_id (integer) (required) — Channel ID. 0 for standalone incidents. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open. + - closer (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed. + - created_at (integer) (required) — Creation timestamp (seconds). + - creator (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - data_source_ids (array) — Deprecated. Use 'integration_ids' instead. + - data_source_type (string) — Deprecated. Use 'integration_type' instead. + - data_source_types (array) — Deprecated. Use 'integration_types' instead. + - dedup_key (string) (required) — Deduplication key used to coalesce alerts. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Incident description. + - detail_url (string) (required) — Web console URL for the incident. + - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active. + - equals_md5 (string) (required) — MD5 hash used for content-equality checks. + - ever_muted (boolean) (required) — Whether the incident has ever been silenced. + - fields (object) (required) — Custom field values keyed by field name. + - frequency (string) — Frequency bucket for recurrence analysis: 'frequent' or 'rare'. [frequent, rare] + - group_method (string) (required) — Alert grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - impact (string) (required) — Impact description. + - incident_id (string) (required) — Incident ID (MongoDB ObjectID). + - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok] + - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok] + - integration_id (integer) (required) — First integration associated with the incident. + - integration_ids (array) (required) — All integration IDs contributing alerts to this incident. + - integration_type (string) — First alert's integration type string, used by the detail page for label mappings. + - integration_types (array) (required) — Integration type strings for all contributing integrations. + - labels (object) (required) — Labels propagated from alerts. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update. + - links (array) — Channel-level link integrations rendered for this incident. + - endpoint (string) (required) — Rendered URL for the link. + - name (string) (required) — Display name of the link. + - open_type (string) (required) — How the link should be opened. [popup, tab] + - manual_overrides (array) (required) — Fields that were manually overridden after auto-population. + - num (string) (required) — Short display identifier; not guaranteed unique. + - owner (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - owner_id (integer) (required) — Primary owner member ID. 0 if none. + - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem. + - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed] + - reporter_email (string) — Reporter email for manually created incidents. + - resolution (string) (required) — Resolution notes. + - responders (array) (required) — Current responders with assignment/acknowledgement state. + - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged. + - as (string) — Role label of this responder. + - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned. + - email (string) — Member email, filled by the server. + - person_id (integer) (required) — Responder member ID. + - person_name (string) — Member display name, filled by the server. + - root_cause (string) (required) — Root cause analysis. + - silence_url (string) (required) — Quick-silence URL for this incident. + - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed. + - start_time (integer) (required) — Unix timestamp (seconds) when the incident started. + - title (string) (required) — Incident title. + - updated_at (integer) (required) — Last update timestamp (seconds). + - search_after_ctx (string) — Opaque cursor to pass as 'search_after_ctx' on the next request. + - total (integer) (required) — Total number of matching incidents. +`, + Example: ` flashduty incident list-by-ids --data '{"incident_ids":["69da451ef77b1b51f40e83ee","69da451ef77b1b51f40e83ef"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ListIncidentsByIDsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.ListByIDs(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to fetch. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsMergeCmd() *cobra.Command { + var dataJSON string + var fComment string + var fOwnerID int64 + var fRemoveSourceIncidents bool + var fSourceIncidentIDs []string + var fTargetIncidentID string + var fTitle string + cmd := &cobra.Command{ + Use: "merge", + Short: "Merge incidents", + Long: `Merge incidents. + +Merge one or more incidents into a target incident. + +API: POST /incident/merge (incidentMerge) + +Request fields: + --comment string — Optional comment recorded on the merge timeline entry. (≤1024 chars) + --owner-id int — Optional new owner member ID for the target incident. + --remove-source-incidents bool — When true, soft-delete the source incidents after merging instead of closing them. + --source-incident-ids []string (required) — Source incident IDs. The target incident is removed from this set automatically. + --target-incident-id string (required) — Target incident ID that source incidents will be merged into. + --title string — Optional new title for the target incident. (≤512 chars) +`, + Example: ` flashduty incident merge --data '{"comment":"Merging related database connectivity incidents into one.","source_incident_ids":["69da451ef77b1b51f40e83ef","69da451ef77b1b51f40e83f0"],"target_incident_id":"69da451ef77b1b51f40e83ee"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("comment") { + body["comment"] = fComment + } + if cmd.Flags().Changed("owner-id") { + body["owner_id"] = fOwnerID + } + if cmd.Flags().Changed("remove-source-incidents") { + body["remove_source_incidents"] = fRemoveSourceIncidents + } + if cmd.Flags().Changed("source-incident-ids") { + body["source_incident_ids"] = fSourceIncidentIDs + } + if cmd.Flags().Changed("target-incident-id") { + body["target_incident_id"] = fTargetIncidentID + } + if cmd.Flags().Changed("title") { + body["title"] = fTitle + } + }) + if err != nil { + return err + } + req := new(flashduty.MergeIncidentsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Merge(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/merge") + return nil + }) + }, + } + cmd.Flags().StringVar(&fComment, "comment", "", "Optional comment recorded on the merge timeline entry. (≤1024 chars)") + cmd.Flags().Int64Var(&fOwnerID, "owner-id", 0, "Optional new owner member ID for the target incident.") + cmd.Flags().BoolVar(&fRemoveSourceIncidents, "remove-source-incidents", false, "When true, soft-delete the source incidents after merging instead of closing them.") + cmd.Flags().StringSliceVar(&fSourceIncidentIDs, "source-incident-ids", nil, "Source incident IDs. The target incident is removed from this set automatically. (required)") + cmd.Flags().StringVar(&fTargetIncidentID, "target-incident-id", "", "Target incident ID that source incidents will be merged into. (required)") + cmd.Flags().StringVar(&fTitle, "title", "", "Optional new title for the target incident. (≤512 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsPastListCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + var fLimit int64 + cmd := &cobra.Command{ + Use: "past-list", + Short: "List past incidents", + Long: `List past incidents. + +List historical incidents related to the current incident for reference during triage. + +API: POST /incident/past/list (incidentPastList) + +Request fields: + --incident-id string (required) — Reference incident ID (MongoDB ObjectID). + --limit int — Maximum number of similar incidents to return. (0-100) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Similar past incidents with similarity scores. + - account_id (integer) (required) — Account ID that owns the incident. + - account_locale (string) (required) — Account locale. + - account_name (string) (required) — Account name. + - account_time_zone (string) (required) — Account time zone. + - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged. + - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state. + - ai_summary (string) (required) — AI-generated summary of the incident. + - alert_cnt (integer) (required) — Total count of alerts merged into this incident. + - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts. + - alerts (array) — Embedded alerts, only populated for notification templates and custom actions. + - account_id (integer) (required) — Account ID. + - alert_id (string) (required) — Alert ID (MongoDB ObjectID). + - alert_key (string) (required) — Deduplication key used to merge events into the alert. + - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok] + - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok] + - channel_id (integer) (required) — Channel ID. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - created_at (integer) (required) — Creation timestamp (seconds). + - data_source_id (integer) (required) — Deprecated. Use 'integration_id' instead. + - data_source_name (string) (required) — Deprecated. Use 'integration_name'. + - data_source_ref_id (string) (required) — Deprecated. Use 'integration_ref_id'. + - data_source_type (string) — Deprecated. Use 'integration_type'. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Alert description. + - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active. + - event_cnt (integer) (required) — Total number of raw events merged into this alert. + - events (array) — Raw alert events, populated when the caller opts in. + - account_id (integer) — Account ID. + - alert_id (string) — Parent alert ID (MongoDB ObjectID). + - alert_key (string) — Deduplication key used to merge events into an alert. + - channel_id (integer) — Channel ID the event is routed to. + - created_at (integer) — Record creation time, Unix epoch seconds. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) — Event description. + - event_id (string) — Event ID (MongoDB ObjectID). + - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok] + - event_status (string) — Status of this event. [Critical, Warning, Info, Ok] + - event_time (integer) — Event timestamp, Unix epoch seconds. + - images (array) — Images attached to the event. + - integration_id (integer) — Integration that produced this event. + - integration_type (string) — Type/plugin key of the integration that produced this event. + - labels (object) — Label key-value pairs. + - title (string) — Event title. + - title_rule (string) — Title template used to derive 'title' from labels. + - updated_at (integer) — Record update time, Unix epoch seconds. + - ever_muted (boolean) (required) — Whether this alert has ever been silenced. + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - incident (object) — Brief incident reference embedded in an alert. + - incident_id (string) — Incident ID (ObjectID hex string). + - progress (string) — Incident progress — one of 'Triggered', 'Processing', 'Closed'. + - title (string) — Incident title. + - integration_id (integer) (required) — Integration ID that produced the alert. + - integration_name (string) (required) — Integration display name. + - integration_ref_id (string) (required) — Integration reference ID. + - integration_type (string) (required) — Integration type string. + - labels (object) (required) — Alert labels. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event. + - responder_email (string) (required) — Primary responder email, if any. + - responder_name (string) (required) — Primary responder name, if any. + - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired. + - title (string) (required) — Alert title. + - title_rule (string) (required) — Title rendering rule. + - updated_at (integer) (required) — Last update timestamp (seconds). + - assigned_to (object) (required) — Incident assignment target. Either 'person_ids' or 'escalate_rule_id' must be provided. + - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made. + - emails (array) — Email recipients, used by integrations such as ServiceNow. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment. + - escalate_rule_name (string) — Escalation rule display name, filled by the server. + - id (string) — Opaque assignment ID generated by the server. + - layer_idx (integer) — Current level index within the escalation rule. + - person_ids (array) — Member IDs to assign directly. + - type (string) — Assignment type: 'assign' direct assignment, 'reassign' reassignment, 'escalate' escalation-rule driven, 'reopen' automatic reassignment on reopen. [assign, reassign, escalate, reopen] + - channel_id (integer) (required) — Channel ID. 0 for standalone incidents. + - channel_name (string) (required) — Channel display name. + - channel_status (string) (required) — Channel status. + - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open. + - closer (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed. + - created_at (integer) (required) — Creation timestamp (seconds). + - creator (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system. + - data_source_id (integer) — Deprecated. Use 'integration_id' instead. + - data_source_ids (array) — Deprecated. Use 'integration_ids' instead. + - data_source_type (string) — Deprecated. Use 'integration_type' instead. + - data_source_types (array) — Deprecated. Use 'integration_types' instead. + - dedup_key (string) (required) — Deduplication key used to coalesce alerts. + - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted. + - description (string) (required) — Incident description. + - detail_url (string) (required) — Web console URL for the incident. + - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active. + - equals_md5 (string) (required) — MD5 hash used for content-equality checks. + - ever_muted (boolean) (required) — Whether the incident has ever been silenced. + - fields (object) (required) — Custom field values keyed by field name. + - frequency (string) — Frequency bucket for recurrence analysis: 'frequent' or 'rare'. [frequent, rare] + - group_method (string) (required) — Alert grouping method: 'i' intelligent, 'p' pattern, 'n' none. [i, p, n] + - images (array) (required) — Attached images. + - alt (string) — Alt text. + - href (string) — Optional link the image points to. + - src (string) (required) — Image source. Either an 'img_' upload token or an 'http(s)' URL. + - impact (string) (required) — Impact description. + - incident_id (string) (required) — Incident ID (MongoDB ObjectID). + - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok] + - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok] + - integration_id (integer) (required) — First integration associated with the incident. + - integration_ids (array) (required) — All integration IDs contributing alerts to this incident. + - integration_type (string) — First alert's integration type string, used by the detail page for label mappings. + - integration_types (array) (required) — Integration type strings for all contributing integrations. + - labels (object) (required) — Labels propagated from alerts. + - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update. + - links (array) — Channel-level link integrations rendered for this incident. + - endpoint (string) (required) — Rendered URL for the link. + - name (string) (required) — Display name of the link. + - open_type (string) (required) — How the link should be opened. [popup, tab] + - manual_overrides (array) (required) — Fields that were manually overridden after auto-population. + - num (string) (required) — Short display identifier; not guaranteed unique. + - owner (object) — A Flashduty member reference. + - as (string) — Role label for this member in the context of the current object. + - email (string) — Member email address. + - person_id (integer) — Member ID. + - person_name (string) — Member display name. + - owner_id (integer) (required) — Primary owner member ID. 0 if none. + - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem. + - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed] + - reporter_email (string) — Reporter email for manually created incidents. + - resolution (string) (required) — Resolution notes. + - responders (array) (required) — Current responders with assignment/acknowledgement state. + - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged. + - as (string) — Role label of this responder. + - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned. + - email (string) — Member email, filled by the server. + - person_id (integer) (required) — Responder member ID. + - person_name (string) — Member display name, filled by the server. + - root_cause (string) (required) — Root cause analysis. + - score (number) (required) — Similarity score from the vector search. + - silence_url (string) (required) — Quick-silence URL for this incident. + - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed. + - start_time (integer) (required) — Unix timestamp (seconds) when the incident started. + - title (string) (required) — Incident title. + - updated_at (integer) (required) — Last update timestamp (seconds). +`, + Example: ` flashduty incident past-list --data '{"incident_id":"69da451ef77b1b51f40e83ee","limit":5}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + }) + if err != nil { + return err + } + req := new(flashduty.ListPastIncidentsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.PastList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Reference incident ID (MongoDB ObjectID). (required)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Maximum number of similar incidents to return. (0-100)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsPostMortemDeleteCmd() *cobra.Command { + var dataJSON string + var fPostMortemID string + cmd := &cobra.Command{ + Use: "post-mortem-delete", + Short: "Delete post-mortem", + Long: `Delete post-mortem. + +Delete a post-mortem report. + +API: POST /incident/post-mortem/delete (incidentPostMortemDelete) + +Request fields: + --post-mortem-id string (required) — Post-mortem ID. +`, + Example: ` flashduty incident post-mortem-delete --data '{"post_mortem_id":"8104935102bf89dc01ac638a5261fe7e"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("post-mortem-id") { + body["post_mortem_id"] = fPostMortemID + } + }) + if err != nil { + return err + } + req := new(flashduty.DeletePostMortemRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.PostMortemDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/post-mortem/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fPostMortemID, "post-mortem-id", "", "Post-mortem ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsPostMortemInfoCmd() *cobra.Command { + var dataJSON string + var fPostMortemID string + cmd := &cobra.Command{ + Use: "post-mortem-info", + Short: "Get post-mortem", + Long: `Get post-mortem. + +Retrieve a post-mortem report by its 'post_mortem_id'. List reports via '/incident/post-mortem/list' first — each row carries the incident it covers — then fetch the full report here by that id. + +API: GET /incident/post-mortem/info (incidentPostMortemInfo) + +Request fields: + --post-mortem-id string (required) — Post-mortem ID. Deterministic hash derived from account ID and the set of linked incident IDs. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - basics (object) (required) + - incidents_earliest_start_seconds (integer) (required) — Earliest start time among linked incidents (seconds). + - incidents_highest_severity (string) (required) — Highest severity among linked incidents. + - incidents_latest_close_seconds (integer) (required) — Latest close time among linked incidents (seconds). + - incidents_total_duration_seconds (integer) (required) — Cumulative duration in seconds. + - responders (array) (required) — Responders involved in the incident(s). + - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged. + - as (string) — Role label of this responder. + - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned. + - email (string) — Member email, filled by the server. + - person_id (integer) (required) — Responder member ID. + - person_name (string) — Member display name, filled by the server. + - content (object) (required) + - content (string) (required) — Report body content (BlockNote JSON). + - follow_ups (string) (required) — Follow-up action items rendered as a single string. + - meta (object) (required) — Post-mortem metadata (lightweight shape used in lists). + - account_id (integer) (required) — Account ID. + - author_ids (array) (required) — Member IDs that contributed to the report. + - channel_id (integer) (required) — Owning channel ID. 0 if none. + - channel_name (string) (required) — Channel name, filled by the server. + - created_at_seconds (integer) (required) — Creation timestamp (seconds). + - incident_ids (array) (required) — Linked incident IDs. + - is_private (boolean) (required) — When true, only team members and admins can view. + - media_count (integer) (required) — Number of uploaded media files. + - post_mortem_id (string) (required) — Deterministic post-mortem ID derived from account and incident IDs. + - status (string) (required) — Report status. [drafting, published] + - team_id (integer) (required) — Owning team ID. 0 if none. + - template_id (string) (required) — Template used to initialize the report. + - title (string) (required) — Report title. + - updated_at_seconds (integer) (required) — Last update timestamp (seconds). +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("post-mortem-id") { + body["post_mortem_id"] = fPostMortemID + } + }) + if err != nil { + return err + } + req := new(flashduty.IncidentsPostMortemInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.PostMortemInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fPostMortemID, "post-mortem-id", "", "Post-mortem ID. Deterministic hash derived from account ID and the set of linked incident IDs. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsPostMortemListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fChannelIDs []int + var fCreatedAtEndSeconds int64 + var fCreatedAtStartSeconds int64 + var fOrderBy string + var fStatus string + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "post-mortem-list", + Short: "List post-mortems", + Long: `List post-mortems. + +List post-mortem reports with optional filters. + +API: POST /incident/post-mortem/list (incidentPostMortemList) + +Request fields: + --page int — Page number starting at 1. (min 0) + --limit int — Page size, at most 100. (0-100) + --search-after-ctx string — Cursor from a previous response for forward pagination. + --asc bool — Ascending order when true. + --channel-ids []int — Channel IDs to restrict the query to. + --created-at-end-seconds int — Filter by creation time: upper bound in seconds. (min 0) + --created-at-start-seconds int — Filter by creation time: lower bound in seconds. (min 0) + --order-by string — Field used to order results. [created_at_seconds, updated_at_seconds] + --status string — Report status. Defaults to 'published' on the server when omitted. [drafting, published] + --team-ids []int — Team IDs to restrict the query to. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — True when more results are available beyond this page. + - items (array) (required) — Post-mortem metadata for the current page. + - account_id (integer) (required) — Account ID. + - author_ids (array) (required) — Member IDs that contributed to the report. + - channel_id (integer) (required) — Owning channel ID. 0 if none. + - channel_name (string) (required) — Channel name, filled by the server. + - created_at_seconds (integer) (required) — Creation timestamp (seconds). + - incident_ids (array) (required) — Linked incident IDs. + - is_private (boolean) (required) — When true, only team members and admins can view. + - media_count (integer) (required) — Number of uploaded media files. + - post_mortem_id (string) (required) — Deterministic post-mortem ID derived from account and incident IDs. + - status (string) (required) — Report status. [drafting, published] + - team_id (integer) (required) — Owning team ID. 0 if none. + - template_id (string) (required) — Template used to initialize the report. + - title (string) (required) — Report title. + - updated_at_seconds (integer) (required) — Last update timestamp (seconds). + - search_after_ctx (string) — Cursor for forward pagination. + - total (integer) (required) — Total matching reports. +`, + Example: ` flashduty incident post-mortem-list --data '{"limit":20,"p":1,"status":"published"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("channel-ids") { + body["channel_ids"] = fChannelIDs + } + if cmd.Flags().Changed("created-at-end-seconds") { + body["created_at_end_seconds"] = fCreatedAtEndSeconds + } + if cmd.Flags().Changed("created-at-start-seconds") { + body["created_at_start_seconds"] = fCreatedAtStartSeconds + } + if cmd.Flags().Changed("order-by") { + body["order_by"] = fOrderBy + } + if cmd.Flags().Changed("status") { + body["status"] = fStatus + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ListPostMortemsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.PostMortemList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number starting at 1. (min 0)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, at most 100. (0-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Cursor from a previous response for forward pagination.") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending order when true.") + cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Channel IDs to restrict the query to.") + cmd.Flags().Int64Var(&fCreatedAtEndSeconds, "created-at-end-seconds", 0, "Filter by creation time: upper bound in seconds. (min 0)") + cmd.Flags().Int64Var(&fCreatedAtStartSeconds, "created-at-start-seconds", 0, "Filter by creation time: lower bound in seconds. (min 0)") + cmd.Flags().StringVar(&fOrderBy, "order-by", "", "Field used to order results. [created_at_seconds, updated_at_seconds]") + cmd.Flags().StringVar(&fStatus, "status", "", "Report status. Defaults to 'published' on the server when omitted. [drafting, published]") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Team IDs to restrict the query to.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsRemoveCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + cmd := &cobra.Command{ + Use: "remove", + Short: "Delete an incident", + Long: `Delete an incident. + +Permanently delete an incident and all associated data. + +API: POST /incident/remove (incidentRemove) + +Request fields: + --incident-ids []string (required) — Incident IDs to remove. At most 100 per call. The caller must have access to every channel the incidents belong to. +`, + Example: ` flashduty incident remove --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.RemoveIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Remove(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/remove") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to remove. At most 100 per call. The caller must have access to every channel the incidents belong to. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsReopenCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + var fReason string + cmd := &cobra.Command{ + Use: "reopen", + Short: "Reopen incident", + Long: `Reopen incident. + +Reopen a previously resolved incident. + +API: POST /incident/reopen (incidentReopen) + +Request fields: + --incident-ids []string (required) — Incident IDs to reopen. At most 100 per call. + --reason string — Optional reason recorded on the timeline. (≤1024 chars) +`, + Example: ` flashduty incident reopen --data '{"incident_ids":["69da451ef77b1b51f40e83ee"],"reason":"Monitoring detected the issue recurred after the initial fix."}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("reason") { + body["reason"] = fReason + } + }) + if err != nil { + return err + } + req := new(flashduty.ReopenIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Reopen(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/reopen") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to reopen. At most 100 per call. (required)") + cmd.Flags().StringVar(&fReason, "reason", "", "Optional reason recorded on the timeline. (≤1024 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsResetCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fImpact string + var fIncidentID string + var fIncidentSeverity string + var fResolution string + var fRootCause string + var fTitle string + cmd := &cobra.Command{ + Use: "reset", + Short: "Update incident fields", + Long: `Update incident fields. + +Update one or more editable fields of an incident in a single call, including title, description, impact, root cause, resolution, and severity. At least one field must be provided. + +API: POST /incident/reset (incidentReset) + +Request fields: + --description string — New description. (3-6144 chars) + --impact string — New impact description. (3-6144 chars) + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --incident-severity string — New severity. [Info, Warning, Critical] + --resolution string — New resolution notes. (3-6144 chars) + --root-cause string — New root cause analysis. (3-6144 chars) + --title string — New incident title. (3-200 chars) +`, + Example: ` flashduty incident reset --data '{"incident_id":"69da451ef77b1b51f40e83ee","incident_severity":"Critical","title":"Database connection timeout - prod-db-01 primary"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("impact") { + body["impact"] = fImpact + } + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("incident-severity") { + body["incident_severity"] = fIncidentSeverity + } + if cmd.Flags().Changed("resolution") { + body["resolution"] = fResolution + } + if cmd.Flags().Changed("root-cause") { + body["root_cause"] = fRootCause + } + if cmd.Flags().Changed("title") { + body["title"] = fTitle + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateIncidentFieldsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Reset(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/reset") + return nil + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "New description. (3-6144 chars)") + cmd.Flags().StringVar(&fImpact, "impact", "", "New impact description. (3-6144 chars)") + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().StringVar(&fIncidentSeverity, "incident-severity", "", "New severity. [Info, Warning, Critical]") + cmd.Flags().StringVar(&fResolution, "resolution", "", "New resolution notes. (3-6144 chars)") + cmd.Flags().StringVar(&fRootCause, "root-cause", "", "New root cause analysis. (3-6144 chars)") + cmd.Flags().StringVar(&fTitle, "title", "", "New incident title. (3-200 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsResolveCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + var fResolution string + var fRootCause string + cmd := &cobra.Command{ + Use: "resolve", + Short: "Resolve incident", + Long: `Resolve incident. + +Mark an incident as resolved. + +API: POST /incident/resolve (incidentResolve) + +Request fields: + --incident-ids []string (required) — Incident IDs to resolve. At most 100 per call. + --resolution string — Optional resolution note applied to every resolved incident. (≤1024 chars) + --root-cause string — Optional root cause note applied to every resolved incident. (≤1024 chars) +`, + Example: ` flashduty incident resolve --data '{"incident_ids":["69da451ef77b1b51f40e83ee"],"resolution":"Deployed hotfix v2.3.1 and restarted the affected service.","root_cause":"Memory leak in the connection pool caused by a missing cleanup call."}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("resolution") { + body["resolution"] = fResolution + } + if cmd.Flags().Changed("root-cause") { + body["root_cause"] = fRootCause + } + }) + if err != nil { + return err + } + req := new(flashduty.ResolveIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Resolve(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/resolve") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to resolve. At most 100 per call. (required)") + cmd.Flags().StringVar(&fResolution, "resolution", "", "Optional resolution note applied to every resolved incident. (≤1024 chars)") + cmd.Flags().StringVar(&fRootCause, "root-cause", "", "Optional root cause note applied to every resolved incident. (≤1024 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsResponderAddCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + var fPersonIDs []int + cmd := &cobra.Command{ + Use: "responder-add", + Short: "Add incident responder", + Long: `Add incident responder. + +Add a responder to an existing incident. + +API: POST /incident/responder/add (incidentResponderAdd) + +Request fields: + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --person-ids []int (required) — Member IDs to add as responders. + notify (object, via --data) — Optional notification override. Defaults to following each person's personal preference. + - follow_preference (boolean) — When true, fall back to each responder's personal preference. + - personal_channels (array) — Channels to use (e.g. 'voice', 'sms', 'email'). + - template_id (string) — Notification template ID (MongoDB ObjectID). +`, + Example: ` flashduty incident responder-add --data '{"incident_id":"69da451ef77b1b51f40e83ee","person_ids":[2476444212131,2476444212132]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("person-ids") { + body["person_ids"] = fPersonIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.AddIncidentResponderRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.ResponderAdd(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/responder/add") + return nil + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().IntSliceVar(&fPersonIDs, "person-ids", nil, "Member IDs to add as responders. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsSnoozeCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + var fMinutes int64 + cmd := &cobra.Command{ + Use: "snooze", + Short: "Snooze incident", + Long: `Snooze incident. + +Temporarily snooze notifications for an incident until a specified time. + +API: POST /incident/snooze (incidentSnooze) + +Request fields: + --incident-ids []string (required) — Incident IDs to snooze. At most 100 per call. + --minutes int (required) — Duration in minutes. Must be greater than 0 and at most 1440 (24h). (max 1440) +`, + Example: ` flashduty incident snooze --data '{"incident_ids":["69da451ef77b1b51f40e83ee"],"minutes":60}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + if cmd.Flags().Changed("minutes") { + body["minutes"] = fMinutes + } + }) + if err != nil { + return err + } + req := new(flashduty.SnoozeIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Snooze(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/snooze") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to snooze. At most 100 per call. (required)") + cmd.Flags().Int64Var(&fMinutes, "minutes", 0, "Duration in minutes. Must be greater than 0 and at most 1440 (24h). (required) (max 1440)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsUnackCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + cmd := &cobra.Command{ + Use: "unack", + Short: "Unacknowledge incident", + Long: `Unacknowledge incident. + +Remove the acknowledge status from an incident. + +API: POST /incident/unack (incidentUnack) + +Request fields: + --incident-ids []string (required) — Incident IDs to unacknowledge. At most 100 per call. +`, + Example: ` flashduty incident unack --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.UnackIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Unack(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/unack") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to unacknowledge. At most 100 per call. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsWakeCmd() *cobra.Command { + var dataJSON string + var fIncidentIDs []string + cmd := &cobra.Command{ + Use: "wake", + Short: "Wake incident", + Long: `Wake incident. + +Cancel the snooze on an incident and resume notifications. + +API: POST /incident/wake (incidentWake) + +Request fields: + --incident-ids []string (required) — Incident IDs to wake. At most 100 per call. +`, + Example: ` flashduty incident wake --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-ids") { + body["incident_ids"] = fIncidentIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.WakeIncidentRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.Wake(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/wake") + return nil + }) + }, + } + cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to wake. At most 100 per call. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsWarRoomCreateCmd() *cobra.Command { + var dataJSON string + var fAddObservers bool + var fIncidentID string + var fIntegrationID int64 + var fMemberIDs []int + cmd := &cobra.Command{ + Use: "war-room-create", + Short: "Create war room", + Long: `Create war room. + +Create a war room channel for collaborative incident response. + +API: POST /incident/war-room/create (incidentWarRoomCreate) + +Request fields: + --add-observers bool — When true, also add historical responders of the incident as observers. + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --integration-id int (required) — IM integration ID. Must have war room enabled; Feishu, DingTalk, WeCom (self-built), Slack and Teams are supported. + --member-ids []int — Additional member IDs to add to the war room. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - chat_id (string) (required) — Chat/group ID on the IM side. + - chat_name (string) (required) — Chat/group display name. + - share_link (string) (required) — Join link for the war room, if provided by the IM. +`, + Example: ` flashduty incident war-room-create --data '{"add_observers":true,"incident_id":"69da451ef77b1b51f40e83ee","integration_id":2490562293131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("add-observers") { + body["add_observers"] = fAddObservers + } + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + if cmd.Flags().Changed("member-ids") { + body["member_ids"] = fMemberIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateWarRoomRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.WarRoomCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().BoolVar(&fAddObservers, "add-observers", false, "When true, also add historical responders of the incident as observers.") + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration ID. Must have war room enabled; Feishu, DingTalk, WeCom (self-built), Slack and Teams are supported. (required)") + cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Additional member IDs to add to the war room.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsWarRoomDeleteCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "war-room-delete", + Short: "Delete war room", + Long: `Delete war room. + +Delete an incident war room. + +API: POST /incident/war-room/delete (incidentWarRoomDelete) + +Request fields: + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --integration-id int (required) — IM integration ID. +`, + Example: ` flashduty incident war-room-delete --data '{"incident_id":"69da451ef77b1b51f40e83ee","integration_id":2490562293131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.DeleteWarRoomRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Incidents.WarRoomDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /incident/war-room/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsWarRoomDetailCmd() *cobra.Command { + var dataJSON string + var fChatID string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "war-room-detail", + Short: "Get war room detail", + Long: `Get war room detail. + +Retrieve the war room configuration and members for an incident. + +API: POST /incident/war-room/detail (incidentWarRoomDetail) + +Request fields: + --chat-id string (required) — Chat/group ID on the IM side. + --integration-id int (required) — IM integration ID that hosts the war room. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - chat_id (string) (required) — Chat/group ID on the IM side. + - chat_name (string) (required) — Chat/group display name. + - share_link (string) (required) — Join link for the war room, if provided by the IM. +`, + Example: ` flashduty incident war-room-detail --data '{"chat_id":"oc_a0553eda9014c2de1b3a8f75b4e0c000","integration_id":2490562293131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("chat-id") { + body["chat_id"] = fChatID + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.GetWarRoomDetailRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.WarRoomDetail(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fChatID, "chat-id", "", "Chat/group ID on the IM side. (required)") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration ID that hosts the war room. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIncidentsWarRoomListCmd() *cobra.Command { + var dataJSON string + var fIncidentID string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "war-room-list", + Short: "List war rooms", + Long: `List war rooms. + +List all war rooms associated with an incident. + +API: POST /incident/war-room/list (incidentWarRoomList) + +Request fields: + --incident-id string (required) — Incident ID (MongoDB ObjectID). + --integration-id int — Optional filter: only return war rooms for this IM integration. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — War room records. + - account_id (integer) (required) — Account ID. + - chat_id (string) (required) — Chat/group ID on the IM side. + - created_at (integer) (required) — Creation timestamp (seconds). + - created_by (integer) (required) — Member ID that created the war room. + - incident_id (string) (required) — Associated incident ID (MongoDB ObjectID). + - integration_id (integer) (required) — IM integration ID. + - plugin_type (string) (required) — IM plugin type (e.g. 'feishu', 'dingtalk', 'wecom', 'slack'). + - status (string) (required) — War room status. +`, + Example: ` flashduty incident war-room-list --data '{"incident_id":"69da451ef77b1b51f40e83ee"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.ListWarRoomsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Incidents.WarRoomList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Optional filter: only return war rooms for this IM integration.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedIncidents(root *cobra.Command) { + gIncident := genGroup(root, "incident", "On-call/Incidents API") + genAddLeaf(gIncident, genIncidentsReadGetWarRoomDefaultObserversCmd()) + genAddLeaf(gIncident, genIncidentsWriteAddWarRoomMemberCmd()) + genAddLeaf(gIncident, genIncidentsAckCmd()) + genAddLeaf(gIncident, genIncidentsAlertListCmd()) + genAddLeaf(gIncident, genIncidentsAssignCmd()) + genAddLeaf(gIncident, genIncidentsCommentCmd()) + genAddLeaf(gIncident, genIncidentsCreateCmd()) + genAddLeaf(gIncident, genIncidentsCustomActionDoCmd()) + genAddLeaf(gIncident, genIncidentsDisableMergeCmd()) + genAddLeaf(gIncident, genIncidentsFeedCmd()) + genAddLeaf(gIncident, genIncidentsFieldResetCmd()) + genAddLeaf(gIncident, genIncidentsInfoCmd()) + genAddLeaf(gIncident, genIncidentsListCmd()) + genAddLeaf(gIncident, genIncidentsListByIDsCmd()) + genAddLeaf(gIncident, genIncidentsMergeCmd()) + genAddLeaf(gIncident, genIncidentsPastListCmd()) + genAddLeaf(gIncident, genIncidentsPostMortemDeleteCmd()) + genAddLeaf(gIncident, genIncidentsPostMortemInfoCmd()) + genAddLeaf(gIncident, genIncidentsPostMortemListCmd()) + genAddLeaf(gIncident, genIncidentsRemoveCmd()) + genAddLeaf(gIncident, genIncidentsReopenCmd()) + genAddLeaf(gIncident, genIncidentsResetCmd()) + genAddLeaf(gIncident, genIncidentsResolveCmd()) + genAddLeaf(gIncident, genIncidentsResponderAddCmd()) + genAddLeaf(gIncident, genIncidentsSnoozeCmd()) + genAddLeaf(gIncident, genIncidentsUnackCmd()) + genAddLeaf(gIncident, genIncidentsWakeCmd()) + genAddLeaf(gIncident, genIncidentsWarRoomCreateCmd()) + genAddLeaf(gIncident, genIncidentsWarRoomDeleteCmd()) + genAddLeaf(gIncident, genIncidentsWarRoomDetailCmd()) + genAddLeaf(gIncident, genIncidentsWarRoomListCmd()) +} diff --git a/internal/cli/zz_generated_integrations.go b/internal/cli/zz_generated_integrations.go new file mode 100644 index 0000000..6b4eabf --- /dev/null +++ b/internal/cli/zz_generated_integrations.go @@ -0,0 +1,204 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genIntegrationsDetailCmd() *cobra.Command { + var dataJSON string + var fEventID string + var fIntegrationID int64 + cmd := &cobra.Command{ + Use: "history-detail", + Short: "Get webhook delivery detail", + Long: `Get webhook delivery detail. + +Retrieve the detailed payload and response for a specific webhook delivery attempt. + +API: POST /webhook/history/detail (webhookHistoryDetail) + +Request fields: + --event-id string (required) — Event ID returned by 'ListWebhookHistory'. + --integration-id int (required) — Integration ID the event belongs to. (min 1) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - attempt (integer) (required) — Attempt sequence number. + - channel_id (integer) — Channel ID when applicable. + - channel_name (string) — Name of the associated channel, resolved at query time. + - duration (integer) (required) — Total elapsed time of the attempt in milliseconds. + - endpoint (string) (required) — Destination URL. + - error_message (string) — Error message when delivery failed. + - event_id (string) (required) — Event ID. + - event_time (string) (required) — Event time as a formatted timestamp string. + - event_type (string) (required) — Event type. + - integration_id (integer) (required) — Integration ID. + - ref_id (string) — Source object ID. + - ref_title (string) — Title of the source incident or alert, resolved at query time. + - request_body (string) — Outbound request body payload. + - request_headers (string) — Serialized outbound request headers. + - response_body (string) — Response body. + - response_headers (string) — Serialized response headers. + - status (string) (required) — Delivery outcome. [success, failed] + - status_code (integer) (required) — HTTP status code. + - webhook_type (string) (required) — Source object kind. 'incident' or 'alert'. +`, + Example: ` flashduty webhook history-detail --data '{"event_id":"20260412Xatt9hrXsgmFkBR78WF655","integration_id":6113996590131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("event-id") { + body["event_id"] = fEventID + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + }) + if err != nil { + return err + } + req := new(flashduty.GetWebhookHistoryDetailRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Integrations.Detail(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fEventID, "event-id", "", "Event ID returned by 'ListWebhookHistory'. (required)") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID the event belongs to. (required) (min 1)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIntegrationsListCmd() *cobra.Command { + var dataJSON string + var fAsc bool + var fEndTime int64 + var fEventTypes []string + var fIntegrationID int64 + var fLimit int64 + var fOrderby string + var fRefID string + var fSearchAfterCtx string + var fStartTime int64 + var fStatus string + cmd := &cobra.Command{ + Use: "history-list", + Short: "List webhook delivery history", + Long: `List webhook delivery history. + +List the delivery history for outbound webhook notifications. + +API: POST /webhook/history/list (webhookHistoryList) + +Request fields: + --asc bool — Ascending order by 'event_time' when true; otherwise descending. + --end-time int (required) — Window end time in Unix milliseconds. Must be greater than 'start_time'. (1000000000000-9999999999999) + --event-types []string — Filter by event type values. + --integration-id int — Filter by integration ID. (min 0) + --limit int (required) — Page size. (1-100) + --orderby string — Sort field. Currently only 'event_time' is supported. [event_time] + --ref-id string — Reference ID filter (incident or alert ID). (≤128 chars) + --search-after-ctx string — Opaque cursor returned by a previous call for fetching the next page. + --start-time int (required) — Window start time in Unix milliseconds. (1000000000000-9999999999999) + --status string — Filter by delivery status. [success, failed] + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - attempt (integer) (required) — Attempt sequence number. + - channel_id (integer) — Channel ID associated with the event, when applicable. + - duration (integer) (required) — Total elapsed time of the attempt in milliseconds. + - endpoint (string) (required) — Destination URL. + - error_message (string) — Error message when delivery failed. + - event_id (string) (required) — Unique event identifier for the delivery attempt. + - event_time (string) (required) — Event time as a formatted timestamp string. + - event_type (string) (required) — Event type (e.g. 'created', 'acknowledged', 'closed'). + - integration_id (integer) (required) — Integration ID that triggered the webhook. + - ref_id (string) — Source object ID (incident ID or alert ID). + - request_body (string) — Outbound request body payload. + - request_headers (string) — Serialized outbound request headers. + - response_body (string) — Response body returned by the destination. + - response_headers (string) — Serialized response headers from the destination. + - status (string) (required) — Delivery outcome. [success, failed] + - status_code (integer) (required) — HTTP status code returned by the destination. + - webhook_type (string) (required) — Source object kind. 'incident' or 'alert'. + - search_after_ctx (string) (required) — Cursor to pass as 'search_after_ctx' to fetch the next page. Empty when no further pages are available. + - total (integer) (required) — Total number of matching records. +`, + Example: ` flashduty webhook history-list --data '{"end_time":1775203200000,"integration_id":6113996590131,"limit":20,"start_time":1775116800000,"status":"success"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("event-types") { + body["event_types"] = fEventTypes + } + if cmd.Flags().Changed("integration-id") { + body["integration_id"] = fIntegrationID + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("ref-id") { + body["ref_id"] = fRefID + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("status") { + body["status"] = fStatus + } + }) + if err != nil { + return err + } + req := new(flashduty.ListWebhookHistoryRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Integrations.List(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending order by 'event_time' when true; otherwise descending.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "Window end time in Unix milliseconds. Must be greater than 'start_time'. (required) (1000000000000-9999999999999)") + cmd.Flags().StringSliceVar(&fEventTypes, "event-types", nil, "Filter by event type values.") + cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Filter by integration ID. (min 0)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. (required) (1-100)") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. Currently only 'event_time' is supported. [event_time]") + cmd.Flags().StringVar(&fRefID, "ref-id", "", "Reference ID filter (incident or alert ID). (≤128 chars)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque cursor returned by a previous call for fetching the next page.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Window start time in Unix milliseconds. (required) (1000000000000-9999999999999)") + cmd.Flags().StringVar(&fStatus, "status", "", "Filter by delivery status. [success, failed]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedIntegrations(root *cobra.Command) { + gWebhook := genGroup(root, "webhook", "On-call/Integrations API") + genAddLeaf(gWebhook, genIntegrationsDetailCmd()) + genAddLeaf(gWebhook, genIntegrationsListCmd()) +} diff --git a/internal/cli/zz_generated_issues.go b/internal/cli/zz_generated_issues.go new file mode 100644 index 0000000..75ccc1b --- /dev/null +++ b/internal/cli/zz_generated_issues.go @@ -0,0 +1,320 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genIssuesReadInfoCmd() *cobra.Command { + var dataJSON string + var fIssueID string + cmd := &cobra.Command{ + Use: "issue-info", + Short: "Get issue detail", + Long: `Get issue detail. + +Retrieve full details of a single issue by 'issue_id'. + +API: POST /rum/issue/info (rum-issue-read-info) + +Request fields: + --issue-id string (required) — Issue ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - age (integer) + - application_id (string) + - application_name (string) + - created_at (integer) + - error (object) + - message (string) + - type (string) + - error_count (integer) — Total error occurrences. + - first_seen (object) + - timestamp (integer) + - version (string) + - is_crash (boolean) — Whether the error caused an app crash. + - issue_id (string) — Unique issue ID. + - last_seen (object) + - timestamp (integer) + - version (string) + - regression (object) — Regression metadata. Present only when a previously resolved issue re-occurred. + - regressed_at (integer) — Timestamp when the regression was detected. + - regressed_at_version (string) — Application version in which the regression was observed. + - resolved_at (integer) — Timestamp of the previous resolution before the regression. + - resolved_at (integer) + - resolved_by (integer) + - service (string) + - session_count (integer) — Affected user sessions. + - severity (string) — Issue severity level. + - status (string) [for_review, reviewed, ignored, resolved] + - suspected_cause (object) + - person_id (integer) + - reason (string) + - source (string) [auto, user] + - value (string) [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown] + - team_id (integer) + - updated_at (integer) + - versions (array) +`, + Example: ` flashduty rum issue-info --data '{"issue_id":"NHEacQHi2DhXqobr9qPQz9"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("issue-id") { + body["issue_id"] = fIssueID + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMIssueIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Issues.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fIssueID, "issue-id", "", "Issue ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIssuesReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fApplicationIDs []string + var fAsc bool + var fByIntersection bool + var fDql string + var fEndTime int64 + var fErrorRequired bool + var fOrderby string + var fSql string + var fStartTime int64 + var fStatuses []string + var fSuspectedCauses []string + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "issue-list", + Short: "List issues", + Long: `List issues. + +Return a paginated list of RUM error tracking issues matching the given filters. + +API: POST /rum/issue/list (rum-issue-read-list) + +Request fields: + --page int — Page number. Default: 1. + --limit int — Page size. Range: 1–100. Default: 20. + --search-after-ctx string + --application-ids []string — Filter by application IDs. + --asc bool + --by-intersection bool + --dql string — DQL query for advanced filtering. Cannot be used with 'sql'. + --end-time int (required) — End of time range, millisecond timestamp. Maximum range: 183 days. + --error-required bool — If 'true', only return issues with at least one associated error event. + --orderby string [created_at, updated_at, session_count, error_count] + --sql string — SQL-style query for advanced filtering. Cannot be used with 'dql'. + --start-time int (required) — Start of time range, millisecond timestamp. + --statuses []string — Filter by statuses. [for_review, reviewed, ignored, resolved] + --suspected-causes []string — Filter by suspected causes. + --team-ids []int — Filter by team IDs. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) + - items (array) + - age (integer) + - application_id (string) + - application_name (string) + - created_at (integer) + - error (object) + - message (string) + - type (string) + - error_count (integer) — Total error occurrences. + - first_seen (object) + - timestamp (integer) + - version (string) + - is_crash (boolean) — Whether the error caused an app crash. + - issue_id (string) — Unique issue ID. + - last_seen (object) + - timestamp (integer) + - version (string) + - regression (object) — Regression metadata. Present only when a previously resolved issue re-occurred. + - regressed_at (integer) — Timestamp when the regression was detected. + - regressed_at_version (string) — Application version in which the regression was observed. + - resolved_at (integer) — Timestamp of the previous resolution before the regression. + - resolved_at (integer) + - resolved_by (integer) + - service (string) + - session_count (integer) — Affected user sessions. + - severity (string) — Issue severity level. + - status (string) [for_review, reviewed, ignored, resolved] + - suspected_cause (object) + - person_id (integer) + - reason (string) + - source (string) [auto, user] + - value (string) [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown] + - team_id (integer) + - updated_at (integer) + - versions (array) + - total (integer) +`, + Example: ` flashduty rum issue-list --data '{"application_ids":["eWbr4xk3ZRnLabRa6unqwD"],"end_time":1775961914595,"limit":20,"orderby":"updated_at","p":1,"start_time":1772611200000,"statuses":["for_review"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("application-ids") { + body["application_ids"] = fApplicationIDs + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("by-intersection") { + body["by_intersection"] = fByIntersection + } + if cmd.Flags().Changed("dql") { + body["dql"] = fDql + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("error-required") { + body["error_required"] = fErrorRequired + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("sql") { + body["sql"] = fSql + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("statuses") { + body["statuses"] = fStatuses + } + if cmd.Flags().Changed("suspected-causes") { + body["suspected_causes"] = fSuspectedCauses + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMIssueListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Issues.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number. Default: 1.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Range: 1–100. Default: 20.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().StringSliceVar(&fApplicationIDs, "application-ids", nil, "Filter by application IDs.") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Request field asc") + cmd.Flags().BoolVar(&fByIntersection, "by-intersection", false, "Request field by_intersection") + cmd.Flags().StringVar(&fDql, "dql", "", "DQL query for advanced filtering. Cannot be used with 'sql'.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End of time range, millisecond timestamp. Maximum range: 183 days. (required)") + cmd.Flags().BoolVar(&fErrorRequired, "error-required", false, "If 'true', only return issues with at least one associated error event.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Request field orderby [created_at, updated_at, session_count, error_count]") + cmd.Flags().StringVar(&fSql, "sql", "", "SQL-style query for advanced filtering. Cannot be used with 'dql'.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start of time range, millisecond timestamp. (required)") + cmd.Flags().StringSliceVar(&fStatuses, "statuses", nil, "Filter by statuses. [for_review, reviewed, ignored, resolved]") + cmd.Flags().StringSliceVar(&fSuspectedCauses, "suspected-causes", nil, "Filter by suspected causes.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genIssuesWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fIssueID string + var fStatus string + var fSuspectedCause string + cmd := &cobra.Command{ + Use: "issue-update", + Short: "Update issue", + Long: `Update issue. + +Update the status or suspected cause of an issue. + +API: POST /rum/issue/update (rum-issue-write-update) + +Request fields: + --issue-id string (required) — Issue ID to update. + --status string — New status. [for_review, reviewed, ignored, resolved] + --suspected-cause string — Suspected cause. [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown] +`, + Example: ` flashduty rum issue-update --data '{"issue_id":"NHEacQHi2DhXqobr9qPQz9","status":"resolved"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("issue-id") { + body["issue_id"] = fIssueID + } + if cmd.Flags().Changed("status") { + body["status"] = fStatus + } + if cmd.Flags().Changed("suspected-cause") { + body["suspected_cause"] = fSuspectedCause + } + }) + if err != nil { + return err + } + req := new(flashduty.RUMIssueUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Issues.WriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /rum/issue/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fIssueID, "issue-id", "", "Issue ID to update. (required)") + cmd.Flags().StringVar(&fStatus, "status", "", "New status. [for_review, reviewed, ignored, resolved]") + cmd.Flags().StringVar(&fSuspectedCause, "suspected-cause", "", "Suspected cause. [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedIssues(root *cobra.Command) { + gRUM := genGroup(root, "rum", "RUM API") + genAddLeaf(gRUM, genIssuesReadInfoCmd()) + genAddLeaf(gRUM, genIssuesReadListCmd()) + genAddLeaf(gRUM, genIssuesWriteUpdateCmd()) +} diff --git a/internal/cli/zz_generated_manifest.go b/internal/cli/zz_generated_manifest.go new file mode 100644 index 0000000..546a93d --- /dev/null +++ b/internal/cli/zz_generated_manifest.go @@ -0,0 +1,262 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +// generatedOpIDs are the OpenAPI operationIds the generator emitted a +// path-derived command for (every operation in the spec). +var generatedOpIDs = []string{ + "account-read-info", + "alert-event-read-list", + "alert-read-event-list", + "alert-read-feed", + "alert-read-info", + "alert-read-list", + "alert-read-list-by-ids", + "alert-read-pipeline-info", + "alert-read-pipeline-list", + "alert-write-merge", + "alert-write-pipeline-upsert", + "audit-read-operation-list", + "audit-read-search", + "calEventDelete", + "calEventList", + "calEventUpsert", + "calendarCreate", + "calendarDelete", + "calendarInfo", + "calendarList", + "calendarUpdate", + "change-read-list", + "channelCreate", + "channelDelete", + "channelDisable", + "channelEnable", + "channelEscalateRuleCreate", + "channelEscalateRuleDelete", + "channelEscalateRuleDisable", + "channelEscalateRuleEnable", + "channelEscalateRuleInfo", + "channelEscalateRuleList", + "channelEscalateRuleUpdate", + "channelInfo", + "channelInfos", + "channelInhibitRuleCreate", + "channelInhibitRuleDelete", + "channelInhibitRuleDisable", + "channelInhibitRuleEnable", + "channelInhibitRuleList", + "channelInhibitRuleUpdate", + "channelList", + "channelNotifyRuleCreate", + "channelNotifyRuleDelete", + "channelNotifyRuleDisable", + "channelNotifyRuleEnable", + "channelNotifyRuleList", + "channelNotifyRuleUpdate", + "channelSilenceRuleCreate", + "channelSilenceRuleDelete", + "channelSilenceRuleDisable", + "channelSilenceRuleEnable", + "channelSilenceRuleList", + "channelSilenceRuleUpdate", + "channelUnsubscribeRuleCreate", + "channelUnsubscribeRuleDelete", + "channelUnsubscribeRuleDisable", + "channelUnsubscribeRuleEnable", + "channelUnsubscribeRuleList", + "channelUnsubscribeRuleUpdate", + "channelUpdate", + "enrichment-read-info", + "enrichment-read-list", + "enrichment-write-upsert", + "field-read-info", + "field-read-list", + "field-write-create", + "field-write-delete", + "field-write-update", + "im-war-room-enabled-list", + "incident-read-get-war-room-default-observers", + "incident-write-add-war-room-member", + "incidentAck", + "incidentAlertList", + "incidentAssign", + "incidentComment", + "incidentCreate", + "incidentCustomActionDo", + "incidentDisableMerge", + "incidentFeed", + "incidentFieldReset", + "incidentInfo", + "incidentList", + "incidentListByIds", + "incidentMerge", + "incidentPastList", + "incidentPostMortemDelete", + "incidentPostMortemInfo", + "incidentPostMortemList", + "incidentRemove", + "incidentReopen", + "incidentReset", + "incidentResolve", + "incidentResponderAdd", + "incidentSnooze", + "incidentUnack", + "incidentWake", + "incidentWarRoomCreate", + "incidentWarRoomDelete", + "incidentWarRoomDetail", + "incidentWarRoomList", + "insightByAccount", + "insightByChannel", + "insightByResponder", + "insightByTeam", + "insightChannelExport", + "insightIncidentExport", + "insightIncidentList", + "insightResponderExport", + "insightTeamExport", + "insightTopkAlertsByLabel", + "mapping-api-read-info", + "mapping-api-read-list", + "mapping-api-write-create", + "mapping-api-write-delete", + "mapping-api-write-update", + "mapping-data-read-download", + "mapping-data-read-list", + "mapping-data-write-delete", + "mapping-data-write-truncate", + "mapping-data-write-upload", + "mapping-data-write-upsert", + "mapping-schema-read-info", + "mapping-schema-read-list", + "mapping-schema-write-create", + "mapping-schema-write-delete", + "mapping-schema-write-update", + "mcp-read-server-get", + "mcp-read-server-list", + "mcp-write-server-create", + "mcp-write-server-delete", + "mcp-write-server-disable", + "mcp-write-server-enable", + "mcp-write-server-update", + "memberDelete", + "memberGrantRole", + "memberInfo", + "memberInvite", + "memberList", + "memberResetInfo", + "memberRevokeRole", + "memberUpdateRole", + "monit-datasource-read-info", + "monit-datasource-read-list", + "monit-datasource-read-sls-logstores", + "monit-datasource-read-sls-projects", + "monit-datasource-write-create", + "monit-datasource-write-delete", + "monit-datasource-write-update", + "monit-read-query-diagnose", + "monit-read-query-rows", + "monit-read-targets-list", + "monit-read-tools-catalog", + "monit-read-tools-invoke", + "monit-rule-read-audit-detail", + "monit-rule-read-audits", + "monit-rule-read-counter-channel", + "monit-rule-read-counter-node", + "monit-rule-read-counter-status", + "monit-rule-read-counter-total", + "monit-rule-read-dstypes", + "monit-rule-read-export", + "monit-rule-read-info", + "monit-rule-read-list", + "monit-rule-write-create", + "monit-rule-write-delete", + "monit-rule-write-delete-batch", + "monit-rule-write-fields-update", + "monit-rule-write-import", + "monit-rule-write-move", + "monit-rule-write-status", + "monit-rule-write-update", + "monit-store-ruleset-create", + "monit-store-ruleset-delete", + "monit-store-ruleset-info", + "monit-store-ruleset-list", + "monit-store-ruleset-update", + "personInfos", + "remote-agent-read-get", + "remote-agent-read-list", + "remote-agent-write-create", + "remote-agent-write-delete", + "remote-agent-write-disable", + "remote-agent-write-enable", + "remote-agent-write-update", + "role-read-info", + "role-read-list", + "role-read-list-permission", + "role-read-list-permission-factor", + "role-write-delete", + "role-write-disable", + "role-write-enable", + "role-write-grant-role", + "role-write-revoke-role", + "role-write-upsert", + "routeInfo", + "routeList", + "routeUpsert", + "rum-application-read-info", + "rum-application-read-infos", + "rum-application-read-list", + "rum-application-write-create", + "rum-application-write-delete", + "rum-application-write-update", + "rum-issue-read-info", + "rum-issue-read-list", + "rum-issue-write-update", + "scheduleCreate", + "scheduleDelete", + "scheduleInfo", + "scheduleInfos", + "scheduleList", + "schedulePreview", + "scheduleSelf", + "scheduleUpdate", + "skill-read-download", + "skill-read-enable", + "skill-read-get", + "skill-read-list", + "skill-write-delete", + "skill-write-disable", + "skill-write-update", + "skill-write-upload", + "sourcemap-read-list", + "status-page-read-page-list", + "statusPageChangeActiveList", + "statusPageChangeCreate", + "statusPageChangeDelete", + "statusPageChangeInfo", + "statusPageChangeList", + "statusPageChangeTimelineCreate", + "statusPageChangeTimelineDelete", + "statusPageChangeTimelineUpdate", + "statusPageChangeUpdate", + "statusPageMigrateEmailSubscribers", + "statusPageMigrateStructure", + "statusPageMigrationCancel", + "statusPageMigrationStatus", + "statusPageSubscriberExport", + "statusPageSubscriberImport", + "statusPageSubscriberList", + "team-read-info", + "team-read-infos", + "team-read-list", + "team-write-delete", + "team-write-upsert", + "template-read-info", + "template-read-list", + "template-read-preview", + "template-write-create", + "template-write-delete", + "template-write-update", + "webhookHistoryDetail", + "webhookHistoryList", +} diff --git a/internal/cli/zz_generated_mcp_servers.go b/internal/cli/zz_generated_mcp_servers.go new file mode 100644 index 0000000..a04f6e1 --- /dev/null +++ b/internal/cli/zz_generated_mcp_servers.go @@ -0,0 +1,619 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genMcpServersReadServerGetCmd() *cobra.Command { + var dataJSON string + var fServerID string + cmd := &cobra.Command{ + Use: "mcp-server-get", + Short: "Get MCP server detail", + Long: `Get MCP server detail. + +Return the full configuration of a single MCP server, with a live tool catalogue. + +API: POST /safari/mcp/server/get (mcp-read-server-get) + +Request fields: + --server-id string (required) — Identifier of the server to fetch. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account. + - ai_description (string) — LLM-generated description, preferred over description when present. + - args (array) — Command-line arguments for the stdio executable. + - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth. + - call_timeout (integer) (required) — Per-call timeout in seconds. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - command (string) — Executable launched for stdio transport. + - connect_timeout (integer) (required) — Connection timeout in seconds. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What this MCP server provides. + - env (object) — Environment variables passed to the stdio process. + - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked. + - list_error (string) — Tool-probe failure message; present when the live probe failed. + - oauth_metadata (string) — OAuth metadata JSON. + - proxy_url (string) — Outbound proxy URL used to reach the server. + - secret_schema (string) — JSON schema of the per-user secret. + - server_id (string) (required) — Unique identifier of the MCP server. + - server_name (string) (required) — Display name of the MCP server. + - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled] + - team_id (integer) (required) — Owning team; 0 means account scope. + - tool_count (integer) — Number of tools discovered on the server. + - tools (array) — Live tool catalogue; populated only by get and test. + - description (string) (required) — What the tool does. + - input_schema (any) — JSON schema of the tool's input arguments. + - name (string) (required) — Tool name as advertised by the server. + - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - url (string) — Endpoint URL for sse or streamable-http transport. +`, + Example: ` flashduty safari mcp-server-get --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("server-id") { + body["server_id"] = fServerID + } + }) + if err != nil { + return err + } + req := new(flashduty.McpServerGetRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.McpServers.ReadServerGet(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the server to fetch. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMcpServersReadServerListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fIncludeAccount bool + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "mcp-server-list", + Short: "List MCP servers", + Long: `List MCP servers. + +List configured MCP servers visible to the caller across account and team scopes, with pagination. + +API: POST /safari/mcp/server/list (mcp-read-server-list) + +Request fields: + --page int — Page number, starting at 1. + --limit int — Page size; defaults to 20. + --search-after-ctx string + --include-account bool — Include account-scoped rows alongside team-scoped ones; defaults to true. + --team-ids []int — Restrict results to resources owned by these teams; intersected with the caller's visible set. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - servers (array) (required) — MCP servers on the current page. + - account_id (integer) (required) — Owning account. + - ai_description (string) — LLM-generated description, preferred over description when present. + - args (array) — Command-line arguments for the stdio executable. + - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth. + - call_timeout (integer) (required) — Per-call timeout in seconds. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - command (string) — Executable launched for stdio transport. + - connect_timeout (integer) (required) — Connection timeout in seconds. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What this MCP server provides. + - env (object) — Environment variables passed to the stdio process. + - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked. + - list_error (string) — Tool-probe failure message; present when the live probe failed. + - oauth_metadata (string) — OAuth metadata JSON. + - proxy_url (string) — Outbound proxy URL used to reach the server. + - secret_schema (string) — JSON schema of the per-user secret. + - server_id (string) (required) — Unique identifier of the MCP server. + - server_name (string) (required) — Display name of the MCP server. + - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled] + - team_id (integer) (required) — Owning team; 0 means account scope. + - tool_count (integer) — Number of tools discovered on the server. + - tools (array) — Live tool catalogue; populated only by get and test. + - description (string) (required) — What the tool does. + - input_schema (any) — JSON schema of the tool's input arguments. + - name (string) (required) — Tool name as advertised by the server. + - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - url (string) — Endpoint URL for sse or streamable-http transport. + - total (integer) (required) — Total number of servers matching the filters. +`, + Example: ` flashduty safari mcp-server-list --data '{"include_account":true,"limit":20,"p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("include-account") { + body["include_account"] = fIncludeAccount + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.McpServerListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.McpServers.ReadServerList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size; defaults to 20.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped rows alongside team-scoped ones; defaults to true.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Restrict results to resources owned by these teams; intersected with the caller's visible set.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMcpServersWriteServerCreateCmd() *cobra.Command { + var dataJSON string + var fArgs []string + var fAuthMode string + var fCallTimeout int64 + var fCommand string + var fConnectTimeout int64 + var fDescription string + var fOauthMetadata string + var fSecretSchema string + var fServerName string + var fStatus string + var fTeamID int64 + var fTransport string + var fURL string + cmd := &cobra.Command{ + Use: "mcp-server-create", + Short: "Create MCP server", + Long: `Create MCP server. + +Register a new MCP server the agent can call tools through. + +API: POST /safari/mcp/server/create (mcp-write-server-create) + +Request fields: + --args []string — Command-line arguments for the stdio executable. + --auth-mode string — Credential model; defaults to shared. + --call-timeout int — Per-call timeout in seconds. + --command string — Executable to launch for stdio transport. + --connect-timeout int — Connection timeout in seconds. + --description string (required) — What this MCP server provides. (1-1024 chars) + --oauth-metadata string — OAuth metadata JSON; reserved for OAuth-based auth. + --secret-schema string — JSON schema of the per-user secret; required when auth_mode is per_user_secret. + --server-name string (required) — Display name of the server. (1-255 chars) + --status string — Initial lifecycle state of the server. [enabled, disabled] + --team-id int — Owning team for the new server; 0 for account scope. + --transport string (required) — Transport used to reach the server. [stdio, sse, streamable-http] + --url string — Endpoint URL for sse or streamable-http transport. + env (object, via --data) — Environment variables for the stdio process. + headers (object, via --data) — HTTP headers sent to the remote endpoint. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account. + - ai_description (string) — LLM-generated description, preferred over description when present. + - args (array) — Command-line arguments for the stdio executable. + - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth. + - call_timeout (integer) (required) — Per-call timeout in seconds. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - command (string) — Executable launched for stdio transport. + - connect_timeout (integer) (required) — Connection timeout in seconds. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What this MCP server provides. + - env (object) — Environment variables passed to the stdio process. + - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked. + - list_error (string) — Tool-probe failure message; present when the live probe failed. + - oauth_metadata (string) — OAuth metadata JSON. + - proxy_url (string) — Outbound proxy URL used to reach the server. + - secret_schema (string) — JSON schema of the per-user secret. + - server_id (string) (required) — Unique identifier of the MCP server. + - server_name (string) (required) — Display name of the MCP server. + - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled] + - team_id (integer) (required) — Owning team; 0 means account scope. + - tool_count (integer) — Number of tools discovered on the server. + - tools (array) — Live tool catalogue; populated only by get and test. + - description (string) (required) — What the tool does. + - input_schema (any) — JSON schema of the tool's input arguments. + - name (string) (required) — Tool name as advertised by the server. + - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - url (string) — Endpoint URL for sse or streamable-http transport. +`, + Example: ` flashduty safari mcp-server-create --data '{"args":["-y","@modelcontextprotocol/server-github"],"command":"npx","description":"Read issues and pull requests from GitHub.","env":{"GITHUB_TOKEN":"ghp_xxx"},"server_name":"GitHub Tools","status":"enabled","team_id":0,"transport":"stdio"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("args") { + body["args"] = fArgs + } + if cmd.Flags().Changed("auth-mode") { + body["auth_mode"] = fAuthMode + } + if cmd.Flags().Changed("call-timeout") { + body["call_timeout"] = fCallTimeout + } + if cmd.Flags().Changed("command") { + body["command"] = fCommand + } + if cmd.Flags().Changed("connect-timeout") { + body["connect_timeout"] = fConnectTimeout + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("oauth-metadata") { + body["oauth_metadata"] = fOauthMetadata + } + if cmd.Flags().Changed("secret-schema") { + body["secret_schema"] = fSecretSchema + } + if cmd.Flags().Changed("server-name") { + body["server_name"] = fServerName + } + if cmd.Flags().Changed("status") { + body["status"] = fStatus + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("transport") { + body["transport"] = fTransport + } + if cmd.Flags().Changed("url") { + body["url"] = fURL + } + }) + if err != nil { + return err + } + req := new(flashduty.McpServerCreateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.McpServers.WriteServerCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringSliceVar(&fArgs, "args", nil, "Command-line arguments for the stdio executable.") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "Credential model; defaults to shared.") + cmd.Flags().Int64Var(&fCallTimeout, "call-timeout", 0, "Per-call timeout in seconds.") + cmd.Flags().StringVar(&fCommand, "command", "", "Executable to launch for stdio transport.") + cmd.Flags().Int64Var(&fConnectTimeout, "connect-timeout", 0, "Connection timeout in seconds.") + cmd.Flags().StringVar(&fDescription, "description", "", "What this MCP server provides. (required) (1-1024 chars)") + cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "OAuth metadata JSON; reserved for OAuth-based auth.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON schema of the per-user secret; required when auth_mode is per_user_secret.") + cmd.Flags().StringVar(&fServerName, "server-name", "", "Display name of the server. (required) (1-255 chars)") + cmd.Flags().StringVar(&fStatus, "status", "", "Initial lifecycle state of the server. [enabled, disabled]") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team for the new server; 0 for account scope.") + cmd.Flags().StringVar(&fTransport, "transport", "", "Transport used to reach the server. (required) [stdio, sse, streamable-http]") + cmd.Flags().StringVar(&fURL, "url", "", "Endpoint URL for sse or streamable-http transport.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMcpServersWriteServerDeleteCmd() *cobra.Command { + var dataJSON string + var fServerID string + cmd := &cobra.Command{ + Use: "mcp-server-delete", + Short: "Delete MCP server", + Long: `Delete MCP server. + +Permanently remove an MCP server so agents can no longer use its tools. + +API: POST /safari/mcp/server/delete (mcp-write-server-delete) + +Request fields: + --server-id string (required) — Identifier of the server to delete. +`, + Example: ` flashduty safari mcp-server-delete --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("server-id") { + body["server_id"] = fServerID + } + }) + if err != nil { + return err + } + req := new(flashduty.McpServerDeleteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.McpServers.WriteServerDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the server to delete. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMcpServersWriteServerDisableCmd() *cobra.Command { + var dataJSON string + var fServerID string + cmd := &cobra.Command{ + Use: "mcp-server-disable", + Short: "Disable MCP server", + Long: `Disable MCP server. + +Deactivate an MCP server so agents stop calling its tools. + +API: POST /safari/mcp/server/disable (mcp-write-server-disable) + +Request fields: + --server-id string (required) — Identifier of the target server. +`, + Example: ` flashduty safari mcp-server-disable --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("server-id") { + body["server_id"] = fServerID + } + }) + if err != nil { + return err + } + req := new(flashduty.McpServerStatusRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.McpServers.WriteServerDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the target server. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMcpServersWriteServerEnableCmd() *cobra.Command { + var dataJSON string + var fServerID string + cmd := &cobra.Command{ + Use: "mcp-server-enable", + Short: "Enable MCP server", + Long: `Enable MCP server. + +Activate a disabled MCP server so agents can call its tools. + +API: POST /safari/mcp/server/enable (mcp-write-server-enable) + +Request fields: + --server-id string (required) — Identifier of the target server. +`, + Example: ` flashduty safari mcp-server-enable --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("server-id") { + body["server_id"] = fServerID + } + }) + if err != nil { + return err + } + req := new(flashduty.McpServerStatusRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.McpServers.WriteServerEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the target server. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMcpServersWriteServerUpdateCmd() *cobra.Command { + var dataJSON string + var fArgs []string + var fAuthMode string + var fCallTimeout int64 + var fCommand string + var fConnectTimeout int64 + var fDescription string + var fOauthMetadata string + var fSecretSchema string + var fServerID string + var fServerName string + var fTeamID int64 + var fTransport string + var fURL string + cmd := &cobra.Command{ + Use: "mcp-server-update", + Short: "Update MCP server", + Long: `Update MCP server. + +Edit an MCP server's connection settings or reassign its owning team. + +API: POST /safari/mcp/server/update (mcp-write-server-update) + +Request fields: + --args []string — New stdio arguments. + --auth-mode string — New credential model. + --call-timeout int — New per-call timeout in seconds. + --command string — New stdio executable. + --connect-timeout int — New connection timeout in seconds. + --description string — New description. (1-1024 chars) + --oauth-metadata string — New OAuth metadata JSON. + --secret-schema string — New per-user secret JSON schema. + --server-id string (required) — Identifier of the server to update. + --server-name string — New display name. (1-255 chars) + --team-id int — Reassign the server to this team; omit to leave unchanged, 0 for account scope. + --transport string — New transport for the server. [stdio, sse, streamable-http] + --url string — New endpoint URL for remote transports. + env (object, via --data) — New stdio environment variables. + headers (object, via --data) — New HTTP headers for the remote endpoint. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account. + - ai_description (string) — LLM-generated description, preferred over description when present. + - args (array) — Command-line arguments for the stdio executable. + - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth. + - call_timeout (integer) (required) — Per-call timeout in seconds. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - command (string) — Executable launched for stdio transport. + - connect_timeout (integer) (required) — Connection timeout in seconds. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What this MCP server provides. + - env (object) — Environment variables passed to the stdio process. + - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked. + - list_error (string) — Tool-probe failure message; present when the live probe failed. + - oauth_metadata (string) — OAuth metadata JSON. + - proxy_url (string) — Outbound proxy URL used to reach the server. + - secret_schema (string) — JSON schema of the per-user secret. + - server_id (string) (required) — Unique identifier of the MCP server. + - server_name (string) (required) — Display name of the MCP server. + - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled] + - team_id (integer) (required) — Owning team; 0 means account scope. + - tool_count (integer) — Number of tools discovered on the server. + - tools (array) — Live tool catalogue; populated only by get and test. + - description (string) (required) — What the tool does. + - input_schema (any) — JSON schema of the tool's input arguments. + - name (string) (required) — Tool name as advertised by the server. + - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - url (string) — Endpoint URL for sse or streamable-http transport. +`, + Example: ` flashduty safari mcp-server-update --data '{"description":"Read issues, PRs, and commits from GitHub.","server_id":"mcp-2b5e8d14a7c9"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("args") { + body["args"] = fArgs + } + if cmd.Flags().Changed("auth-mode") { + body["auth_mode"] = fAuthMode + } + if cmd.Flags().Changed("call-timeout") { + body["call_timeout"] = fCallTimeout + } + if cmd.Flags().Changed("command") { + body["command"] = fCommand + } + if cmd.Flags().Changed("connect-timeout") { + body["connect_timeout"] = fConnectTimeout + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("oauth-metadata") { + body["oauth_metadata"] = fOauthMetadata + } + if cmd.Flags().Changed("secret-schema") { + body["secret_schema"] = fSecretSchema + } + if cmd.Flags().Changed("server-id") { + body["server_id"] = fServerID + } + if cmd.Flags().Changed("server-name") { + body["server_name"] = fServerName + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("transport") { + body["transport"] = fTransport + } + if cmd.Flags().Changed("url") { + body["url"] = fURL + } + }) + if err != nil { + return err + } + req := new(flashduty.McpServerUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.McpServers.WriteServerUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringSliceVar(&fArgs, "args", nil, "New stdio arguments.") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "New credential model.") + cmd.Flags().Int64Var(&fCallTimeout, "call-timeout", 0, "New per-call timeout in seconds.") + cmd.Flags().StringVar(&fCommand, "command", "", "New stdio executable.") + cmd.Flags().Int64Var(&fConnectTimeout, "connect-timeout", 0, "New connection timeout in seconds.") + cmd.Flags().StringVar(&fDescription, "description", "", "New description. (1-1024 chars)") + cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "New OAuth metadata JSON.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "New per-user secret JSON schema.") + cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the server to update. (required)") + cmd.Flags().StringVar(&fServerName, "server-name", "", "New display name. (1-255 chars)") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign the server to this team; omit to leave unchanged, 0 for account scope.") + cmd.Flags().StringVar(&fTransport, "transport", "", "New transport for the server. [stdio, sse, streamable-http]") + cmd.Flags().StringVar(&fURL, "url", "", "New endpoint URL for remote transports.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedMcpServers(root *cobra.Command) { + gSafari := genGroup(root, "safari", "AI SRE API") + genAddLeaf(gSafari, genMcpServersReadServerGetCmd()) + genAddLeaf(gSafari, genMcpServersReadServerListCmd()) + genAddLeaf(gSafari, genMcpServersWriteServerCreateCmd()) + genAddLeaf(gSafari, genMcpServersWriteServerDeleteCmd()) + genAddLeaf(gSafari, genMcpServersWriteServerDisableCmd()) + genAddLeaf(gSafari, genMcpServersWriteServerEnableCmd()) + genAddLeaf(gSafari, genMcpServersWriteServerUpdateCmd()) +} diff --git a/internal/cli/zz_generated_members.go b/internal/cli/zz_generated_members.go new file mode 100644 index 0000000..e3c3d05 --- /dev/null +++ b/internal/cli/zz_generated_members.go @@ -0,0 +1,625 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genMembersMemberDeleteCmd() *cobra.Command { + var dataJSON string + var fCountryCode string + var fEmail string + var fIsForce bool + var fMemberID int64 + var fMemberName string + var fPhone string + var fRefID string + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete member", + Long: `Delete member. + +Remove a member from the organization by ID, email, phone, or name. + +API: POST /member/delete (memberDelete) + +Request fields: + --country-code string — Phone country code, used with phone + --email string — Email address + --is-force bool — Force delete. Defaults to false, which checks for references from escalation rules, schedules, etc. Set to true to skip the reference check and delete immediately + --member-id int — Member ID + --member-name string — Member name + --phone string — Phone number + --ref-id string — External reference ID +`, + Example: ` flashduty member delete --data '{"member_id":5068740052131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("country-code") { + body["country_code"] = fCountryCode + } + if cmd.Flags().Changed("email") { + body["email"] = fEmail + } + if cmd.Flags().Changed("is-force") { + body["is_force"] = fIsForce + } + if cmd.Flags().Changed("member-id") { + body["member_id"] = fMemberID + } + if cmd.Flags().Changed("member-name") { + body["member_name"] = fMemberName + } + if cmd.Flags().Changed("phone") { + body["phone"] = fPhone + } + if cmd.Flags().Changed("ref-id") { + body["ref_id"] = fRefID + } + }) + if err != nil { + return err + } + req := new(flashduty.MemberDeleteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Members.MemberDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /member/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fCountryCode, "country-code", "", "Phone country code, used with phone") + cmd.Flags().StringVar(&fEmail, "email", "", "Email address") + cmd.Flags().BoolVar(&fIsForce, "is-force", false, "Force delete. Defaults to false, which checks for references from escalation rules, schedules, etc. Set to true to skip the reference check and delete immediately") + cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID") + cmd.Flags().StringVar(&fMemberName, "member-name", "", "Member name") + cmd.Flags().StringVar(&fPhone, "phone", "", "Phone number") + cmd.Flags().StringVar(&fRefID, "ref-id", "", "External reference ID") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersMemberGrantRoleCmd() *cobra.Command { + var dataJSON string + var fMemberID int64 + var fRoleIDs []int + cmd := &cobra.Command{ + Use: "role-grant", + Short: "Grant role to member", + Long: `Grant role to member. + +Add a role assignment to a member. + +API: POST /member/role/grant (memberGrantRole) + +Request fields: + --member-id int (required) — Member ID + --role-ids []int (required) — Role IDs to grant; appended to the member's current roles (duplicates are deduplicated). +`, + Example: ` flashduty member role-grant --data '{"member_id":5068740052131,"role_ids":[6]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("member-id") { + body["member_id"] = fMemberID + } + if cmd.Flags().Changed("role-ids") { + body["role_ids"] = fRoleIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.MemberRoleGrantRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Members.MemberGrantRole(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /member/role/grant") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID (required)") + cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "Role IDs to grant; appended to the member's current roles (duplicates are deduplicated). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersMemberInfoCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "info", + Short: "Get current member info", + Long: `Get current member info. + +Return the current session member's full profile. + +API: POST /member/info (memberInfo) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_avatar (string) — Account avatar URL + - account_email (string) — Account email + - account_id (integer) — Account ID + - account_locale (string) — Account-level locale preference (e.g. zh-CN or en-US) + - account_name (string) — Account name + - account_role_ids (array) — Assigned role IDs + - account_time_zone (string) — Account-level time zone (e.g. Asia/Shanghai) + - avatar (string) — Member avatar URL + - country_code (string) — Phone country code + - domain (string) — Account domain + - email (string) — Email address + - email_verified (boolean) — Whether email is verified + - is_external (boolean) — Whether provisioned via SSO + - locale (string) — Locale preference + - member_id (integer) — Member ID + - member_name (string) — Member display name + - phone (string) — Masked phone number + - phone_verified (boolean) — Whether phone is verified + - status (string) — Member status. 'enabled' — active member; 'pending' — invited but not yet accepted; 'deleted' — removed from the organization. [enabled, pending, deleted] + - time_zone (string) — Time zone +`, + Example: ` flashduty member info --data '{}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.Members.MemberInfo(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersMemberInviteCmd() *cobra.Command { + var dataJSON string + var fFrom string + cmd := &cobra.Command{ + Use: "invite", + Short: "Invite members", + Long: `Invite members. + +Batch invite new members to the organization by email or phone. + +API: POST /member/invite (memberInvite) + +Request fields: + --from string — Invite source context + members (array, via --data) (required) — Members to invite (max 20) + - country_code (string) — Country code + - email (string) — Email address + - locale (string) — Locale [zh-CN, en-US] + - member_name (string) — Display name (2-39 chars) + - phone (string) — Phone number + - ref_id (string) — External reference ID + - role_ids (array) — Role IDs to assign + - time_zone (string) — Time zone + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) — Newly created members + - member_id (integer) — Member ID + - member_name (string) — Member display name +`, + Example: ` flashduty member invite --data '{"members":[{"email":"charlie@example.com","locale":"en-US","member_name":"Charlie","role_ids":[6],"time_zone":"Asia/Shanghai"}]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("from") { + body["from"] = fFrom + } + }) + if err != nil { + return err + } + req := new(flashduty.MemberInviteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Members.MemberInvite(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fFrom, "from", "", "Invite source context") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersMemberListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fOrderby string + var fQuery string + var fRoleID int64 + cmd := &cobra.Command{ + Use: "list", + Short: "List members", + Long: `List members. + +Return a paginated list of organization members. + +API: POST /member/list (memberList) + +Request fields: + --page int — Page number (min 1) + --limit int — Page size (1-100) + --search-after-ctx string + --asc bool — Ascending order + --orderby string — Sort field [created_at, updated_at] + --query string — Search keyword + --role-id int — Filter by role ID + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) — Member items + - account_id (integer) (required) — Account ID + - account_role_ids (array) (required) — Role IDs + - avatar (string) (required) — Avatar URL + - country_code (string) (required) — Phone country code + - created_at (integer) (required) — Creation timestamp (Unix seconds) + - email (string) (required) — Email address + - email_verified (boolean) (required) — Email verified + - is_external (boolean) (required) — Provisioned via SSO + - locale (string) — Locale + - member_id (integer) (required) — Member ID + - member_name (string) (required) — Display name + - phone (string) (required) — Masked phone number + - phone_verified (boolean) (required) — Phone verified + - ref_id (string) (required) — External reference ID + - status (string) (required) — Member status. 'enabled' — active member; 'pending' — invited but not yet accepted; 'deleted' — removed from the organization. [enabled, pending, deleted] + - time_zone (string) — Time zone + - updated_at (integer) (required) — Update timestamp (Unix seconds) + - limit (integer) — Page size + - p (integer) — Current page + - total (integer) — Total count +`, + Example: ` flashduty member list --data '{"limit":5,"p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + }) + if err != nil { + return err + } + req := new(flashduty.MemberListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Members.MemberList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size (1-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending order") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field [created_at, updated_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Search keyword") + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Filter by role ID") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersMemberResetInfoCmd() *cobra.Command { + var dataJSON string + var fAvatar string + var fCountryCode string + var fEmail string + var fLocale string + var fMemberID int64 + var fMemberName string + var fPhone string + var fTimeZone string + cmd := &cobra.Command{ + Use: "info-reset", + Short: "Reset member info", + Long: `Reset member info. + +Batch-update multiple profile fields of the current member. + +API: POST /member/info/reset (memberResetInfo) + +Request fields: + --avatar string — Avatar URL + --country-code string — Country code + --email string — Email address + --locale string — Locale [zh-CN, en-US] + --member-id int (required) — Member ID of the member to update + --member-name string — Display name (2-39 chars) + --phone string — Phone number + --time-zone string — Time zone +`, + Example: ` flashduty member info-reset --data '{"locale":"zh-CN","member_id":2476444212131,"member_name":"Alice","time_zone":"Asia/Shanghai"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("avatar") { + body["avatar"] = fAvatar + } + if cmd.Flags().Changed("country-code") { + body["country_code"] = fCountryCode + } + if cmd.Flags().Changed("email") { + body["email"] = fEmail + } + if cmd.Flags().Changed("locale") { + body["locale"] = fLocale + } + if cmd.Flags().Changed("member-id") { + body["member_id"] = fMemberID + } + if cmd.Flags().Changed("member-name") { + body["member_name"] = fMemberName + } + if cmd.Flags().Changed("phone") { + body["phone"] = fPhone + } + if cmd.Flags().Changed("time-zone") { + body["time_zone"] = fTimeZone + } + }) + if err != nil { + return err + } + req := new(flashduty.MemberResetInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Members.MemberResetInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /member/info/reset") + return nil + }) + }, + } + cmd.Flags().StringVar(&fAvatar, "avatar", "", "Avatar URL") + cmd.Flags().StringVar(&fCountryCode, "country-code", "", "Country code") + cmd.Flags().StringVar(&fEmail, "email", "", "Email address") + cmd.Flags().StringVar(&fLocale, "locale", "", "Locale [zh-CN, en-US]") + cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID of the member to update (required)") + cmd.Flags().StringVar(&fMemberName, "member-name", "", "Display name (2-39 chars)") + cmd.Flags().StringVar(&fPhone, "phone", "", "Phone number") + cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "Time zone") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersMemberRevokeRoleCmd() *cobra.Command { + var dataJSON string + var fMemberID int64 + var fRoleIDs []int + cmd := &cobra.Command{ + Use: "role-revoke", + Short: "Revoke role from member", + Long: `Revoke role from member. + +Remove a role assignment from a member. + +API: POST /member/role/revoke (memberRevokeRole) + +Request fields: + --member-id int (required) — Member ID + --role-ids []int (required) — Role IDs to remove from the member. +`, + Example: ` flashduty member role-revoke --data '{"member_id":5068740052131,"role_ids":[6]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("member-id") { + body["member_id"] = fMemberID + } + if cmd.Flags().Changed("role-ids") { + body["role_ids"] = fRoleIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.MemberRoleRevokeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Members.MemberRevokeRole(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /member/role/revoke") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID (required)") + cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "Role IDs to remove from the member. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersMemberUpdateRoleCmd() *cobra.Command { + var dataJSON string + var fMemberID int64 + var fRoleIDs []int + cmd := &cobra.Command{ + Use: "role-update", + Short: "Update member roles", + Long: `Update member roles. + +Replace all role assignments for a member at once. + +API: POST /member/role/update (memberUpdateRole) + +Request fields: + --member-id int (required) — Member ID + --role-ids []int (required) — New set of role IDs +`, + Example: ` flashduty member role-update --data '{"member_id":5068740052131,"role_ids":[2,6]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("member-id") { + body["member_id"] = fMemberID + } + if cmd.Flags().Changed("role-ids") { + body["role_ids"] = fRoleIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.MemberRoleUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Members.MemberUpdateRole(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /member/role/update") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID (required)") + cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "New set of role IDs (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genMembersPersonInfosCmd() *cobra.Command { + var dataJSON string + var fPersonIDs []int + cmd := &cobra.Command{ + Use: "infos", + Short: "Batch get persons", + Long: `Batch get persons. + +Return profile information for a batch of person IDs (members or accounts). + +API: POST /person/infos (personInfos) + +Request fields: + --person-ids []int (required) — List of person IDs + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) — Person profiles + - account_id (integer) (required) — Account ID + - as (string) — Login role (account/member) + - avatar (string) — Avatar URL + - email (string) — Email address + - email_verified (boolean) (required) — Email verified + - locale (string) — Locale + - person_id (integer) (required) — Person ID + - person_name (string) — Display name + - phone (string) — Phone number + - phone_verified (boolean) (required) — Phone verified + - status (string) — Person status. 'enabled' — active; 'pending' — invited but not yet accepted; 'deleted' — removed. [enabled, pending, deleted] + - time_zone (string) — Time zone +`, + Example: ` flashduty person infos --data '{"person_ids":[2476444212131,3790925372131]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("person-ids") { + body["person_ids"] = fPersonIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.PersonInfosRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Members.PersonInfos(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fPersonIDs, "person-ids", nil, "List of person IDs (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedMembers(root *cobra.Command) { + gMember := genGroup(root, "member", "Platform/Members API") + genAddLeaf(gMember, genMembersMemberDeleteCmd()) + genAddLeaf(gMember, genMembersMemberGrantRoleCmd()) + genAddLeaf(gMember, genMembersMemberInfoCmd()) + genAddLeaf(gMember, genMembersMemberInviteCmd()) + genAddLeaf(gMember, genMembersMemberListCmd()) + genAddLeaf(gMember, genMembersMemberResetInfoCmd()) + genAddLeaf(gMember, genMembersMemberRevokeRoleCmd()) + genAddLeaf(gMember, genMembersMemberUpdateRoleCmd()) + gPerson := genGroup(root, "person", "Platform/Members API") + genAddLeaf(gPerson, genMembersPersonInfosCmd()) +} diff --git a/internal/cli/zz_generated_notification_templates.go b/internal/cli/zz_generated_notification_templates.go new file mode 100644 index 0000000..efed3c3 --- /dev/null +++ b/internal/cli/zz_generated_notification_templates.go @@ -0,0 +1,609 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genNotificationTemplatesReadInfoCmd() *cobra.Command { + var dataJSON string + var fTemplateID string + cmd := &cobra.Command{ + Use: "info", + Short: "Get template detail", + Long: `Get template detail. + +Return a single notification template by ID. + +API: POST /template/info (template-read-info) + +Request fields: + --template-id string (required) — Target template ID. Pass '000000000000000000000001' to address the built-in preset. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — ID of the owning account. + - created_at (integer) (required) — Unix epoch seconds the template was created. + - creator_id (integer) (required) — Member ID of the creator. + - deleted_at (integer) — Unix epoch seconds the template was soft-deleted. Absent (omitempty) when the template is live. + - description (string) (required) — Free-form description. + - dingtalk (string) (required) — DingTalk robot message template source. + - dingtalk_app (string) (required) — DingTalk app message template source. + - email (string) (required) — Email body template source (Go 'html/template' syntax). + - feishu (string) (required) — Feishu robot message template source. + - feishu_app (string) (required) — Feishu app message template source. + - slack (string) (required) — Slack robot message template source. + - slack_app (string) (required) — Slack app message template source. + - sms (string) (required) — SMS template source (Go 'text/template' syntax). + - status (string) (required) — Template lifecycle status. [enabled, disabled, deleted] + - team_id (integer) (required) — ID of the team this template is scoped to, or 0 for account-wide. + - teams_app (string) (required) — Microsoft Teams app message template source. + - telegram (string) (required) — Telegram bot message template source. + - template_id (string) (required) — Template ID. + - template_name (string) (required) — Unique template name within the account. + - updated_at (integer) (required) — Unix epoch seconds the template was last updated. + - updated_by (integer) (required) — Member ID of the last editor. + - voice (string) (required) — Voice call script template source. + - wecom (string) (required) — WeCom robot message template source. + - wecom_app (string) (required) — WeCom app message template source. + - zoom (string) (required) — Zoom bot message template source. +`, + Example: ` flashduty template info --data '{"template_id":"6605a1b2c3d4e5f6a7b8c9d0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("template-id") { + body["template_id"] = fTemplateID + } + }) + if err != nil { + return err + } + req := new(flashduty.TemplateIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.NotificationTemplates.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Target template ID. Pass '000000000000000000000001' to address the built-in preset. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genNotificationTemplatesReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fCreatorID int64 + var fIsMyTeam bool + var fOrderby string + var fQuery string + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "list", + Short: "List templates", + Long: `List templates. + +Return a paginated list of notification templates. + +API: POST /template/list (template-read-list) + +Request fields: + --page int — Page number, starting at 1. (min 1) + --limit int — Page size. Capped at 100. (1-100) + --search-after-ctx string + --asc bool — Ascending sort order. + --creator-id int — Filter by creator member ID. + --is-my-team bool — When true, only return templates scoped to teams the caller belongs to. + --orderby string — Sort field. [created_at, updated_at] + --query string — Regex or substring match on template_name. + --team-ids []int — Filter by specific team IDs. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — True if another page exists after the returned one. + - items (array) (required) + - account_id (integer) (required) — ID of the owning account. + - created_at (integer) (required) — Unix epoch seconds the template was created. + - creator_id (integer) (required) — Member ID of the creator. + - deleted_at (integer) — Unix epoch seconds the template was soft-deleted. Absent (omitempty) when the template is live. + - description (string) (required) — Free-form description. + - dingtalk (string) (required) — DingTalk robot message template source. + - dingtalk_app (string) (required) — DingTalk app message template source. + - email (string) (required) — Email body template source (Go 'html/template' syntax). + - feishu (string) (required) — Feishu robot message template source. + - feishu_app (string) (required) — Feishu app message template source. + - slack (string) (required) — Slack robot message template source. + - slack_app (string) (required) — Slack app message template source. + - sms (string) (required) — SMS template source (Go 'text/template' syntax). + - status (string) (required) — Template lifecycle status. [enabled, disabled, deleted] + - team_id (integer) (required) — ID of the team this template is scoped to, or 0 for account-wide. + - teams_app (string) (required) — Microsoft Teams app message template source. + - telegram (string) (required) — Telegram bot message template source. + - template_id (string) (required) — Template ID. + - template_name (string) (required) — Unique template name within the account. + - updated_at (integer) (required) — Unix epoch seconds the template was last updated. + - updated_by (integer) (required) — Member ID of the last editor. + - voice (string) (required) — Voice call script template source. + - wecom (string) (required) — WeCom robot message template source. + - wecom_app (string) (required) — WeCom app message template source. + - zoom (string) (required) — Zoom bot message template source. + - total (integer) (required) — Total number of templates matching the filter, across all pages. +`, + Example: ` flashduty template list --data '{"asc":false,"is_my_team":false,"limit":20,"orderby":"updated_at","p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("creator-id") { + body["creator_id"] = fCreatorID + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.TemplateListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.NotificationTemplates.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1. (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Capped at 100. (1-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending sort order.") + cmd.Flags().Int64Var(&fCreatorID, "creator-id", 0, "Filter by creator member ID.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "When true, only return templates scoped to teams the caller belongs to.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Regex or substring match on template_name.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by specific team IDs.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genNotificationTemplatesReadPreviewCmd() *cobra.Command { + var dataJSON string + var fContent string + var fIncidentID string + var fType string + cmd := &cobra.Command{ + Use: "preview", + Short: "Preview template", + Long: `Preview template. + +Render a notification template against incident data or mock data and return the output. + +API: POST /template/preview (template-read-preview) + +Request fields: + --content string (required) — Template content to render. + --incident-id string — Incident ID whose data is used to render the template; mock data is used when omitted. A MongoDB ObjectID hex string. + --type string (required) — Template channel type that selects the rendering engine. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - content (string) — Rendered template output, present when success is true. + - message (string) — Error message describing why rendering failed, present when success is false. + - success (boolean) — Whether the template rendered without errors. +`, + Example: ` flashduty template preview --data '{"content":"Incident {{.Title}} is {{.Status}}","incident_id":"664a1b2c3d4e5f6a7b8c9d0e","type":"feishu_app"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("content") { + body["content"] = fContent + } + if cmd.Flags().Changed("incident-id") { + body["incident_id"] = fIncidentID + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + }) + if err != nil { + return err + } + req := new(flashduty.PreviewTemplateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.NotificationTemplates.ReadPreview(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fContent, "content", "", "Template content to render. (required)") + cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID whose data is used to render the template; mock data is used when omitted. A MongoDB ObjectID hex string.") + cmd.Flags().StringVar(&fType, "type", "", "Template channel type that selects the rendering engine. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genNotificationTemplatesWriteCreateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fDingtalk string + var fDingtalkApp string + var fEmail string + var fFeishu string + var fFeishuApp string + var fSlack string + var fSlackApp string + var fSMS string + var fTeamID int64 + var fTeamsApp string + var fTelegram string + var fTemplateName string + var fVoice string + var fWecom string + var fWecomApp string + var fZoom string + cmd := &cobra.Command{ + Use: "create", + Short: "Create a template", + Long: `Create a template. + +Create a new notification template. + +API: POST /template/create (template-write-create) + +Request fields: + --description string — Free-form description. Up to 500 characters. (≤500 chars) + --dingtalk string — DingTalk robot message template source. + --dingtalk-app string — DingTalk app message template source. + --email string — Email body template source (Go 'html/template' syntax). + --feishu string — Feishu robot message template source. + --feishu-app string — Feishu app message template source. + --slack string — Slack robot message template source. + --slack-app string — Slack app message template source. + --sms string — SMS template source (Go 'text/template' syntax). + --team-id int — Team scope. 0 for account-wide. + --teams-app string — Microsoft Teams app message template source. + --telegram string — Telegram bot message template source. + --template-name string (required) — Template name, unique per account. 1–39 characters. (1-39 chars) + --voice string — Voice call script template source. + --wecom string — WeCom robot message template source. + --wecom-app string — WeCom app message template source. + --zoom string — Zoom bot message template source. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - template_id (string) (required) — Newly created template ID. + - template_name (string) (required) — Template name echoed from the request. +`, + Example: ` flashduty template create --data '{"description":"Default template for production incidents.","email":"Incident {{ .IncidentName }} on {{ .Severity }}","sms":"[Flashduty] {{ .IncidentName }} — {{ .Severity }}","team_id":0,"template_name":"Prod incident default"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("dingtalk") { + body["dingtalk"] = fDingtalk + } + if cmd.Flags().Changed("dingtalk-app") { + body["dingtalk_app"] = fDingtalkApp + } + if cmd.Flags().Changed("email") { + body["email"] = fEmail + } + if cmd.Flags().Changed("feishu") { + body["feishu"] = fFeishu + } + if cmd.Flags().Changed("feishu-app") { + body["feishu_app"] = fFeishuApp + } + if cmd.Flags().Changed("slack") { + body["slack"] = fSlack + } + if cmd.Flags().Changed("slack-app") { + body["slack_app"] = fSlackApp + } + if cmd.Flags().Changed("sms") { + body["sms"] = fSMS + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("teams-app") { + body["teams_app"] = fTeamsApp + } + if cmd.Flags().Changed("telegram") { + body["telegram"] = fTelegram + } + if cmd.Flags().Changed("template-name") { + body["template_name"] = fTemplateName + } + if cmd.Flags().Changed("voice") { + body["voice"] = fVoice + } + if cmd.Flags().Changed("wecom") { + body["wecom"] = fWecom + } + if cmd.Flags().Changed("wecom-app") { + body["wecom_app"] = fWecomApp + } + if cmd.Flags().Changed("zoom") { + body["zoom"] = fZoom + } + }) + if err != nil { + return err + } + req := new(flashduty.TemplateCreateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.NotificationTemplates.WriteCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Free-form description. Up to 500 characters. (≤500 chars)") + cmd.Flags().StringVar(&fDingtalk, "dingtalk", "", "DingTalk robot message template source.") + cmd.Flags().StringVar(&fDingtalkApp, "dingtalk-app", "", "DingTalk app message template source.") + cmd.Flags().StringVar(&fEmail, "email", "", "Email body template source (Go 'html/template' syntax).") + cmd.Flags().StringVar(&fFeishu, "feishu", "", "Feishu robot message template source.") + cmd.Flags().StringVar(&fFeishuApp, "feishu-app", "", "Feishu app message template source.") + cmd.Flags().StringVar(&fSlack, "slack", "", "Slack robot message template source.") + cmd.Flags().StringVar(&fSlackApp, "slack-app", "", "Slack app message template source.") + cmd.Flags().StringVar(&fSMS, "sms", "", "SMS template source (Go 'text/template' syntax).") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team scope. 0 for account-wide.") + cmd.Flags().StringVar(&fTeamsApp, "teams-app", "", "Microsoft Teams app message template source.") + cmd.Flags().StringVar(&fTelegram, "telegram", "", "Telegram bot message template source.") + cmd.Flags().StringVar(&fTemplateName, "template-name", "", "Template name, unique per account. 1–39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&fVoice, "voice", "", "Voice call script template source.") + cmd.Flags().StringVar(&fWecom, "wecom", "", "WeCom robot message template source.") + cmd.Flags().StringVar(&fWecomApp, "wecom-app", "", "WeCom app message template source.") + cmd.Flags().StringVar(&fZoom, "zoom", "", "Zoom bot message template source.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genNotificationTemplatesWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fTemplateID string + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a template", + Long: `Delete a template. + +Soft-delete a template by ID. + +API: POST /template/delete (template-write-delete) + +Request fields: + --template-id string (required) — Target template ID. Pass '000000000000000000000001' to address the built-in preset. +`, + Example: ` flashduty template delete --data '{"template_id":"6605a1b2c3d4e5f6a7b8c9d0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("template-id") { + body["template_id"] = fTemplateID + } + }) + if err != nil { + return err + } + req := new(flashduty.TemplateIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.NotificationTemplates.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /template/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Target template ID. Pass '000000000000000000000001' to address the built-in preset. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genNotificationTemplatesWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fDingtalk string + var fDingtalkApp string + var fEmail string + var fFeishu string + var fFeishuApp string + var fSlack string + var fSlackApp string + var fSMS string + var fTeamID int64 + var fTeamsApp string + var fTelegram string + var fTemplateID string + var fTemplateName string + var fVoice string + var fWecom string + var fWecomApp string + var fZoom string + cmd := &cobra.Command{ + Use: "update", + Short: "Update a template", + Long: `Update a template. + +Replace the content of every channel on an existing template. + +API: POST /template/update (template-write-update) + +Request fields: + --description string — Free-form description. Up to 500 characters. (≤500 chars) + --dingtalk string — DingTalk robot message template source. + --dingtalk-app string — DingTalk app message template source. + --email string — Email body template source (Go 'html/template' syntax). + --feishu string — Feishu robot message template source. + --feishu-app string — Feishu app message template source. + --slack string — Slack robot message template source. + --slack-app string — Slack app message template source. + --sms string — SMS template source (Go 'text/template' syntax). + --team-id int — Team scope. 0 for account-wide. + --teams-app string — Microsoft Teams app message template source. + --telegram string — Telegram bot message template source. + --template-id string (required) — Target template ID. + --template-name string (required) — Template name. 1–39 characters. (1-39 chars) + --voice string — Voice call script template source. + --wecom string — WeCom robot message template source. + --wecom-app string — WeCom app message template source. + --zoom string — Zoom bot message template source. +`, + Example: ` flashduty template update --data '{"description":"Updated description.","email":"Incident {{ .IncidentName }} on {{ .Severity }}","sms":"[Flashduty] {{ .IncidentName }} — {{ .Severity }}","template_id":"6605a1b2c3d4e5f6a7b8c9d0","template_name":"Prod incident default"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("dingtalk") { + body["dingtalk"] = fDingtalk + } + if cmd.Flags().Changed("dingtalk-app") { + body["dingtalk_app"] = fDingtalkApp + } + if cmd.Flags().Changed("email") { + body["email"] = fEmail + } + if cmd.Flags().Changed("feishu") { + body["feishu"] = fFeishu + } + if cmd.Flags().Changed("feishu-app") { + body["feishu_app"] = fFeishuApp + } + if cmd.Flags().Changed("slack") { + body["slack"] = fSlack + } + if cmd.Flags().Changed("slack-app") { + body["slack_app"] = fSlackApp + } + if cmd.Flags().Changed("sms") { + body["sms"] = fSMS + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("teams-app") { + body["teams_app"] = fTeamsApp + } + if cmd.Flags().Changed("telegram") { + body["telegram"] = fTelegram + } + if cmd.Flags().Changed("template-id") { + body["template_id"] = fTemplateID + } + if cmd.Flags().Changed("template-name") { + body["template_name"] = fTemplateName + } + if cmd.Flags().Changed("voice") { + body["voice"] = fVoice + } + if cmd.Flags().Changed("wecom") { + body["wecom"] = fWecom + } + if cmd.Flags().Changed("wecom-app") { + body["wecom_app"] = fWecomApp + } + if cmd.Flags().Changed("zoom") { + body["zoom"] = fZoom + } + }) + if err != nil { + return err + } + req := new(flashduty.TemplateUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.NotificationTemplates.WriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /template/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Free-form description. Up to 500 characters. (≤500 chars)") + cmd.Flags().StringVar(&fDingtalk, "dingtalk", "", "DingTalk robot message template source.") + cmd.Flags().StringVar(&fDingtalkApp, "dingtalk-app", "", "DingTalk app message template source.") + cmd.Flags().StringVar(&fEmail, "email", "", "Email body template source (Go 'html/template' syntax).") + cmd.Flags().StringVar(&fFeishu, "feishu", "", "Feishu robot message template source.") + cmd.Flags().StringVar(&fFeishuApp, "feishu-app", "", "Feishu app message template source.") + cmd.Flags().StringVar(&fSlack, "slack", "", "Slack robot message template source.") + cmd.Flags().StringVar(&fSlackApp, "slack-app", "", "Slack app message template source.") + cmd.Flags().StringVar(&fSMS, "sms", "", "SMS template source (Go 'text/template' syntax).") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team scope. 0 for account-wide.") + cmd.Flags().StringVar(&fTeamsApp, "teams-app", "", "Microsoft Teams app message template source.") + cmd.Flags().StringVar(&fTelegram, "telegram", "", "Telegram bot message template source.") + cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Target template ID. (required)") + cmd.Flags().StringVar(&fTemplateName, "template-name", "", "Template name. 1–39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&fVoice, "voice", "", "Voice call script template source.") + cmd.Flags().StringVar(&fWecom, "wecom", "", "WeCom robot message template source.") + cmd.Flags().StringVar(&fWecomApp, "wecom-app", "", "WeCom app message template source.") + cmd.Flags().StringVar(&fZoom, "zoom", "", "Zoom bot message template source.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedNotificationTemplates(root *cobra.Command) { + gTemplate := genGroup(root, "template", "On-call/Notification templates API") + genAddLeaf(gTemplate, genNotificationTemplatesReadInfoCmd()) + genAddLeaf(gTemplate, genNotificationTemplatesReadListCmd()) + genAddLeaf(gTemplate, genNotificationTemplatesReadPreviewCmd()) + genAddLeaf(gTemplate, genNotificationTemplatesWriteCreateCmd()) + genAddLeaf(gTemplate, genNotificationTemplatesWriteDeleteCmd()) + genAddLeaf(gTemplate, genNotificationTemplatesWriteUpdateCmd()) +} diff --git a/internal/cli/zz_generated_register.go b/internal/cli/zz_generated_register.go new file mode 100644 index 0000000..29496e6 --- /dev/null +++ b/internal/cli/zz_generated_register.go @@ -0,0 +1,37 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import "github.com/spf13/cobra" + +// registerGenerated attaches every generated command group to root. Called +// from root.go init() after curated commands so curated leaves win on conflict. +func registerGenerated(root *cobra.Command) { + registerGeneratedA2aAgents(root) + registerGeneratedMcpServers(root) + registerGeneratedSkills(root) + registerGeneratedAlertRules(root) + registerGeneratedDataSources(root) + registerGeneratedDiagnostics(root) + registerGeneratedRuleSets(root) + registerGeneratedAlertEnrichment(root) + registerGeneratedAlerts(root) + registerGeneratedAnalytics(root) + registerGeneratedCalendars(root) + registerGeneratedChanges(root) + registerGeneratedChannels(root) + registerGeneratedImIntegrations(root) + registerGeneratedIncidents(root) + registerGeneratedIntegrations(root) + registerGeneratedNotificationTemplates(root) + registerGeneratedSchedules(root) + registerGeneratedStatusPages(root) + registerGeneratedAccount(root) + registerGeneratedAuditLogs(root) + registerGeneratedMembers(root) + registerGeneratedRolesPermissions(root) + registerGeneratedTeams(root) + registerGeneratedApplications(root) + registerGeneratedIssues(root) + registerGeneratedSourcemaps(root) +} diff --git a/internal/cli/zz_generated_response_help.go b/internal/cli/zz_generated_response_help.go new file mode 100644 index 0000000..549d75e --- /dev/null +++ b/internal/cli/zz_generated_response_help.go @@ -0,0 +1,156 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +// responseHelpBySDKMethod maps an SDK "Service.Method" to its rendered +// Response-fields help block. Curated commands look this up via responseHelp() +// so they document the same output shape as the generated commands. +var responseHelpBySDKMethod = map[string]string{ + "A2aAgents.ReadGet": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account.\n - agent_card_name (string) — Name resolved from the fetched agent card.\n - agent_card_skills (array) — Skills advertised on the fetched agent card.\n - agent_id (string) (required) — Unique identifier of the A2A agent.\n - agent_name (string) (required) — Display name of the agent.\n - auth_config (object) — Authentication parameters keyed by name.\n - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth.\n - auth_type (string) (required) — Authentication scheme used when calling the agent.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - card_resolve_timeout (integer) (required) — Timeout for fetching the agent card, in seconds.\n - card_url (string) (required) — URL of the agent's published A2A agent card.\n - created_at (integer) (required) — Creation time as a Unix timestamp in seconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What this agent does and when to delegate to it.\n - oauth_metadata (string) — OAuth metadata JSON.\n - secret_schema (string) — JSON schema of the per-user secret.\n - status (string) (required) — Whether the agent is active and reachable. [enabled, disabled]\n - streaming (boolean) (required) — Whether the agent supports streaming responses.\n - task_timeout (integer) (required) — Timeout for a single delegated task, in seconds.\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in seconds.\n", + "A2aAgents.ReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Owning account.\n - agent_card_name (string) — Name resolved from the fetched agent card.\n - agent_card_skills (array) — Skills advertised on the fetched agent card.\n - agent_id (string) (required) — Unique identifier of the A2A agent.\n - agent_name (string) (required) — Display name of the agent.\n - auth_config (object) — Authentication parameters keyed by name.\n - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth.\n - auth_type (string) (required) — Authentication scheme used when calling the agent.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - card_resolve_timeout (integer) (required) — Timeout for fetching the agent card, in seconds.\n - card_url (string) (required) — URL of the agent's published A2A agent card.\n - created_at (integer) (required) — Creation time as a Unix timestamp in seconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What this agent does and when to delegate to it.\n - oauth_metadata (string) — OAuth metadata JSON.\n - secret_schema (string) — JSON schema of the per-user secret.\n - status (string) (required) — Whether the agent is active and reachable. [enabled, disabled]\n - streaming (boolean) (required) — Whether the agent supports streaming responses.\n - task_timeout (integer) (required) — Timeout for a single delegated task, in seconds.\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in seconds.\n", + "A2aAgents.WriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - agent_id (string) (required) — Identifier of the created agent.\n", + "Account.Info": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) — Account identifier.\n - account_name (string) — Account name.\n - avatar (string) — Account avatar URL.\n - country_code (string) — Calling country code for the contact phone.\n - created_at (integer) — Account creation time, Unix timestamp in seconds.\n - domain (string) — Primary account domain (login subdomain).\n - email (string) — Account contact email.\n - extra_domains (array) — Additional account domains.\n - locale (string) — Account language preference (e.g. zh-CN, en-US).\n - mp_account_id (string) — Account identifier on the cloud marketplace platform (present only for marketplace accounts).\n - mp_plat (string) — Cloud marketplace platform the account was provisioned from (present only for marketplace accounts).\n - phone (string) — Account contact phone, masked for privacy.\n - restrictions (object) — Account access restrictions (present only when configured).\n - allow_subdomain (boolean) — Whether subdomains of the allowed email domains are also accepted.\n - email_domains (array) — Allowed login email domains.\n - ips (array) — Allowed source IP/CIDR whitelist.\n - time_zone (string) — Account default timezone (IANA name, e.g. Asia/Shanghai).\n", + "AlertEnrichment.EnrichmentReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - created_at (integer) (required) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - integration_id (integer) (required) — Integration ID.\n - rules (array) (required) — Ordered enrichment rules.\n - if (array) — Optional AND-filter list. The rule is skipped if the condition does not match.\n - key (string) (required) — Alert label key.\n - oper (string) (required) — Match operator. `IN` matches when any value matches; `NOTIN` matches when none of the values match. [IN, NOTIN]\n - vals (array) (required) — Values to match against.\n - kind (string) (required) — Rule type. `extraction` extracts a label via regex or GJson. `composition` builds a label from a template. `mapping` looks up values from a schema or API. `drop` removes labels. [extraction, composition, mapping, drop]\n - settings (any) (required) — Rule-kind–specific settings. The shape depends on `kind`.\n - status (string) (required) — Rule set status.\n - updated_at (integer) (required) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n", + "AlertEnrichment.EnrichmentReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - created_at (integer) (required) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - integration_id (integer) (required) — Integration ID.\n - rules (array) (required) — Ordered enrichment rules.\n - if (array) — Optional AND-filter list. The rule is skipped if the condition does not match.\n - key (string) (required) — Alert label key.\n - oper (string) (required) — Match operator. `IN` matches when any value matches; `NOTIN` matches when none of the values match. [IN, NOTIN]\n - vals (array) (required) — Values to match against.\n - kind (string) (required) — Rule type. `extraction` extracts a label via regex or GJson. `composition` builds a label from a template. `mapping` looks up values from a schema or API. `drop` removes labels. [extraction, composition, mapping, drop]\n - settings (any) (required) — Rule-kind–specific settings. The shape depends on `kind`.\n - status (string) (required) — Rule set status.\n - updated_at (integer) (required) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n", + "AlertEnrichment.FieldReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - created_at (integer) (required) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - default_value (any) — Default value. Type depends on `field_type`: `bool` for checkbox; `string` for single_select/text; `string[]` for multi_select; may be `null` if no default.\n - deleted_at (integer) — Deletion timestamp, Unix seconds. Only present for soft-deleted fields.\n - description (string) — Optional free-text description. (≤499 chars)\n - display_name (string) (required) — Human-readable name shown in the UI. (≤39 chars)\n - field_id (string) (required) — Field ID — 24-character hex ObjectID.\n - field_name (string) (required) — Machine name used in incident payloads under `fields.`. Immutable. (≤39 chars)\n - field_type (string) (required) — Field input type. [checkbox, multi_select, single_select, text]\n - options (any) — Allowed choices for `single_select`/`multi_select` (non-empty unique string array). `null` or empty for `checkbox`/`text`.\n - status (string) (required) — Field status (e.g. `enabled`, `deleted`).\n - updated_at (integer) (required) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n - value_type (string) (required) — Stored value type. `checkbox` is always `bool`; `single_select`/`multi_select`/`text` are always `string`. [string, bool, float]\n", + "AlertEnrichment.FieldReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Owning account ID.\n - created_at (integer) (required) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - default_value (any) — Default value. Type depends on `field_type`: `bool` for checkbox; `string` for single_select/text; `string[]` for multi_select; may be `null` if no default.\n - deleted_at (integer) — Deletion timestamp, Unix seconds. Only present for soft-deleted fields.\n - description (string) — Optional free-text description. (≤499 chars)\n - display_name (string) (required) — Human-readable name shown in the UI. (≤39 chars)\n - field_id (string) (required) — Field ID — 24-character hex ObjectID.\n - field_name (string) (required) — Machine name used in incident payloads under `fields.`. Immutable. (≤39 chars)\n - field_type (string) (required) — Field input type. [checkbox, multi_select, single_select, text]\n - options (any) — Allowed choices for `single_select`/`multi_select` (non-empty unique string array). `null` or empty for `checkbox`/`text`.\n - status (string) (required) — Field status (e.g. `enabled`, `deleted`).\n - updated_at (integer) (required) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n - value_type (string) (required) — Stored value type. `checkbox` is always `bool`; `single_select`/`multi_select`/`text` are always `string`. [string, bool, float]\n", + "AlertEnrichment.FieldWriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - field_id (string) (required) — Newly assigned field ID — 24-character hex ObjectID.\n - field_name (string) (required) — Echo of the submitted `field_name`.\n", + "AlertEnrichment.MappingAPIReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - api_id (string) (required) — API ID (MongoDB ObjectID hex).\n - api_name (string) (required) — API name.\n - created_at (integer) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - description (string) (required) — Description.\n - headers (object) (required) — Custom request headers.\n - insecure_skip_verify (boolean) (required) — Whether TLS verification is skipped.\n - retry_count (integer) (required) — Retry count.\n - status (string) (required) — API status.\n - team_id (integer) (required) — Owning team ID.\n - timeout (integer) (required) — Request timeout in seconds.\n - updated_at (integer) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n - url (string) (required) — Endpoint URL.\n", + "AlertEnrichment.MappingAPIReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - api_id (string) (required) — API ID (MongoDB ObjectID hex).\n - api_name (string) (required) — API name.\n - created_at (integer) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - description (string) (required) — Description.\n - headers (object) (required) — Custom request headers.\n - insecure_skip_verify (boolean) (required) — Whether TLS verification is skipped.\n - retry_count (integer) (required) — Retry count.\n - status (string) (required) — API status.\n - team_id (integer) (required) — Owning team ID.\n - timeout (integer) (required) — Request timeout in seconds.\n - updated_at (integer) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n - url (string) (required) — Endpoint URL.\n", + "AlertEnrichment.MappingAPIWriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - api_id (string) (required) — Created API ID (MongoDB ObjectID hex).\n - api_name (string) (required) — API name.\n", + "AlertEnrichment.MappingDataReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - created_at (integer) — Creation timestamp, Unix seconds.\n - fields (object) — All label key-value pairs for this row.\n - key (string) — Composite key derived from source label values.\n - updated_at (integer) — Last update timestamp, Unix seconds.\n", + "AlertEnrichment.MappingDataWriteUpsert": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - keys (array) (required) — Composite keys of upserted rows.\n", + "AlertEnrichment.MappingSchemaReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - created_at (integer) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - description (string) (required) — Schema description.\n - result_labels (array) (required) — Output label names.\n - schema_id (string) (required) — Schema ID (MongoDB ObjectID hex).\n - schema_name (string) (required) — Schema name.\n - source_labels (array) (required) — Lookup key label names.\n - status (string) (required) — Schema status.\n - team_id (integer) (required) — Owning team ID.\n - updated_at (integer) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n", + "AlertEnrichment.MappingSchemaReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - created_at (integer) — Creation timestamp, Unix seconds.\n - creator_id (integer) (required) — Creator member ID.\n - description (string) (required) — Schema description.\n - result_labels (array) (required) — Output label names.\n - schema_id (string) (required) — Schema ID (MongoDB ObjectID hex).\n - schema_name (string) (required) — Schema name.\n - source_labels (array) (required) — Lookup key label names.\n - status (string) (required) — Schema status.\n - team_id (integer) (required) — Owning team ID.\n - updated_at (integer) — Last update timestamp, Unix seconds.\n - updated_by (integer) (required) — Last updater member ID.\n", + "AlertEnrichment.MappingSchemaWriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - schema_id (string) (required) — Created schema ID (MongoDB ObjectID hex).\n - schema_name (string) (required) — Schema name.\n", + "AlertRules.ReadAuditDetail": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required)\n - action (string) (required) — Action performed, e.g. `create`, `update`.\n - alert_rule_id (integer) (required) — ID of the alert rule this record belongs to.\n - content (string) — JSON string of the full rule snapshot at audit time. Populated on `/monit/rule/audit/detail`, omitted on list responses.\n - created_at (integer) (required)\n - creator_id (integer) (required)\n - creator_name (string) (required)\n - id (integer) (required) — Audit record ID.\n", + "AlertRules.ReadAudits": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required)\n - action (string) (required) — Action performed, e.g. `create`, `update`.\n - alert_rule_id (integer) (required) — ID of the alert rule this record belongs to.\n - content (string) — JSON string of the full rule snapshot at audit time. Populated on `/monit/rule/audit/detail`, omitted on list responses.\n - created_at (integer) (required)\n - creator_id (integer) (required)\n - creator_name (string) (required)\n - id (integer) (required) — Audit record ID.\n", + "AlertRules.ReadCounterStatus": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - folder_id (integer) (required)\n - folder_name (string)\n - rule_total (integer) (required) — Total rules in the folder family.\n - triggered_rule_count (integer) (required) — Rules with active alerts.\n", + "AlertRules.ReadCounterTotal": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required)\n - clock (integer) (required) — Sample timestamp, Unix epoch seconds.\n - id (integer) (required)\n - num (integer) (required) — Rule count at the sample time.\n", + "AlertRules.ReadDstypes": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Owning account ID. `0` for global types.\n - id (integer) (required)\n - ident (string) (required) — Identifier used as the `ds_type` of rules, e.g. `prometheus`.\n - name (string) (required) — Display name, e.g. `Prometheus`.\n - weight (integer) (required) — Display order weight; higher appears first.\n", + "AlertRules.ReadExport": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - annotations (object)\n - cron_pattern (string) (required)\n - debug_log_enabled (boolean) (required)\n - delay_seconds (integer)\n - description (string)\n - description_type (string) [text, markdown]\n - ds_ids (array)\n - ds_list (array)\n - ds_type (string) (required)\n - enabled (boolean) (required)\n - enabled_times (array)\n - days (array) — Days of week, 0 = Sunday.\n - etime (string) — End time, e.g. `18:00`.\n - stime (string) — Start time, e.g. `09:00`.\n - labels (object)\n - name (string) (required)\n - repeat_interval (integer)\n - repeat_total (integer)\n - rule_configs (object) — Rule evaluation configuration.\n - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery (object) — Recovery condition for any-data check. If omitted or `mode` is empty, treated as `nodata`.\n - args (object)\n - condition (string) — Recovery expression. Required when `mode` is `ql`.\n - mode (string) — `nodata` = recover when the query returns no data; `ql` = recover when the `condition` expression evaluates to true. When `mode` is `ql`, only a single query (`name=A`) is permitted. [nodata, ql]\n - recovery_check_times (integer)\n - severity (string) [Critical, Warning, Info]\n - check_nodata (object) — No-data check configuration.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery_check_times (integer)\n - resolve_timeout (integer) — Auto-resolve after N seconds.\n - severity (string) [Critical, Warning, Info]\n - check_threshold (object) — Threshold check configuration.\n - alerting_check_times (integer)\n - critical (string)\n - enabled (boolean)\n - info (string)\n - push_recovery_event (boolean)\n - recovery (object)\n - condition (string)\n - mode (string) [invert, threshold, ql]\n - recovery_check_times (integer)\n - warning (string)\n - queries (array)\n - args (object)\n - expr (string) — Query expression.\n - label_fields (array)\n - name (string) — Query identifier (letter, e.g. `A`). The name `R` is reserved and must not be used.\n - value_fields (array)\n - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique `name` (not duplicating any query name) and a non-empty `expr`.\n - args (object)\n - expr (string) — Query expression.\n - name (string) — Relate-query identifier.\n", + "AlertRules.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required)\n - annotations (object)\n - channel_ids (array) — Channel IDs to send alerts to.\n - created_at (integer) (required)\n - creator_id (integer) (required)\n - creator_name (string) (required)\n - cron_pattern (string) (required) — 5-field cron schedule.\n - debug_log_enabled (boolean) (required)\n - delay_seconds (integer) (required)\n - description (string)\n - description_type (string) [text, markdown]\n - ds_ids (array) — Specific data source IDs.\n - ds_list (array) — Data source name patterns (supports wildcards).\n - ds_type (string) (required) — Data source type.\n - enabled (boolean) (required)\n - enabled_times (array) — Time windows when the rule is active.\n - days (array) — Days of week (0=Sunday).\n - etime (string) — End time, e.g. `18:00`.\n - stime (string) — Start time, e.g. `09:00`.\n - folder_id (integer) (required) — Folder the rule belongs to.\n - id (integer) (required)\n - labels (object) — Custom labels.\n - name (string) (required) — Rule name.\n - repeat_interval (integer) — Notification repeat interval in seconds.\n - repeat_total (integer) — Max number of repeat notifications.\n - rule_configs (object) — Rule evaluation configuration.\n - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery (object) — Recovery condition for any-data check. If omitted or `mode` is empty, treated as `nodata`.\n - args (object)\n - condition (string) — Recovery expression. Required when `mode` is `ql`.\n - mode (string) — `nodata` = recover when the query returns no data; `ql` = recover when the `condition` expression evaluates to true. When `mode` is `ql`, only a single query (`name=A`) is permitted. [nodata, ql]\n - recovery_check_times (integer)\n - severity (string) [Critical, Warning, Info]\n - check_nodata (object) — No-data check configuration.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery_check_times (integer)\n - resolve_timeout (integer) — Auto-resolve after N seconds.\n - severity (string) [Critical, Warning, Info]\n - check_threshold (object) — Threshold check configuration.\n - alerting_check_times (integer)\n - critical (string)\n - enabled (boolean)\n - info (string)\n - push_recovery_event (boolean)\n - recovery (object)\n - condition (string)\n - mode (string) [invert, threshold, ql]\n - recovery_check_times (integer)\n - warning (string)\n - queries (array)\n - args (object)\n - expr (string) — Query expression.\n - label_fields (array)\n - name (string) — Query identifier (letter, e.g. `A`). The name `R` is reserved and must not be used.\n - value_fields (array)\n - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique `name` (not duplicating any query name) and a non-empty `expr`.\n - args (object)\n - expr (string) — Query expression.\n - name (string) — Relate-query identifier.\n - updated_at (integer) (required)\n - updater_id (integer) (required)\n - updater_name (string) (required)\n", + "AlertRules.ReadList": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - created_at (integer) (required)\n - creator_id (integer) (required)\n - creator_name (string) (required)\n - cron_pattern (string) (required) — 5-field cron schedule, e.g. `* * * * *`.\n - debug_log_enabled (boolean) (required) — Whether debug logging is enabled.\n - delay_seconds (integer) (required) — Evaluation delay in seconds.\n - ds_type (string) (required) — Data source type, e.g. `prometheus`.\n - enabled (boolean) (required) — Whether the rule is enabled.\n - folder_id (integer) (required) — Folder ID.\n - id (integer) (required) — Unique rule ID.\n - labels (object) — Custom labels.\n - name (string) (required) — Rule name.\n - triggered (boolean) (required) — True if the rule currently has active alerts.\n - updated_at (integer) (required)\n - updater_id (integer) (required)\n - updater_name (string) (required)\n", + "AlertRules.WriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer)\n - annotations (object)\n - channel_ids (array) — Channel IDs to send alerts to.\n - created_at (integer)\n - creator_id (integer)\n - creator_name (string)\n - cron_pattern (string) — 5-field cron schedule.\n - debug_log_enabled (boolean)\n - delay_seconds (integer)\n - description (string)\n - description_type (string) [text, markdown]\n - ds_ids (array) — Specific data source IDs.\n - ds_list (array) — Data source name patterns (supports wildcards).\n - ds_type (string) — Data source type.\n - enabled (boolean)\n - enabled_times (array) — Time windows when the rule is active.\n - days (array) — Days of week (0=Sunday).\n - etime (string) — End time, e.g. `18:00`.\n - stime (string) — Start time, e.g. `09:00`.\n - folder_id (integer) — Folder the rule belongs to.\n - id (integer)\n - labels (object) — Custom labels.\n - name (string) — Rule name.\n - repeat_interval (integer) — Notification repeat interval in seconds.\n - repeat_total (integer) — Max number of repeat notifications.\n - rule_configs (object) — Rule evaluation configuration.\n - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery (object) — Recovery condition for any-data check. If omitted or `mode` is empty, treated as `nodata`.\n - args (object)\n - condition (string) — Recovery expression. Required when `mode` is `ql`.\n - mode (string) — `nodata` = recover when the query returns no data; `ql` = recover when the `condition` expression evaluates to true. When `mode` is `ql`, only a single query (`name=A`) is permitted. [nodata, ql]\n - recovery_check_times (integer)\n - severity (string) [Critical, Warning, Info]\n - check_nodata (object) — No-data check configuration.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery_check_times (integer)\n - resolve_timeout (integer) — Auto-resolve after N seconds.\n - severity (string) [Critical, Warning, Info]\n - check_threshold (object) — Threshold check configuration.\n - alerting_check_times (integer)\n - critical (string)\n - enabled (boolean)\n - info (string)\n - push_recovery_event (boolean)\n - recovery (object)\n - condition (string)\n - mode (string) [invert, threshold, ql]\n - recovery_check_times (integer)\n - warning (string)\n - queries (array)\n - args (object)\n - expr (string) — Query expression.\n - label_fields (array)\n - name (string) — Query identifier (letter, e.g. `A`). The name `R` is reserved and must not be used.\n - value_fields (array)\n - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique `name` (not duplicating any query name) and a non-empty `expr`.\n - args (object)\n - expr (string) — Query expression.\n - name (string) — Relate-query identifier.\n - updated_at (integer)\n - updater_id (integer)\n - updater_name (string)\n", + "AlertRules.WriteFieldsUpdate": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - message (string) (required) — Empty on success, error message on failure.\n - name (string) (required) — Rule name.\n", + "AlertRules.WriteImport": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - message (string) (required) — Empty on success, error message on failure.\n - name (string) (required) — Rule name.\n", + "AlertRules.WriteMove": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - message (string) (required) — Empty on success, error message on failure.\n - name (string) (required) — Rule name.\n", + "AlertRules.WriteStatus": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - folder_id (integer) (required)\n - folder_name (string)\n - rule_total (integer) (required) — Total rules in the folder family.\n - triggered_rule_count (integer) (required) — Rules with active alerts.\n", + "AlertRules.WriteUpdate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer)\n - annotations (object)\n - channel_ids (array) — Channel IDs to send alerts to.\n - created_at (integer)\n - creator_id (integer)\n - creator_name (string)\n - cron_pattern (string) — 5-field cron schedule.\n - debug_log_enabled (boolean)\n - delay_seconds (integer)\n - description (string)\n - description_type (string) [text, markdown]\n - ds_ids (array) — Specific data source IDs.\n - ds_list (array) — Data source name patterns (supports wildcards).\n - ds_type (string) — Data source type.\n - enabled (boolean)\n - enabled_times (array) — Time windows when the rule is active.\n - days (array) — Days of week (0=Sunday).\n - etime (string) — End time, e.g. `18:00`.\n - stime (string) — Start time, e.g. `09:00`.\n - folder_id (integer) — Folder the rule belongs to.\n - id (integer)\n - labels (object) — Custom labels.\n - name (string) — Rule name.\n - repeat_interval (integer) — Notification repeat interval in seconds.\n - repeat_total (integer) — Max number of repeat notifications.\n - rule_configs (object) — Rule evaluation configuration.\n - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery (object) — Recovery condition for any-data check. If omitted or `mode` is empty, treated as `nodata`.\n - args (object)\n - condition (string) — Recovery expression. Required when `mode` is `ql`.\n - mode (string) — `nodata` = recover when the query returns no data; `ql` = recover when the `condition` expression evaluates to true. When `mode` is `ql`, only a single query (`name=A`) is permitted. [nodata, ql]\n - recovery_check_times (integer)\n - severity (string) [Critical, Warning, Info]\n - check_nodata (object) — No-data check configuration.\n - alerting_check_times (integer)\n - enabled (boolean)\n - push_recovery_event (boolean)\n - recovery_check_times (integer)\n - resolve_timeout (integer) — Auto-resolve after N seconds.\n - severity (string) [Critical, Warning, Info]\n - check_threshold (object) — Threshold check configuration.\n - alerting_check_times (integer)\n - critical (string)\n - enabled (boolean)\n - info (string)\n - push_recovery_event (boolean)\n - recovery (object)\n - condition (string)\n - mode (string) [invert, threshold, ql]\n - recovery_check_times (integer)\n - warning (string)\n - queries (array)\n - args (object)\n - expr (string) — Query expression.\n - label_fields (array)\n - name (string) — Query identifier (letter, e.g. `A`). The name `R` is reserved and must not be used.\n - value_fields (array)\n - relate_queries (array) — Optional auxiliary queries whose results are attached to alert events as context. Each entry must have a unique `name` (not duplicating any query name) and a non-empty `expr`.\n - args (object)\n - expr (string) — Query expression.\n - name (string) — Relate-query identifier.\n - updated_at (integer)\n - updater_id (integer)\n - updater_name (string)\n", + "Alerts.EventReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n", + "Alerts.ReadEventList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n", + "Alerts.ReadFeed": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - created_at (integer) (required) — Creation timestamp in Unix epoch milliseconds.\n - creator_id (integer) (required) — Member ID of the creator. 0 for system-generated entries.\n - detail (any) (required) — Type-specific payload. The concrete shape is determined by `type`.\n - ref_id (string) (required) — ObjectID of the alert this entry references.\n - type (string) (required) — Alert activity feed entry type. Each value identifies one alert lifecycle event; the matching `detail` payload shape is determined by this field. | Type | Meaning | |---|---| | `a_new` | Alert triggered. | | `a_comm` | Comment added on the alert. | | `a_close` | Alert closed. | [a_new, a_comm, a_close]\n - updated_at (integer) (required) — Last update timestamp in Unix epoch milliseconds.\n", + "Alerts.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) — Account ID.\n - alert_id (string) — Unique alert ID (ObjectID hex string).\n - alert_key (string) — Deduplication key.\n - alert_severity (string) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) — ID of the channel the alert belongs to.\n - channel_name (string) — Display name of the channel.\n - channel_status (string) — Status of the channel (e.g. `enabled`, `disabled`).\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead. Deprecated: use `integration_id` instead.\n - data_source_name (string) — Deprecated. Use `integration_name` instead.\n - data_source_ref_id (string) — Deprecated. Use `integration_ref_id` instead.\n - data_source_type (string) — Deprecated. Use `integration_type` instead.\n - description (string) — Alert description.\n - end_time (integer) — Resolution time, Unix epoch seconds. 0 if still active.\n - event_cnt (integer) — Total number of raw events received by this alert.\n - events (array) — Recent raw events attached to this alert. Populated only by some endpoints.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) — True if this alert has ever been silenced.\n - images (array) — Images attached to the alert.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) — ID of the integration that produced this alert.\n - integration_name (string) — Display name of the integration.\n - integration_ref_id (string) — External reference ID of the integration.\n - integration_type (string) — Type/plugin key of the integration.\n - labels (object) — Label key-value pairs.\n - last_time (integer) — Last-event time, Unix epoch seconds.\n - responder_email (string) — Email of the current responder (from the associated incident).\n - responder_name (string) — Display name of the current responder (from the associated incident).\n - start_time (integer) — First-seen time, Unix epoch seconds.\n - title (string) — Alert title.\n - title_rule (string) — Title template used to derive `title` from the event labels (e.g. `$service::$cluster`).\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n", + "Alerts.ReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account ID.\n - alert_id (string) — Unique alert ID (ObjectID hex string).\n - alert_key (string) — Deduplication key.\n - alert_severity (string) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) — ID of the channel the alert belongs to.\n - channel_name (string) — Display name of the channel.\n - channel_status (string) — Status of the channel (e.g. `enabled`, `disabled`).\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead. Deprecated: use `integration_id` instead.\n - data_source_name (string) — Deprecated. Use `integration_name` instead.\n - data_source_ref_id (string) — Deprecated. Use `integration_ref_id` instead.\n - data_source_type (string) — Deprecated. Use `integration_type` instead.\n - description (string) — Alert description.\n - end_time (integer) — Resolution time, Unix epoch seconds. 0 if still active.\n - event_cnt (integer) — Total number of raw events received by this alert.\n - events (array) — Recent raw events attached to this alert. Populated only by some endpoints.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) — True if this alert has ever been silenced.\n - images (array) — Images attached to the alert.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) — ID of the integration that produced this alert.\n - integration_name (string) — Display name of the integration.\n - integration_ref_id (string) — External reference ID of the integration.\n - integration_type (string) — Type/plugin key of the integration.\n - labels (object) — Label key-value pairs.\n - last_time (integer) — Last-event time, Unix epoch seconds.\n - responder_email (string) — Email of the current responder (from the associated incident).\n - responder_name (string) — Display name of the current responder (from the associated incident).\n - start_time (integer) — First-seen time, Unix epoch seconds.\n - title (string) — Alert title.\n - title_rule (string) — Title template used to derive `title` from the event labels (e.g. `$service::$cluster`).\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n", + "Alerts.ReadListByIDs": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account ID.\n - alert_id (string) — Unique alert ID (ObjectID hex string).\n - alert_key (string) — Deduplication key.\n - alert_severity (string) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) — ID of the channel the alert belongs to.\n - channel_name (string) — Display name of the channel.\n - channel_status (string) — Status of the channel (e.g. `enabled`, `disabled`).\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead. Deprecated: use `integration_id` instead.\n - data_source_name (string) — Deprecated. Use `integration_name` instead.\n - data_source_ref_id (string) — Deprecated. Use `integration_ref_id` instead.\n - data_source_type (string) — Deprecated. Use `integration_type` instead.\n - description (string) — Alert description.\n - end_time (integer) — Resolution time, Unix epoch seconds. 0 if still active.\n - event_cnt (integer) — Total number of raw events received by this alert.\n - events (array) — Recent raw events attached to this alert. Populated only by some endpoints.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) — True if this alert has ever been silenced.\n - images (array) — Images attached to the alert.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) — ID of the integration that produced this alert.\n - integration_name (string) — Display name of the integration.\n - integration_ref_id (string) — External reference ID of the integration.\n - integration_type (string) — Type/plugin key of the integration.\n - labels (object) — Label key-value pairs.\n - last_time (integer) — Last-event time, Unix epoch seconds.\n - responder_email (string) — Email of the current responder (from the associated incident).\n - responder_name (string) — Display name of the current responder (from the associated incident).\n - start_time (integer) — First-seen time, Unix epoch seconds.\n - title (string) — Alert title.\n - title_rule (string) — Title template used to derive `title` from the event labels (e.g. `$service::$cluster`).\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n", + "Alerts.ReadPipelineInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - creator_id (integer) — Member ID who created the pipeline.\n - integration_id (integer) — Integration ID this pipeline applies to.\n - rules (array) — Ordered list of processing rules.\n - if (array) — OR-of-AND filter tree. Outer array is a list of AND groups; the condition passes if **any** AND group matches. Within each AND group, **all** conditions must match.\n - kind (string) — Rule type. [title_reset, description_reset, severity_reset, alert_drop, alert_inhibit]\n - settings (object) — Kind-specific settings. Shape depends on `kind`: - `title_reset`: `{ \"title\": \"\" }` - `description_reset`: `{ \"description\": \"\" }` - `severity_reset`: `{ \"severity\": \"Critical\"|\"Warning\"|\"Info\" }` - `alert_drop`: `{}` (empty object) - `alert_inhibit`: `{ \"equals\": [\"\", ...], \"source_filters\": }`\n - status (string) — Pipeline status. Possible values: `enabled`, `disabled`.\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n - updated_by (integer) — Member ID who last updated the pipeline.\n", + "Alerts.ReadPipelineList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - creator_id (integer) — Member ID who created the pipeline.\n - integration_id (integer) — Integration ID this pipeline applies to.\n - rules (array) — Ordered list of processing rules.\n - if (array) — OR-of-AND filter tree. Outer array is a list of AND groups; the condition passes if **any** AND group matches. Within each AND group, **all** conditions must match.\n - kind (string) — Rule type. [title_reset, description_reset, severity_reset, alert_drop, alert_inhibit]\n - settings (object) — Kind-specific settings. Shape depends on `kind`: - `title_reset`: `{ \"title\": \"\" }` - `description_reset`: `{ \"description\": \"\" }` - `severity_reset`: `{ \"severity\": \"Critical\"|\"Warning\"|\"Info\" }` - `alert_drop`: `{}` (empty object) - `alert_inhibit`: `{ \"equals\": [\"\", ...], \"source_filters\": }`\n - status (string) — Pipeline status. Possible values: `enabled`, `disabled`.\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n - updated_by (integer) — Member ID who last updated the pipeline.\n", + "Analytics.ByAccount": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer)\n - acknowledgement_pct (number)\n - channel_id (integer)\n - channel_name (string)\n - hours (string) — Hour bucket when `split_hours` is enabled. [work, sleep, off]\n - mean_seconds_to_ack (number)\n - mean_seconds_to_close (number)\n - noise_reduction_pct (number)\n - responder_id (integer)\n - responder_name (string)\n - team_id (integer)\n - team_name (string)\n - total_alert_cnt (integer)\n - total_alert_event_cnt (integer)\n - total_engaged_seconds (integer)\n - total_incident_cnt (integer)\n - total_incidents_acknowledged (integer)\n - total_incidents_auto_closed (integer)\n - total_incidents_closed (integer)\n - total_incidents_escalated (integer)\n - total_incidents_manually_closed (integer)\n - total_incidents_manually_escalated (integer)\n - total_incidents_reassigned (integer)\n - total_incidents_timeout_closed (integer)\n - total_incidents_timeout_escalated (integer)\n - total_interruptions (integer)\n - total_notifications (integer)\n - total_seconds_to_ack (integer)\n - total_seconds_to_close (integer)\n - ts (integer) — Aggregation bucket start time, Unix seconds. Present when `aggregate_unit` is used.\n", + "Analytics.ByChannel": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer)\n - acknowledgement_pct (number)\n - channel_id (integer)\n - channel_name (string)\n - hours (string) — Hour bucket when `split_hours` is enabled. [work, sleep, off]\n - mean_seconds_to_ack (number)\n - mean_seconds_to_close (number)\n - noise_reduction_pct (number)\n - responder_id (integer)\n - responder_name (string)\n - team_id (integer)\n - team_name (string)\n - total_alert_cnt (integer)\n - total_alert_event_cnt (integer)\n - total_engaged_seconds (integer)\n - total_incident_cnt (integer)\n - total_incidents_acknowledged (integer)\n - total_incidents_auto_closed (integer)\n - total_incidents_closed (integer)\n - total_incidents_escalated (integer)\n - total_incidents_manually_closed (integer)\n - total_incidents_manually_escalated (integer)\n - total_incidents_reassigned (integer)\n - total_incidents_timeout_closed (integer)\n - total_incidents_timeout_escalated (integer)\n - total_interruptions (integer)\n - total_notifications (integer)\n - total_seconds_to_ack (integer)\n - total_seconds_to_close (integer)\n - ts (integer) — Aggregation bucket start time, Unix seconds. Present when `aggregate_unit` is used.\n", + "Analytics.ByResponder": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer)\n - acknowledgement_pct (number)\n - channel_id (integer)\n - channel_name (string)\n - hours (string) — Hour bucket when `split_hours` is enabled. [work, sleep, off]\n - mean_seconds_to_ack (number)\n - responder_id (integer)\n - responder_name (string)\n - team_id (integer)\n - team_name (string)\n - total_engaged_seconds (integer)\n - total_incident_cnt (integer)\n - total_incidents_acknowledged (integer)\n - total_incidents_escalated (integer)\n - total_incidents_manually_escalated (integer)\n - total_incidents_reassigned (integer)\n - total_incidents_timeout_escalated (integer)\n - total_interruptions (integer)\n - total_notifications (integer)\n - total_seconds_to_ack (integer)\n - ts (integer) — Aggregation bucket start time, Unix seconds. Present when `aggregate_unit` is used.\n", + "Analytics.ByTeam": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer)\n - acknowledgement_pct (number)\n - channel_id (integer)\n - channel_name (string)\n - hours (string) — Hour bucket when `split_hours` is enabled. [work, sleep, off]\n - mean_seconds_to_ack (number)\n - mean_seconds_to_close (number)\n - noise_reduction_pct (number)\n - responder_id (integer)\n - responder_name (string)\n - team_id (integer)\n - team_name (string)\n - total_alert_cnt (integer)\n - total_alert_event_cnt (integer)\n - total_engaged_seconds (integer)\n - total_incident_cnt (integer)\n - total_incidents_acknowledged (integer)\n - total_incidents_auto_closed (integer)\n - total_incidents_closed (integer)\n - total_incidents_escalated (integer)\n - total_incidents_manually_closed (integer)\n - total_incidents_manually_escalated (integer)\n - total_incidents_reassigned (integer)\n - total_incidents_timeout_closed (integer)\n - total_incidents_timeout_escalated (integer)\n - total_interruptions (integer)\n - total_notifications (integer)\n - total_seconds_to_ack (integer)\n - total_seconds_to_close (integer)\n - ts (integer) — Aggregation bucket start time, Unix seconds. Present when `aggregate_unit` is used.\n", + "Analytics.IncidentList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - acknowledgements (integer)\n - assigned_to (object) — Current assignment target for the incident.\n - assigned_at (integer) — Unix timestamp (seconds) when this assignment was made.\n - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) driving the assignment.\n - escalate_rule_name (string) — Display name of the escalation rule.\n - id (string) — Internal assignment record ID.\n - layer_idx (integer) — Current level index within the escalation rule.\n - person_ids (array) — Member IDs assigned directly to this incident.\n - type (string) — Assignment type. [assign, reassign, escalate, reopen]\n - assignments (integer)\n - channel_id (integer)\n - channel_name (string)\n - closed_by (string) [auto, timeout, manually]\n - created_at (integer)\n - creator_id (integer)\n - creator_name (string)\n - description (string)\n - engaged_seconds (integer)\n - escalations (integer)\n - fields (object)\n - hours (string)\n - incident_id (string)\n - interruptions (integer)\n - labels (object)\n - manual_escalations (integer)\n - notifications (integer)\n - progress (string) — Incident progress state — one of `Triggered`, `Processing`, `Closed`.\n - reassignments (integer)\n - responders (array)\n - seconds_to_ack (integer)\n - seconds_to_close (integer)\n - severity (string) [Critical, Warning, Info, Ok]\n - team_id (integer)\n - team_name (string)\n - timeout_escalations (integer)\n - title (string)\n", + "Analytics.TopkAlertsByLabel": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - hours (string) — Hour bucket when `split_hours` is enabled.\n - label (string) — Aggregation key value (check name or resource identifier).\n - total_alert_cnt (integer)\n - total_alert_event_cnt (integer)\n", + "Applications.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) — Account ID.\n - alerting (object) — Alert settings for the application.\n - channel_ids (array) — Channel IDs to send alerts to.\n - enabled (boolean) — Whether alerting is enabled.\n - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned).\n - application_id (string) — Unique application ID.\n - application_name (string) — Application display name.\n - client_token (string) — Token used to initialize the RUM SDK.\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - created_by (integer) — Creator member ID.\n - is_private (boolean) — If `true`, the application is only accessible to team members.\n - no_geo (boolean) — If `true`, geographic location is not inferred from IP.\n - no_ip (boolean) — If `true`, IP addresses are not collected.\n - status (string) — Application status. [enabled, disabled, deleted]\n - team_id (integer) — Owning team ID.\n - tracing (object) — APM tracing integration settings.\n - enabled (boolean) — Whether tracing integration is enabled.\n - endpoint (string) — Trace endpoint URL (http or https).\n - open_type (string) — How to open the trace link. [popup, tab]\n - type (string) — Application type. [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity]\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n - updated_by (integer) — Last updater member ID.\n", + "Applications.ReadInfos": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account ID.\n - alerting (object) — Alert settings for the application.\n - channel_ids (array) — Channel IDs to send alerts to.\n - enabled (boolean) — Whether alerting is enabled.\n - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned).\n - application_id (string) — Unique application ID.\n - application_name (string) — Application display name.\n - client_token (string) — Token used to initialize the RUM SDK.\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - created_by (integer) — Creator member ID.\n - is_private (boolean) — If `true`, the application is only accessible to team members.\n - no_geo (boolean) — If `true`, geographic location is not inferred from IP.\n - no_ip (boolean) — If `true`, IP addresses are not collected.\n - status (string) — Application status. [enabled, disabled, deleted]\n - team_id (integer) — Owning team ID.\n - tracing (object) — APM tracing integration settings.\n - enabled (boolean) — Whether tracing integration is enabled.\n - endpoint (string) — Trace endpoint URL (http or https).\n - open_type (string) — How to open the trace link. [popup, tab]\n - type (string) — Application type. [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity]\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n - updated_by (integer) — Last updater member ID.\n", + "Applications.ReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account ID.\n - alerting (object) — Alert settings for the application.\n - channel_ids (array) — Channel IDs to send alerts to.\n - enabled (boolean) — Whether alerting is enabled.\n - integration_id (integer) — Associated on-call integration ID (read-only, auto-assigned).\n - application_id (string) — Unique application ID.\n - application_name (string) — Application display name.\n - client_token (string) — Token used to initialize the RUM SDK.\n - created_at (integer) — Creation timestamp, Unix epoch seconds.\n - created_by (integer) — Creator member ID.\n - is_private (boolean) — If `true`, the application is only accessible to team members.\n - no_geo (boolean) — If `true`, geographic location is not inferred from IP.\n - no_ip (boolean) — If `true`, IP addresses are not collected.\n - status (string) — Application status. [enabled, disabled, deleted]\n - team_id (integer) — Owning team ID.\n - tracing (object) — APM tracing integration settings.\n - enabled (boolean) — Whether tracing integration is enabled.\n - endpoint (string) — Trace endpoint URL (http or https).\n - open_type (string) — How to open the trace link. [popup, tab]\n - type (string) — Application type. [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity]\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n - updated_by (integer) — Last updater member ID.\n", + "Applications.WriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - application_id (string) — Auto-generated unique application ID.\n - application_name (string) — Application display name.\n - client_token (string) — Token for RUM SDK initialization.\n", + "AuditLogs.OperationList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - name (string) (required) — Stable machine-readable operation name for use as a filter.\n - name_cn (string) (required) — Human-readable Chinese label shown in the console.\n", + "AuditLogs.Search": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — ID of the account.\n - body (string) (required) — JSON-encoded request body (may be truncated at 10 KB).\n - created_at (integer) (required) — Timestamp of the operation in Unix epoch milliseconds.\n - ip (string) (required) — Client IP address of the caller.\n - is_dangerous (boolean) (required) — True if this is flagged as a high-risk operation.\n - is_write (boolean) (required) — True for mutating operations; false for read-only ones.\n - member_id (integer) (required) — ID of the member who performed the action.\n - member_name (string) (required) — Display name of the member.\n - operation (string) (required) — Stable machine-readable operation name, e.g. `template:write:create`.\n - operation_name (string) (required) — Human-readable operation label in the account's locale.\n - params (array) (required) — URL path parameters as an array of key-value pairs, or an empty array when none.\n - Key (string)\n - Value (string)\n - request_id (string) (required) — Unique request ID for correlation.\n", + "Calendars.CalEventList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account ID. Only present for private events.\n - cal_id (string) (required) — Calendar ID. For public events this is a locale key such as zh-cn.china.official.\n - created_at (integer) (required) — Creation timestamp (Unix seconds).\n - creator_id (integer) — Creator person ID. Only present for private events.\n - description (string) (required) — Event description.\n - end_at (string) (required) — Event end date (YYYY-MM-DD, exclusive).\n - event_id (string) (required) — Event ID.\n - is_off (boolean) (required) — Whether the event marks a non-working day.\n - start_at (string) (required) — Event start date (YYYY-MM-DD).\n - summary (string) (required) — Event summary.\n - updated_at (integer) (required) — Last update timestamp (Unix seconds).\n", + "Calendars.CalEventUpsert": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - cal_id (string) (required) — Calendar ID.\n - event_id (string) (required) — Event ID (existing or newly generated).\n - summary (string) (required) — Event summary.\n", + "Calendars.CalendarCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - cal_id (string) (required) — ID of the newly created calendar (format cal.).\n - cal_name (string) (required) — Calendar display name.\n", + "Calendars.CalendarInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Account ID.\n - cal_id (string) (required) — Calendar ID.\n - cal_name (string) (required) — Calendar display name.\n - created_at (integer) (required) — Creation timestamp (Unix seconds).\n - creator_id (integer) (required) — Creator person ID.\n - description (string) (required) — Calendar description.\n - extra_cal_ids (array) — Inherited public-holiday calendar IDs.\n - kind (string) (required) — Calendar kind. [region.official.holiday, religion.holiday, personal]\n - status (string) (required) — Calendar status. [enabled, deleted]\n - team_id (integer) (required) — Owning team ID (0 when not assigned).\n - timezone (string) (required) — IANA timezone.\n - updated_at (integer) (required) — Last update timestamp (Unix seconds).\n - updated_by (integer) (required) — Last updater person ID.\n - workdays (array) — Workday numbers (0 = Sunday, 6 = Saturday).\n", + "Calendars.CalendarList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - cal_id (string) (required) — Calendar ID.\n - cal_name (string) (required) — Calendar display name.\n - created_at (integer) (required) — Creation timestamp (Unix seconds).\n - creator_id (integer) (required) — Creator person ID.\n - description (string) (required) — Calendar description.\n - extra_cal_ids (array) — Inherited public-holiday calendar IDs.\n - kind (string) (required) — Calendar kind. [region.official.holiday, religion.holiday, personal]\n - status (string) (required) — Calendar status. [enabled, deleted]\n - team_id (integer) (required) — Owning team ID (0 when not assigned).\n - timezone (string) (required) — IANA timezone.\n - updated_at (integer) (required) — Last update timestamp (Unix seconds).\n - updated_by (integer) (required) — Last updater person ID.\n - workdays (array) — Workday numbers (0 = Sunday, 6 = Saturday).\n", + "Changes.List": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account this change belongs to.\n - change_id (string) — Change ID, a MongoDB ObjectID hex string.\n - change_key (string) — Stable key that groups events belonging to the same change.\n - change_status (string) — Current lifecycle status of the change.\n - channel_id (integer) — Collaboration channel this change is routed to.\n - channel_name (string) — Name of the collaboration channel.\n - channel_status (string) — Status of the collaboration channel.\n - description (string) — Change description.\n - end_time (integer) — Unix timestamp in seconds when the change ended.\n - events (array) — Underlying change events, returned only when include_events is true.\n - account_id (integer) — Account this change event belongs to.\n - change_key (string) — Stable key that groups events belonging to the same change.\n - change_status (string) — Lifecycle status of the change event. [Planned, Ready, Processing, Canceled, Done]\n - channel_id (integer) — Collaboration channel this change event is routed to.\n - created_at (integer) — Unix timestamp in seconds when the change event was created.\n - deleted_at (integer) — Unix timestamp in seconds when the change event was deleted.\n - description (string) — Change event description.\n - event_id (string) — Change event ID, a MongoDB ObjectID hex string.\n - event_time (integer) — Unix timestamp in seconds when the change event occurred.\n - integration_id (integer) — Integration that reported this change event.\n - labels (object) — Key-value labels attached to the change event.\n - link (string) — External link to the source change record.\n - title (string) — Change event title.\n - updated_at (integer) — Unix timestamp in seconds when the change event was last updated.\n - integration_id (integer) — Integration that reported this change.\n - integration_name (string) — Name of the reporting integration.\n - labels (object) — Key-value labels attached to the change.\n - last_time (integer) — Unix timestamp in seconds of the most recent change activity.\n - link (string) — External link to the source change record.\n - start_time (integer) — Unix timestamp in seconds when the change started.\n - title (string) — Change title.\n", + "Channels.ChannelCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - channel_id (integer) (required) — Newly created channel ID.\n - channel_name (string) (required) — Channel name echoed back from the request.\n - external_report_token (string) — External report token. Emitted only when external reporting is enabled.\n", + "Channels.ChannelEscalateRuleCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID).\n - rule_name (string) (required) — Rule name echoed back from the request.\n", + "Channels.ChannelEscalateRuleInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - aggr_window (integer) (required) — Aggregation window in seconds.\n - channel_id (integer) (required) — Channel the rule belongs to.\n - channel_name (string) — Channel name, populated for cross-channel listing responses.\n - created_at (integer) (required) — Creation timestamp (unix seconds).\n - deleted_at (integer) — Deletion timestamp (unix seconds). Emitted only for soft-deleted rules.\n - description (string) (required) — Rule description.\n - filters (object) (required)\n - layers (array) (required) — Escalation levels in order.\n - escalate_window (integer) — Wait before moving to the next level, in minutes. (0-720)\n - force_escalate (boolean) — When true, always escalate regardless of acknowledgement.\n - max_times (integer) — Max repeat notifications within the level. (0-6)\n - notify_step (number) — Repeat interval in minutes. (0.5-120)\n - target (object) (required) — Notification target. At least one of `person_ids`, `team_ids`, `schedule_to_role_ids`, or `emails` must be set, together with either `by` or `webhooks`.\n - by (object) — Per-severity personal notification channels. Required unless `webhooks` is provided.\n - critical (array) — Channels for Critical events (e.g. `voice`, `sms`, `email`, `feishu`).\n - follow_preference (boolean) — When true, use each responder's personal preference instead of the lists below.\n - info (array) — Channels for Info events.\n - warning (array) — Channels for Warning events.\n - emails (array) — Email addresses to notify (push-only scenarios).\n - person_ids (array) — Member IDs to notify directly.\n - schedule_to_role_ids (object) — Map of schedule ID to the role IDs on that schedule to notify.\n - team_ids (array) — Team IDs to notify.\n - webhooks (array) — Group chat / webhook targets. Required unless `by` is provided.\n - settings (object) (required) — Type-specific settings (chat IDs, URLs, etc.).\n - type (string) (required) — Webhook type (e.g. `feishu`, `dingtalk_app`, `wecom_app`, `slack`, `teams`, `custom`).\n - priority (integer) (required) — Evaluation priority. Lower runs first.\n - rule_id (string) (required) — Escalation rule ID (MongoDB ObjectID).\n - rule_name (string) (required) — Rule name.\n - status (string) (required) — Rule status. [enabled, disabled]\n - template_id (string) (required) — Notification template ID (MongoDB ObjectID).\n - time_filters (array) (required) — Recurring time windows during which the rule applies.\n - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar.\n - end (string) — End of the window in `HH:MM`.\n - is_off (boolean) — When true, match days marked as days-off in the calendar.\n - repeat (array) — Days of the week this window repeats on. Empty means every day.\n - start (string) — Start of the window in `HH:MM`.\n - updated_at (integer) (required) — Last update timestamp (unix seconds).\n - updated_by (integer) (required) — Member ID that last updated the rule.\n", + "Channels.ChannelEscalateRuleList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Owning account ID.\n - aggr_window (integer) (required) — Aggregation window in seconds.\n - channel_id (integer) (required) — Channel the rule belongs to.\n - channel_name (string) — Channel name, populated for cross-channel listing responses.\n - created_at (integer) (required) — Creation timestamp (unix seconds).\n - deleted_at (integer) — Deletion timestamp (unix seconds). Emitted only for soft-deleted rules.\n - description (string) (required) — Rule description.\n - filters (object) (required)\n - layers (array) (required) — Escalation levels in order.\n - escalate_window (integer) — Wait before moving to the next level, in minutes. (0-720)\n - force_escalate (boolean) — When true, always escalate regardless of acknowledgement.\n - max_times (integer) — Max repeat notifications within the level. (0-6)\n - notify_step (number) — Repeat interval in minutes. (0.5-120)\n - target (object) (required) — Notification target. At least one of `person_ids`, `team_ids`, `schedule_to_role_ids`, or `emails` must be set, together with either `by` or `webhooks`.\n - by (object) — Per-severity personal notification channels. Required unless `webhooks` is provided.\n - emails (array) — Email addresses to notify (push-only scenarios).\n - person_ids (array) — Member IDs to notify directly.\n - schedule_to_role_ids (object) — Map of schedule ID to the role IDs on that schedule to notify.\n - team_ids (array) — Team IDs to notify.\n - webhooks (array) — Group chat / webhook targets. Required unless `by` is provided.\n - priority (integer) (required) — Evaluation priority. Lower runs first.\n - rule_id (string) (required) — Escalation rule ID (MongoDB ObjectID).\n - rule_name (string) (required) — Rule name.\n - status (string) (required) — Rule status. [enabled, disabled]\n - template_id (string) (required) — Notification template ID (MongoDB ObjectID).\n - time_filters (array) (required) — Recurring time windows during which the rule applies.\n - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar.\n - end (string) — End of the window in `HH:MM`.\n - is_off (boolean) — When true, match days marked as days-off in the calendar.\n - repeat (array) — Days of the week this window repeats on. Empty means every day.\n - start (string) — Start of the window in `HH:MM`.\n - updated_at (integer) (required) — Last update timestamp (unix seconds).\n - updated_by (integer) (required) — Member ID that last updated the rule.\n", + "Channels.ChannelInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) — Owning account ID.\n - active_incident_highest_severity (string) — Highest severity among active incidents in the channel.\n - auto_resolve_mode (string) — Auto-resolve timer reset mode. [trigger, update]\n - auto_resolve_timeout (integer) — Auto-resolve timeout in seconds. 0 disables auto-resolve.\n - channel_id (integer) — Channel ID.\n - channel_name (string) — Channel name.\n - created_at (integer) — Creation timestamp (unix seconds).\n - creator_id (integer) — Member ID who created the channel.\n - deleted_at (integer) — Deletion timestamp (unix seconds). Non-zero only for soft-deleted channels.\n - description (string) — Free-form description.\n - disable_auto_close (boolean) — When true, automatic incident closing is disabled.\n - disable_outlier_detection (boolean) — When true, outlier incident detection is disabled.\n - external_report_token (string) — Token granted to external reporters when external reporting is enabled.\n - flapping (object) — Flapping detection configuration.\n - in_mins (integer) — Observation window in minutes. (1-1440)\n - is_disabled (boolean) — Disable flapping detection.\n - max_changes (integer) — Max state changes allowed within `in_mins`. (2-100)\n - mute_mins (integer) — Mute duration in minutes after flapping is detected. (0-1440)\n - group (object) — Alert grouping configuration.\n - all_equals_required (boolean) — When true, all listed keys must be present for grouping.\n - cases (array) — Per-filter grouping overrides.\n - equals (array) — Groups of label keys whose equality defines a bucket.\n - i_keys (array) — Label keys used for intelligent grouping embeddings.\n - i_score_threshold (number) — Intelligent grouping similarity threshold. (0.5-1)\n - method (string) (required) — Grouping method: `i` intelligent, `p` pattern, `n` none. [i, p, n]\n - storm_threshold (integer) — Alert storm threshold. (0-10000)\n - storm_thresholds (array) — Multi-level storm thresholds.\n - time_window (integer) — Grouping time window in minutes. Default max is 1440 minutes (24 h); extended accounts may allow up to 43200 minutes (30 days). (min 0)\n - window_type (string) — Window type. Defaults to `tumbling`. [tumbling, sliding]\n - is_external_report_enabled (boolean) — Whether external reporters can file incidents into this channel.\n - is_private (boolean) — When true, the channel is visible only to its managing teams.\n - is_starred (boolean) — Whether the current user has starred this channel.\n - last_incident_at (integer) — Timestamp of the most recent incident (unix seconds).\n - managing_team_ids (array) — Additional teams that can manage the channel.\n - progress_to_incident_cnts (object)\n - Processing (integer) (required) — Count of processing incidents in the last 30 days.\n - Triggered (integer) (required) — Count of triggered incidents in the last 30 days.\n - status (string) — Channel status. [enabled, disabled, deleted]\n - team_id (integer) — Owning team ID.\n - updated_at (integer) — Last update timestamp (unix seconds).\n", + "Channels.ChannelInfos": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - channel_id (integer) (required) — Channel ID.\n - channel_name (string) (required) — Channel name.\n - status (string) — Channel status. [enabled, disabled]\n", + "Channels.ChannelInhibitRuleCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID).\n - rule_name (string) (required) — Rule name echoed back from the request.\n", + "Channels.ChannelInhibitRuleList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required)\n - channel_id (integer) (required)\n - created_at (integer) (required)\n - deleted_at (integer)\n - description (string) (required)\n - equals (array) (required) — Label keys used to pair source and target alerts.\n - is_directly_discard (boolean) (required)\n - priority (integer) (required)\n - rule_id (string) (required)\n - rule_name (string) (required)\n - source_filters (object) (required)\n - status (string) (required) [enabled, disabled]\n - target_filters (object) (required)\n - updated_at (integer) (required)\n - updated_by (integer) (required)\n", + "Channels.ChannelList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Owning account ID.\n - active_incident_highest_severity (string) — Highest severity among active incidents in the channel.\n - auto_resolve_mode (string) — Auto-resolve timer reset mode. [trigger, update]\n - auto_resolve_timeout (integer) — Auto-resolve timeout in seconds. 0 disables auto-resolve.\n - channel_id (integer) — Channel ID.\n - channel_name (string) — Channel name.\n - created_at (integer) — Creation timestamp (unix seconds).\n - creator_id (integer) — Member ID who created the channel.\n - deleted_at (integer) — Deletion timestamp (unix seconds). Non-zero only for soft-deleted channels.\n - description (string) — Free-form description.\n - disable_auto_close (boolean) — When true, automatic incident closing is disabled.\n - disable_outlier_detection (boolean) — When true, outlier incident detection is disabled.\n - external_report_token (string) — Token granted to external reporters when external reporting is enabled.\n - flapping (object) — Flapping detection configuration.\n - in_mins (integer) — Observation window in minutes. (1-1440)\n - is_disabled (boolean) — Disable flapping detection.\n - max_changes (integer) — Max state changes allowed within `in_mins`. (2-100)\n - mute_mins (integer) — Mute duration in minutes after flapping is detected. (0-1440)\n - group (object) — Alert grouping configuration.\n - all_equals_required (boolean) — When true, all listed keys must be present for grouping.\n - cases (array) — Per-filter grouping overrides.\n - equals (array) — Groups of label keys whose equality defines a bucket.\n - i_keys (array) — Label keys used for intelligent grouping embeddings.\n - i_score_threshold (number) — Intelligent grouping similarity threshold. (0.5-1)\n - method (string) (required) — Grouping method: `i` intelligent, `p` pattern, `n` none. [i, p, n]\n - storm_threshold (integer) — Alert storm threshold. (0-10000)\n - storm_thresholds (array) — Multi-level storm thresholds.\n - time_window (integer) — Grouping time window in minutes. Default max is 1440 minutes (24 h); extended accounts may allow up to 43200 minutes (30 days). (min 0)\n - window_type (string) — Window type. Defaults to `tumbling`. [tumbling, sliding]\n - is_external_report_enabled (boolean) — Whether external reporters can file incidents into this channel.\n - is_private (boolean) — When true, the channel is visible only to its managing teams.\n - is_starred (boolean) — Whether the current user has starred this channel.\n - last_incident_at (integer) — Timestamp of the most recent incident (unix seconds).\n - managing_team_ids (array) — Additional teams that can manage the channel.\n - progress_to_incident_cnts (object)\n - Processing (integer) (required) — Count of processing incidents in the last 30 days.\n - Triggered (integer) (required) — Count of triggered incidents in the last 30 days.\n - status (string) — Channel status. [enabled, disabled, deleted]\n - team_id (integer) — Owning team ID.\n - updated_at (integer) — Last update timestamp (unix seconds).\n", + "Channels.ChannelSilenceRuleCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID).\n - rule_name (string) (required) — Rule name echoed back from the request.\n", + "Channels.ChannelSilenceRuleList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required)\n - channel_id (integer) (required)\n - created_at (integer) (required)\n - deleted_at (integer)\n - description (string) (required)\n - filters (object) (required)\n - from_incident_id (string) — Source incident ID when the silence was created from an incident.\n - is_auto_delete (boolean) — When true, the silence rule is automatically deleted after its time window expires. Defaults to false.\n - is_directly_discard (boolean) (required) — When true, silenced alerts are dropped instead of suppressed into incidents.\n - is_effective (boolean) (required) — Whether the rule is currently in effect.\n - priority (integer) (required) — Evaluation priority. Lower runs first.\n - rule_id (string) (required)\n - rule_name (string) (required)\n - status (string) (required) [enabled, disabled]\n - time_filter (object) (required) — One-off time window defined by unix seconds.\n - end_time (integer) (required) — Window end (unix seconds). Must be > 0.\n - start_time (integer) (required) — Window start (unix seconds). Must be > 0 and less than `end_time`.\n - time_filters (array) (required) — Recurring time windows.\n - cal_id (string) — Optional calendar ID; restricts the window to days matching the calendar.\n - end (string) — End of the window in `HH:MM`.\n - is_off (boolean) — When true, match days marked as days-off in the calendar.\n - repeat (array) — Days of the week this window repeats on. Empty means every day.\n - start (string) — Start of the window in `HH:MM`.\n - updated_at (integer) (required)\n - updated_by (integer) (required)\n", + "Channels.ChannelUnsubscribeRuleCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID).\n - rule_name (string) (required) — Rule name echoed back from the request.\n", + "Channels.ChannelUnsubscribeRuleList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required)\n - channel_id (integer) (required)\n - created_at (integer) (required)\n - deleted_at (integer)\n - description (string) (required)\n - filters (object) (required)\n - priority (integer) (required)\n - rule_id (string) (required)\n - rule_name (string) (required)\n - status (string) (required) [enabled, disabled]\n - updated_at (integer) (required)\n - updated_by (integer) (required)\n", + "Channels.ChannelUpdate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - external_report_token (string) — Newly generated token for external reporters. Only returned when `is_external_report_enabled` is set to `true` in the request. Callers should store this value; it cannot be retrieved afterwards.\n", + "Channels.RouteInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - cases (array) — Ordered list of case branches.\n - channel_ids (array) (required) — Target channel IDs. Required when `routing_mode` is `standard` (or empty).\n - fallthrough (boolean) (required) — If `true`, evaluation continues to the next case after this one matches; otherwise matching stops at the first hit.\n - if (array) (required) — List of match conditions that are AND-ed together.\n - key (string) (required) — Field key to match against the alert event (e.g. `alert_severity`, `labels.service`).\n - oper (string) (required) — Match operator. `IN` matches when the field value is one of `vals`; `NOTIN` matches when it is not. [IN, NOTIN]\n - vals (array) (required) — Values to compare against. Each value may be a literal string, a wildcard (`*`, `?`), a regular expression wrapped in slashes (`/pattern/`), a CIDR (`cidr:10.0.0.0/8`), or a numeric comparison (`num:lt:100`).\n - name_mapping_label (string) — Label key whose value is used as the target channel name. Required when `routing_mode` is `name_mapping`.\n - routing_mode (string) — Routing mode. `standard` (default, also used when left empty) routes to the fixed channel IDs; `name_mapping` resolves channels by reading a label value from the alert event. [standard, name_mapping]\n - created_at (integer) — Creation time, Unix timestamp in seconds.\n - creator_id (integer) (required) — ID of the person who created the rule.\n - default (object) — Default branch used when no case matches (or all matched cases yield no valid channels).\n - channel_ids (array) — Channel IDs to fall back to.\n - deleted_at (integer) — Soft-delete timestamp, Unix seconds. Omitted when the rule is active.\n - integration_id (integer) — Integration the rule belongs to.\n - sections (array) — Optional sections that visually group cases.\n - name (string) (required) — Section name. Must be unique within the rule.\n - position (integer) (required) — Index in `cases` where this section starts. Must be between 0 and the length of `cases`.\n - status (string) — Rule status. [enabled, deleted]\n - updated_at (integer) — Last update time, Unix timestamp in seconds.\n - updated_by (integer) (required) — ID of the person who performed the last update.\n - version (integer) (required) — Monotonic version number, incremented on each update. Use it for optimistic concurrency control.\n", + "Channels.RouteList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - cases (array) — Ordered list of case branches.\n - channel_ids (array) (required) — Target channel IDs. Required when `routing_mode` is `standard` (or empty).\n - fallthrough (boolean) (required) — If `true`, evaluation continues to the next case after this one matches; otherwise matching stops at the first hit.\n - if (array) (required) — List of match conditions that are AND-ed together.\n - key (string) (required) — Field key to match against the alert event (e.g. `alert_severity`, `labels.service`).\n - oper (string) (required) — Match operator. `IN` matches when the field value is one of `vals`; `NOTIN` matches when it is not. [IN, NOTIN]\n - vals (array) (required) — Values to compare against. Each value may be a literal string, a wildcard (`*`, `?`), a regular expression wrapped in slashes (`/pattern/`), a CIDR (`cidr:10.0.0.0/8`), or a numeric comparison (`num:lt:100`).\n - name_mapping_label (string) — Label key whose value is used as the target channel name. Required when `routing_mode` is `name_mapping`.\n - routing_mode (string) — Routing mode. `standard` (default, also used when left empty) routes to the fixed channel IDs; `name_mapping` resolves channels by reading a label value from the alert event. [standard, name_mapping]\n - created_at (integer) — Creation time, Unix timestamp in seconds.\n - creator_id (integer) (required) — ID of the person who created the rule.\n - default (object) — Default branch used when no case matches (or all matched cases yield no valid channels).\n - channel_ids (array) — Channel IDs to fall back to.\n - deleted_at (integer) — Soft-delete timestamp, Unix seconds. Omitted when the rule is active.\n - integration_id (integer) — Integration the rule belongs to.\n - sections (array) — Optional sections that visually group cases.\n - name (string) (required) — Section name. Must be unique within the rule.\n - position (integer) (required) — Index in `cases` where this section starts. Must be between 0 and the length of `cases`.\n - status (string) — Rule status. [enabled, deleted]\n - updated_at (integer) — Last update time, Unix timestamp in seconds.\n - updated_by (integer) (required) — ID of the person who performed the last update.\n - version (integer) (required) — Monotonic version number, incremented on each update. Use it for optimistic concurrency control.\n", + "DataSources.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Account ID.\n - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: `host:port`. For SLS: endpoint without http/https prefix.\n - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource.\n - enabled (boolean) (required) — Whether the datasource is active.\n - id (integer) (required) — Unique datasource ID.\n - name (string) (required) — Datasource display name.\n - note (string) (required) — Optional description.\n - payload (object) — Type-specific datasource configuration. Include only the block matching `type_ident`.\n - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig.\n - database (string) — Default database for authentication.\n - dial_timeout_mills (integer) — Dial timeout in milliseconds.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - max_execution_seconds (integer) — Max query execution time in seconds.\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_enabled (boolean)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - elasticsearch (object) — Elasticsearch datasource configuration.\n - api_key (string) — Elastic Cloud API key. Only for `cloud` deployment.\n - certificate_fingerprint (string)\n - cloud_id (string) — Elastic Cloud deployment ID. Only for `cloud` deployment.\n - deployment (string) — Deployment type. `cloud` uses Elastic Cloud; `self-managed` uses a self-hosted cluster. [cloud, self-managed]\n - headers (array)\n - password (string)\n - service_token (string) — Service token; overrides username/password if set.\n - timeout_mills (integer)\n - tls_ca (string)\n - username (string) — Username for `self-managed` deployment.\n - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig.\n - idle_conns (integer) — Maximum idle connections.\n - lifetime_seconds (integer) — Connection maximum lifetime in seconds.\n - open_conns (integer) — Maximum open connections.\n - password (string)\n - timeout_mills (integer) — Query timeout in milliseconds.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - oracle (object) — Oracle datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - options (object) — Extra connection options as key-value pairs.\n - password (string)\n - timeout_mills (integer)\n - username (string)\n - postgres (object) — PostgreSQL datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - username (string)\n - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean) — Enable HTTP Basic Auth.\n - basic_auth_password (string) — Basic auth password.\n - basic_auth_username (string) — Basic auth username.\n - headers (array) — Custom HTTP headers in `Key: Value` format.\n - params (array) — Custom query parameters in `key=value` format.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - sls (object) — Alibaba Cloud SLS datasource configuration.\n - access_key_id (string) — Alibaba Cloud Access Key ID.\n - access_key_secret (string) — Alibaba Cloud Access Key Secret.\n - headers (array) — Custom HTTP headers.\n - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - type_ident (string) (required) — Datasource type identifier. Allowed: `prometheus`, `loki`, `mysql`, `oracle`, `postgres`, `clickhouse`, `elasticsearch`, `sls`, `victorialogs`.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "DataSources.ReadList": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: `host:port`. For SLS: endpoint without http/https prefix.\n - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource.\n - enabled (boolean) (required) — Whether the datasource is active.\n - id (integer) (required) — Unique datasource ID.\n - name (string) (required) — Datasource display name.\n - note (string) (required) — Optional description.\n - payload (object) — Type-specific datasource configuration. Include only the block matching `type_ident`.\n - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig.\n - database (string) — Default database for authentication.\n - dial_timeout_mills (integer) — Dial timeout in milliseconds.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - max_execution_seconds (integer) — Max query execution time in seconds.\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_enabled (boolean)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - elasticsearch (object) — Elasticsearch datasource configuration.\n - api_key (string) — Elastic Cloud API key. Only for `cloud` deployment.\n - certificate_fingerprint (string)\n - cloud_id (string) — Elastic Cloud deployment ID. Only for `cloud` deployment.\n - deployment (string) — Deployment type. `cloud` uses Elastic Cloud; `self-managed` uses a self-hosted cluster. [cloud, self-managed]\n - headers (array)\n - password (string)\n - service_token (string) — Service token; overrides username/password if set.\n - timeout_mills (integer)\n - tls_ca (string)\n - username (string) — Username for `self-managed` deployment.\n - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig.\n - idle_conns (integer) — Maximum idle connections.\n - lifetime_seconds (integer) — Connection maximum lifetime in seconds.\n - open_conns (integer) — Maximum open connections.\n - password (string)\n - timeout_mills (integer) — Query timeout in milliseconds.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - oracle (object) — Oracle datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - options (object) — Extra connection options as key-value pairs.\n - password (string)\n - timeout_mills (integer)\n - username (string)\n - postgres (object) — PostgreSQL datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - username (string)\n - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean) — Enable HTTP Basic Auth.\n - basic_auth_password (string) — Basic auth password.\n - basic_auth_username (string) — Basic auth username.\n - headers (array) — Custom HTTP headers in `Key: Value` format.\n - params (array) — Custom query parameters in `key=value` format.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - sls (object) — Alibaba Cloud SLS datasource configuration.\n - access_key_id (string) — Alibaba Cloud Access Key ID.\n - access_key_secret (string) — Alibaba Cloud Access Key Secret.\n - headers (array) — Custom HTTP headers.\n - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - type_ident (string) (required) — Datasource type identifier. Allowed: `prometheus`, `loki`, `mysql`, `oracle`, `postgres`, `clickhouse`, `elasticsearch`, `sls`, `victorialogs`.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "DataSources.WriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Account ID.\n - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: `host:port`. For SLS: endpoint without http/https prefix.\n - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource.\n - enabled (boolean) (required) — Whether the datasource is active.\n - id (integer) (required) — Unique datasource ID.\n - name (string) (required) — Datasource display name.\n - note (string) (required) — Optional description.\n - payload (object) — Type-specific datasource configuration. Include only the block matching `type_ident`.\n - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig.\n - database (string) — Default database for authentication.\n - dial_timeout_mills (integer) — Dial timeout in milliseconds.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - max_execution_seconds (integer) — Max query execution time in seconds.\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_enabled (boolean)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - elasticsearch (object) — Elasticsearch datasource configuration.\n - api_key (string) — Elastic Cloud API key. Only for `cloud` deployment.\n - certificate_fingerprint (string)\n - cloud_id (string) — Elastic Cloud deployment ID. Only for `cloud` deployment.\n - deployment (string) — Deployment type. `cloud` uses Elastic Cloud; `self-managed` uses a self-hosted cluster. [cloud, self-managed]\n - headers (array)\n - password (string)\n - service_token (string) — Service token; overrides username/password if set.\n - timeout_mills (integer)\n - tls_ca (string)\n - username (string) — Username for `self-managed` deployment.\n - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig.\n - idle_conns (integer) — Maximum idle connections.\n - lifetime_seconds (integer) — Connection maximum lifetime in seconds.\n - open_conns (integer) — Maximum open connections.\n - password (string)\n - timeout_mills (integer) — Query timeout in milliseconds.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - oracle (object) — Oracle datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - options (object) — Extra connection options as key-value pairs.\n - password (string)\n - timeout_mills (integer)\n - username (string)\n - postgres (object) — PostgreSQL datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - username (string)\n - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean) — Enable HTTP Basic Auth.\n - basic_auth_password (string) — Basic auth password.\n - basic_auth_username (string) — Basic auth username.\n - headers (array) — Custom HTTP headers in `Key: Value` format.\n - params (array) — Custom query parameters in `key=value` format.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - sls (object) — Alibaba Cloud SLS datasource configuration.\n - access_key_id (string) — Alibaba Cloud Access Key ID.\n - access_key_secret (string) — Alibaba Cloud Access Key Secret.\n - headers (array) — Custom HTTP headers.\n - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - type_ident (string) (required) — Datasource type identifier. Allowed: `prometheus`, `loki`, `mysql`, `oracle`, `postgres`, `clickhouse`, `elasticsearch`, `sls`, `victorialogs`.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "DataSources.WriteUpdate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Account ID.\n - address (string) (required) — Connection address. For Prometheus/Loki/VictoriaLogs: HTTP URL. For MySQL/Oracle/Postgres/ClickHouse: `host:port`. For SLS: endpoint without http/https prefix.\n - edge_cluster_name (string) (required) — Monitors edge cluster name responsible for evaluating rules using this datasource.\n - enabled (boolean) (required) — Whether the datasource is active.\n - id (integer) (required) — Unique datasource ID.\n - name (string) (required) — Datasource display name.\n - note (string) (required) — Optional description.\n - payload (object) — Type-specific datasource configuration. Include only the block matching `type_ident`.\n - clickhouse (object) — ClickHouse datasource configuration. TLS fields are inherited from TLSClientConfig.\n - database (string) — Default database for authentication.\n - dial_timeout_mills (integer) — Dial timeout in milliseconds.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - max_execution_seconds (integer) — Max query execution time in seconds.\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_enabled (boolean)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - elasticsearch (object) — Elasticsearch datasource configuration.\n - api_key (string) — Elastic Cloud API key. Only for `cloud` deployment.\n - certificate_fingerprint (string)\n - cloud_id (string) — Elastic Cloud deployment ID. Only for `cloud` deployment.\n - deployment (string) — Deployment type. `cloud` uses Elastic Cloud; `self-managed` uses a self-hosted cluster. [cloud, self-managed]\n - headers (array)\n - password (string)\n - service_token (string) — Service token; overrides username/password if set.\n - timeout_mills (integer)\n - tls_ca (string)\n - username (string) — Username for `self-managed` deployment.\n - loki (object) — Loki datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - mysql (object) — MySQL datasource configuration. TLS fields are inherited from TLSClientConfig.\n - idle_conns (integer) — Maximum idle connections.\n - lifetime_seconds (integer) — Connection maximum lifetime in seconds.\n - open_conns (integer) — Maximum open connections.\n - password (string)\n - timeout_mills (integer) — Query timeout in milliseconds.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - username (string)\n - oracle (object) — Oracle datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - options (object) — Extra connection options as key-value pairs.\n - password (string)\n - timeout_mills (integer)\n - username (string)\n - postgres (object) — PostgreSQL datasource configuration.\n - idle_conns (integer)\n - lifetime_seconds (integer)\n - open_conns (integer)\n - password (string)\n - timeout_mills (integer)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - username (string)\n - prometheus (object) — Prometheus datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean) — Enable HTTP Basic Auth.\n - basic_auth_password (string) — Basic auth password.\n - basic_auth_username (string) — Basic auth username.\n - headers (array) — Custom HTTP headers in `Key: Value` format.\n - params (array) — Custom query parameters in `key=value` format.\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - sls (object) — Alibaba Cloud SLS datasource configuration.\n - access_key_id (string) — Alibaba Cloud Access Key ID.\n - access_key_secret (string) — Alibaba Cloud Access Key Secret.\n - headers (array) — Custom HTTP headers.\n - victorialogs (object) — VictoriaLogs datasource configuration. TLS fields are inherited from TLSClientConfig.\n - basic_auth_enabled (boolean)\n - basic_auth_password (string)\n - basic_auth_username (string)\n - headers (array)\n - params (array)\n - tls_ca (string)\n - tls_cert (string)\n - tls_key (string)\n - tls_key_pwd (string)\n - tls_max_version (string)\n - tls_min_version (string)\n - tls_server_name (string)\n - tls_skip_verify (boolean)\n - type_ident (string) (required) — Datasource type identifier. Allowed: `prometheus`, `loki`, `mysql`, `oracle`, `postgres`, `clickhouse`, `elasticsearch`, `sls`, `victorialogs`.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "Diagnostics.QueryDiagnose": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - ds_name (string)\n - ds_type (string)\n - operation (string) [log_patterns, metric_trends]\n - query (string) — Query string echoed back from the request.\n - results (array) — One entry per `methods[]` in the request, in the same order.\n - baseline (string) — Only present for compare-style methods.\n - baseline_window (object) — Only present for compare-style methods.\n - end (integer)\n - start (integer)\n - method (string) — `pattern_snapshot` / `pattern_compare` for `log_patterns`; `single_window_shape` / `window_compare` for `metric_trends`.\n - patterns (array) — `log_patterns` only. Sorted RCA-first; each item carries pattern_hash, template, count, severity, sources, examples, and (for compare) baseline_count / change_ratio / is_new / is_gone.\n - series (array) — `metric_trends` only. Notable series with current / baseline / change / notable_period.\n - summary (object) — Aggregate summary for this method. Shape differs between `log_patterns` (logs_scanned, patterns_total, surging_threshold, …) and `metric_trends` (series_total, data_quality, observations, …).\n - warnings (array) — Per-method advisory messages (e.g. `examples redacted`, sampling notices).\n - window (object)\n - end (integer)\n - start (integer)\n - window (object)\n - end (integer)\n - start (integer)\n", + "Diagnostics.QueryRows": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - fields (object) — String-valued fields (labels, log fields, SQL columns).\n - values (object) — Numeric fields. For metric queries the canonical key is `__value__`. May be `null` for detail-oriented sources.\n", + "Diagnostics.TargetsList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - agent_version (string) — Most recently observed Agent version.\n - cluster_name (string) — Edge cluster name.\n - edge_ipport (string) — Edge instance address (`ip:port`), surfaced for diagnostics.\n - target_kind (string) — Target kind, e.g. `host`, `mysql`. Filtering by kind is not supported in v1.\n - target_locator (string) — Target identifier; the list is sorted by this field ascending.\n - updated_at (integer) — Last route-projection upsert time, Unix seconds. Treat as 'most recently observed', not a live-online indicator.\n", + "Diagnostics.ToolsCatalog": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - error (object) — Business error. `null` on success.\n - code (string) [target_unavailable, unknown_toolset_hash, ambiguous_target_kind]\n - message (string)\n - target_kinds (array) — Returned for `ambiguous_target_kind`; lists the candidate kinds.\n - target (object) — Resolved target. `null` when locator could not be uniquely resolved.\n - kind (string)\n - locator (string)\n - tools (array) — Tool catalog entries. Empty when `error` is non-null.\n - description (string) — Tool capability description for UI / AI-SRE consumption.\n - input_schema (object) — JSON Schema for `tools[].params`.\n - name (string) — Tool name; pass into `/monit/tools/invoke` as `tools[].tool`.\n - output_shape (object) — Optional output JSON Schema; only returned when `include_output_shape=true`.\n - target_kind (string) — Target kind this tool applies to.\n", + "Diagnostics.ToolsInvoke": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - error (object) — Request-level business error. `null` on success.\n - code (string) [target_unavailable, unknown_toolset_hash, forward_failed, invalid_tool_result, ambiguous_target_kind]\n - message (string)\n - target_kinds (array)\n - results (array) — Per-tool results aligned with the request `tools[]` order. Empty when `error` is non-null.\n - agent_elapsed_ms (integer) — Agent-self-reported tool execution time in milliseconds, excludes network. May be 0 when the failure occurred before the agent started executing.\n - data (object) — Successful tool payload — passthrough of monit-agent `ToolResultPayload.data` (typically `data` / `summary` / `truncated`). `null` when the per-tool `error` is set.\n - e2e_elapsed_ms (integer) — Webapi-observed end-to-end time in milliseconds (webapi → ws → edge → agent → ws → webapi). A large gap vs `agent_elapsed_ms` indicates network / edge slowness.\n - error (object) — Per-tool error. Mutually exclusive with `data`.\n - code (string) — Common values: `timeout`, `target_unavailable`, `edge_unsupported`, `invalid_tool_result`, `internal`, `invalid_args`, `unknown_tool`, `unknown_tool_version`, `unknown_toolset_hash`, `target_not_owned`, `wrong_agent`, `overloaded`, `denied`, `permission_denied`, `credential_unavailable`, `target_unreachable`.\n - message (string)\n - tool (string)\n - tool_version (string) — Agent-executed tool version. Empty when execution failed before the agent picked a version.\n - target (object) — Resolved target.\n - kind (string)\n - locator (string)\n", + "ImIntegrations.List": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) — Account this integration belongs to.\n - category (string) — Category of the integration plugin.\n - created_at (integer) — Unix timestamp in seconds when the integration was created.\n - creator_id (integer) — Person who created the integration.\n - data_source_id (integer) — Integration ID.\n - description (string) — Integration description.\n - exclusive_data_source_id (integer) — Exclusive integration ID associated with this integration.\n - integration_id (integer) — Integration ID, alias of data_source_id.\n - integration_key (string) — Push key used by alert sources to send to this integration.\n - last_time (integer) — Unix timestamp in seconds of the most recent activity on the integration.\n - name (string) — Integration name.\n - no_editable (boolean) — Whether the integration is read-only.\n - plugin_id (integer) — Plugin ID backing this integration.\n - plugin_type (string) — Type identifier of the integration plugin.\n - plugin_type_name (string) — Localized display name of the integration plugin type.\n - ref_id (string) — External reference ID of the integration.\n - settings (object) — Plugin-specific configuration of the integration.\n - status (string) — Current status of the integration.\n - team_id (integer) — Team that owns this integration.\n - updated_at (integer) — Unix timestamp in seconds when the integration was last updated.\n - updated_by (integer) — Person who last updated the integration.\n", + "Incidents.AlertList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - alert_id (string) (required) — Alert ID (MongoDB ObjectID).\n - alert_key (string) (required) — Deduplication key used to merge events into the alert.\n - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) (required) — Channel ID.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - data_source_id (integer) (required) — Deprecated. Use `integration_id` instead.\n - data_source_name (string) (required) — Deprecated. Use `integration_name`.\n - data_source_ref_id (string) (required) — Deprecated. Use `integration_ref_id`.\n - data_source_type (string) — Deprecated. Use `integration_type`.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Alert description.\n - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active.\n - event_cnt (integer) (required) — Total number of raw events merged into this alert.\n - events (array) — Raw alert events, populated when the caller opts in.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) (required) — Whether this alert has ever been silenced.\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) (required) — Integration ID that produced the alert.\n - integration_name (string) (required) — Integration display name.\n - integration_ref_id (string) (required) — Integration reference ID.\n - integration_type (string) (required) — Integration type string.\n - labels (object) (required) — Alert labels.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event.\n - responder_email (string) (required) — Primary responder email, if any.\n - responder_name (string) (required) — Primary responder name, if any.\n - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired.\n - title (string) (required) — Alert title.\n - title_rule (string) (required) — Title rendering rule.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n", + "Incidents.Create": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - incident_id (string) (required) — Newly created incident ID (MongoDB ObjectID).\n - title (string) (required) — Echoes the incident title from the request.\n", + "Incidents.CustomActionDo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - message (string) — Error message if the action's HTTP call failed; omitted on success.\n", + "Incidents.Feed": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - created_at (integer) (required) — Creation timestamp in milliseconds.\n - creator_id (integer) (required) — User ID of the actor. `0` means system-generated.\n - deleted_at (integer) — Soft-delete timestamp (ms). Zero if not deleted.\n - detail (any) (required) — Type-specific payload. The concrete shape is determined by `type`.\n - ref_id (string) (required) — ObjectID of the source alert or incident this entry references.\n - type (string) (required) — Incident timeline entry type. Each value identifies one lifecycle event; the matching `detail` payload shape is determined by this field. Incident types are prefixed with `i_`. | Type | Meaning | |---|---| | `i_new` | Incident Created: A new incident was created automatically or manually. | | `i_assign` | Assigned: Incident was assigned to responders. | | `i_a_rspd` | Responder Added: Additional responders joined the incident. | | `i_notify` | Notification dispatched through a channel at a specific escalation level. | | `i_storm` | Alert storm threshold reached on the incident. | | `i_snooze` | Notifications snoozed for a given duration. | | `i_wake` | Snooze cancelled and notifications resumed. | | `i_ack` | Acknowledged: Responder confirmed they are working on the incident. | | `i_unack` | Acknowledgement removed. | | `i_comm` | Comment: Responder logged progress or key information. | | `i_rslv` | Resolved: Incident was marked as resolved. | | `i_reopen` | Reopened: Resolved incident was reopened, possibly due to recurrence. | | `i_merge` | Merged: Multiple related incidents were merged into one. | | `i_r_title` | Title updated. | | `i_r_desc` | Description updated. | | `i_r_impact` | Impact updated. | | `i_r_rc` | Root cause updated. | | `i_r_rsltn` | Resolution updated. | | `i_r_severity` | Severity Changed: Incident severity level was adjusted. | | `i_r_field` | Custom field value updated. | | `i_m_flapping` | Incident muted by flapping detection. | | `i_m_reply` | Mute reply marker on a comment. | | `i_custom` | Action: Automated action or script was triggered. | | `i_wr_create` | War Room Created: Chat group was created for collaborative response. | | `i_wr_delete` | War room chat group deleted. | | `i_auto_refresh` | Card auto-refresh event posted back to the timeline. | | `a_merge` | Alert Merged: An alert was merged into an existing incident. | [i_new, i_assign, i_a_rspd, i_notify, i_storm, i_snooze, i_wake, i_ack, i_unack, i_comm, i_rslv, i_reopen, i_merge, i_r_title, i_r_desc, i_r_impact, i_r_rc, i_r_rsltn, i_r_severity, i_r_field, i_m_flapping, i_m_reply, i_custom, i_wr_create, i_wr_delete, i_auto_refresh, a_merge]\n - updated_at (integer) (required) — Last update timestamp in milliseconds.\n", + "Incidents.Info": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Account ID that owns the incident.\n - account_locale (string) (required) — Account locale.\n - account_name (string) (required) — Account name.\n - account_time_zone (string) (required) — Account time zone.\n - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged.\n - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state.\n - ai_summary (string) (required) — AI-generated summary of the incident.\n - alert_cnt (integer) (required) — Total count of alerts merged into this incident.\n - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts.\n - alerts (array) — Embedded alerts, only populated for notification templates and custom actions.\n - account_id (integer) (required) — Account ID.\n - alert_id (string) (required) — Alert ID (MongoDB ObjectID).\n - alert_key (string) (required) — Deduplication key used to merge events into the alert.\n - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) (required) — Channel ID.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - data_source_id (integer) (required) — Deprecated. Use `integration_id` instead.\n - data_source_name (string) (required) — Deprecated. Use `integration_name`.\n - data_source_ref_id (string) (required) — Deprecated. Use `integration_ref_id`.\n - data_source_type (string) — Deprecated. Use `integration_type`.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Alert description.\n - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active.\n - event_cnt (integer) (required) — Total number of raw events merged into this alert.\n - events (array) — Raw alert events, populated when the caller opts in.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - alt (string) — Alt text.\n - href (string) — Optional link URL when the image is clicked.\n - src (string) (required) — Image source URL or internal image reference (starts with `img_` or `http`).\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) (required) — Whether this alert has ever been silenced.\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) (required) — Integration ID that produced the alert.\n - integration_name (string) (required) — Integration display name.\n - integration_ref_id (string) (required) — Integration reference ID.\n - integration_type (string) (required) — Integration type string.\n - labels (object) (required) — Alert labels.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event.\n - responder_email (string) (required) — Primary responder email, if any.\n - responder_name (string) (required) — Primary responder name, if any.\n - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired.\n - title (string) (required) — Alert title.\n - title_rule (string) (required) — Title rendering rule.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n - assigned_to (object) (required) — Incident assignment target. Either `person_ids` or `escalate_rule_id` must be provided.\n - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made.\n - emails (array) — Email recipients, used by integrations such as ServiceNow.\n - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment.\n - escalate_rule_name (string) — Escalation rule display name, filled by the server.\n - id (string) — Opaque assignment ID generated by the server.\n - layer_idx (integer) — Current level index within the escalation rule.\n - person_ids (array) — Member IDs to assign directly.\n - type (string) — Assignment type: `assign` direct assignment, `reassign` reassignment, `escalate` escalation-rule driven, `reopen` automatic reassignment on reopen. [assign, reassign, escalate, reopen]\n - channel_id (integer) (required) — Channel ID. 0 for standalone incidents.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open.\n - closer (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - creator (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - data_source_ids (array) — Deprecated. Use `integration_ids` instead.\n - data_source_type (string) — Deprecated. Use `integration_type` instead.\n - data_source_types (array) — Deprecated. Use `integration_types` instead.\n - dedup_key (string) (required) — Deduplication key used to coalesce alerts.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Incident description.\n - detail_url (string) (required) — Web console URL for the incident.\n - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active.\n - equals_md5 (string) (required) — MD5 hash used for content-equality checks.\n - ever_muted (boolean) (required) — Whether the incident has ever been silenced.\n - fields (object) (required) — Custom field values keyed by field name.\n - frequency (string) — Frequency bucket for recurrence analysis: `frequent` or `rare`. [frequent, rare]\n - group_method (string) (required) — Alert grouping method: `i` intelligent, `p` pattern, `n` none. [i, p, n]\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - impact (string) (required) — Impact description.\n - incident_id (string) (required) — Incident ID (MongoDB ObjectID).\n - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok]\n - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok]\n - integration_id (integer) (required) — First integration associated with the incident.\n - integration_ids (array) (required) — All integration IDs contributing alerts to this incident.\n - integration_type (string) — First alert's integration type string, used by the detail page for label mappings.\n - integration_types (array) (required) — Integration type strings for all contributing integrations.\n - labels (object) (required) — Labels propagated from alerts.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update.\n - links (array) — Channel-level link integrations rendered for this incident.\n - endpoint (string) (required) — Rendered URL for the link.\n - name (string) (required) — Display name of the link.\n - open_type (string) (required) — How the link should be opened. [popup, tab]\n - manual_overrides (array) (required) — Fields that were manually overridden after auto-population.\n - num (string) (required) — Short display identifier; not guaranteed unique.\n - owner (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - owner_id (integer) (required) — Primary owner member ID. 0 if none.\n - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem.\n - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed]\n - reporter_email (string) — Reporter email for manually created incidents.\n - resolution (string) (required) — Resolution notes.\n - responders (array) (required) — Current responders with assignment/acknowledgement state.\n - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged.\n - as (string) — Role label of this responder.\n - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned.\n - email (string) — Member email, filled by the server.\n - person_id (integer) (required) — Responder member ID.\n - person_name (string) — Member display name, filled by the server.\n - root_cause (string) (required) — Root cause analysis.\n - silence_url (string) (required) — Quick-silence URL for this incident.\n - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed.\n - start_time (integer) (required) — Unix timestamp (seconds) when the incident started.\n - title (string) (required) — Incident title.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n", + "Incidents.List": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID that owns the incident.\n - account_locale (string) (required) — Account locale.\n - account_name (string) (required) — Account name.\n - account_time_zone (string) (required) — Account time zone.\n - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged.\n - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state.\n - ai_summary (string) (required) — AI-generated summary of the incident.\n - alert_cnt (integer) (required) — Total count of alerts merged into this incident.\n - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts.\n - alerts (array) — Embedded alerts, only populated for notification templates and custom actions.\n - account_id (integer) (required) — Account ID.\n - alert_id (string) (required) — Alert ID (MongoDB ObjectID).\n - alert_key (string) (required) — Deduplication key used to merge events into the alert.\n - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) (required) — Channel ID.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - data_source_id (integer) (required) — Deprecated. Use `integration_id` instead.\n - data_source_name (string) (required) — Deprecated. Use `integration_name`.\n - data_source_ref_id (string) (required) — Deprecated. Use `integration_ref_id`.\n - data_source_type (string) — Deprecated. Use `integration_type`.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Alert description.\n - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active.\n - event_cnt (integer) (required) — Total number of raw events merged into this alert.\n - events (array) — Raw alert events, populated when the caller opts in.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) (required) — Whether this alert has ever been silenced.\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) (required) — Integration ID that produced the alert.\n - integration_name (string) (required) — Integration display name.\n - integration_ref_id (string) (required) — Integration reference ID.\n - integration_type (string) (required) — Integration type string.\n - labels (object) (required) — Alert labels.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event.\n - responder_email (string) (required) — Primary responder email, if any.\n - responder_name (string) (required) — Primary responder name, if any.\n - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired.\n - title (string) (required) — Alert title.\n - title_rule (string) (required) — Title rendering rule.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n - assigned_to (object) (required) — Incident assignment target. Either `person_ids` or `escalate_rule_id` must be provided.\n - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made.\n - emails (array) — Email recipients, used by integrations such as ServiceNow.\n - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment.\n - escalate_rule_name (string) — Escalation rule display name, filled by the server.\n - id (string) — Opaque assignment ID generated by the server.\n - layer_idx (integer) — Current level index within the escalation rule.\n - person_ids (array) — Member IDs to assign directly.\n - type (string) — Assignment type: `assign` direct assignment, `reassign` reassignment, `escalate` escalation-rule driven, `reopen` automatic reassignment on reopen. [assign, reassign, escalate, reopen]\n - channel_id (integer) (required) — Channel ID. 0 for standalone incidents.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open.\n - closer (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - creator (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - data_source_ids (array) — Deprecated. Use `integration_ids` instead.\n - data_source_type (string) — Deprecated. Use `integration_type` instead.\n - data_source_types (array) — Deprecated. Use `integration_types` instead.\n - dedup_key (string) (required) — Deduplication key used to coalesce alerts.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Incident description.\n - detail_url (string) (required) — Web console URL for the incident.\n - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active.\n - equals_md5 (string) (required) — MD5 hash used for content-equality checks.\n - ever_muted (boolean) (required) — Whether the incident has ever been silenced.\n - fields (object) (required) — Custom field values keyed by field name.\n - frequency (string) — Frequency bucket for recurrence analysis: `frequent` or `rare`. [frequent, rare]\n - group_method (string) (required) — Alert grouping method: `i` intelligent, `p` pattern, `n` none. [i, p, n]\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - impact (string) (required) — Impact description.\n - incident_id (string) (required) — Incident ID (MongoDB ObjectID).\n - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok]\n - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok]\n - integration_id (integer) (required) — First integration associated with the incident.\n - integration_ids (array) (required) — All integration IDs contributing alerts to this incident.\n - integration_type (string) — First alert's integration type string, used by the detail page for label mappings.\n - integration_types (array) (required) — Integration type strings for all contributing integrations.\n - labels (object) (required) — Labels propagated from alerts.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update.\n - links (array) — Channel-level link integrations rendered for this incident.\n - endpoint (string) (required) — Rendered URL for the link.\n - name (string) (required) — Display name of the link.\n - open_type (string) (required) — How the link should be opened. [popup, tab]\n - manual_overrides (array) (required) — Fields that were manually overridden after auto-population.\n - num (string) (required) — Short display identifier; not guaranteed unique.\n - owner (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - owner_id (integer) (required) — Primary owner member ID. 0 if none.\n - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem.\n - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed]\n - reporter_email (string) — Reporter email for manually created incidents.\n - resolution (string) (required) — Resolution notes.\n - responders (array) (required) — Current responders with assignment/acknowledgement state.\n - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged.\n - as (string) — Role label of this responder.\n - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned.\n - email (string) — Member email, filled by the server.\n - person_id (integer) (required) — Responder member ID.\n - person_name (string) — Member display name, filled by the server.\n - root_cause (string) (required) — Root cause analysis.\n - silence_url (string) (required) — Quick-silence URL for this incident.\n - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed.\n - start_time (integer) (required) — Unix timestamp (seconds) when the incident started.\n - title (string) (required) — Incident title.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n", + "Incidents.ListByIDs": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID that owns the incident.\n - account_locale (string) (required) — Account locale.\n - account_name (string) (required) — Account name.\n - account_time_zone (string) (required) — Account time zone.\n - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged.\n - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state.\n - ai_summary (string) (required) — AI-generated summary of the incident.\n - alert_cnt (integer) (required) — Total count of alerts merged into this incident.\n - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts.\n - alerts (array) — Embedded alerts, only populated for notification templates and custom actions.\n - account_id (integer) (required) — Account ID.\n - alert_id (string) (required) — Alert ID (MongoDB ObjectID).\n - alert_key (string) (required) — Deduplication key used to merge events into the alert.\n - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) (required) — Channel ID.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - data_source_id (integer) (required) — Deprecated. Use `integration_id` instead.\n - data_source_name (string) (required) — Deprecated. Use `integration_name`.\n - data_source_ref_id (string) (required) — Deprecated. Use `integration_ref_id`.\n - data_source_type (string) — Deprecated. Use `integration_type`.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Alert description.\n - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active.\n - event_cnt (integer) (required) — Total number of raw events merged into this alert.\n - events (array) — Raw alert events, populated when the caller opts in.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) (required) — Whether this alert has ever been silenced.\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) (required) — Integration ID that produced the alert.\n - integration_name (string) (required) — Integration display name.\n - integration_ref_id (string) (required) — Integration reference ID.\n - integration_type (string) (required) — Integration type string.\n - labels (object) (required) — Alert labels.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event.\n - responder_email (string) (required) — Primary responder email, if any.\n - responder_name (string) (required) — Primary responder name, if any.\n - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired.\n - title (string) (required) — Alert title.\n - title_rule (string) (required) — Title rendering rule.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n - assigned_to (object) (required) — Incident assignment target. Either `person_ids` or `escalate_rule_id` must be provided.\n - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made.\n - emails (array) — Email recipients, used by integrations such as ServiceNow.\n - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment.\n - escalate_rule_name (string) — Escalation rule display name, filled by the server.\n - id (string) — Opaque assignment ID generated by the server.\n - layer_idx (integer) — Current level index within the escalation rule.\n - person_ids (array) — Member IDs to assign directly.\n - type (string) — Assignment type: `assign` direct assignment, `reassign` reassignment, `escalate` escalation-rule driven, `reopen` automatic reassignment on reopen. [assign, reassign, escalate, reopen]\n - channel_id (integer) (required) — Channel ID. 0 for standalone incidents.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open.\n - closer (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - creator (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - data_source_ids (array) — Deprecated. Use `integration_ids` instead.\n - data_source_type (string) — Deprecated. Use `integration_type` instead.\n - data_source_types (array) — Deprecated. Use `integration_types` instead.\n - dedup_key (string) (required) — Deduplication key used to coalesce alerts.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Incident description.\n - detail_url (string) (required) — Web console URL for the incident.\n - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active.\n - equals_md5 (string) (required) — MD5 hash used for content-equality checks.\n - ever_muted (boolean) (required) — Whether the incident has ever been silenced.\n - fields (object) (required) — Custom field values keyed by field name.\n - frequency (string) — Frequency bucket for recurrence analysis: `frequent` or `rare`. [frequent, rare]\n - group_method (string) (required) — Alert grouping method: `i` intelligent, `p` pattern, `n` none. [i, p, n]\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - impact (string) (required) — Impact description.\n - incident_id (string) (required) — Incident ID (MongoDB ObjectID).\n - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok]\n - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok]\n - integration_id (integer) (required) — First integration associated with the incident.\n - integration_ids (array) (required) — All integration IDs contributing alerts to this incident.\n - integration_type (string) — First alert's integration type string, used by the detail page for label mappings.\n - integration_types (array) (required) — Integration type strings for all contributing integrations.\n - labels (object) (required) — Labels propagated from alerts.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update.\n - links (array) — Channel-level link integrations rendered for this incident.\n - endpoint (string) (required) — Rendered URL for the link.\n - name (string) (required) — Display name of the link.\n - open_type (string) (required) — How the link should be opened. [popup, tab]\n - manual_overrides (array) (required) — Fields that were manually overridden after auto-population.\n - num (string) (required) — Short display identifier; not guaranteed unique.\n - owner (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - owner_id (integer) (required) — Primary owner member ID. 0 if none.\n - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem.\n - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed]\n - reporter_email (string) — Reporter email for manually created incidents.\n - resolution (string) (required) — Resolution notes.\n - responders (array) (required) — Current responders with assignment/acknowledgement state.\n - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged.\n - as (string) — Role label of this responder.\n - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned.\n - email (string) — Member email, filled by the server.\n - person_id (integer) (required) — Responder member ID.\n - person_name (string) — Member display name, filled by the server.\n - root_cause (string) (required) — Root cause analysis.\n - silence_url (string) (required) — Quick-silence URL for this incident.\n - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed.\n - start_time (integer) (required) — Unix timestamp (seconds) when the incident started.\n - title (string) (required) — Incident title.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n", + "Incidents.PastList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID that owns the incident.\n - account_locale (string) (required) — Account locale.\n - account_name (string) (required) — Account name.\n - account_time_zone (string) (required) — Account time zone.\n - ack_time (integer) (required) — Unix timestamp (seconds) when the incident was first acknowledged. 0 if unacknowledged.\n - active_alert_cnt (integer) (required) — Count of alerts currently in Critical/Warning/Info state.\n - ai_summary (string) (required) — AI-generated summary of the incident.\n - alert_cnt (integer) (required) — Total count of alerts merged into this incident.\n - alert_event_cnt (integer) (required) — Total raw alert event count across all merged alerts.\n - alerts (array) — Embedded alerts, only populated for notification templates and custom actions.\n - account_id (integer) (required) — Account ID.\n - alert_id (string) (required) — Alert ID (MongoDB ObjectID).\n - alert_key (string) (required) — Deduplication key used to merge events into the alert.\n - alert_severity (string) (required) — Current severity. [Critical, Warning, Info, Ok]\n - alert_status (string) (required) — Current status. [Critical, Warning, Info, Ok]\n - channel_id (integer) (required) — Channel ID.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - data_source_id (integer) (required) — Deprecated. Use `integration_id` instead.\n - data_source_name (string) (required) — Deprecated. Use `integration_name`.\n - data_source_ref_id (string) (required) — Deprecated. Use `integration_ref_id`.\n - data_source_type (string) — Deprecated. Use `integration_type`.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Alert description.\n - end_time (integer) (required) — Unix timestamp (seconds) when the alert recovered. 0 if still active.\n - event_cnt (integer) (required) — Total number of raw events merged into this alert.\n - events (array) — Raw alert events, populated when the caller opts in.\n - account_id (integer) — Account ID.\n - alert_id (string) — Parent alert ID (MongoDB ObjectID).\n - alert_key (string) — Deduplication key used to merge events into an alert.\n - channel_id (integer) — Channel ID the event is routed to.\n - created_at (integer) — Record creation time, Unix epoch seconds.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) — Event description.\n - event_id (string) — Event ID (MongoDB ObjectID).\n - event_severity (string) — Severity of this event. [Critical, Warning, Info, Ok]\n - event_status (string) — Status of this event. [Critical, Warning, Info, Ok]\n - event_time (integer) — Event timestamp, Unix epoch seconds.\n - images (array) — Images attached to the event.\n - integration_id (integer) — Integration that produced this event.\n - integration_type (string) — Type/plugin key of the integration that produced this event.\n - labels (object) — Label key-value pairs.\n - title (string) — Event title.\n - title_rule (string) — Title template used to derive `title` from labels.\n - updated_at (integer) — Record update time, Unix epoch seconds.\n - ever_muted (boolean) (required) — Whether this alert has ever been silenced.\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - incident (object) — Brief incident reference embedded in an alert.\n - incident_id (string) — Incident ID (ObjectID hex string).\n - progress (string) — Incident progress — one of `Triggered`, `Processing`, `Closed`.\n - title (string) — Incident title.\n - integration_id (integer) (required) — Integration ID that produced the alert.\n - integration_name (string) (required) — Integration display name.\n - integration_ref_id (string) (required) — Integration reference ID.\n - integration_type (string) (required) — Integration type string.\n - labels (object) (required) — Alert labels.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent event.\n - responder_email (string) (required) — Primary responder email, if any.\n - responder_name (string) (required) — Primary responder name, if any.\n - start_time (integer) (required) — Unix timestamp (seconds) when the alert first fired.\n - title (string) (required) — Alert title.\n - title_rule (string) (required) — Title rendering rule.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n - assigned_to (object) (required) — Incident assignment target. Either `person_ids` or `escalate_rule_id` must be provided.\n - assigned_at (integer) — Unix timestamp (seconds) when the assignment was made.\n - emails (array) — Email recipients, used by integrations such as ServiceNow.\n - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID) to drive assignment.\n - escalate_rule_name (string) — Escalation rule display name, filled by the server.\n - id (string) — Opaque assignment ID generated by the server.\n - layer_idx (integer) — Current level index within the escalation rule.\n - person_ids (array) — Member IDs to assign directly.\n - type (string) — Assignment type: `assign` direct assignment, `reassign` reassignment, `escalate` escalation-rule driven, `reopen` automatic reassignment on reopen. [assign, reassign, escalate, reopen]\n - channel_id (integer) (required) — Channel ID. 0 for standalone incidents.\n - channel_name (string) (required) — Channel display name.\n - channel_status (string) (required) — Channel status.\n - close_time (integer) (required) — Unix timestamp (seconds) when the incident was closed. 0 if still open.\n - closer (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - closer_id (integer) (required) — Member ID that closed the incident. 0 if auto-closed.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - creator (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - creator_id (integer) (required) — Member ID that created the incident. 0 if auto-created by the system.\n - data_source_id (integer) — Deprecated. Use `integration_id` instead.\n - data_source_ids (array) — Deprecated. Use `integration_ids` instead.\n - data_source_type (string) — Deprecated. Use `integration_type` instead.\n - data_source_types (array) — Deprecated. Use `integration_types` instead.\n - dedup_key (string) (required) — Deduplication key used to coalesce alerts.\n - deleted_at (integer) — Soft-delete timestamp (seconds). Zero if not deleted.\n - description (string) (required) — Incident description.\n - detail_url (string) (required) — Web console URL for the incident.\n - end_time (integer) (required) — Unix timestamp (seconds) when the incident ended. 0 if still active.\n - equals_md5 (string) (required) — MD5 hash used for content-equality checks.\n - ever_muted (boolean) (required) — Whether the incident has ever been silenced.\n - fields (object) (required) — Custom field values keyed by field name.\n - frequency (string) — Frequency bucket for recurrence analysis: `frequent` or `rare`. [frequent, rare]\n - group_method (string) (required) — Alert grouping method: `i` intelligent, `p` pattern, `n` none. [i, p, n]\n - images (array) (required) — Attached images.\n - alt (string) — Alt text.\n - href (string) — Optional link the image points to.\n - src (string) (required) — Image source. Either an `img_` upload token or an `http(s)` URL.\n - impact (string) (required) — Impact description.\n - incident_id (string) (required) — Incident ID (MongoDB ObjectID).\n - incident_severity (string) (required) — Configured incident severity. [Critical, Warning, Info, Ok]\n - incident_status (string) (required) — Current incident status, derived from alert statuses. [Critical, Warning, Info, Ok]\n - integration_id (integer) (required) — First integration associated with the incident.\n - integration_ids (array) (required) — All integration IDs contributing alerts to this incident.\n - integration_type (string) — First alert's integration type string, used by the detail page for label mappings.\n - integration_types (array) (required) — Integration type strings for all contributing integrations.\n - labels (object) (required) — Labels propagated from alerts.\n - last_time (integer) (required) — Unix timestamp (seconds) of the most recent update.\n - links (array) — Channel-level link integrations rendered for this incident.\n - endpoint (string) (required) — Rendered URL for the link.\n - name (string) (required) — Display name of the link.\n - open_type (string) (required) — How the link should be opened. [popup, tab]\n - manual_overrides (array) (required) — Fields that were manually overridden after auto-population.\n - num (string) (required) — Short display identifier; not guaranteed unique.\n - owner (object) — A Flashduty member reference.\n - as (string) — Role label for this member in the context of the current object.\n - email (string) — Member email address.\n - person_id (integer) — Member ID.\n - person_name (string) — Member display name.\n - owner_id (integer) (required) — Primary owner member ID. 0 if none.\n - post_mortem_id (string) (required) — Associated post-mortem ID, if any. One incident can only link to a single post-mortem.\n - progress (string) (required) — Incident progress state. [Triggered, Processing, Closed]\n - reporter_email (string) — Reporter email for manually created incidents.\n - resolution (string) (required) — Resolution notes.\n - responders (array) (required) — Current responders with assignment/acknowledgement state.\n - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged.\n - as (string) — Role label of this responder.\n - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned.\n - email (string) — Member email, filled by the server.\n - person_id (integer) (required) — Responder member ID.\n - person_name (string) — Member display name, filled by the server.\n - root_cause (string) (required) — Root cause analysis.\n - score (number) (required) — Similarity score from the vector search.\n - silence_url (string) (required) — Quick-silence URL for this incident.\n - snoozed_before (integer) (required) — Unix timestamp (seconds) until which notifications are snoozed. 0 if not snoozed.\n - start_time (integer) (required) — Unix timestamp (seconds) when the incident started.\n - title (string) (required) — Incident title.\n - updated_at (integer) (required) — Last update timestamp (seconds).\n", + "Incidents.PostMortemInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - basics (object) (required)\n - incidents_earliest_start_seconds (integer) (required) — Earliest start time among linked incidents (seconds).\n - incidents_highest_severity (string) (required) — Highest severity among linked incidents.\n - incidents_latest_close_seconds (integer) (required) — Latest close time among linked incidents (seconds).\n - incidents_total_duration_seconds (integer) (required) — Cumulative duration in seconds.\n - responders (array) (required) — Responders involved in the incident(s).\n - acknowledged_at (integer) (required) — Unix timestamp (seconds) when the member acknowledged. 0 if not yet acknowledged.\n - as (string) — Role label of this responder.\n - assigned_at (integer) (required) — Unix timestamp (seconds) when the member was assigned.\n - email (string) — Member email, filled by the server.\n - person_id (integer) (required) — Responder member ID.\n - person_name (string) — Member display name, filled by the server.\n - content (object) (required)\n - content (string) (required) — Report body content (BlockNote JSON).\n - follow_ups (string) (required) — Follow-up action items rendered as a single string.\n - meta (object) (required) — Post-mortem metadata (lightweight shape used in lists).\n - account_id (integer) (required) — Account ID.\n - author_ids (array) (required) — Member IDs that contributed to the report.\n - channel_id (integer) (required) — Owning channel ID. 0 if none.\n - channel_name (string) (required) — Channel name, filled by the server.\n - created_at_seconds (integer) (required) — Creation timestamp (seconds).\n - incident_ids (array) (required) — Linked incident IDs.\n - is_private (boolean) (required) — When true, only team members and admins can view.\n - media_count (integer) (required) — Number of uploaded media files.\n - post_mortem_id (string) (required) — Deterministic post-mortem ID derived from account and incident IDs.\n - status (string) (required) — Report status. [drafting, published]\n - team_id (integer) (required) — Owning team ID. 0 if none.\n - template_id (string) (required) — Template used to initialize the report.\n - title (string) (required) — Report title.\n - updated_at_seconds (integer) (required) — Last update timestamp (seconds).\n", + "Incidents.PostMortemList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - author_ids (array) (required) — Member IDs that contributed to the report.\n - channel_id (integer) (required) — Owning channel ID. 0 if none.\n - channel_name (string) (required) — Channel name, filled by the server.\n - created_at_seconds (integer) (required) — Creation timestamp (seconds).\n - incident_ids (array) (required) — Linked incident IDs.\n - is_private (boolean) (required) — When true, only team members and admins can view.\n - media_count (integer) (required) — Number of uploaded media files.\n - post_mortem_id (string) (required) — Deterministic post-mortem ID derived from account and incident IDs.\n - status (string) (required) — Report status. [drafting, published]\n - team_id (integer) (required) — Owning team ID. 0 if none.\n - template_id (string) (required) — Template used to initialize the report.\n - title (string) (required) — Report title.\n - updated_at_seconds (integer) (required) — Last update timestamp (seconds).\n", + "Incidents.ReadGetWarRoomDefaultObservers": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - observers (array) — Historical responders suggested as default war-room observers.\n - account_id (integer) — Account this person belongs to.\n - as (string) — Role the person holds in the related context.\n - avatar (string) — URL of the person's avatar image.\n - email (string) — Email address of the person.\n - locale (string) — Preferred language locale of the person.\n - person_id (integer) — Person ID.\n - person_name (string) — Display name of the person.\n - phone (string) — Phone number of the person.\n - status (string) — Current status of the person.\n - time_zone (string) — Time zone of the person.\n", + "Incidents.WarRoomCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - chat_id (string) (required) — Chat/group ID on the IM side.\n - chat_name (string) (required) — Chat/group display name.\n - share_link (string) (required) — Join link for the war room, if provided by the IM.\n", + "Incidents.WarRoomDetail": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - chat_id (string) (required) — Chat/group ID on the IM side.\n - chat_name (string) (required) — Chat/group display name.\n - share_link (string) (required) — Join link for the war room, if provided by the IM.\n", + "Incidents.WarRoomList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - chat_id (string) (required) — Chat/group ID on the IM side.\n - created_at (integer) (required) — Creation timestamp (seconds).\n - created_by (integer) (required) — Member ID that created the war room.\n - incident_id (string) (required) — Associated incident ID (MongoDB ObjectID).\n - integration_id (integer) (required) — IM integration ID.\n - plugin_type (string) (required) — IM plugin type (e.g. `feishu`, `dingtalk`, `wecom`, `slack`).\n - status (string) (required) — War room status.\n", + "Integrations.Detail": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - attempt (integer) (required) — Attempt sequence number.\n - channel_id (integer) — Channel ID when applicable.\n - channel_name (string) — Name of the associated channel, resolved at query time.\n - duration (integer) (required) — Total elapsed time of the attempt in milliseconds.\n - endpoint (string) (required) — Destination URL.\n - error_message (string) — Error message when delivery failed.\n - event_id (string) (required) — Event ID.\n - event_time (string) (required) — Event time as a formatted timestamp string.\n - event_type (string) (required) — Event type.\n - integration_id (integer) (required) — Integration ID.\n - ref_id (string) — Source object ID.\n - ref_title (string) — Title of the source incident or alert, resolved at query time.\n - request_body (string) — Outbound request body payload.\n - request_headers (string) — Serialized outbound request headers.\n - response_body (string) — Response body.\n - response_headers (string) — Serialized response headers.\n - status (string) (required) — Delivery outcome. [success, failed]\n - status_code (integer) (required) — HTTP status code.\n - webhook_type (string) (required) — Source object kind. `incident` or `alert`.\n", + "Integrations.List": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - attempt (integer) (required) — Attempt sequence number.\n - channel_id (integer) — Channel ID associated with the event, when applicable.\n - duration (integer) (required) — Total elapsed time of the attempt in milliseconds.\n - endpoint (string) (required) — Destination URL.\n - error_message (string) — Error message when delivery failed.\n - event_id (string) (required) — Unique event identifier for the delivery attempt.\n - event_time (string) (required) — Event time as a formatted timestamp string.\n - event_type (string) (required) — Event type (e.g. `created`, `acknowledged`, `closed`).\n - integration_id (integer) (required) — Integration ID that triggered the webhook.\n - ref_id (string) — Source object ID (incident ID or alert ID).\n - request_body (string) — Outbound request body payload.\n - request_headers (string) — Serialized outbound request headers.\n - response_body (string) — Response body returned by the destination.\n - response_headers (string) — Serialized response headers from the destination.\n - status (string) (required) — Delivery outcome. [success, failed]\n - status_code (integer) (required) — HTTP status code returned by the destination.\n - webhook_type (string) (required) — Source object kind. `incident` or `alert`.\n", + "Issues.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - age (integer)\n - application_id (string)\n - application_name (string)\n - created_at (integer)\n - error (object)\n - message (string)\n - type (string)\n - error_count (integer) — Total error occurrences.\n - first_seen (object)\n - timestamp (integer)\n - version (string)\n - is_crash (boolean) — Whether the error caused an app crash.\n - issue_id (string) — Unique issue ID.\n - last_seen (object)\n - timestamp (integer)\n - version (string)\n - regression (object) — Regression metadata. Present only when a previously resolved issue re-occurred.\n - regressed_at (integer) — Timestamp when the regression was detected.\n - regressed_at_version (string) — Application version in which the regression was observed.\n - resolved_at (integer) — Timestamp of the previous resolution before the regression.\n - resolved_at (integer)\n - resolved_by (integer)\n - service (string)\n - session_count (integer) — Affected user sessions.\n - severity (string) — Issue severity level.\n - status (string) [for_review, reviewed, ignored, resolved]\n - suspected_cause (object)\n - person_id (integer)\n - reason (string)\n - source (string) [auto, user]\n - value (string) [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown]\n - team_id (integer)\n - updated_at (integer)\n - versions (array)\n", + "Issues.ReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - age (integer)\n - application_id (string)\n - application_name (string)\n - created_at (integer)\n - error (object)\n - message (string)\n - type (string)\n - error_count (integer) — Total error occurrences.\n - first_seen (object)\n - timestamp (integer)\n - version (string)\n - is_crash (boolean) — Whether the error caused an app crash.\n - issue_id (string) — Unique issue ID.\n - last_seen (object)\n - timestamp (integer)\n - version (string)\n - regression (object) — Regression metadata. Present only when a previously resolved issue re-occurred.\n - regressed_at (integer) — Timestamp when the regression was detected.\n - regressed_at_version (string) — Application version in which the regression was observed.\n - resolved_at (integer) — Timestamp of the previous resolution before the regression.\n - resolved_at (integer)\n - resolved_by (integer)\n - service (string)\n - session_count (integer) — Affected user sessions.\n - severity (string) — Issue severity level.\n - status (string) [for_review, reviewed, ignored, resolved]\n - suspected_cause (object)\n - person_id (integer)\n - reason (string)\n - source (string) [auto, user]\n - value (string) [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown]\n - team_id (integer)\n - updated_at (integer)\n - versions (array)\n", + "McpServers.ReadServerGet": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account.\n - ai_description (string) — LLM-generated description, preferred over description when present.\n - args (array) — Command-line arguments for the stdio executable.\n - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth.\n - call_timeout (integer) (required) — Per-call timeout in seconds.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - command (string) — Executable launched for stdio transport.\n - connect_timeout (integer) (required) — Connection timeout in seconds.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What this MCP server provides.\n - env (object) — Environment variables passed to the stdio process.\n - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked.\n - list_error (string) — Tool-probe failure message; present when the live probe failed.\n - oauth_metadata (string) — OAuth metadata JSON.\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON schema of the per-user secret.\n - server_id (string) (required) — Unique identifier of the MCP server.\n - server_name (string) (required) — Display name of the MCP server.\n - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled]\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tool_count (integer) — Number of tools discovered on the server.\n - tools (array) — Live tool catalogue; populated only by get and test.\n - description (string) (required) — What the tool does.\n - input_schema (any) — JSON schema of the tool's input arguments.\n - name (string) (required) — Tool name as advertised by the server.\n - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - url (string) — Endpoint URL for sse or streamable-http transport.\n", + "McpServers.ReadServerList": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - servers (array) (required) — MCP servers on the current page.\n - account_id (integer) (required) — Owning account.\n - ai_description (string) — LLM-generated description, preferred over description when present.\n - args (array) — Command-line arguments for the stdio executable.\n - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth.\n - call_timeout (integer) (required) — Per-call timeout in seconds.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - command (string) — Executable launched for stdio transport.\n - connect_timeout (integer) (required) — Connection timeout in seconds.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What this MCP server provides.\n - env (object) — Environment variables passed to the stdio process.\n - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked.\n - list_error (string) — Tool-probe failure message; present when the live probe failed.\n - oauth_metadata (string) — OAuth metadata JSON.\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON schema of the per-user secret.\n - server_id (string) (required) — Unique identifier of the MCP server.\n - server_name (string) (required) — Display name of the MCP server.\n - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled]\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tool_count (integer) — Number of tools discovered on the server.\n - tools (array) — Live tool catalogue; populated only by get and test.\n - description (string) (required) — What the tool does.\n - input_schema (any) — JSON schema of the tool's input arguments.\n - name (string) (required) — Tool name as advertised by the server.\n - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - url (string) — Endpoint URL for sse or streamable-http transport.\n - total (integer) (required) — Total number of servers matching the filters.\n", + "McpServers.WriteServerCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account.\n - ai_description (string) — LLM-generated description, preferred over description when present.\n - args (array) — Command-line arguments for the stdio executable.\n - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth.\n - call_timeout (integer) (required) — Per-call timeout in seconds.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - command (string) — Executable launched for stdio transport.\n - connect_timeout (integer) (required) — Connection timeout in seconds.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What this MCP server provides.\n - env (object) — Environment variables passed to the stdio process.\n - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked.\n - list_error (string) — Tool-probe failure message; present when the live probe failed.\n - oauth_metadata (string) — OAuth metadata JSON.\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON schema of the per-user secret.\n - server_id (string) (required) — Unique identifier of the MCP server.\n - server_name (string) (required) — Display name of the MCP server.\n - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled]\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tool_count (integer) — Number of tools discovered on the server.\n - tools (array) — Live tool catalogue; populated only by get and test.\n - description (string) (required) — What the tool does.\n - input_schema (any) — JSON schema of the tool's input arguments.\n - name (string) (required) — Tool name as advertised by the server.\n - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - url (string) — Endpoint URL for sse or streamable-http transport.\n", + "McpServers.WriteServerUpdate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account.\n - ai_description (string) — LLM-generated description, preferred over description when present.\n - args (array) — Command-line arguments for the stdio executable.\n - auth_mode (string) — Credential model: shared, per_user_secret, or per_user_oauth.\n - call_timeout (integer) (required) — Per-call timeout in seconds.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - command (string) — Executable launched for stdio transport.\n - connect_timeout (integer) (required) — Connection timeout in seconds.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What this MCP server provides.\n - env (object) — Environment variables passed to the stdio process.\n - headers (object) — HTTP headers sent to the remote endpoint; secret values are masked.\n - list_error (string) — Tool-probe failure message; present when the live probe failed.\n - oauth_metadata (string) — OAuth metadata JSON.\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON schema of the per-user secret.\n - server_id (string) (required) — Unique identifier of the MCP server.\n - server_name (string) (required) — Display name of the MCP server.\n - status (string) (required) — Whether the server is active and usable by agents. [enabled, disabled]\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tool_count (integer) — Number of tools discovered on the server.\n - tools (array) — Live tool catalogue; populated only by get and test.\n - description (string) (required) — What the tool does.\n - input_schema (any) — JSON schema of the tool's input arguments.\n - name (string) (required) — Tool name as advertised by the server.\n - transport (string) (required) — Transport used to reach the server. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - url (string) — Endpoint URL for sse or streamable-http transport.\n", + "Members.MemberInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_avatar (string) — Account avatar URL\n - account_email (string) — Account email\n - account_id (integer) — Account ID\n - account_locale (string) — Account-level locale preference (e.g. zh-CN or en-US)\n - account_name (string) — Account name\n - account_role_ids (array) — Assigned role IDs\n - account_time_zone (string) — Account-level time zone (e.g. Asia/Shanghai)\n - avatar (string) — Member avatar URL\n - country_code (string) — Phone country code\n - domain (string) — Account domain\n - email (string) — Email address\n - email_verified (boolean) — Whether email is verified\n - is_external (boolean) — Whether provisioned via SSO\n - locale (string) — Locale preference\n - member_id (integer) — Member ID\n - member_name (string) — Member display name\n - phone (string) — Masked phone number\n - phone_verified (boolean) — Whether phone is verified\n - status (string) — Member status. `enabled` — active member; `pending` — invited but not yet accepted; `deleted` — removed from the organization. [enabled, pending, deleted]\n - time_zone (string) — Time zone\n", + "Members.MemberInvite": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - member_id (integer) — Member ID\n - member_name (string) — Member display name\n", + "Members.MemberList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID\n - account_role_ids (array) (required) — Role IDs\n - avatar (string) (required) — Avatar URL\n - country_code (string) (required) — Phone country code\n - created_at (integer) (required) — Creation timestamp (Unix seconds)\n - email (string) (required) — Email address\n - email_verified (boolean) (required) — Email verified\n - is_external (boolean) (required) — Provisioned via SSO\n - locale (string) — Locale\n - member_id (integer) (required) — Member ID\n - member_name (string) (required) — Display name\n - phone (string) (required) — Masked phone number\n - phone_verified (boolean) (required) — Phone verified\n - ref_id (string) (required) — External reference ID\n - status (string) (required) — Member status. `enabled` — active member; `pending` — invited but not yet accepted; `deleted` — removed from the organization. [enabled, pending, deleted]\n - time_zone (string) — Time zone\n - updated_at (integer) (required) — Update timestamp (Unix seconds)\n", + "Members.PersonInfos": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID\n - as (string) — Login role (account/member)\n - avatar (string) — Avatar URL\n - email (string) — Email address\n - email_verified (boolean) (required) — Email verified\n - locale (string) — Locale\n - person_id (integer) (required) — Person ID\n - person_name (string) — Display name\n - phone (string) — Phone number\n - phone_verified (boolean) (required) — Phone verified\n - status (string) — Person status. `enabled` — active; `pending` — invited but not yet accepted; `deleted` — removed. [enabled, pending, deleted]\n - time_zone (string) — Time zone\n", + "NotificationTemplates.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — ID of the owning account.\n - created_at (integer) (required) — Unix epoch seconds the template was created.\n - creator_id (integer) (required) — Member ID of the creator.\n - deleted_at (integer) — Unix epoch seconds the template was soft-deleted. Absent (omitempty) when the template is live.\n - description (string) (required) — Free-form description.\n - dingtalk (string) (required) — DingTalk robot message template source.\n - dingtalk_app (string) (required) — DingTalk app message template source.\n - email (string) (required) — Email body template source (Go `html/template` syntax).\n - feishu (string) (required) — Feishu robot message template source.\n - feishu_app (string) (required) — Feishu app message template source.\n - slack (string) (required) — Slack robot message template source.\n - slack_app (string) (required) — Slack app message template source.\n - sms (string) (required) — SMS template source (Go `text/template` syntax).\n - status (string) (required) — Template lifecycle status. [enabled, disabled, deleted]\n - team_id (integer) (required) — ID of the team this template is scoped to, or 0 for account-wide.\n - teams_app (string) (required) — Microsoft Teams app message template source.\n - telegram (string) (required) — Telegram bot message template source.\n - template_id (string) (required) — Template ID.\n - template_name (string) (required) — Unique template name within the account.\n - updated_at (integer) (required) — Unix epoch seconds the template was last updated.\n - updated_by (integer) (required) — Member ID of the last editor.\n - voice (string) (required) — Voice call script template source.\n - wecom (string) (required) — WeCom robot message template source.\n - wecom_app (string) (required) — WeCom app message template source.\n - zoom (string) (required) — Zoom bot message template source.\n", + "NotificationTemplates.ReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — ID of the owning account.\n - created_at (integer) (required) — Unix epoch seconds the template was created.\n - creator_id (integer) (required) — Member ID of the creator.\n - deleted_at (integer) — Unix epoch seconds the template was soft-deleted. Absent (omitempty) when the template is live.\n - description (string) (required) — Free-form description.\n - dingtalk (string) (required) — DingTalk robot message template source.\n - dingtalk_app (string) (required) — DingTalk app message template source.\n - email (string) (required) — Email body template source (Go `html/template` syntax).\n - feishu (string) (required) — Feishu robot message template source.\n - feishu_app (string) (required) — Feishu app message template source.\n - slack (string) (required) — Slack robot message template source.\n - slack_app (string) (required) — Slack app message template source.\n - sms (string) (required) — SMS template source (Go `text/template` syntax).\n - status (string) (required) — Template lifecycle status. [enabled, disabled, deleted]\n - team_id (integer) (required) — ID of the team this template is scoped to, or 0 for account-wide.\n - teams_app (string) (required) — Microsoft Teams app message template source.\n - telegram (string) (required) — Telegram bot message template source.\n - template_id (string) (required) — Template ID.\n - template_name (string) (required) — Unique template name within the account.\n - updated_at (integer) (required) — Unix epoch seconds the template was last updated.\n - updated_by (integer) (required) — Member ID of the last editor.\n - voice (string) (required) — Voice call script template source.\n - wecom (string) (required) — WeCom robot message template source.\n - wecom_app (string) (required) — WeCom app message template source.\n - zoom (string) (required) — Zoom bot message template source.\n", + "NotificationTemplates.ReadPreview": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - content (string) — Rendered template output, present when success is true.\n - message (string) — Error message describing why rendering failed, present when success is false.\n - success (boolean) — Whether the template rendered without errors.\n", + "NotificationTemplates.WriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - template_id (string) (required) — Newly created template ID.\n - template_name (string) (required) — Template name echoed from the request.\n", + "RolesPermissions.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - created_at (integer) (required) — Unix epoch seconds the role was created.\n - description (string) (required) — Role description.\n - editable (boolean) (required) — False for built-in roles which cannot be modified.\n - permission_ids (array) (required) — IDs of permissions granted by this role.\n - role_id (integer) (required) — Unique role ID.\n - role_name (string) (required) — Role display name.\n - status (string) (required) — Role status. [enabled, disabled]\n - updated_at (integer) (required) — Unix epoch seconds the role was last updated.\n", + "RolesPermissions.ReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - created_at (integer) (required) — Unix epoch seconds the role was created.\n - description (string) (required) — Role description.\n - editable (boolean) (required) — False for built-in roles which cannot be modified.\n - permission_ids (array) (required) — IDs of permissions granted by this role.\n - role_id (integer) (required) — Unique role ID.\n - role_name (string) (required) — Role display name.\n - status (string) (required) — Role status. [enabled, disabled]\n - updated_at (integer) (required) — Unix epoch seconds the role was last updated.\n", + "RolesPermissions.ReadListPermission": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - class (string) (required) — Permission class (e.g., 'On-call', 'Organization').\n - description (string) (required) — Human-readable permission description.\n - id (integer) (required) — Unique permission ID.\n - is_granted (boolean) — Present when with_all is true. Indicates whether this permission is granted to the requested roles.\n - permission_name (string) (required) — Permission display name.\n - permission_type (string) (required) — Whether this is a read or manage permission. [read, manage]\n - scope (string) (required) — Permission scope (e.g., 'on-call', 'organization').\n - status (string) (required) — Permission status. [enabled, disabled]\n", + "RolesPermissions.ReadListPermissionFactor": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - factor_name (string) (required) — Factor identifier (e.g., 'template:read:info').\n - factor_type (string) (required) — Factor type. [api, button, visit, menu, url]\n", + "RolesPermissions.WriteUpsert": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - role_id (integer) (required) — Created or updated role ID.\n - role_name (string) (required) — Role name echoed from the request.\n", + "RuleSets.Create": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - created_at (integer) (required) — Creation timestamp, Unix epoch seconds.\n - creator_account_id (integer) (required) — Account ID of the creator.\n - creator_id (integer) (required) — Member ID of the creator.\n - creator_name (string) (required) — Display name of the creator.\n - id (integer) (required) — Ruleset ID.\n - note (string) (required) — Description or title of the ruleset.\n - open_flag (integer) (required) — Sharing scope. `0` = private (creator only), `1` = account-shared, `2` = public.\n - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses.\n - type_ident (string) (required) — Datasource type identifier this ruleset applies to.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "RuleSets.Info": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - created_at (integer) (required) — Creation timestamp, Unix epoch seconds.\n - creator_account_id (integer) (required) — Account ID of the creator.\n - creator_id (integer) (required) — Member ID of the creator.\n - creator_name (string) (required) — Display name of the creator.\n - id (integer) (required) — Ruleset ID.\n - note (string) (required) — Description or title of the ruleset.\n - open_flag (integer) (required) — Sharing scope. `0` = private (creator only), `1` = account-shared, `2` = public.\n - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses.\n - type_ident (string) (required) — Datasource type identifier this ruleset applies to.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "RuleSets.List": "Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - created_at (integer) (required) — Creation timestamp, Unix epoch seconds.\n - creator_account_id (integer) (required) — Account ID of the creator.\n - creator_id (integer) (required) — Member ID of the creator.\n - creator_name (string) (required) — Display name of the creator.\n - id (integer) (required) — Ruleset ID.\n - note (string) (required) — Description or title of the ruleset.\n - open_flag (integer) (required) — Sharing scope. `0` = private (creator only), `1` = account-shared, `2` = public.\n - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses.\n - type_ident (string) (required) — Datasource type identifier this ruleset applies to.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "RuleSets.Update": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - created_at (integer) (required) — Creation timestamp, Unix epoch seconds.\n - creator_account_id (integer) (required) — Account ID of the creator.\n - creator_id (integer) (required) — Member ID of the creator.\n - creator_name (string) (required) — Display name of the creator.\n - id (integer) (required) — Ruleset ID.\n - note (string) (required) — Description or title of the ruleset.\n - open_flag (integer) (required) — Sharing scope. `0` = private (creator only), `1` = account-shared, `2` = public.\n - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses.\n - type_ident (string) (required) — Datasource type identifier this ruleset applies to.\n - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds.\n", + "Schedules.Create": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - schedule_id (integer) (required) — ID of the newly created schedule.\n", + "Schedules.Info": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - cur_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - person_ids (array) (required) — Person IDs in this slot.\n - role_id (integer) (required) — Oncall role ID.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - description (any) (required) — Schedule description. null when returned from /schedule/preview.\n - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview.\n - end (integer) — Window end (Unix seconds).\n - field (string) — Field name used by the legacy update-field endpoint.\n - final_schedule (object) (required) — Computed schedule for a single layer.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview.\n - id (any) (required) — Schedule ID. null when returned from /schedule/preview.\n - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - layers (array) (required) — Rotation layers defined on the schedule.\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - day_mask (object) (required) — Day-of-week mask for a rotation layer.\n - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation.\n - enable_time (integer) (required) — When the layer becomes effective (Unix seconds).\n - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never).\n - fair_rotation (boolean) (required) — Whether fair rotation is enabled.\n - groups (array) (required) — Oncall groups participating in the rotation.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - person_ids (array) (required) — Person IDs in this slot.\n - role_id (integer) (required) — Oncall role ID.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds).\n - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes).\n - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended.\n - layer_name (string) — User-facing layer name.\n - layer_start (integer) — Layer start timestamp (Unix seconds).\n - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds).\n - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week.\n - restrict_periods (array) (required) — Restriction windows inside each rotation cycle.\n - restrict_end (integer) (required) — End offset inside the rotation cycle.\n - restrict_start (integer) (required) — Start offset inside the rotation cycle.\n - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds).\n - rotation_duration (integer) (required) — Rotation duration in seconds.\n - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month]\n - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle).\n - schedule_id (integer) (required) — Parent schedule ID.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n - weight (integer) (required) — Layer weight for ordering.\n - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview.\n - next_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - person_ids (array) (required) — Person IDs in this slot.\n - role_id (integer) (required) — Oncall role ID.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - notify (object) (required) — Notification configuration attached to a schedule.\n - advance_in_time (integer) — Advance notification lead time (seconds).\n - by (object) (required) — Per-recipient notification preference.\n - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference.\n - personal_channels (array) (required) — Personal notification channel keys.\n - fixed_time (object) (required) — Fixed-time notification config.\n - cycle (string) (required) — Notification cycle.\n - start (string) (required) — Notification start time within the cycle.\n - im (object) — Legacy IM-type to token map.\n - webhooks (array) (required) — IM webhook notification channels.\n - settings (object) (required) — Settings for an IM webhook notification channel.\n - alias (string) (required) — Channel alias.\n - chat_ids (array) (required) — Chat IDs.\n - data_source_id (integer) (required) — Data source ID.\n - sign_secret (string) (required) — Signature secret.\n - token (string) (required) — Webhook token.\n - verify_token (string) (required) — Verification token.\n - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app).\n - schedule_id (integer) (required) — Schedule ID.\n - schedule_layers (array) (required) — Computed layers for the requested window.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview.\n - start (integer) — Window start (Unix seconds).\n - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview.\n - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n", + "Schedules.Infos": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - cur_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - description (any) (required) — Schedule description. null when returned from /schedule/preview.\n - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview.\n - end (integer) — Window end (Unix seconds).\n - field (string) — Field name used by the legacy update-field endpoint.\n - final_schedule (object) (required) — Computed schedule for a single layer.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview.\n - id (any) (required) — Schedule ID. null when returned from /schedule/preview.\n - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - layers (array) (required) — Rotation layers defined on the schedule.\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - day_mask (object) (required) — Day-of-week mask for a rotation layer.\n - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation.\n - enable_time (integer) (required) — When the layer becomes effective (Unix seconds).\n - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never).\n - fair_rotation (boolean) (required) — Whether fair rotation is enabled.\n - groups (array) (required) — Oncall groups participating in the rotation.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds).\n - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes).\n - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended.\n - layer_name (string) — User-facing layer name.\n - layer_start (integer) — Layer start timestamp (Unix seconds).\n - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds).\n - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week.\n - restrict_periods (array) (required) — Restriction windows inside each rotation cycle.\n - restrict_end (integer) (required) — End offset inside the rotation cycle.\n - restrict_start (integer) (required) — Start offset inside the rotation cycle.\n - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds).\n - rotation_duration (integer) (required) — Rotation duration in seconds.\n - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month]\n - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle).\n - schedule_id (integer) (required) — Parent schedule ID.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n - weight (integer) (required) — Layer weight for ordering.\n - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview.\n - next_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - notify (object) (required) — Notification configuration attached to a schedule.\n - advance_in_time (integer) — Advance notification lead time (seconds).\n - by (object) (required) — Per-recipient notification preference.\n - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference.\n - personal_channels (array) (required) — Personal notification channel keys.\n - fixed_time (object) (required) — Fixed-time notification config.\n - cycle (string) (required) — Notification cycle.\n - start (string) (required) — Notification start time within the cycle.\n - im (object) — Legacy IM-type to token map.\n - webhooks (array) (required) — IM webhook notification channels.\n - settings (object) (required) — Settings for an IM webhook notification channel.\n - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app).\n - schedule_id (integer) (required) — Schedule ID.\n - schedule_layers (array) (required) — Computed layers for the requested window.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview.\n - start (integer) — Window start (Unix seconds).\n - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview.\n - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n", + "Schedules.List": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - cur_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - description (any) (required) — Schedule description. null when returned from /schedule/preview.\n - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview.\n - end (integer) — Window end (Unix seconds).\n - field (string) — Field name used by the legacy update-field endpoint.\n - final_schedule (object) (required) — Computed schedule for a single layer.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview.\n - id (any) (required) — Schedule ID. null when returned from /schedule/preview.\n - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - layers (array) (required) — Rotation layers defined on the schedule.\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - day_mask (object) (required) — Day-of-week mask for a rotation layer.\n - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation.\n - enable_time (integer) (required) — When the layer becomes effective (Unix seconds).\n - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never).\n - fair_rotation (boolean) (required) — Whether fair rotation is enabled.\n - groups (array) (required) — Oncall groups participating in the rotation.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds).\n - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes).\n - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended.\n - layer_name (string) — User-facing layer name.\n - layer_start (integer) — Layer start timestamp (Unix seconds).\n - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds).\n - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week.\n - restrict_periods (array) (required) — Restriction windows inside each rotation cycle.\n - restrict_end (integer) (required) — End offset inside the rotation cycle.\n - restrict_start (integer) (required) — Start offset inside the rotation cycle.\n - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds).\n - rotation_duration (integer) (required) — Rotation duration in seconds.\n - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month]\n - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle).\n - schedule_id (integer) (required) — Parent schedule ID.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n - weight (integer) (required) — Layer weight for ordering.\n - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview.\n - next_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - notify (object) (required) — Notification configuration attached to a schedule.\n - advance_in_time (integer) — Advance notification lead time (seconds).\n - by (object) (required) — Per-recipient notification preference.\n - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference.\n - personal_channels (array) (required) — Personal notification channel keys.\n - fixed_time (object) (required) — Fixed-time notification config.\n - cycle (string) (required) — Notification cycle.\n - start (string) (required) — Notification start time within the cycle.\n - im (object) — Legacy IM-type to token map.\n - webhooks (array) (required) — IM webhook notification channels.\n - settings (object) (required) — Settings for an IM webhook notification channel.\n - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app).\n - schedule_id (integer) (required) — Schedule ID.\n - schedule_layers (array) (required) — Computed layers for the requested window.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview.\n - start (integer) — Window start (Unix seconds).\n - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview.\n - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n", + "Schedules.Preview": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - cur_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - person_ids (array) (required) — Person IDs in this slot.\n - role_id (integer) (required) — Oncall role ID.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - description (any) (required) — Schedule description. null when returned from /schedule/preview.\n - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview.\n - end (integer) — Window end (Unix seconds).\n - field (string) — Field name used by the legacy update-field endpoint.\n - final_schedule (object) (required) — Computed schedule for a single layer.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview.\n - id (any) (required) — Schedule ID. null when returned from /schedule/preview.\n - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - layers (array) (required) — Rotation layers defined on the schedule.\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - day_mask (object) (required) — Day-of-week mask for a rotation layer.\n - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation.\n - enable_time (integer) (required) — When the layer becomes effective (Unix seconds).\n - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never).\n - fair_rotation (boolean) (required) — Whether fair rotation is enabled.\n - groups (array) (required) — Oncall groups participating in the rotation.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - person_ids (array) (required) — Person IDs in this slot.\n - role_id (integer) (required) — Oncall role ID.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds).\n - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes).\n - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended.\n - layer_name (string) — User-facing layer name.\n - layer_start (integer) — Layer start timestamp (Unix seconds).\n - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds).\n - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week.\n - restrict_periods (array) (required) — Restriction windows inside each rotation cycle.\n - restrict_end (integer) (required) — End offset inside the rotation cycle.\n - restrict_start (integer) (required) — Start offset inside the rotation cycle.\n - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds).\n - rotation_duration (integer) (required) — Rotation duration in seconds.\n - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month]\n - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle).\n - schedule_id (integer) (required) — Parent schedule ID.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n - weight (integer) (required) — Layer weight for ordering.\n - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview.\n - next_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - person_ids (array) (required) — Person IDs in this slot.\n - role_id (integer) (required) — Oncall role ID.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - notify (object) (required) — Notification configuration attached to a schedule.\n - advance_in_time (integer) — Advance notification lead time (seconds).\n - by (object) (required) — Per-recipient notification preference.\n - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference.\n - personal_channels (array) (required) — Personal notification channel keys.\n - fixed_time (object) (required) — Fixed-time notification config.\n - cycle (string) (required) — Notification cycle.\n - start (string) (required) — Notification start time within the cycle.\n - im (object) — Legacy IM-type to token map.\n - webhooks (array) (required) — IM webhook notification channels.\n - settings (object) (required) — Settings for an IM webhook notification channel.\n - alias (string) (required) — Channel alias.\n - chat_ids (array) (required) — Chat IDs.\n - data_source_id (integer) (required) — Data source ID.\n - sign_secret (string) (required) — Signature secret.\n - token (string) (required) — Webhook token.\n - verify_token (string) (required) — Verification token.\n - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app).\n - schedule_id (integer) (required) — Schedule ID.\n - schedule_layers (array) (required) — Computed layers for the requested window.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview.\n - start (integer) — Window start (Unix seconds).\n - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview.\n - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n", + "Schedules.Self": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - cur_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - description (any) (required) — Schedule description. null when returned from /schedule/preview.\n - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview.\n - end (integer) — Window end (Unix seconds).\n - field (string) — Field name used by the legacy update-field endpoint.\n - final_schedule (object) (required) — Computed schedule for a single layer.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview.\n - id (any) (required) — Schedule ID. null when returned from /schedule/preview.\n - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - layers (array) (required) — Rotation layers defined on the schedule.\n - account_id (integer) (required) — Account ID.\n - create_at (integer) (required) — Creation timestamp (Unix seconds).\n - create_by (integer) (required) — Creator person ID.\n - day_mask (object) (required) — Day-of-week mask for a rotation layer.\n - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation.\n - enable_time (integer) (required) — When the layer becomes effective (Unix seconds).\n - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never).\n - fair_rotation (boolean) (required) — Whether fair rotation is enabled.\n - groups (array) (required) — Oncall groups participating in the rotation.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds).\n - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes).\n - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended.\n - layer_name (string) — User-facing layer name.\n - layer_start (integer) — Layer start timestamp (Unix seconds).\n - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds).\n - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week.\n - restrict_periods (array) (required) — Restriction windows inside each rotation cycle.\n - restrict_end (integer) (required) — End offset inside the rotation cycle.\n - restrict_start (integer) (required) — Start offset inside the rotation cycle.\n - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds).\n - rotation_duration (integer) (required) — Rotation duration in seconds.\n - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month]\n - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle).\n - schedule_id (integer) (required) — Parent schedule ID.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n - weight (integer) (required) — Layer weight for ordering.\n - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview.\n - next_oncall (object) (required) — Snapshot of the currently or next on-call group.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - end (integer) (required) — Group end timestamp (Unix seconds).\n - group_name (string) (required) — Group display name.\n - members (array) (required) — Members of this group.\n - name (string) (required) — Legacy group name.\n - start (integer) (required) — Group start timestamp (Unix seconds).\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - update_at (integer) (required) — Update timestamp (Unix seconds).\n - weight (integer) (required) — Layer weight the shift comes from.\n - notify (object) (required) — Notification configuration attached to a schedule.\n - advance_in_time (integer) — Advance notification lead time (seconds).\n - by (object) (required) — Per-recipient notification preference.\n - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference.\n - personal_channels (array) (required) — Personal notification channel keys.\n - fixed_time (object) (required) — Fixed-time notification config.\n - cycle (string) (required) — Notification cycle.\n - start (string) (required) — Notification start time within the cycle.\n - im (object) — Legacy IM-type to token map.\n - webhooks (array) (required) — IM webhook notification channels.\n - settings (object) (required) — Settings for an IM webhook notification channel.\n - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app).\n - schedule_id (integer) (required) — Schedule ID.\n - schedule_layers (array) (required) — Computed layers for the requested window.\n - layer_name (string) (required) — Layer display name.\n - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override.\n - name (string) (required) — Layer internal name.\n - schedules (array) (required) — Computed shifts.\n - end (integer) (required) — Shift end timestamp (Unix seconds).\n - group (object) (required) — Oncall group definition within a rotation layer.\n - index (integer) (required) — Index inside the rotation.\n - start (integer) (required) — Shift start timestamp (Unix seconds).\n - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview.\n - start (integer) — Window start (Unix seconds).\n - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview.\n - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview.\n - update_at (integer) (required) — Last update timestamp (Unix seconds).\n - update_by (integer) (required) — Last updater person ID.\n", + "Skills.ReadGet": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account.\n - author (string) — Author declared in the skill frontmatter.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - checksum (string) — SHA-256 checksum of the skill's zip package.\n - content (string) — Full SKILL.md body; omitted in list responses.\n - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What the skill does and when the agent should use it.\n - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it.\n - license (string) — License declared in the skill frontmatter.\n - s3_key (string) — Object-storage key of the skill's zip package.\n - skill_id (string) (required) — Unique identifier of the skill.\n - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter.\n - source_template_name (string) — Marketplace template this skill was installed from, if any.\n - source_template_version (string) — Marketplace template version captured at install time.\n - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled]\n - tags (array) — Tags declared in the skill frontmatter.\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tools (array) — Tools the skill requires, declared in its frontmatter.\n - update_available (boolean) (required) — A newer marketplace template version exists for this skill.\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - version (string) — Skill version string from its frontmatter.\n", + "Skills.ReadList": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - skills (array) (required) — Skills on the current page.\n - account_id (integer) (required) — Owning account.\n - author (string) — Author declared in the skill frontmatter.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - checksum (string) — SHA-256 checksum of the skill's zip package.\n - content (string) — Full SKILL.md body; omitted in list responses.\n - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What the skill does and when the agent should use it.\n - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it.\n - license (string) — License declared in the skill frontmatter.\n - s3_key (string) — Object-storage key of the skill's zip package.\n - skill_id (string) (required) — Unique identifier of the skill.\n - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter.\n - source_template_name (string) — Marketplace template this skill was installed from, if any.\n - source_template_version (string) — Marketplace template version captured at install time.\n - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled]\n - tags (array) — Tags declared in the skill frontmatter.\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tools (array) — Tools the skill requires, declared in its frontmatter.\n - update_available (boolean) (required) — A newer marketplace template version exists for this skill.\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - version (string) — Skill version string from its frontmatter.\n - total (integer) (required) — Total number of skills matching the filters.\n", + "Skills.WriteUpdate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account.\n - author (string) — Author declared in the skill frontmatter.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - checksum (string) — SHA-256 checksum of the skill's zip package.\n - content (string) — Full SKILL.md body; omitted in list responses.\n - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What the skill does and when the agent should use it.\n - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it.\n - license (string) — License declared in the skill frontmatter.\n - s3_key (string) — Object-storage key of the skill's zip package.\n - skill_id (string) (required) — Unique identifier of the skill.\n - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter.\n - source_template_name (string) — Marketplace template this skill was installed from, if any.\n - source_template_version (string) — Marketplace template version captured at install time.\n - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled]\n - tags (array) — Tags declared in the skill frontmatter.\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tools (array) — Tools the skill requires, declared in its frontmatter.\n - update_available (boolean) (required) — A newer marketplace template version exists for this skill.\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - version (string) — Skill version string from its frontmatter.\n", + "Skills.WriteUpload": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account.\n - author (string) — Author declared in the skill frontmatter.\n - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource.\n - checksum (string) — SHA-256 checksum of the skill's zip package.\n - content (string) — Full SKILL.md body; omitted in list responses.\n - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert.\n - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member who created this resource.\n - description (string) (required) — What the skill does and when the agent should use it.\n - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it.\n - license (string) — License declared in the skill frontmatter.\n - s3_key (string) — Object-storage key of the skill's zip package.\n - skill_id (string) (required) — Unique identifier of the skill.\n - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter.\n - source_template_name (string) — Marketplace template this skill was installed from, if any.\n - source_template_version (string) — Marketplace template version captured at install time.\n - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled]\n - tags (array) — Tags declared in the skill frontmatter.\n - team_id (integer) (required) — Owning team; 0 means account scope.\n - tools (array) — Tools the skill requires, declared in its frontmatter.\n - update_available (boolean) (required) — A newer marketplace template version exists for this skill.\n - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds.\n - version (string) — Skill version string from its frontmatter.\n", + "Sourcemaps.List": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - created_at (integer) — Upload timestamp, Unix epoch seconds.\n - git_commit_sha (string) — Git commit SHA for this build.\n - git_repository_url (string) — Git repository URL associated with this build.\n - key (string) — Storage key uniquely identifying this sourcemap file.\n - metadata (object) — Free-form key-value metadata attached to the sourcemap. Shape depends on the upload client; common keys include `git_repository_url` and `git_commit_sha` (though those are also promoted to top-level fields).\n - service (string) — Application or service name.\n - size (integer) — File size in bytes.\n - type (string) — Platform type: `browser`, `android`, or `ios`. [browser, android, ios]\n - updated_at (integer) — Last update timestamp, Unix epoch seconds.\n - version (string) — Application version string.\n", + "StatusPages.ChangeActiveList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - affected_components (array) — Components currently affected by this event, with their resulting status.\n - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds.\n - component_id (string) — Component ID.\n - description (string) — Component description.\n - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints.\n - hide_uptime (boolean) — When true, uptime data is hidden from summary responses.\n - name (string) (required) — Component display name.\n - order_id (integer) — Display order within its section.\n - section_id (string) — Parent section ID.\n - status (string) (required) — Current component status resulting from the event. [operational, degraded, partial_outage, full_outage, under_maintenance]\n - auto_update_by_schedule (boolean) — Maintenance only: whether the status advances automatically based on the scheduled window.\n - change_id (integer) (required) — Event ID.\n - close_at_seconds (integer) — Scheduled close time in unix seconds. Set for retrospective and maintenance events.\n - description (string) — Event description (Markdown).\n - is_retrospective (boolean) — Whether this event is a retrospective (historical) one.\n - linked_change_ids (array) — Linked event IDs (related incidents, deployments, etc.).\n - notify_subscribers (boolean) — Whether subscribers were notified about this event.\n - page_id (integer) — Parent status page ID.\n - responder_ids (array) — Member IDs responsible for this event.\n - start_at_seconds (integer) — Event start time in unix seconds.\n - status (string) — Current event status. Incident statuses: `investigating`/`identified`/`monitoring`/`resolved`. Maintenance statuses: `scheduled`/`ongoing`/`completed`. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]\n - title (string) (required) — Event title.\n - type (string) (required) — Event type. [incident, maintenance]\n - updates (array) — Timeline updates attached to this event, ordered by time.\n - at_seconds (integer) (required) — Update timestamp in unix seconds.\n - component_changes (array) — Component status transitions applied by this update.\n - component_id (string) (required) — Component ID.\n - component_name (string) — Component display name. Populated by the backend on read; ignored on write.\n - status (string) (required) — New component status. Incidents support `operational`/`degraded`/`partial_outage`/`full_outage`; maintenances support `operational`/`under_maintenance`. [operational, degraded, partial_outage, full_outage, under_maintenance]\n - description (string) — Update description (Markdown).\n - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]\n - update_id (string) (required) — Update ID.\n", + "StatusPages.ChangeCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - change_id (integer) (required) — Newly created event ID.\n - change_name (string) (required) — Event title (echoed from the request).\n", + "StatusPages.ChangeInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - affected_components (array) — Components currently affected by this event, with their resulting status.\n - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds.\n - component_id (string) — Component ID.\n - description (string) — Component description.\n - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints.\n - hide_uptime (boolean) — When true, uptime data is hidden from summary responses.\n - name (string) (required) — Component display name.\n - order_id (integer) — Display order within its section.\n - section_id (string) — Parent section ID.\n - status (string) (required) — Current component status resulting from the event. [operational, degraded, partial_outage, full_outage, under_maintenance]\n - auto_update_by_schedule (boolean) — Maintenance only: whether the status advances automatically based on the scheduled window.\n - change_id (integer) (required) — Event ID.\n - close_at_seconds (integer) — Scheduled close time in unix seconds. Set for retrospective and maintenance events.\n - description (string) — Event description (Markdown).\n - is_retrospective (boolean) — Whether this event is a retrospective (historical) one.\n - linked_change_ids (array) — Linked event IDs (related incidents, deployments, etc.).\n - notify_subscribers (boolean) — Whether subscribers were notified about this event.\n - page_id (integer) — Parent status page ID.\n - responder_ids (array) — Member IDs responsible for this event.\n - start_at_seconds (integer) — Event start time in unix seconds.\n - status (string) — Current event status. Incident statuses: `investigating`/`identified`/`monitoring`/`resolved`. Maintenance statuses: `scheduled`/`ongoing`/`completed`. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]\n - title (string) (required) — Event title.\n - type (string) (required) — Event type. [incident, maintenance]\n - updates (array) — Timeline updates attached to this event, ordered by time.\n - at_seconds (integer) (required) — Update timestamp in unix seconds.\n - component_changes (array) — Component status transitions applied by this update.\n - component_id (string) (required) — Component ID.\n - component_name (string) — Component display name. Populated by the backend on read; ignored on write.\n - status (string) (required) — New component status. Incidents support `operational`/`degraded`/`partial_outage`/`full_outage`; maintenances support `operational`/`under_maintenance`. [operational, degraded, partial_outage, full_outage, under_maintenance]\n - description (string) — Update description (Markdown).\n - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]\n - update_id (string) (required) — Update ID.\n", + "StatusPages.ChangeList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - affected_components (array) — Components currently affected by this event, with their resulting status.\n - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds.\n - component_id (string) — Component ID.\n - description (string) — Component description.\n - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints.\n - hide_uptime (boolean) — When true, uptime data is hidden from summary responses.\n - name (string) (required) — Component display name.\n - order_id (integer) — Display order within its section.\n - section_id (string) — Parent section ID.\n - status (string) (required) — Current component status resulting from the event. [operational, degraded, partial_outage, full_outage, under_maintenance]\n - auto_update_by_schedule (boolean) — Maintenance only: whether the status advances automatically based on the scheduled window.\n - change_id (integer) (required) — Event ID.\n - close_at_seconds (integer) — Scheduled close time in unix seconds. Set for retrospective and maintenance events.\n - description (string) — Event description (Markdown).\n - is_retrospective (boolean) — Whether this event is a retrospective (historical) one.\n - linked_change_ids (array) — Linked event IDs (related incidents, deployments, etc.).\n - notify_subscribers (boolean) — Whether subscribers were notified about this event.\n - page_id (integer) — Parent status page ID.\n - responder_ids (array) — Member IDs responsible for this event.\n - start_at_seconds (integer) — Event start time in unix seconds.\n - status (string) — Current event status. Incident statuses: `investigating`/`identified`/`monitoring`/`resolved`. Maintenance statuses: `scheduled`/`ongoing`/`completed`. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]\n - title (string) (required) — Event title.\n - type (string) (required) — Event type. [incident, maintenance]\n - updates (array) — Timeline updates attached to this event, ordered by time.\n - at_seconds (integer) (required) — Update timestamp in unix seconds.\n - component_changes (array) — Component status transitions applied by this update.\n - component_id (string) (required) — Component ID.\n - component_name (string) — Component display name. Populated by the backend on read; ignored on write.\n - status (string) (required) — New component status. Incidents support `operational`/`degraded`/`partial_outage`/`full_outage`; maintenances support `operational`/`under_maintenance`. [operational, degraded, partial_outage, full_outage, under_maintenance]\n - description (string) — Update description (Markdown).\n - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]\n - update_id (string) (required) — Update ID.\n", + "StatusPages.ChangeTimelineCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - update_id (string) (required) — Newly created update ID.\n", + "StatusPages.MigrateEmailSubscribers": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - job_id (string) (required) — Migration job ID. Use this to poll status or request cancellation.\n", + "StatusPages.MigrateStructure": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - job_id (string) (required) — Migration job ID. Use this to poll status or request cancellation.\n", + "StatusPages.MigrationStatus": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owner account ID.\n - created_at (integer) (required) — Job creation time, unix seconds.\n - error (string) — Terminal error message when `status` is `failed`.\n - job_id (string) (required) — Migration job ID.\n - phase (string) (required) — Current migration phase. [structure, history, subscribers]\n - progress (object) (required) — Progress counters for a migration job.\n - completed_steps (integer) (required) — Steps completed so far.\n - components_imported (integer) (required)\n - incidents_imported (integer) (required)\n - maintenances_imported (integer) (required)\n - sections_imported (integer) (required)\n - subscribers_imported (integer) (required)\n - subscribers_skipped (integer) (required) — Number of subscribers skipped (e.g. because they would create duplicates).\n - templates_imported (integer) (required)\n - total_steps (integer) (required) — Total steps this job will perform.\n - warnings (array) — Non-fatal warnings recorded during the job.\n - source_page_id (string) (required) — Atlassian Statuspage source page ID.\n - status (string) (required) — Current job status. [pending, running, completed, failed, cancelled]\n - target_page_id (integer) (required) — Flashduty target status page ID. Set once the job produces one, or supplied up front for subscriber migration.\n - updated_at (integer) (required) — Last status update time, unix seconds.\n", + "StatusPages.ReadPageList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - components (array) — Components tracked on the status page.\n - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds.\n - component_id (string) — Component ID.\n - description (string) — Component description.\n - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints.\n - hide_uptime (boolean) — When true, uptime data is hidden from summary responses.\n - name (string) (required) — Component display name.\n - order_id (integer) — Display order within its section.\n - section_id (string) — Parent section ID.\n - contact_info (string) — Get-in-touch contact, a mailto or website URL.\n - custom_domain (string) — Custom domain pointing to the status page.\n - custom_links (array) — Custom navigation links shown on the status page.\n - dark_logo (string) — Dark-mode logo image of the status page.\n - date_view (string) — How the timeline is displayed. [calendar, list]\n - display_uptime_mode (string) — How uptime is displayed. [chart_and_percentage, chart, none]\n - favicon (string) — Favicon of the status page.\n - logo (string) — Logo image of the status page.\n - logo_url (string) — URL opened when the logo is clicked.\n - name (string) — Display name of the status page.\n - page_footer (string) — Footer content of the status page.\n - page_header (string) — Header content of the status page.\n - page_id (integer) — Status page ID.\n - sections (array) — Sections grouping the components.\n - description (string) — Section description.\n - hide_all (boolean) — Whether the section and its components are hidden from summary endpoints.\n - hide_uptime (boolean) — Whether uptime data is hidden from summary responses.\n - name (string) — Section name.\n - order_id (integer) — Display order of the section.\n - section_id (string) — Section ID.\n - subscription (object)\n - email (boolean) — Whether email subscription is enabled.\n - im (boolean) — Whether IM subscription is enabled.\n - template_preference (string) — Preferred change-event template type.\n - type (string) — Visibility type of the status page. [public, internal]\n - url_name (string) — URL-safe slug, unique per account.\n", + "StatusPages.SubscriberList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - all (boolean) (required) — Whether the subscriber is subscribed to all components.\n - components (array) (required) — Components this subscriber has subscribed to.\n - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds.\n - component_id (string) — Component ID.\n - description (string) — Component description.\n - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints.\n - hide_uptime (boolean) — When true, uptime data is hidden from summary responses.\n - name (string) (required) — Component display name.\n - order_id (integer) — Display order within its section.\n - section_id (string) — Parent section ID.\n - locale (string) — Preferred locale for notifications.\n - method (string) (required) — Subscription delivery method. [email, im]\n - recipient (string) (required) — Subscriber recipient: email address for public pages, user ID for internal pages.\n", + "Teams.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - created_at (integer) (required) — Unix epoch seconds the team was created.\n - creator_id (integer) (required) — Member ID of the creator.\n - creator_name (string) (required) — Display name of the creator.\n - description (string) (required) — Free-form description.\n - person_ids (array) (required) — Member IDs of team members.\n - ref_id (string) (required) — External reference ID for third-party HR system integration.\n - status (string) (required) — Team status. [enabled, disabled]\n - team_id (integer) (required) — Unique team ID.\n - team_name (string) (required) — Team display name. 1–39 characters, unique per account.\n - updated_at (integer) (required) — Unix epoch seconds the team was last updated.\n - updated_by (integer) (required) — Member ID of the last editor.\n - updated_by_name (string) (required) — Display name of the last editor.\n", + "Teams.ReadInfos": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - person_ids (array)\n - team_id (integer)\n - team_name (string)\n", + "Teams.ReadList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - account_id (integer) (required) — Owning account ID.\n - created_at (integer) (required) — Unix epoch seconds the team was created.\n - creator_id (integer) (required) — Member ID of the creator.\n - creator_name (string) (required) — Display name of the creator.\n - description (string) (required) — Free-form description.\n - person_ids (array) (required) — Member IDs of team members.\n - ref_id (string) (required) — External reference ID for third-party HR system integration.\n - status (string) (required) — Team status. [enabled, disabled]\n - team_id (integer) (required) — Unique team ID.\n - team_name (string) (required) — Team display name. 1–39 characters, unique per account.\n - updated_at (integer) (required) — Unix epoch seconds the team was last updated.\n - updated_by (integer) (required) — Member ID of the last editor.\n - updated_by_name (string) (required) — Display name of the last editor.\n", + "Teams.WriteUpsert": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - team_id (integer) (required) — Created or updated team ID.\n - team_name (string) (required) — Team name echoed from the request.\n", +} diff --git a/internal/cli/zz_generated_roles_permissions.go b/internal/cli/zz_generated_roles_permissions.go new file mode 100644 index 0000000..113677a --- /dev/null +++ b/internal/cli/zz_generated_roles_permissions.go @@ -0,0 +1,556 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genRolesPermissionsReadInfoCmd() *cobra.Command { + var dataJSON string + var fRoleID int64 + cmd := &cobra.Command{ + Use: "info", + Short: "Get role detail", + Long: `Get role detail. + +Return the detail of a single role by its ID. + +API: POST /role/info (role-read-info) + +Request fields: + --role-id int (required) — Role ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - created_at (integer) (required) — Unix epoch seconds the role was created. + - description (string) (required) — Role description. + - editable (boolean) (required) — False for built-in roles which cannot be modified. + - permission_ids (array) (required) — IDs of permissions granted by this role. + - role_id (integer) (required) — Unique role ID. + - role_name (string) (required) — Role display name. + - status (string) (required) — Role status. [enabled, disabled] + - updated_at (integer) (required) — Unix epoch seconds the role was last updated. +`, + Example: ` flashduty role info --data '{"role_id":2}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RolesPermissions.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsReadListCmd() *cobra.Command { + var dataJSON string + var fAsc bool + var fOrderby string + cmd := &cobra.Command{ + Use: "list", + Short: "List roles", + Long: `List roles. + +Return all custom and built-in roles for the current account. + +API: POST /role/list (role-read-list) + +Request fields: + --asc bool — Ascending sort order. + --orderby string — Sort field. [created_at, updated_at] + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - created_at (integer) (required) — Unix epoch seconds the role was created. + - description (string) (required) — Role description. + - editable (boolean) (required) — False for built-in roles which cannot be modified. + - permission_ids (array) (required) — IDs of permissions granted by this role. + - role_id (integer) (required) — Unique role ID. + - role_name (string) (required) — Role display name. + - status (string) (required) — Role status. [enabled, disabled] + - updated_at (integer) (required) — Unix epoch seconds the role was last updated. + - total (integer) (required) — Total role count. +`, + Example: ` flashduty role list --data '{"asc":false,"orderby":"created_at"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RolesPermissions.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending sort order.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsReadListPermissionCmd() *cobra.Command { + var dataJSON string + var fRoleIDs []int + var fWithAll bool + cmd := &cobra.Command{ + Use: "permission-list", + Short: "List permissions", + Long: `List permissions. + +Return all available permissions, optionally filtered to those granted to specific roles. + +API: POST /role/permission/list (role-read-list-permission) + +Request fields: + --role-ids []int — Filter to permissions granted to these roles. + --with-all bool — If true, return all permissions with is_granted set to indicate which are granted. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - class (string) (required) — Permission class (e.g., 'On-call', 'Organization'). + - description (string) (required) — Human-readable permission description. + - id (integer) (required) — Unique permission ID. + - is_granted (boolean) — Present when with_all is true. Indicates whether this permission is granted to the requested roles. + - permission_name (string) (required) — Permission display name. + - permission_type (string) (required) — Whether this is a read or manage permission. [read, manage] + - scope (string) (required) — Permission scope (e.g., 'on-call', 'organization'). + - status (string) (required) — Permission status. [enabled, disabled] +`, + Example: ` flashduty role permission-list --data '{"role_ids":[150],"with_all":true}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("role-ids") { + body["role_ids"] = fRoleIDs + } + if cmd.Flags().Changed("with-all") { + body["with_all"] = fWithAll + } + }) + if err != nil { + return err + } + req := new(flashduty.RolePermissionListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RolesPermissions.ReadListPermission(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "Filter to permissions granted to these roles.") + cmd.Flags().BoolVar(&fWithAll, "with-all", false, "If true, return all permissions with is_granted set to indicate which are granted.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsReadListPermissionFactorCmd() *cobra.Command { + var dataJSON string + var fFactorTypes []string + cmd := &cobra.Command{ + Use: "permission-factor-list", + Short: "List permission factors", + Long: `List permission factors. + +Return all permission factors (API, button, menu, URL, visit) optionally filtered by type. + +API: POST /role/permission/factor/list (role-read-list-permission-factor) + +Request fields: + --factor-types []string — Filter by factor type. [api, button, visit, menu, url] + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - factor_name (string) (required) — Factor identifier (e.g., 'template:read:info'). + - factor_type (string) (required) — Factor type. [api, button, visit, menu, url] +`, + Example: ` flashduty role permission-factor-list --data '{"factor_types":["api"]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("factor-types") { + body["factor_types"] = fFactorTypes + } + }) + if err != nil { + return err + } + req := new(flashduty.PermissionFactorListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RolesPermissions.ReadListPermissionFactor(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringSliceVar(&fFactorTypes, "factor-types", nil, "Filter by factor type. [api, button, visit, menu, url]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fRoleID int64 + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a role", + Long: `Delete a role. + +Permanently delete a custom role and revoke it from all members. + +API: POST /role/delete (role-write-delete) + +Request fields: + --role-id int (required) — Role ID. +`, + Example: ` flashduty role delete --data '{"role_id":150}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.RolesPermissions.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /role/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsWriteDisableCmd() *cobra.Command { + var dataJSON string + var fRoleID int64 + cmd := &cobra.Command{ + Use: "disable", + Short: "Disable a role", + Long: `Disable a role. + +Disable a custom role to prevent it from granting permissions. + +API: POST /role/disable (role-write-disable) + +Request fields: + --role-id int (required) — Role ID. +`, + Example: ` flashduty role disable --data '{"role_id":150}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.RolesPermissions.WriteDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /role/disable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsWriteEnableCmd() *cobra.Command { + var dataJSON string + var fRoleID int64 + cmd := &cobra.Command{ + Use: "enable", + Short: "Enable a role", + Long: `Enable a role. + +Re-enable a previously disabled custom role. + +API: POST /role/enable (role-write-enable) + +Request fields: + --role-id int (required) — Role ID. +`, + Example: ` flashduty role enable --data '{"role_id":150}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleIDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.RolesPermissions.WriteEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /role/enable") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsWriteGrantRoleCmd() *cobra.Command { + var dataJSON string + var fMemberIDs []int + var fRoleID int64 + cmd := &cobra.Command{ + Use: "member-grant", + Short: "Grant role to members", + Long: `Grant role to members. + +Assign a role to one or more members, giving them its permissions. + +API: POST /role/member/grant (role-write-grant-role) + +Request fields: + --member-ids []int (required) — Member IDs to grant/revoke the role. Max 100. + --role-id int (required) — Role ID to grant or revoke. +`, + Example: ` flashduty role member-grant --data '{"member_ids":[80011,80012],"role_id":150}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("member-ids") { + body["member_ids"] = fMemberIDs + } + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleGrantRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.RolesPermissions.WriteGrantRole(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /role/member/grant") + return nil + }) + }, + } + cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Member IDs to grant/revoke the role. Max 100. (required)") + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID to grant or revoke. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsWriteRevokeRoleCmd() *cobra.Command { + var dataJSON string + var fMemberIDs []int + var fRoleID int64 + cmd := &cobra.Command{ + Use: "member-revoke", + Short: "Revoke role from members", + Long: `Revoke role from members. + +Remove a role from one or more members, revoking the permissions it granted. + +API: POST /role/member/revoke (role-write-revoke-role) + +Request fields: + --member-ids []int (required) — Member IDs to grant/revoke the role. Max 100. + --role-id int (required) — Role ID to grant or revoke. +`, + Example: ` flashduty role member-revoke --data '{"member_ids":[80011],"role_id":150}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("member-ids") { + body["member_ids"] = fMemberIDs + } + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleGrantRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.RolesPermissions.WriteRevokeRole(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /role/member/revoke") + return nil + }) + }, + } + cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Member IDs to grant/revoke the role. Max 100. (required)") + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID to grant or revoke. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRolesPermissionsWriteUpsertCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fPermissionIDs []int + var fRoleID int64 + var fRoleName string + cmd := &cobra.Command{ + Use: "upsert", + Short: "Create or update a role", + Long: `Create or update a role. + +Create a new custom role or update an existing one. Pass 'role_id' to update. + +API: POST /role/upsert (role-write-upsert) + +Request fields: + --description string — Role description. (≤499 chars) + --permission-ids []int — Permission IDs to grant. Replaces the existing set. + --role-id int — Role ID. Omit or set to 0 to create. + --role-name string (required) — Role display name. 1–39 characters. (1-39 chars) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - role_id (integer) (required) — Created or updated role ID. + - role_name (string) (required) — Role name echoed from the request. +`, + Example: ` flashduty role upsert --data '{"description":"Manage on-call rotations and incidents.","permission_ids":[501,502],"role_name":"On-call Manager"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("permission-ids") { + body["permission_ids"] = fPermissionIDs + } + if cmd.Flags().Changed("role-id") { + body["role_id"] = fRoleID + } + if cmd.Flags().Changed("role-name") { + body["role_name"] = fRoleName + } + }) + if err != nil { + return err + } + req := new(flashduty.RoleUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RolesPermissions.WriteUpsert(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Role description. (≤499 chars)") + cmd.Flags().IntSliceVar(&fPermissionIDs, "permission-ids", nil, "Permission IDs to grant. Replaces the existing set.") + cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. Omit or set to 0 to create.") + cmd.Flags().StringVar(&fRoleName, "role-name", "", "Role display name. 1–39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedRolesPermissions(root *cobra.Command) { + gRole := genGroup(root, "role", "Platform/Roles & permissions API") + genAddLeaf(gRole, genRolesPermissionsReadInfoCmd()) + genAddLeaf(gRole, genRolesPermissionsReadListCmd()) + genAddLeaf(gRole, genRolesPermissionsReadListPermissionCmd()) + genAddLeaf(gRole, genRolesPermissionsReadListPermissionFactorCmd()) + genAddLeaf(gRole, genRolesPermissionsWriteDeleteCmd()) + genAddLeaf(gRole, genRolesPermissionsWriteDisableCmd()) + genAddLeaf(gRole, genRolesPermissionsWriteEnableCmd()) + genAddLeaf(gRole, genRolesPermissionsWriteGrantRoleCmd()) + genAddLeaf(gRole, genRolesPermissionsWriteRevokeRoleCmd()) + genAddLeaf(gRole, genRolesPermissionsWriteUpsertCmd()) +} diff --git a/internal/cli/zz_generated_rule_sets.go b/internal/cli/zz_generated_rule_sets.go new file mode 100644 index 0000000..f1ebea3 --- /dev/null +++ b/internal/cli/zz_generated_rule_sets.go @@ -0,0 +1,321 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genRuleSetsCreateCmd() *cobra.Command { + var dataJSON string + var fNote string + var fOpenFlag int64 + var fPayload string + var fTypeIdent string + cmd := &cobra.Command{ + Use: "store-ruleset-create", + Short: "Create ruleset", + Long: `Create ruleset. + +Create a new ruleset in the rule repository. + +API: POST /monit/store/ruleset/create (monit-store-ruleset-create) + +Request fields: + --note string (required) — Description or title of the ruleset. + --open-flag int — Sharing scope. '0' = private (creator only), '1' = account-shared, '2' = public. Defaults to '0' if omitted. + --payload string (required) — JSON string containing the alert rule definitions. + --type-ident string (required) — Datasource type identifier this ruleset applies to, e.g. 'prometheus'. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - created_at (integer) (required) — Creation timestamp, Unix epoch seconds. + - creator_account_id (integer) (required) — Account ID of the creator. + - creator_id (integer) (required) — Member ID of the creator. + - creator_name (string) (required) — Display name of the creator. + - id (integer) (required) — Ruleset ID. + - note (string) (required) — Description or title of the ruleset. + - open_flag (integer) (required) — Sharing scope. '0' = private (creator only), '1' = account-shared, '2' = public. + - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses. + - type_ident (string) (required) — Datasource type identifier this ruleset applies to. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit store-ruleset-create --data '{"note":"CPU usage alerts","open_flag":1,"payload":"[{\"prom_ql\":\"rate(cpu_usage[5m]) \u003e 0.8\"}]","type_ident":"prometheus"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("note") { + body["note"] = fNote + } + if cmd.Flags().Changed("open-flag") { + body["open_flag"] = fOpenFlag + } + if cmd.Flags().Changed("payload") { + body["payload"] = fPayload + } + if cmd.Flags().Changed("type-ident") { + body["type_ident"] = fTypeIdent + } + }) + if err != nil { + return err + } + req := new(flashduty.StoreRulesetUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RuleSets.Create(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fNote, "note", "", "Description or title of the ruleset. (required)") + cmd.Flags().Int64Var(&fOpenFlag, "open-flag", 0, "Sharing scope. '0' = private (creator only), '1' = account-shared, '2' = public. Defaults to '0' if omitted.") + cmd.Flags().StringVar(&fPayload, "payload", "", "JSON string containing the alert rule definitions. (required)") + cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier this ruleset applies to, e.g. 'prometheus'. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRuleSetsDeleteCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "store-ruleset-delete", + Short: "Delete ruleset", + Long: `Delete ruleset. + +Delete a ruleset from the rule repository by ID. + +API: POST /monit/store/ruleset/delete (monit-store-ruleset-delete) + +Request fields: + --id int (required) — Resource ID. +`, + Example: ` flashduty monit store-ruleset-delete --data '{"id":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.IDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.RuleSets.Delete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /monit/store/ruleset/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRuleSetsInfoCmd() *cobra.Command { + var dataJSON string + var fID int64 + cmd := &cobra.Command{ + Use: "store-ruleset-info", + Short: "Get ruleset detail", + Long: `Get ruleset detail. + +Retrieve the full details of a ruleset including its 'payload' (the alert rule definitions as a JSON string). + +API: POST /monit/store/ruleset/info (monit-store-ruleset-info) + +Request fields: + --id int (required) — Resource ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - created_at (integer) (required) — Creation timestamp, Unix epoch seconds. + - creator_account_id (integer) (required) — Account ID of the creator. + - creator_id (integer) (required) — Member ID of the creator. + - creator_name (string) (required) — Display name of the creator. + - id (integer) (required) — Ruleset ID. + - note (string) (required) — Description or title of the ruleset. + - open_flag (integer) (required) — Sharing scope. '0' = private (creator only), '1' = account-shared, '2' = public. + - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses. + - type_ident (string) (required) — Datasource type identifier this ruleset applies to. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit store-ruleset-info --data '{"id":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + }) + if err != nil { + return err + } + req := new(flashduty.IDRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RuleSets.Info(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRuleSetsListCmd() *cobra.Command { + var dataJSON string + var fTypeIdent string + cmd := &cobra.Command{ + Use: "store-ruleset-list", + Short: "List rulesets", + Long: `List rulesets. + +Return all rulesets for a given datasource type that are accessible to the current user. + +API: POST /monit/store/ruleset/list (monit-store-ruleset-list) + +Request fields: + --type-ident string (required) — Datasource type identifier to filter by, e.g. 'prometheus'. + +Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq '.[]'', NOT '.items[]'): + - created_at (integer) (required) — Creation timestamp, Unix epoch seconds. + - creator_account_id (integer) (required) — Account ID of the creator. + - creator_id (integer) (required) — Member ID of the creator. + - creator_name (string) (required) — Display name of the creator. + - id (integer) (required) — Ruleset ID. + - note (string) (required) — Description or title of the ruleset. + - open_flag (integer) (required) — Sharing scope. '0' = private (creator only), '1' = account-shared, '2' = public. + - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses. + - type_ident (string) (required) — Datasource type identifier this ruleset applies to. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit store-ruleset-list --data '{"type_ident":"prometheus"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("type-ident") { + body["type_ident"] = fTypeIdent + } + }) + if err != nil { + return err + } + req := new(flashduty.StoreRulesetListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RuleSets.List(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier to filter by, e.g. 'prometheus'. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genRuleSetsUpdateCmd() *cobra.Command { + var dataJSON string + var fID int64 + var fNote string + var fOpenFlag int64 + var fPayload string + cmd := &cobra.Command{ + Use: "store-ruleset-update", + Short: "Update ruleset", + Long: `Update ruleset. + +Update the note, sharing flag, and payload of an existing ruleset. + +API: POST /monit/store/ruleset/update (monit-store-ruleset-update) + +Request fields: + --id int (required) — Ruleset ID to update. + --note string (required) — New description. + --open-flag int — New sharing scope. '0' = private, '1' = account-shared, '2' = public. + --payload string (required) — New JSON string of alert rule definitions. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - created_at (integer) (required) — Creation timestamp, Unix epoch seconds. + - creator_account_id (integer) (required) — Account ID of the creator. + - creator_id (integer) (required) — Member ID of the creator. + - creator_name (string) (required) — Display name of the creator. + - id (integer) (required) — Ruleset ID. + - note (string) (required) — Description or title of the ruleset. + - open_flag (integer) (required) — Sharing scope. '0' = private (creator only), '1' = account-shared, '2' = public. + - payload (string) — JSON string containing the alert rule definitions. Omitted in list responses. + - type_ident (string) (required) — Datasource type identifier this ruleset applies to. + - updated_at (integer) (required) — Last update timestamp, Unix epoch seconds. +`, + Example: ` flashduty monit store-ruleset-update --data '{"id":1,"note":"Updated CPU alerts","open_flag":2,"payload":"[{\"prom_ql\":\"rate(cpu_usage[5m]) \u003e 0.9\"}]"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("id") { + body["id"] = fID + } + if cmd.Flags().Changed("note") { + body["note"] = fNote + } + if cmd.Flags().Changed("open-flag") { + body["open_flag"] = fOpenFlag + } + if cmd.Flags().Changed("payload") { + body["payload"] = fPayload + } + }) + if err != nil { + return err + } + req := new(flashduty.StoreRulesetUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.RuleSets.Update(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fID, "id", 0, "Ruleset ID to update. (required)") + cmd.Flags().StringVar(&fNote, "note", "", "New description. (required)") + cmd.Flags().Int64Var(&fOpenFlag, "open-flag", 0, "New sharing scope. '0' = private, '1' = account-shared, '2' = public.") + cmd.Flags().StringVar(&fPayload, "payload", "", "New JSON string of alert rule definitions. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedRuleSets(root *cobra.Command) { + gMonit := genGroup(root, "monit", "Monitors API") + genAddLeaf(gMonit, genRuleSetsCreateCmd()) + genAddLeaf(gMonit, genRuleSetsDeleteCmd()) + genAddLeaf(gMonit, genRuleSetsInfoCmd()) + genAddLeaf(gMonit, genRuleSetsListCmd()) + genAddLeaf(gMonit, genRuleSetsUpdateCmd()) +} diff --git a/internal/cli/zz_generated_schedules.go b/internal/cli/zz_generated_schedules.go new file mode 100644 index 0000000..874541f --- /dev/null +++ b/internal/cli/zz_generated_schedules.go @@ -0,0 +1,1366 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genSchedulesCreateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fEnd int64 + var fName string + var fScheduleID int64 + var fScheduleName string + var fStart int64 + var fTeamID int64 + cmd := &cobra.Command{ + Use: "create", + Short: "Create schedule", + Long: `Create schedule. + +Create a new on-call schedule (escalation rule schedule). + +API: POST /schedule/create (scheduleCreate) + +Request fields: + --description string — Schedule description. Max 500 characters. (≤500 chars) + --end int — Preview window end (Unix seconds, 10 digits). Required for /schedule/preview. Max 45 days after start. + --name string — Legacy schedule name field. Used when schedule_name is empty. (≤40 chars) + --schedule-id int — Schedule ID. Required on update. + --schedule-name string — Schedule display name. Max 40 characters. (≤40 chars) + --start int — Preview window start (Unix seconds, 10 digits). Required for /schedule/preview. + --team-id int — Owning team ID. + layers (array, via --data) — Rotation layers. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + notify (object, via --data) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - alias (string) (required) — Channel alias. + - chat_ids (array) (required) — Chat IDs. + - data_source_id (integer) (required) — Data source ID. + - sign_secret (string) (required) — Signature secret. + - token (string) (required) — Webhook token. + - verify_token (string) (required) — Verification token. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - schedule_id (integer) (required) — ID of the newly created schedule. +`, + Example: ` flashduty schedule create --data '{"description":"Primary on-call rotation for the production team","layers":[{"day_mask":{"repeat":[1,2,3,4,5]},"enable_time":1712000000,"expire_time":0,"fair_rotation":false,"groups":[{"end":0,"group_name":"A","members":[{"person_ids":[2451002751131],"role_id":0}],"name":"A","start":0},{"end":0,"group_name":"B","members":[{"person_ids":[2476123212131],"role_id":0}],"name":"B","start":0}],"handoff_time":0,"hidden":0,"layer_name":"Layer 1","mask_continuous_enabled":false,"mode":0,"name":"Layer 1","restrict_end":0,"restrict_mode":0,"restrict_periods":[],"restrict_start":0,"rotation_duration":86400,"rotation_unit":"day","rotation_value":1,"weight":0}],"notify":{"advance_in_time":300,"by":{"follow_preference":true,"personal_channels":null},"fixed_time":null,"webhooks":null},"schedule_name":"Production On-Call","team_id":4291079133131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("end") { + body["end"] = fEnd + } + if cmd.Flags().Changed("name") { + body["name"] = fName + } + if cmd.Flags().Changed("schedule-id") { + body["schedule_id"] = fScheduleID + } + if cmd.Flags().Changed("schedule-name") { + body["schedule_name"] = fScheduleName + } + if cmd.Flags().Changed("start") { + body["start"] = fStart + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Schedules.Create(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Schedule description. Max 500 characters. (≤500 chars)") + cmd.Flags().Int64Var(&fEnd, "end", 0, "Preview window end (Unix seconds, 10 digits). Required for /schedule/preview. Max 45 days after start.") + cmd.Flags().StringVar(&fName, "name", "", "Legacy schedule name field. Used when schedule_name is empty. (≤40 chars)") + cmd.Flags().Int64Var(&fScheduleID, "schedule-id", 0, "Schedule ID. Required on update.") + cmd.Flags().StringVar(&fScheduleName, "schedule-name", "", "Schedule display name. Max 40 characters. (≤40 chars)") + cmd.Flags().Int64Var(&fStart, "start", 0, "Preview window start (Unix seconds, 10 digits). Required for /schedule/preview.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSchedulesDeleteCmd() *cobra.Command { + var dataJSON string + var fScheduleIDs []int + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete schedules", + Long: `Delete schedules. + +Delete one or more on-call schedules by ID. + +API: POST /schedule/delete (scheduleDelete) + +Request fields: + --schedule-ids []int (required) — Schedule IDs to operate on. +`, + Example: ` flashduty schedule delete --data '{"schedule_ids":[2001]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("schedule-ids") { + body["schedule_ids"] = fScheduleIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleIDsBodyRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Schedules.Delete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /schedule/delete") + return nil + }) + }, + } + cmd.Flags().IntSliceVar(&fScheduleIDs, "schedule-ids", nil, "Schedule IDs to operate on. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSchedulesInfoCmd() *cobra.Command { + var dataJSON string + var fEnd int64 + var fScheduleID int64 + var fStart int64 + cmd := &cobra.Command{ + Use: "info", + Short: "Get schedule info", + Long: `Get schedule info. + +Return details of an on-call schedule including the computed schedule layers for the requested time window (max 45 days). + +API: POST /schedule/info (scheduleInfo) + +Request fields: + --end int (required) — Preview end timestamp (Unix seconds, 10 digits). + --schedule-id int (required) — Schedule ID. + --start int (required) — Preview start timestamp (Unix seconds, 10 digits). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - cur_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - description (any) (required) — Schedule description. null when returned from /schedule/preview. + - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview. + - end (integer) — Window end (Unix seconds). + - field (string) — Field name used by the legacy update-field endpoint. + - final_schedule (object) (required) — Computed schedule for a single layer. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview. + - id (any) (required) — Schedule ID. null when returned from /schedule/preview. + - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - layers (array) (required) — Rotation layers defined on the schedule. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview. + - next_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - notify (object) (required) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - alias (string) (required) — Channel alias. + - chat_ids (array) (required) — Chat IDs. + - data_source_id (integer) (required) — Data source ID. + - sign_secret (string) (required) — Signature secret. + - token (string) (required) — Webhook token. + - verify_token (string) (required) — Verification token. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). + - schedule_id (integer) (required) — Schedule ID. + - schedule_layers (array) (required) — Computed layers for the requested window. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview. + - start (integer) — Window start (Unix seconds). + - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview. + - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. +`, + Example: ` flashduty schedule info --data '{"end":1712086400,"schedule_id":2001,"start":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("end") { + body["end"] = fEnd + } + if cmd.Flags().Changed("schedule-id") { + body["schedule_id"] = fScheduleID + } + if cmd.Flags().Changed("start") { + body["start"] = fStart + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Schedules.Info(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fEnd, "end", 0, "Preview end timestamp (Unix seconds, 10 digits). (required)") + cmd.Flags().Int64Var(&fScheduleID, "schedule-id", 0, "Schedule ID. (required)") + cmd.Flags().Int64Var(&fStart, "start", 0, "Preview start timestamp (Unix seconds, 10 digits). (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSchedulesInfosCmd() *cobra.Command { + var dataJSON string + var fScheduleIDs []int + cmd := &cobra.Command{ + Use: "infos", + Short: "Batch get schedules", + Long: `Batch get schedules. + +Return details of multiple on-call schedules by their IDs. + +API: POST /schedule/infos (scheduleInfos) + +Request fields: + --schedule-ids []int (required) — Schedule ID list. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Schedules assigned to the current user (or matching the requested IDs). + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - cur_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - description (any) (required) — Schedule description. null when returned from /schedule/preview. + - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview. + - end (integer) — Window end (Unix seconds). + - field (string) — Field name used by the legacy update-field endpoint. + - final_schedule (object) (required) — Computed schedule for a single layer. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview. + - id (any) (required) — Schedule ID. null when returned from /schedule/preview. + - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - layers (array) (required) — Rotation layers defined on the schedule. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview. + - next_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - notify (object) (required) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). + - schedule_id (integer) (required) — Schedule ID. + - schedule_layers (array) (required) — Computed layers for the requested window. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview. + - start (integer) — Window start (Unix seconds). + - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview. + - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. +`, + Example: ` flashduty schedule infos --data '{"schedule_ids":[2001,2002,2003]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("schedule-ids") { + body["schedule_ids"] = fScheduleIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleIDsRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Schedules.Infos(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fScheduleIDs, "schedule-ids", nil, "Schedule ID list. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSchedulesListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fEnd int64 + var fIsMyManage bool + var fIsMyTeam bool + var fQuery string + var fStart int64 + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "list", + Short: "List schedules", + Long: `List schedules. + +Return a paginated list of on-call schedules. When both start and end are provided (max 45 days apart), computed layer schedules are included. + +API: POST /schedule/list (scheduleList) + +Request fields: + --page int — Page number (1-indexed). + --limit int — Page size. Default 10, max 100. (max 100) + --search-after-ctx string + --end int — Window end timestamp (Unix seconds). + --is-my-manage bool — Only return schedules created by the current user within their teams. + --is-my-team bool — Only return schedules whose owning team the current user belongs to. + --query string — Search keyword matched against schedule names. + --start int — When set together with end, computed layer schedules are returned. Span must be less than 45 days. + --team-ids []int — Filter by team IDs. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Schedules on this page. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - cur_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - description (any) (required) — Schedule description. null when returned from /schedule/preview. + - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview. + - end (integer) — Window end (Unix seconds). + - field (string) — Field name used by the legacy update-field endpoint. + - final_schedule (object) (required) — Computed schedule for a single layer. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview. + - id (any) (required) — Schedule ID. null when returned from /schedule/preview. + - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - layers (array) (required) — Rotation layers defined on the schedule. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview. + - next_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - notify (object) (required) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). + - schedule_id (integer) (required) — Schedule ID. + - schedule_layers (array) (required) — Computed layers for the requested window. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview. + - start (integer) — Window start (Unix seconds). + - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview. + - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - total (integer) (required) — Total number of schedules matching the filters. +`, + Example: ` flashduty schedule list --data '{"is_my_team":true,"limit":20,"p":1,"query":"production"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("end") { + body["end"] = fEnd + } + if cmd.Flags().Changed("is-my-manage") { + body["is_my_manage"] = fIsMyManage + } + if cmd.Flags().Changed("is-my-team") { + body["is_my_team"] = fIsMyTeam + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("start") { + body["start"] = fStart + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Schedules.List(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number (1-indexed).") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Default 10, max 100. (max 100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().Int64Var(&fEnd, "end", 0, "Window end timestamp (Unix seconds).") + cmd.Flags().BoolVar(&fIsMyManage, "is-my-manage", false, "Only return schedules created by the current user within their teams.") + cmd.Flags().BoolVar(&fIsMyTeam, "is-my-team", false, "Only return schedules whose owning team the current user belongs to.") + cmd.Flags().StringVar(&fQuery, "query", "", "Search keyword matched against schedule names.") + cmd.Flags().Int64Var(&fStart, "start", 0, "When set together with end, computed layer schedules are returned. Span must be less than 45 days.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSchedulesPreviewCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fEnd int64 + var fName string + var fScheduleID int64 + var fScheduleName string + var fStart int64 + var fTeamID int64 + cmd := &cobra.Command{ + Use: "preview", + Short: "Preview schedule", + Long: `Preview schedule. + +Preview the coverage generated by a schedule configuration without persisting it. The request accepts the same body as create/update plus a required start/end window (max 45 days). + +API: POST /schedule/preview (schedulePreview) + +Request fields: + --description string — Schedule description. Max 500 characters. (≤500 chars) + --end int — Preview window end (Unix seconds, 10 digits). Required for /schedule/preview. Max 45 days after start. + --name string — Legacy schedule name field. Used when schedule_name is empty. (≤40 chars) + --schedule-id int — Schedule ID. Required on update. + --schedule-name string — Schedule display name. Max 40 characters. (≤40 chars) + --start int — Preview window start (Unix seconds, 10 digits). Required for /schedule/preview. + --team-id int — Owning team ID. + layers (array, via --data) — Rotation layers. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + notify (object, via --data) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - alias (string) (required) — Channel alias. + - chat_ids (array) (required) — Chat IDs. + - data_source_id (integer) (required) — Data source ID. + - sign_secret (string) (required) — Signature secret. + - token (string) (required) — Webhook token. + - verify_token (string) (required) — Verification token. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - cur_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - description (any) (required) — Schedule description. null when returned from /schedule/preview. + - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview. + - end (integer) — Window end (Unix seconds). + - field (string) — Field name used by the legacy update-field endpoint. + - final_schedule (object) (required) — Computed schedule for a single layer. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview. + - id (any) (required) — Schedule ID. null when returned from /schedule/preview. + - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - layers (array) (required) — Rotation layers defined on the schedule. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview. + - next_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - notify (object) (required) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - alias (string) (required) — Channel alias. + - chat_ids (array) (required) — Chat IDs. + - data_source_id (integer) (required) — Data source ID. + - sign_secret (string) (required) — Signature secret. + - token (string) (required) — Webhook token. + - verify_token (string) (required) — Verification token. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). + - schedule_id (integer) (required) — Schedule ID. + - schedule_layers (array) (required) — Computed layers for the requested window. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview. + - start (integer) — Window start (Unix seconds). + - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview. + - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. +`, + Example: ` flashduty schedule preview --data '{"end":1712086400,"layers":[{"day_mask":{"repeat":[1,2,3,4,5]},"enable_time":1712000000,"expire_time":0,"fair_rotation":false,"groups":[{"end":0,"group_name":"A","members":[{"person_ids":[2451002751131],"role_id":0}],"name":"A","start":0}],"handoff_time":0,"hidden":0,"layer_name":"Layer 1","mask_continuous_enabled":false,"mode":0,"name":"Layer 1","restrict_end":0,"restrict_mode":0,"restrict_periods":[],"restrict_start":0,"rotation_duration":86400,"rotation_unit":"day","rotation_value":1,"weight":0}],"schedule_name":"Preview Schedule","start":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("end") { + body["end"] = fEnd + } + if cmd.Flags().Changed("name") { + body["name"] = fName + } + if cmd.Flags().Changed("schedule-id") { + body["schedule_id"] = fScheduleID + } + if cmd.Flags().Changed("schedule-name") { + body["schedule_name"] = fScheduleName + } + if cmd.Flags().Changed("start") { + body["start"] = fStart + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Schedules.Preview(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Schedule description. Max 500 characters. (≤500 chars)") + cmd.Flags().Int64Var(&fEnd, "end", 0, "Preview window end (Unix seconds, 10 digits). Required for /schedule/preview. Max 45 days after start.") + cmd.Flags().StringVar(&fName, "name", "", "Legacy schedule name field. Used when schedule_name is empty. (≤40 chars)") + cmd.Flags().Int64Var(&fScheduleID, "schedule-id", 0, "Schedule ID. Required on update.") + cmd.Flags().StringVar(&fScheduleName, "schedule-name", "", "Schedule display name. Max 40 characters. (≤40 chars)") + cmd.Flags().Int64Var(&fStart, "start", 0, "Preview window start (Unix seconds, 10 digits). Required for /schedule/preview.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSchedulesSelfCmd() *cobra.Command { + var dataJSON string + var fEnd int64 + var fStart int64 + cmd := &cobra.Command{ + Use: "self", + Short: "List my schedules", + Long: `List my schedules. + +Return on-call schedules where the current user is assigned. + +API: POST /schedule/self (scheduleSelf) + +Request fields: + --end int — Window end (Unix seconds, 10 digits). Must be within 30 days of start. + --start int — Window start (Unix seconds, 10 digits). + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) — Schedules assigned to the current user (or matching the requested IDs). + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - cur_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - description (any) (required) — Schedule description. null when returned from /schedule/preview. + - disabled (any) (required) — Disabled flag (0 = enabled, 1 = disabled). Deprecated. null when returned from /schedule/preview. + - end (integer) — Window end (Unix seconds). + - field (string) — Field name used by the legacy update-field endpoint. + - final_schedule (object) (required) — Computed schedule for a single layer. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - group_id (any) (required) — Legacy team/group ID. null when returned from /schedule/preview. + - id (any) (required) — Schedule ID. null when returned from /schedule/preview. + - layer_schedules (array) (required) — Alias of schedule_layers returned for compatibility. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - layers (array) (required) — Rotation layers defined on the schedule. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + - name (any) (required) — Schedule name (legacy field; mirrors schedule_name). null when returned from /schedule/preview. + - next_oncall (object) (required) — Snapshot of the currently or next on-call group. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - update_at (integer) (required) — Update timestamp (Unix seconds). + - weight (integer) (required) — Layer weight the shift comes from. + - notify (object) (required) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). + - schedule_id (integer) (required) — Schedule ID. + - schedule_layers (array) (required) — Computed layers for the requested window. + - layer_name (string) (required) — Layer display name. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - schedules (array) (required) — Computed shifts. + - end (integer) (required) — Shift end timestamp (Unix seconds). + - group (object) (required) — Oncall group definition within a rotation layer. + - index (integer) (required) — Index inside the rotation. + - start (integer) (required) — Shift start timestamp (Unix seconds). + - schedule_name (any) (required) — Schedule display name. null when returned from /schedule/preview. + - start (integer) — Window start (Unix seconds). + - status (any) (required) — Legacy status flag. Deprecated. null when returned from /schedule/preview. + - team_id (any) (required) — Owning team ID. null when returned from /schedule/preview. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. +`, + Example: ` flashduty schedule self --data '{"end":1712086400,"start":1712000000}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("end") { + body["end"] = fEnd + } + if cmd.Flags().Changed("start") { + body["start"] = fStart + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleSelfRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Schedules.Self(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fEnd, "end", 0, "Window end (Unix seconds, 10 digits). Must be within 30 days of start.") + cmd.Flags().Int64Var(&fStart, "start", 0, "Window start (Unix seconds, 10 digits).") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSchedulesUpdateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fEnd int64 + var fName string + var fScheduleID int64 + var fScheduleName string + var fStart int64 + var fTeamID int64 + cmd := &cobra.Command{ + Use: "update", + Short: "Update schedule", + Long: `Update schedule. + +Update an existing on-call schedule. Provide schedule_id to identify the schedule. + +API: POST /schedule/update (scheduleUpdate) + +Request fields: + --description string — Schedule description. Max 500 characters. (≤500 chars) + --end int — Preview window end (Unix seconds, 10 digits). Required for /schedule/preview. Max 45 days after start. + --name string — Legacy schedule name field. Used when schedule_name is empty. (≤40 chars) + --schedule-id int — Schedule ID. Required on update. + --schedule-name string — Schedule display name. Max 40 characters. (≤40 chars) + --start int — Preview window start (Unix seconds, 10 digits). Required for /schedule/preview. + --team-id int — Owning team ID. + layers (array, via --data) — Rotation layers. + - account_id (integer) (required) — Account ID. + - create_at (integer) (required) — Creation timestamp (Unix seconds). + - create_by (integer) (required) — Creator person ID. + - day_mask (object) (required) — Day-of-week mask for a rotation layer. + - repeat (array) — Weekday numbers (0 = Sunday) included in the rotation. + - enable_time (integer) (required) — When the layer becomes effective (Unix seconds). + - expire_time (integer) (required) — When the layer expires (Unix seconds, 0 means never). + - fair_rotation (boolean) (required) — Whether fair rotation is enabled. + - groups (array) (required) — Oncall groups participating in the rotation. + - end (integer) (required) — Group end timestamp (Unix seconds). + - group_name (string) (required) — Group display name. + - members (array) (required) — Members of this group. + - person_ids (array) (required) — Person IDs in this slot. + - role_id (integer) (required) — Oncall role ID. + - name (string) (required) — Legacy group name. + - start (integer) (required) — Group start timestamp (Unix seconds). + - handoff_time (integer) (required) — Handoff time inside the rotation cycle (seconds). + - hidden (integer) (required) — Whether the layer is hidden in the UI (0 = no, 1 = yes). + - layer_end (any) — Layer end timestamp (Unix seconds). null means open-ended. + - layer_name (string) — User-facing layer name. + - layer_start (integer) — Layer start timestamp (Unix seconds). + - mask_continuous_enabled (boolean) (required) — Whether continuous masking is enabled. + - mode (integer) (required) — Layer mode: 0 = common rotation, 1 = override. + - name (string) (required) — Layer internal name. + - restrict_end (integer) (required) — Legacy end offset inside the restriction window (seconds). + - restrict_mode (integer) (required) — Restriction mode: 0 = none, 1 = day, 2 = week. + - restrict_periods (array) (required) — Restriction windows inside each rotation cycle. + - restrict_end (integer) (required) — End offset inside the rotation cycle. + - restrict_start (integer) (required) — Start offset inside the rotation cycle. + - restrict_start (integer) (required) — Legacy start offset inside the restriction window (seconds). + - rotation_duration (integer) (required) — Rotation duration in seconds. + - rotation_unit (string) (required) — Rotation unit. [hour, day, week, month] + - rotation_value (integer) (required) — Rotation quantity (number of rotation_unit per cycle). + - schedule_id (integer) (required) — Parent schedule ID. + - update_at (integer) (required) — Last update timestamp (Unix seconds). + - update_by (integer) (required) — Last updater person ID. + - weight (integer) (required) — Layer weight for ordering. + notify (object, via --data) — Notification configuration attached to a schedule. + - advance_in_time (integer) — Advance notification lead time (seconds). + - by (object) (required) — Per-recipient notification preference. + - follow_preference (boolean) (required) — Whether to follow each responder's personal notification preference. + - personal_channels (array) (required) — Personal notification channel keys. + - fixed_time (object) (required) — Fixed-time notification config. + - cycle (string) (required) — Notification cycle. + - start (string) (required) — Notification start time within the cycle. + - im (object) — Legacy IM-type to token map. + - webhooks (array) (required) — IM webhook notification channels. + - settings (object) (required) — Settings for an IM webhook notification channel. + - alias (string) (required) — Channel alias. + - chat_ids (array) (required) — Chat IDs. + - data_source_id (integer) (required) — Data source ID. + - sign_secret (string) (required) — Signature secret. + - token (string) (required) — Webhook token. + - verify_token (string) (required) — Verification token. + - type (string) (required) — IM provider type (for example feishu_app, dingtalk_app, wecom_app, teams_app, slack_app). +`, + Example: ` flashduty schedule update --data '{"description":"Updated primary on-call rotation","schedule_id":2001,"schedule_name":"Production On-Call (Updated)","team_id":4291079133131}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("end") { + body["end"] = fEnd + } + if cmd.Flags().Changed("name") { + body["name"] = fName + } + if cmd.Flags().Changed("schedule-id") { + body["schedule_id"] = fScheduleID + } + if cmd.Flags().Changed("schedule-name") { + body["schedule_name"] = fScheduleName + } + if cmd.Flags().Changed("start") { + body["start"] = fStart + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.ScheduleUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Schedules.Update(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /schedule/update") + return nil + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "Schedule description. Max 500 characters. (≤500 chars)") + cmd.Flags().Int64Var(&fEnd, "end", 0, "Preview window end (Unix seconds, 10 digits). Required for /schedule/preview. Max 45 days after start.") + cmd.Flags().StringVar(&fName, "name", "", "Legacy schedule name field. Used when schedule_name is empty. (≤40 chars)") + cmd.Flags().Int64Var(&fScheduleID, "schedule-id", 0, "Schedule ID. Required on update.") + cmd.Flags().StringVar(&fScheduleName, "schedule-name", "", "Schedule display name. Max 40 characters. (≤40 chars)") + cmd.Flags().Int64Var(&fStart, "start", 0, "Preview window start (Unix seconds, 10 digits). Required for /schedule/preview.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedSchedules(root *cobra.Command) { + gSchedule := genGroup(root, "schedule", "On-call/Schedules API") + genAddLeaf(gSchedule, genSchedulesCreateCmd()) + genAddLeaf(gSchedule, genSchedulesDeleteCmd()) + genAddLeaf(gSchedule, genSchedulesInfoCmd()) + genAddLeaf(gSchedule, genSchedulesInfosCmd()) + genAddLeaf(gSchedule, genSchedulesListCmd()) + genAddLeaf(gSchedule, genSchedulesPreviewCmd()) + genAddLeaf(gSchedule, genSchedulesSelfCmd()) + genAddLeaf(gSchedule, genSchedulesUpdateCmd()) +} diff --git a/internal/cli/zz_generated_skills.go b/internal/cli/zz_generated_skills.go new file mode 100644 index 0000000..d0e1a00 --- /dev/null +++ b/internal/cli/zz_generated_skills.go @@ -0,0 +1,491 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genSkillsReadDownloadCmd() *cobra.Command { + var dataJSON string + var fSkillID string + cmd := &cobra.Command{ + Use: "skill-download", + Short: "Download skill", + Long: `Download skill. + +Download the original zip package of a skill as a binary attachment. + +API: POST /safari/skill/download (skill-read-download) + +Request fields: + --skill-id string (required) — Identifier of the skill to download. +`, + Example: ` flashduty safari skill-download --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("skill-id") { + body["skill_id"] = fSkillID + } + }) + if err != nil { + return err + } + req := new(flashduty.SkillDownloadRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Skills.ReadDownload(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to download. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSkillsReadEnableCmd() *cobra.Command { + var dataJSON string + var fSkillID string + cmd := &cobra.Command{ + Use: "skill-enable", + Short: "Enable skill", + Long: `Enable skill. + +Activate a disabled skill so agents can load it at session start. + +API: POST /safari/skill/enable (skill-read-enable) + +Request fields: + --skill-id string (required) — Identifier of the target skill. +`, + Example: ` flashduty safari skill-enable --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("skill-id") { + body["skill_id"] = fSkillID + } + }) + if err != nil { + return err + } + req := new(flashduty.SkillStatusRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Skills.ReadEnable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the target skill. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSkillsReadGetCmd() *cobra.Command { + var dataJSON string + var fSkillID string + cmd := &cobra.Command{ + Use: "skill-get", + Short: "Get skill detail", + Long: `Get skill detail. + +Return the full configuration and SKILL.md body of a single skill by ID. + +API: POST /safari/skill/get (skill-read-get) + +Request fields: + --skill-id string (required) — Identifier of the skill to fetch. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account. + - author (string) — Author declared in the skill frontmatter. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - checksum (string) — SHA-256 checksum of the skill's zip package. + - content (string) — Full SKILL.md body; omitted in list responses. + - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What the skill does and when the agent should use it. + - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it. + - license (string) — License declared in the skill frontmatter. + - s3_key (string) — Object-storage key of the skill's zip package. + - skill_id (string) (required) — Unique identifier of the skill. + - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter. + - source_template_name (string) — Marketplace template this skill was installed from, if any. + - source_template_version (string) — Marketplace template version captured at install time. + - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled] + - tags (array) — Tags declared in the skill frontmatter. + - team_id (integer) (required) — Owning team; 0 means account scope. + - tools (array) — Tools the skill requires, declared in its frontmatter. + - update_available (boolean) (required) — A newer marketplace template version exists for this skill. + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - version (string) — Skill version string from its frontmatter. +`, + Example: ` flashduty safari skill-get --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("skill-id") { + body["skill_id"] = fSkillID + } + }) + if err != nil { + return err + } + req := new(flashduty.SkillGetRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Skills.ReadGet(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to fetch. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSkillsReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fIncludeAccount bool + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "skill-list", + Short: "List skills", + Long: `List skills. + +List AI SRE skills visible to the caller across account and team scopes, with pagination. + +API: POST /safari/skill/list (skill-read-list) + +Request fields: + --page int — Page number, starting at 1. + --limit int — Page size; defaults to 20. + --search-after-ctx string + --include-account bool — Include account-scoped rows alongside team-scoped ones; defaults to true. + --team-ids []int — Restrict results to resources owned by these teams; intersected with the caller's visible set. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - skills (array) (required) — Skills on the current page. + - account_id (integer) (required) — Owning account. + - author (string) — Author declared in the skill frontmatter. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - checksum (string) — SHA-256 checksum of the skill's zip package. + - content (string) — Full SKILL.md body; omitted in list responses. + - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What the skill does and when the agent should use it. + - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it. + - license (string) — License declared in the skill frontmatter. + - s3_key (string) — Object-storage key of the skill's zip package. + - skill_id (string) (required) — Unique identifier of the skill. + - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter. + - source_template_name (string) — Marketplace template this skill was installed from, if any. + - source_template_version (string) — Marketplace template version captured at install time. + - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled] + - tags (array) — Tags declared in the skill frontmatter. + - team_id (integer) (required) — Owning team; 0 means account scope. + - tools (array) — Tools the skill requires, declared in its frontmatter. + - update_available (boolean) (required) — A newer marketplace template version exists for this skill. + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - version (string) — Skill version string from its frontmatter. + - total (integer) (required) — Total number of skills matching the filters. +`, + Example: ` flashduty safari skill-list --data '{"include_account":true,"limit":20,"p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("include-account") { + body["include_account"] = fIncludeAccount + } + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.SkillListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Skills.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size; defaults to 20.") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped rows alongside team-scoped ones; defaults to true.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Restrict results to resources owned by these teams; intersected with the caller's visible set.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSkillsWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fSkillID string + cmd := &cobra.Command{ + Use: "skill-delete", + Short: "Delete skill", + Long: `Delete skill. + +Permanently remove a skill so agents can no longer load it. + +API: POST /safari/skill/delete (skill-write-delete) + +Request fields: + --skill-id string (required) — Identifier of the skill to delete. +`, + Example: ` flashduty safari skill-delete --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("skill-id") { + body["skill_id"] = fSkillID + } + }) + if err != nil { + return err + } + req := new(flashduty.SkillDeleteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Skills.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to delete. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSkillsWriteDisableCmd() *cobra.Command { + var dataJSON string + var fSkillID string + cmd := &cobra.Command{ + Use: "skill-disable", + Short: "Disable skill", + Long: `Disable skill. + +Deactivate an enabled skill so agents no longer load it. + +API: POST /safari/skill/disable (skill-write-disable) + +Request fields: + --skill-id string (required) — Identifier of the target skill. +`, + Example: ` flashduty safari skill-disable --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("skill-id") { + body["skill_id"] = fSkillID + } + }) + if err != nil { + return err + } + req := new(flashduty.SkillStatusRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Skills.WriteDisable(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the target skill. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSkillsWriteUpdateCmd() *cobra.Command { + var dataJSON string + var fDescription string + var fSkillID string + var fTeamID int64 + cmd := &cobra.Command{ + Use: "skill-update", + Short: "Update skill", + Long: `Update skill. + +Edit a skill's description or reassign its owning team. + +API: POST /safari/skill/update (skill-write-update) + +Request fields: + --description string — New description for the skill. (≤1024 chars) + --skill-id string (required) — Identifier of the skill to update. + --team-id int — Reassign the skill to this team; omit to leave unchanged, 0 for account scope. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account. + - author (string) — Author declared in the skill frontmatter. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - checksum (string) — SHA-256 checksum of the skill's zip package. + - content (string) — Full SKILL.md body; omitted in list responses. + - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What the skill does and when the agent should use it. + - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it. + - license (string) — License declared in the skill frontmatter. + - s3_key (string) — Object-storage key of the skill's zip package. + - skill_id (string) (required) — Unique identifier of the skill. + - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter. + - source_template_name (string) — Marketplace template this skill was installed from, if any. + - source_template_version (string) — Marketplace template version captured at install time. + - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled] + - tags (array) — Tags declared in the skill frontmatter. + - team_id (integer) (required) — Owning team; 0 means account scope. + - tools (array) — Tools the skill requires, declared in its frontmatter. + - update_available (boolean) (required) — A newer marketplace template version exists for this skill. + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - version (string) — Skill version string from its frontmatter. +`, + Example: ` flashduty safari skill-update --data '{"description":"Diagnose unhealthy Kubernetes workloads.","skill_id":"skl-7f3a9c21b8e0"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("skill-id") { + body["skill_id"] = fSkillID + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + }) + if err != nil { + return err + } + req := new(flashduty.SkillUpdateRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Skills.WriteUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fDescription, "description", "", "New description for the skill. (≤1024 chars)") + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to update. (required)") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign the skill to this team; omit to leave unchanged, 0 for account scope.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genSkillsWriteUploadCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "skill-upload", + Short: "Upload skill", + Long: `Upload skill. + +Upload a skill zip package and register it as a new account or team skill. + +API: POST /safari/skill/upload (skill-write-upload) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account. + - author (string) — Author declared in the skill frontmatter. + - can_edit (boolean) (required) — Whether the calling member may edit or delete this resource. + - checksum (string) — SHA-256 checksum of the skill's zip package. + - content (string) — Full SKILL.md body; omitted in list responses. + - created (boolean) — Install response only: true for a fresh install, false for an in-place upsert. + - created_at (integer) (required) — Creation time as a Unix timestamp in milliseconds. + - created_by (integer) (required) — Member who created this resource. + - description (string) (required) — What the skill does and when the agent should use it. + - is_modified (boolean) (required) — A marketplace-sourced skill has been edited locally; auto-update skips it. + - license (string) — License declared in the skill frontmatter. + - s3_key (string) — Object-storage key of the skill's zip package. + - skill_id (string) (required) — Unique identifier of the skill. + - skill_name (string) (required) — Name of the skill, parsed from its SKILL.md frontmatter. + - source_template_name (string) — Marketplace template this skill was installed from, if any. + - source_template_version (string) — Marketplace template version captured at install time. + - status (string) (required) — Whether the skill is active and loadable by agents. [enabled, disabled] + - tags (array) — Tags declared in the skill frontmatter. + - team_id (integer) (required) — Owning team; 0 means account scope. + - tools (array) — Tools the skill requires, declared in its frontmatter. + - update_available (boolean) (required) — A newer marketplace template version exists for this skill. + - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. + - version (string) — Skill version string from its frontmatter. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.Skills.WriteUpload(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedSkills(root *cobra.Command) { + gSafari := genGroup(root, "safari", "AI SRE API") + genAddLeaf(gSafari, genSkillsReadDownloadCmd()) + genAddLeaf(gSafari, genSkillsReadEnableCmd()) + genAddLeaf(gSafari, genSkillsReadGetCmd()) + genAddLeaf(gSafari, genSkillsReadListCmd()) + genAddLeaf(gSafari, genSkillsWriteDeleteCmd()) + genAddLeaf(gSafari, genSkillsWriteDisableCmd()) + genAddLeaf(gSafari, genSkillsWriteUpdateCmd()) + genAddLeaf(gSafari, genSkillsWriteUploadCmd()) +} diff --git a/internal/cli/zz_generated_sourcemaps.go b/internal/cli/zz_generated_sourcemaps.go new file mode 100644 index 0000000..9cb8cb1 --- /dev/null +++ b/internal/cli/zz_generated_sourcemaps.go @@ -0,0 +1,143 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genSourcemapsListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fBuildID string + var fEndTime int64 + var fOrderby string + var fQuery string + var fServices []string + var fStartTime int64 + var fType string + var fUuid string + var fVersions []string + cmd := &cobra.Command{ + Use: "list", + Short: "List sourcemaps", + Long: `List sourcemaps. + +Return a paginated list of uploaded sourcemap files filtered by platform type, service, and version. + +API: POST /sourcemap/list (sourcemap-read-list) + +Request fields: + --page int — Page number, starting at 1. (min 1) + --limit int — Page size. Maximum 100. Default 20. (max 100) + --search-after-ctx string + --asc bool — Sort ascending. Default false (descending). + --build-id string — Android only. Filter by Gradle plugin build identifier. Max 200 characters. + --end-time int (required) — End of upload time range, Unix epoch milliseconds. Maximum window: 365 days. + --orderby string — Sort field. [created_at, updated_at] + --query string — Substring match on the minified URL (browser) or build ID (android). Max 200 characters. + --services []string — Filter by service names. Up to 100 values. + --start-time int (required) — Start of upload time range, Unix epoch milliseconds. Must be > 0 and before 'end_time'. + --type string — Platform type. Defaults to 'browser' when omitted. [browser, android, ios] + --uuid string — iOS only. Filter by dSYM bundle UUID. Max 200 characters. + --versions []string — Filter by version strings. Up to 100 values. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - created_at (integer) — Upload timestamp, Unix epoch seconds. + - git_commit_sha (string) — Git commit SHA for this build. + - git_repository_url (string) — Git repository URL associated with this build. + - key (string) — Storage key uniquely identifying this sourcemap file. + - metadata (object) — Free-form key-value metadata attached to the sourcemap. Shape depends on the upload client; common keys include 'git_repository_url' and 'git_commit_sha' (though those are also promoted to top-level fields). + - service (string) — Application or service name. + - size (integer) — File size in bytes. + - type (string) — Platform type: 'browser', 'android', or 'ios'. [browser, android, ios] + - updated_at (integer) — Last update timestamp, Unix epoch seconds. + - version (string) — Application version string. + - total (integer) (required) — Total number of matching records. +`, + Example: ` flashduty sourcemap list --data '{"end_time":1712700000000,"limit":20,"p":1,"services":["my-web-app"],"start_time":1712000000000,"type":"browser"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("build-id") { + body["build_id"] = fBuildID + } + if cmd.Flags().Changed("end-time") { + body["end_time"] = fEndTime + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("services") { + body["services"] = fServices + } + if cmd.Flags().Changed("start-time") { + body["start_time"] = fStartTime + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + if cmd.Flags().Changed("uuid") { + body["uuid"] = fUuid + } + if cmd.Flags().Changed("versions") { + body["versions"] = fVersions + } + }) + if err != nil { + return err + } + req := new(flashduty.SourcemapListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Sourcemaps.List(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1. (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Maximum 100. Default 20. (max 100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending. Default false (descending).") + cmd.Flags().StringVar(&fBuildID, "build-id", "", "Android only. Filter by Gradle plugin build identifier. Max 200 characters.") + cmd.Flags().Int64Var(&fEndTime, "end-time", 0, "End of upload time range, Unix epoch milliseconds. Maximum window: 365 days. (required)") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") + cmd.Flags().StringVar(&fQuery, "query", "", "Substring match on the minified URL (browser) or build ID (android). Max 200 characters.") + cmd.Flags().StringSliceVar(&fServices, "services", nil, "Filter by service names. Up to 100 values.") + cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Start of upload time range, Unix epoch milliseconds. Must be > 0 and before 'end_time'. (required)") + cmd.Flags().StringVar(&fType, "type", "", "Platform type. Defaults to 'browser' when omitted. [browser, android, ios]") + cmd.Flags().StringVar(&fUuid, "uuid", "", "iOS only. Filter by dSYM bundle UUID. Max 200 characters.") + cmd.Flags().StringSliceVar(&fVersions, "versions", nil, "Filter by version strings. Up to 100 values.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedSourcemaps(root *cobra.Command) { + gSourcemap := genGroup(root, "sourcemap", "RUM/Sourcemaps API") + genAddLeaf(gSourcemap, genSourcemapsListCmd()) +} diff --git a/internal/cli/zz_generated_status_pages.go b/internal/cli/zz_generated_status_pages.go new file mode 100644 index 0000000..0cdc0cb --- /dev/null +++ b/internal/cli/zz_generated_status_pages.go @@ -0,0 +1,1226 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genStatusPagesReadPageListCmd() *cobra.Command { + var dataJSON string + cmd := &cobra.Command{ + Use: "list", + Short: "List status pages", + Long: `List status pages. + +List all status pages owned by the account, including their components and sections. + +API: GET /status-page/list (status-page-read-page-list) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) — Status pages owned by the account. + - components (array) — Components tracked on the status page. + - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds. + - component_id (string) — Component ID. + - description (string) — Component description. + - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints. + - hide_uptime (boolean) — When true, uptime data is hidden from summary responses. + - name (string) (required) — Component display name. + - order_id (integer) — Display order within its section. + - section_id (string) — Parent section ID. + - contact_info (string) — Get-in-touch contact, a mailto or website URL. + - custom_domain (string) — Custom domain pointing to the status page. + - custom_links (array) — Custom navigation links shown on the status page. + - dark_logo (string) — Dark-mode logo image of the status page. + - date_view (string) — How the timeline is displayed. [calendar, list] + - display_uptime_mode (string) — How uptime is displayed. [chart_and_percentage, chart, none] + - favicon (string) — Favicon of the status page. + - logo (string) — Logo image of the status page. + - logo_url (string) — URL opened when the logo is clicked. + - name (string) — Display name of the status page. + - page_footer (string) — Footer content of the status page. + - page_header (string) — Header content of the status page. + - page_id (integer) — Status page ID. + - sections (array) — Sections grouping the components. + - description (string) — Section description. + - hide_all (boolean) — Whether the section and its components are hidden from summary endpoints. + - hide_uptime (boolean) — Whether uptime data is hidden from summary responses. + - name (string) — Section name. + - order_id (integer) — Display order of the section. + - section_id (string) — Section ID. + - subscription (object) + - email (boolean) — Whether email subscription is enabled. + - im (boolean) — Whether IM subscription is enabled. + - template_preference (string) — Preferred change-event template type. + - type (string) — Visibility type of the status page. [public, internal] + - url_name (string) — URL-safe slug, unique per account. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + }) + if err != nil { + return err + } + _ = body + out, _, err := ctx.Client.StatusPages.ReadPageList(cmdContext(ctx.Cmd)) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeActiveListCmd() *cobra.Command { + var dataJSON string + var fPageID int64 + var fType string + cmd := &cobra.Command{ + Use: "change-active-list", + Short: "List active status page events", + Long: `List active status page events. + +List in-progress (non-terminal) events of a given type for a status page. + +API: GET /status-page/change/active/list (statusPageChangeActiveList) + +Request fields: + --page-id int (required) — Status page ID. + --type string (required) — Event type filter. Required. Returns only in-progress (non-terminal) events — 'investigating'/'identified'/'monitoring' for 'incident', 'scheduled'/'ongoing' for 'maintenance'. [incident, maintenance] + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - affected_components (array) — Components currently affected by this event, with their resulting status. + - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds. + - component_id (string) — Component ID. + - description (string) — Component description. + - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints. + - hide_uptime (boolean) — When true, uptime data is hidden from summary responses. + - name (string) (required) — Component display name. + - order_id (integer) — Display order within its section. + - section_id (string) — Parent section ID. + - status (string) (required) — Current component status resulting from the event. [operational, degraded, partial_outage, full_outage, under_maintenance] + - auto_update_by_schedule (boolean) — Maintenance only: whether the status advances automatically based on the scheduled window. + - change_id (integer) (required) — Event ID. + - close_at_seconds (integer) — Scheduled close time in unix seconds. Set for retrospective and maintenance events. + - description (string) — Event description (Markdown). + - is_retrospective (boolean) — Whether this event is a retrospective (historical) one. + - linked_change_ids (array) — Linked event IDs (related incidents, deployments, etc.). + - notify_subscribers (boolean) — Whether subscribers were notified about this event. + - page_id (integer) — Parent status page ID. + - responder_ids (array) — Member IDs responsible for this event. + - start_at_seconds (integer) — Event start time in unix seconds. + - status (string) — Current event status. Incident statuses: 'investigating'/'identified'/'monitoring'/'resolved'. Maintenance statuses: 'scheduled'/'ongoing'/'completed'. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + - title (string) (required) — Event title. + - type (string) (required) — Event type. [incident, maintenance] + - updates (array) — Timeline updates attached to this event, ordered by time. + - at_seconds (integer) (required) — Update timestamp in unix seconds. + - component_changes (array) — Component status transitions applied by this update. + - component_id (string) (required) — Component ID. + - component_name (string) — Component display name. Populated by the backend on read; ignored on write. + - status (string) (required) — New component status. Incidents support 'operational'/'degraded'/'partial_outage'/'full_outage'; maintenances support 'operational'/'under_maintenance'. [operational, degraded, partial_outage, full_outage, under_maintenance] + - description (string) — Update description (Markdown). + - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + - update_id (string) (required) — Update ID. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + }) + if err != nil { + return err + } + req := new(flashduty.StatusPagesChangeActiveListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.ChangeActiveList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().StringVar(&fType, "type", "", "Event type filter. Required. Returns only in-progress (non-terminal) events — 'investigating'/'identified'/'monitoring' for 'incident', 'scheduled'/'ongoing' for 'maintenance'. (required) [incident, maintenance]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeCreateCmd() *cobra.Command { + var dataJSON string + var fAutoUpdateBySchedule bool + var fCloseAtSeconds int64 + var fDescription string + var fIsRetrospective bool + var fLinkedChanges []string + var fNotifySubscribers bool + var fPageID int64 + var fResponders []int + var fStartAtSeconds int64 + var fStatus string + var fTitle string + var fType string + cmd := &cobra.Command{ + Use: "change-create", + Short: "Create status page event", + Long: `Create status page event. + +Create a new incident or maintenance event on a status page. + +API: POST /status-page/change/create (statusPageChangeCreate) + +Request fields: + --auto-update-by-schedule bool — Maintenance only: automatically advance the status based on the scheduled window. + --close-at-seconds int — Scheduled close time for retrospective events. Must be greater than 'start_at_seconds'. + --description string — Event description (Markdown). Required by the validator. + --is-retrospective bool — Mark this event as a retrospective (historical) one. + --linked-changes []string — Linked change IDs (related incidents, deployments, etc.). + --notify-subscribers bool — Notify subscribers about this event and all its updates. + --page-id int (required) — Status page ID. + --responders []int — Member IDs responsible for this event. + --start-at-seconds int — Event start time in unix seconds. Defaults to now when omitted. + --status string (required) — Initial event status. 'investigating'/'identified'/'monitoring'/'resolved' apply to incidents; 'scheduled'/'ongoing'/'completed' apply to maintenances. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + --title string (required) — Event title, up to 255 characters. (≤255 chars) + --type string (required) — Event type. [incident, maintenance] + updates (array, via --data) (required) — Timeline updates. Immediate events normally pass one update; retrospective events must pass all historical updates. + - at_seconds (integer) — Update timestamp in unix seconds. + - component_changes (array) — Component status transitions applied by this update. + - component_id (string) (required) — Component ID. + - status (string) (required) — New component status. 'operational'/'degraded'/'partial_outage'/'full_outage' apply to incidents; 'operational'/'under_maintenance' apply to maintenances. [operational, degraded, partial_outage, full_outage, under_maintenance] + - description (string) — Update description (Markdown). + - status (string) — Change status after this update. Omit if the overall status does not change. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + - update_id (string) — Update ID. Server-assigned on create; supply when replaying historical updates. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - change_id (integer) (required) — Newly created event ID. + - change_name (string) (required) — Event title (echoed from the request). +`, + Example: ` flashduty status-page change-create --data '{"description":"We are investigating degraded performance affecting the web console.","notify_subscribers":true,"page_id":5750613685214,"start_at_seconds":1712000000,"status":"investigating","title":"Web Console Degraded Performance","type":"incident","updates":[{"component_changes":[{"component_id":"01KC3GAZ6ZJE40H55GM31RPWZE","status":"degraded"}],"description":"We are currently investigating an issue affecting some users.","status":"investigating"}]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("auto-update-by-schedule") { + body["auto_update_by_schedule"] = fAutoUpdateBySchedule + } + if cmd.Flags().Changed("close-at-seconds") { + body["close_at_seconds"] = fCloseAtSeconds + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("is-retrospective") { + body["is_retrospective"] = fIsRetrospective + } + if cmd.Flags().Changed("linked-changes") { + body["linked_changes"] = fLinkedChanges + } + if cmd.Flags().Changed("notify-subscribers") { + body["notify_subscribers"] = fNotifySubscribers + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("responders") { + body["responders"] = fResponders + } + if cmd.Flags().Changed("start-at-seconds") { + body["start_at_seconds"] = fStartAtSeconds + } + if cmd.Flags().Changed("status") { + body["status"] = fStatus + } + if cmd.Flags().Changed("title") { + body["title"] = fTitle + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateStatusPageChangeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.ChangeCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().BoolVar(&fAutoUpdateBySchedule, "auto-update-by-schedule", false, "Maintenance only: automatically advance the status based on the scheduled window.") + cmd.Flags().Int64Var(&fCloseAtSeconds, "close-at-seconds", 0, "Scheduled close time for retrospective events. Must be greater than 'start_at_seconds'.") + cmd.Flags().StringVar(&fDescription, "description", "", "Event description (Markdown). Required by the validator.") + cmd.Flags().BoolVar(&fIsRetrospective, "is-retrospective", false, "Mark this event as a retrospective (historical) one.") + cmd.Flags().StringSliceVar(&fLinkedChanges, "linked-changes", nil, "Linked change IDs (related incidents, deployments, etc.).") + cmd.Flags().BoolVar(&fNotifySubscribers, "notify-subscribers", false, "Notify subscribers about this event and all its updates.") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().IntSliceVar(&fResponders, "responders", nil, "Member IDs responsible for this event.") + cmd.Flags().Int64Var(&fStartAtSeconds, "start-at-seconds", 0, "Event start time in unix seconds. Defaults to now when omitted.") + cmd.Flags().StringVar(&fStatus, "status", "", "Initial event status. 'investigating'/'identified'/'monitoring'/'resolved' apply to incidents; 'scheduled'/'ongoing'/'completed' apply to maintenances. (required) [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]") + cmd.Flags().StringVar(&fTitle, "title", "", "Event title, up to 255 characters. (required) (≤255 chars)") + cmd.Flags().StringVar(&fType, "type", "", "Event type. (required) [incident, maintenance]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeDeleteCmd() *cobra.Command { + var dataJSON string + var fChangeID int64 + var fPageID int64 + cmd := &cobra.Command{ + Use: "change-delete", + Short: "Delete status page event", + Long: `Delete status page event. + +Delete a status page event. + +API: POST /status-page/change/delete (statusPageChangeDelete) + +Request fields: + --change-id int (required) — Target event ID. + --page-id int (required) — Status page ID. +`, + Example: ` flashduty status-page change-delete --data '{"change_id":5821693893131,"page_id":5750613685214}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("change-id") { + body["change_id"] = fChangeID + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + }) + if err != nil { + return err + } + req := new(flashduty.DeleteStatusPageChangeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.StatusPages.ChangeDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /status-page/change/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Target event ID. (required)") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeInfoCmd() *cobra.Command { + var dataJSON string + var fPageID int64 + var fChangeID int64 + cmd := &cobra.Command{ + Use: "change-info", + Short: "Get status page event detail", + Long: `Get status page event detail. + +Retrieve details of a specific status page event (incident or maintenance). + +API: GET /status-page/change/info (statusPageChangeInfo) + +Request fields: + --page-id int (required) — Status page ID. + --change-id int (required) — Event (change) ID. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - affected_components (array) — Components currently affected by this event, with their resulting status. + - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds. + - component_id (string) — Component ID. + - description (string) — Component description. + - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints. + - hide_uptime (boolean) — When true, uptime data is hidden from summary responses. + - name (string) (required) — Component display name. + - order_id (integer) — Display order within its section. + - section_id (string) — Parent section ID. + - status (string) (required) — Current component status resulting from the event. [operational, degraded, partial_outage, full_outage, under_maintenance] + - auto_update_by_schedule (boolean) — Maintenance only: whether the status advances automatically based on the scheduled window. + - change_id (integer) (required) — Event ID. + - close_at_seconds (integer) — Scheduled close time in unix seconds. Set for retrospective and maintenance events. + - description (string) — Event description (Markdown). + - is_retrospective (boolean) — Whether this event is a retrospective (historical) one. + - linked_change_ids (array) — Linked event IDs (related incidents, deployments, etc.). + - notify_subscribers (boolean) — Whether subscribers were notified about this event. + - page_id (integer) — Parent status page ID. + - responder_ids (array) — Member IDs responsible for this event. + - start_at_seconds (integer) — Event start time in unix seconds. + - status (string) — Current event status. Incident statuses: 'investigating'/'identified'/'monitoring'/'resolved'. Maintenance statuses: 'scheduled'/'ongoing'/'completed'. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + - title (string) (required) — Event title. + - type (string) (required) — Event type. [incident, maintenance] + - updates (array) — Timeline updates attached to this event, ordered by time. + - at_seconds (integer) (required) — Update timestamp in unix seconds. + - component_changes (array) — Component status transitions applied by this update. + - component_id (string) (required) — Component ID. + - component_name (string) — Component display name. Populated by the backend on read; ignored on write. + - status (string) (required) — New component status. Incidents support 'operational'/'degraded'/'partial_outage'/'full_outage'; maintenances support 'operational'/'under_maintenance'. [operational, degraded, partial_outage, full_outage, under_maintenance] + - description (string) — Update description (Markdown). + - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + - update_id (string) (required) — Update ID. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("change-id") { + body["change_id"] = fChangeID + } + }) + if err != nil { + return err + } + req := new(flashduty.StatusPagesChangeInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.ChangeInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Event (change) ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeListCmd() *cobra.Command { + var dataJSON string + var fPageID int64 + var fStartAtSeconds int64 + var fEndAtSeconds int64 + var fType string + var fStatus string + cmd := &cobra.Command{ + Use: "change-list", + Short: "List status page events", + Long: `List status page events. + +List events (incidents and maintenances) for a status page. + +API: GET /status-page/change/list (statusPageChangeList) + +Request fields: + --page-id int (required) — Status page ID. + --start-at-seconds int — Filter events started at or after this unix timestamp (seconds). + --end-at-seconds int — Filter events started at or before this unix timestamp (seconds). + --type string (required) — Event type filter. Required. [incident, maintenance] + --status string (required) — Event status filter. Required. Must be a status valid for the given 'type' (e.g. 'investigating'/'identified'/'monitoring'/'resolved' for incidents; 'scheduled'/'ongoing'/'completed' for maintenances). [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - affected_components (array) — Components currently affected by this event, with their resulting status. + - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds. + - component_id (string) — Component ID. + - description (string) — Component description. + - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints. + - hide_uptime (boolean) — When true, uptime data is hidden from summary responses. + - name (string) (required) — Component display name. + - order_id (integer) — Display order within its section. + - section_id (string) — Parent section ID. + - status (string) (required) — Current component status resulting from the event. [operational, degraded, partial_outage, full_outage, under_maintenance] + - auto_update_by_schedule (boolean) — Maintenance only: whether the status advances automatically based on the scheduled window. + - change_id (integer) (required) — Event ID. + - close_at_seconds (integer) — Scheduled close time in unix seconds. Set for retrospective and maintenance events. + - description (string) — Event description (Markdown). + - is_retrospective (boolean) — Whether this event is a retrospective (historical) one. + - linked_change_ids (array) — Linked event IDs (related incidents, deployments, etc.). + - notify_subscribers (boolean) — Whether subscribers were notified about this event. + - page_id (integer) — Parent status page ID. + - responder_ids (array) — Member IDs responsible for this event. + - start_at_seconds (integer) — Event start time in unix seconds. + - status (string) — Current event status. Incident statuses: 'investigating'/'identified'/'monitoring'/'resolved'. Maintenance statuses: 'scheduled'/'ongoing'/'completed'. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + - title (string) (required) — Event title. + - type (string) (required) — Event type. [incident, maintenance] + - updates (array) — Timeline updates attached to this event, ordered by time. + - at_seconds (integer) (required) — Update timestamp in unix seconds. + - component_changes (array) — Component status transitions applied by this update. + - component_id (string) (required) — Component ID. + - component_name (string) — Component display name. Populated by the backend on read; ignored on write. + - status (string) (required) — New component status. Incidents support 'operational'/'degraded'/'partial_outage'/'full_outage'; maintenances support 'operational'/'under_maintenance'. [operational, degraded, partial_outage, full_outage, under_maintenance] + - description (string) — Update description (Markdown). + - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + - update_id (string) (required) — Update ID. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("start-at-seconds") { + body["start_at_seconds"] = fStartAtSeconds + } + if cmd.Flags().Changed("end-at-seconds") { + body["end_at_seconds"] = fEndAtSeconds + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + if cmd.Flags().Changed("status") { + body["status"] = fStatus + } + }) + if err != nil { + return err + } + req := new(flashduty.StatusPagesChangeListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.ChangeList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().Int64Var(&fStartAtSeconds, "start-at-seconds", 0, "Filter events started at or after this unix timestamp (seconds).") + cmd.Flags().Int64Var(&fEndAtSeconds, "end-at-seconds", 0, "Filter events started at or before this unix timestamp (seconds).") + cmd.Flags().StringVar(&fType, "type", "", "Event type filter. Required. (required) [incident, maintenance]") + cmd.Flags().StringVar(&fStatus, "status", "", "Event status filter. Required. Must be a status valid for the given 'type' (e.g. 'investigating'/'identified'/'monitoring'/'resolved' for incidents; 'scheduled'/'ongoing'/'completed' for maintenances). (required) [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeTimelineCreateCmd() *cobra.Command { + var dataJSON string + var fAtSeconds int64 + var fChangeID int64 + var fDescription string + var fPageID int64 + var fStatus string + cmd := &cobra.Command{ + Use: "change-timeline-create", + Short: "Create event timeline entry", + Long: `Create event timeline entry. + +Add a timeline update to a status page event. + +API: POST /status-page/change/timeline/create (statusPageChangeTimelineCreate) + +Request fields: + --at-seconds int — Update timestamp in unix seconds. Defaults to now when omitted. + --change-id int (required) — Target event ID. + --description string — Update description (Markdown). Required. + --page-id int (required) — Status page ID. + --status string (required) — New event status. Must match the event type. When the status transitions to 'resolved' or 'completed', all referenced components must become 'operational'. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] + component_changes (array, via --data) — Component status transitions applied by this update. Component IDs must be unique. + - component_id (string) (required) — Component ID. + - status (string) (required) — New component status. 'operational'/'degraded'/'partial_outage'/'full_outage' apply to incidents; 'operational'/'under_maintenance' apply to maintenances. [operational, degraded, partial_outage, full_outage, under_maintenance] + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - update_id (string) (required) — Newly created update ID. +`, + Example: ` flashduty status-page change-timeline-create --data '{"at_seconds":1712003600,"change_id":5821693893131,"component_changes":[{"component_id":"01KC3GAZ6ZJE40H55GM31RPWZE","status":"partial_outage"}],"description":"We have identified the root cause and are working on a fix.","page_id":5750613685214,"status":"identified"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("at-seconds") { + body["at_seconds"] = fAtSeconds + } + if cmd.Flags().Changed("change-id") { + body["change_id"] = fChangeID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("status") { + body["status"] = fStatus + } + }) + if err != nil { + return err + } + req := new(flashduty.CreateStatusPageChangeTimelineRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.ChangeTimelineCreate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fAtSeconds, "at-seconds", 0, "Update timestamp in unix seconds. Defaults to now when omitted.") + cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Target event ID. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Update description (Markdown). Required.") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().StringVar(&fStatus, "status", "", "New event status. Must match the event type. When the status transitions to 'resolved' or 'completed', all referenced components must become 'operational'. (required) [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeTimelineDeleteCmd() *cobra.Command { + var dataJSON string + var fChangeID int64 + var fPageID int64 + var fUpdateID string + cmd := &cobra.Command{ + Use: "change-timeline-delete", + Short: "Delete event timeline entry", + Long: `Delete event timeline entry. + +Delete a timeline entry from a status page event. + +API: POST /status-page/change/timeline/delete (statusPageChangeTimelineDelete) + +Request fields: + --change-id int (required) — Parent event ID. + --page-id int (required) — Status page ID. + --update-id string (required) — Timeline update ID to delete. +`, + Example: ` flashduty status-page change-timeline-delete --data '{"change_id":5821693893131,"page_id":5750613685214,"update_id":"01KP0311872NVYFRRQ82FWXAP4"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("change-id") { + body["change_id"] = fChangeID + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("update-id") { + body["update_id"] = fUpdateID + } + }) + if err != nil { + return err + } + req := new(flashduty.DeleteStatusPageChangeTimelineRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.StatusPages.ChangeTimelineDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /status-page/change/timeline/delete") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Parent event ID. (required)") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().StringVar(&fUpdateID, "update-id", "", "Timeline update ID to delete. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeTimelineUpdateCmd() *cobra.Command { + var dataJSON string + var fAtSeconds int64 + var fChangeID int64 + var fDescription string + var fPageID int64 + var fUpdateID string + cmd := &cobra.Command{ + Use: "change-timeline-update", + Short: "Update event timeline entry", + Long: `Update event timeline entry. + +Update a timeline entry for a status page event. + +API: POST /status-page/change/timeline/update (statusPageChangeTimelineUpdate) + +Request fields: + --at-seconds int — New update timestamp in unix seconds. + --change-id int (required) — Parent event ID. + --description string — New update description (Markdown). + --page-id int (required) — Status page ID. + --update-id string (required) — Target timeline update ID. +`, + Example: ` flashduty status-page change-timeline-update --data '{"at_seconds":1712003600,"change_id":5821693893131,"description":"Corrected description: root cause identified in database layer.","page_id":5750613685214,"update_id":"01KP0311872NVYFRRQ82FWXAP4"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("at-seconds") { + body["at_seconds"] = fAtSeconds + } + if cmd.Flags().Changed("change-id") { + body["change_id"] = fChangeID + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("update-id") { + body["update_id"] = fUpdateID + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateStatusPageChangeTimelineRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.StatusPages.ChangeTimelineUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /status-page/change/timeline/update") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fAtSeconds, "at-seconds", 0, "New update timestamp in unix seconds.") + cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Parent event ID. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "New update description (Markdown).") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().StringVar(&fUpdateID, "update-id", "", "Target timeline update ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesChangeUpdateCmd() *cobra.Command { + var dataJSON string + var fChangeID int64 + var fLinkedChanges []string + var fPageID int64 + var fResponders []int + var fTitle string + cmd := &cobra.Command{ + Use: "change-update", + Short: "Update status page event", + Long: `Update status page event. + +Update an existing status page event. + +API: POST /status-page/change/update (statusPageChangeUpdate) + +Request fields: + --change-id int (required) — Target event ID. + --linked-changes []string — Linked event IDs. Pass the full replacement list. + --page-id int (required) — Status page ID. + --responders []int — Member IDs responsible for this event. Pass the full replacement list. + --title string — New event title, up to 255 characters. Omit to keep the existing value. (≤255 chars) +`, + Example: ` flashduty status-page change-update --data '{"change_id":5821693893131,"page_id":5750613685214,"title":"Web Console Degraded Performance (Updated)"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("change-id") { + body["change_id"] = fChangeID + } + if cmd.Flags().Changed("linked-changes") { + body["linked_changes"] = fLinkedChanges + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("responders") { + body["responders"] = fResponders + } + if cmd.Flags().Changed("title") { + body["title"] = fTitle + } + }) + if err != nil { + return err + } + req := new(flashduty.UpdateStatusPageChangeRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.StatusPages.ChangeUpdate(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /status-page/change/update") + return nil + }) + }, + } + cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Target event ID. (required)") + cmd.Flags().StringSliceVar(&fLinkedChanges, "linked-changes", nil, "Linked event IDs. Pass the full replacement list.") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().IntSliceVar(&fResponders, "responders", nil, "Member IDs responsible for this event. Pass the full replacement list.") + cmd.Flags().StringVar(&fTitle, "title", "", "New event title, up to 255 characters. Omit to keep the existing value. (≤255 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesMigrateEmailSubscribersCmd() *cobra.Command { + var dataJSON string + var fAPIKey string + var fSourcePageID string + var fTargetPageID int64 + cmd := &cobra.Command{ + Use: "migrate-email-subscribers", + Short: "Migrate email subscribers", + Long: `Migrate email subscribers. + +Start a migration job that imports email subscribers from an Atlassian Statuspage into an existing Flashduty status page. + +API: POST /status-page/migrate-email-subscribers (statusPageMigrateEmailSubscribers) + +Request fields: + --api-key string (required) — Atlassian Statuspage API key with access to the source page. + --source-page-id string (required) — Atlassian Statuspage source page ID. + --target-page-id int (required) — Flashduty target status page ID that will receive the imported subscribers. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - job_id (string) (required) — Migration job ID. Use this to poll status or request cancellation. +`, + Example: ` flashduty status-page migrate-email-subscribers --data '{"api_key":"sk-stsp-xxxxxxxxxxxxxxxxxxxx","source_page_id":"abcdefghij","target_page_id":5750613685214}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("api-key") { + body["api_key"] = fAPIKey + } + if cmd.Flags().Changed("source-page-id") { + body["source_page_id"] = fSourcePageID + } + if cmd.Flags().Changed("target-page-id") { + body["target_page_id"] = fTargetPageID + } + }) + if err != nil { + return err + } + req := new(flashduty.MigrateStatusPageEmailSubscribersRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.MigrateEmailSubscribers(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAPIKey, "api-key", "", "Atlassian Statuspage API key with access to the source page. (required)") + cmd.Flags().StringVar(&fSourcePageID, "source-page-id", "", "Atlassian Statuspage source page ID. (required)") + cmd.Flags().Int64Var(&fTargetPageID, "target-page-id", 0, "Flashduty target status page ID that will receive the imported subscribers. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesMigrateStructureCmd() *cobra.Command { + var dataJSON string + var fAPIKey string + var fSourcePageID string + var fURLName string + cmd := &cobra.Command{ + Use: "migrate-structure", + Short: "Migrate status page structure", + Long: `Migrate status page structure. + +Start a migration job that imports the structure and historical events of an Atlassian Statuspage into a new Flashduty status page. + +API: POST /status-page/migrate-structure (statusPageMigrateStructure) + +Request fields: + --api-key string (required) — Atlassian Statuspage API key with access to the source page. + --source-page-id string (required) — Atlassian Statuspage source page ID. + --url-name string — Target URL name for the migrated status page. When omitted, the source page's URL name is reused. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - job_id (string) (required) — Migration job ID. Use this to poll status or request cancellation. +`, + Example: ` flashduty status-page migrate-structure --data '{"api_key":"sk-stsp-xxxxxxxxxxxxxxxxxxxx","source_page_id":"abcdefghij"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("api-key") { + body["api_key"] = fAPIKey + } + if cmd.Flags().Changed("source-page-id") { + body["source_page_id"] = fSourcePageID + } + if cmd.Flags().Changed("url-name") { + body["url_name"] = fURLName + } + }) + if err != nil { + return err + } + req := new(flashduty.MigrateStatusPageStructureRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.MigrateStructure(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fAPIKey, "api-key", "", "Atlassian Statuspage API key with access to the source page. (required)") + cmd.Flags().StringVar(&fSourcePageID, "source-page-id", "", "Atlassian Statuspage source page ID. (required)") + cmd.Flags().StringVar(&fURLName, "url-name", "", "Target URL name for the migrated status page. When omitted, the source page's URL name is reused.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesMigrationCancelCmd() *cobra.Command { + var dataJSON string + var fJobID string + cmd := &cobra.Command{ + Use: "migration-cancel", + Short: "Cancel status page migration", + Long: `Cancel status page migration. + +Cancel an in-progress status page migration job. Only jobs currently in the 'running' state can be cancelled. + +API: POST /status-page/migration/cancel (statusPageMigrationCancel) + +Request fields: + --job-id string (required) — Migration job ID. +`, + Example: ` flashduty status-page migration-cancel --data '{"job_id":"01KP0311872NVYFRRQ82FW0001"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("job-id") { + body["job_id"] = fJobID + } + }) + if err != nil { + return err + } + req := new(flashduty.CancelStatusPageMigrationRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.StatusPages.MigrationCancel(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /status-page/migration/cancel") + return nil + }) + }, + } + cmd.Flags().StringVar(&fJobID, "job-id", "", "Migration job ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesMigrationStatusCmd() *cobra.Command { + var dataJSON string + var fJobID string + cmd := &cobra.Command{ + Use: "migration-status", + Short: "Get migration status", + Long: `Get migration status. + +Get the current status and progress of a status page migration job. + +API: GET /status-page/migration/status (statusPageMigrationStatus) + +Request fields: + --job-id string (required) — Migration job ID returned by 'migrate-structure' or 'migrate-email-subscribers'. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owner account ID. + - created_at (integer) (required) — Job creation time, unix seconds. + - error (string) — Terminal error message when 'status' is 'failed'. + - job_id (string) (required) — Migration job ID. + - phase (string) (required) — Current migration phase. [structure, history, subscribers] + - progress (object) (required) — Progress counters for a migration job. + - completed_steps (integer) (required) — Steps completed so far. + - components_imported (integer) (required) + - incidents_imported (integer) (required) + - maintenances_imported (integer) (required) + - sections_imported (integer) (required) + - subscribers_imported (integer) (required) + - subscribers_skipped (integer) (required) — Number of subscribers skipped (e.g. because they would create duplicates). + - templates_imported (integer) (required) + - total_steps (integer) (required) — Total steps this job will perform. + - warnings (array) — Non-fatal warnings recorded during the job. + - source_page_id (string) (required) — Atlassian Statuspage source page ID. + - status (string) (required) — Current job status. [pending, running, completed, failed, cancelled] + - target_page_id (integer) (required) — Flashduty target status page ID. Set once the job produces one, or supplied up front for subscriber migration. + - updated_at (integer) (required) — Last status update time, unix seconds. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("job-id") { + body["job_id"] = fJobID + } + }) + if err != nil { + return err + } + req := new(flashduty.StatusPagesMigrationStatusRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.MigrationStatus(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fJobID, "job-id", "", "Migration job ID returned by 'migrate-structure' or 'migrate-email-subscribers'. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesSubscriberExportCmd() *cobra.Command { + var dataJSON string + var fComponentIDs []string + var fPageID int64 + cmd := &cobra.Command{ + Use: "subscriber-export", + Short: "Export subscribers", + Long: `Export subscribers. + +Export subscribers list for a status page as a CSV attachment. The response is a 'text/csv' file with columns: Method, Recipient, Components, Subscribe All, Locale. + +API: POST /status-page/subscriber/export (statusPageSubscriberExport) + +Request fields: + --component-ids []string — Optional component IDs to filter subscribers by. + --page-id int (required) — Status page ID. +`, + Example: ` flashduty status-page subscriber-export --data '{"page_id":5750613685214}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("component-ids") { + body["component_ids"] = fComponentIDs + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + }) + if err != nil { + return err + } + req := new(flashduty.ExportStatusPageSubscribersRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.SubscriberExport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringSliceVar(&fComponentIDs, "component-ids", nil, "Optional component IDs to filter subscribers by.") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesSubscriberImportCmd() *cobra.Command { + var dataJSON string + var fMethod string + var fPageID int64 + cmd := &cobra.Command{ + Use: "subscriber-import", + Short: "Import subscribers", + Long: `Import subscribers. + +Bulk import subscribers for a status page. + +API: POST /status-page/subscriber/import (statusPageSubscriberImport) + +Request fields: + --method string (required) — Subscription method. 'email' is only valid for public pages; 'im' is only valid for internal pages. [email, im] + --page-id int (required) — Target status page ID. + subscribers (array, via --data) — Subscribers to import. + - all (boolean) — When true, the subscriber receives notifications for all components. Must be true when 'component_ids' and 'change_ids' are both empty. + - change_ids (array) — Specific event IDs the subscriber should receive notifications for. + - component_ids (array) — Component IDs the subscriber should receive notifications for. + - locale (string) — Preferred locale for notifications. Defaults to the request locale when omitted. + - recipient (string) (required) — Email address (for public pages) or user ID (for internal pages). (≤255 chars) +`, + Example: ` flashduty status-page subscriber-import --data '{"method":"email","page_id":5750613685214,"subscribers":[{"all":true,"locale":"en-US","recipient":"alice@example.com"},{"all":false,"component_ids":["01KC3GAZ6ZJE40H55GM31RPWZE"],"locale":"zh-CN","recipient":"bob@example.com"}]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("method") { + body["method"] = fMethod + } + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + }) + if err != nil { + return err + } + req := new(flashduty.ImportStatusPageSubscribersRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.StatusPages.SubscriberImport(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /status-page/subscriber/import") + return nil + }) + }, + } + cmd.Flags().StringVar(&fMethod, "method", "", "Subscription method. 'email' is only valid for public pages; 'im' is only valid for internal pages. (required) [email, im]") + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Target status page ID. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genStatusPagesSubscriberListCmd() *cobra.Command { + var dataJSON string + var fPageID int64 + var fComponentIDs string + var fP int64 + var fLimit int64 + cmd := &cobra.Command{ + Use: "subscriber-list", + Short: "List status page subscribers", + Long: `List status page subscribers. + +List subscribers who have signed up for status page notifications. + +API: GET /status-page/subscriber/list (statusPageSubscriberList) + +Request fields: + --page-id int (required) — Status page ID. + --component-ids string — Comma-separated component IDs to filter subscribers by. + --page int — Page number (1-based). (min 1) + --limit int — Page size (1-100). (1-100) + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - has_next_page (boolean) (required) — Whether there is at least one more page after the current one. + - items (array) (required) + - all (boolean) (required) — Whether the subscriber is subscribed to all components. + - components (array) (required) — Components this subscriber has subscribed to. + - available_since_seconds (integer) — Timestamp when the component was first available, in unix seconds. + - component_id (string) — Component ID. + - description (string) — Component description. + - hide_all (boolean) — When true, the component is hidden entirely from summary endpoints. + - hide_uptime (boolean) — When true, uptime data is hidden from summary responses. + - name (string) (required) — Component display name. + - order_id (integer) — Display order within its section. + - section_id (string) — Parent section ID. + - locale (string) — Preferred locale for notifications. + - method (string) (required) — Subscription delivery method. [email, im] + - recipient (string) (required) — Subscriber recipient: email address for public pages, user ID for internal pages. + - total (integer) (required) — Total matching subscribers. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page-id") { + body["page_id"] = fPageID + } + if cmd.Flags().Changed("component-ids") { + body["component_ids"] = fComponentIDs + } + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + }) + if err != nil { + return err + } + req := new(flashduty.StatusPagesSubscriberListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.StatusPages.SubscriberList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") + cmd.Flags().StringVar(&fComponentIDs, "component-ids", "", "Comma-separated component IDs to filter subscribers by.") + cmd.Flags().Int64Var(&fP, "page", 0, "Page number (1-based). (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size (1-100). (1-100)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedStatusPages(root *cobra.Command) { + gStatusPage := genGroup(root, "status-page", "On-call/Status pages API") + genAddLeaf(gStatusPage, genStatusPagesReadPageListCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeActiveListCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeCreateCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeDeleteCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeInfoCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeListCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeTimelineCreateCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeTimelineDeleteCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeTimelineUpdateCmd()) + genAddLeaf(gStatusPage, genStatusPagesChangeUpdateCmd()) + genAddLeaf(gStatusPage, genStatusPagesMigrateEmailSubscribersCmd()) + genAddLeaf(gStatusPage, genStatusPagesMigrateStructureCmd()) + genAddLeaf(gStatusPage, genStatusPagesMigrationCancelCmd()) + genAddLeaf(gStatusPage, genStatusPagesMigrationStatusCmd()) + genAddLeaf(gStatusPage, genStatusPagesSubscriberExportCmd()) + genAddLeaf(gStatusPage, genStatusPagesSubscriberImportCmd()) + genAddLeaf(gStatusPage, genStatusPagesSubscriberListCmd()) +} diff --git a/internal/cli/zz_generated_teams.go b/internal/cli/zz_generated_teams.go new file mode 100644 index 0000000..f00a665 --- /dev/null +++ b/internal/cli/zz_generated_teams.go @@ -0,0 +1,389 @@ +// Code generated by internal/cmd/cligen; DO NOT EDIT. + +package cli + +import ( + "github.com/spf13/cobra" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +func genTeamsReadInfoCmd() *cobra.Command { + var dataJSON string + var fRefID string + var fTeamID int64 + var fTeamName string + cmd := &cobra.Command{ + Use: "info", + Short: "Get team detail", + Long: `Get team detail. + +Return a single team by ID, name, or external reference ID. + +API: POST /team/info (team-read-info) + +Request fields: + --ref-id string — External reference ID. + --team-id int — Team ID. + --team-name string — Team name. + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - account_id (integer) (required) — Owning account ID. + - created_at (integer) (required) — Unix epoch seconds the team was created. + - creator_id (integer) (required) — Member ID of the creator. + - creator_name (string) (required) — Display name of the creator. + - description (string) (required) — Free-form description. + - person_ids (array) (required) — Member IDs of team members. + - ref_id (string) (required) — External reference ID for third-party HR system integration. + - status (string) (required) — Team status. [enabled, disabled] + - team_id (integer) (required) — Unique team ID. + - team_name (string) (required) — Team display name. 1–39 characters, unique per account. + - updated_at (integer) (required) — Unix epoch seconds the team was last updated. + - updated_by (integer) (required) — Member ID of the last editor. + - updated_by_name (string) (required) — Display name of the last editor. +`, + Example: ` flashduty team info --data '{"team_id":1001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("ref-id") { + body["ref_id"] = fRefID + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("team-name") { + body["team_name"] = fTeamName + } + }) + if err != nil { + return err + } + req := new(flashduty.TeamInfoRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Teams.ReadInfo(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fRefID, "ref-id", "", "External reference ID.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team ID.") + cmd.Flags().StringVar(&fTeamName, "team-name", "", "Team name.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genTeamsReadInfosCmd() *cobra.Command { + var dataJSON string + var fTeamIDs []int + cmd := &cobra.Command{ + Use: "infos", + Short: "Batch get teams", + Long: `Batch get teams. + +Return basic info for multiple teams by their IDs in a single request. + +API: POST /team/infos (team-read-infos) + +Request fields: + --team-ids []int (required) — List of team IDs to look up. Max 100. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - person_ids (array) + - team_id (integer) + - team_name (string) +`, + Example: ` flashduty team infos --data '{"team_ids":[1001,1002]}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("team-ids") { + body["team_ids"] = fTeamIDs + } + }) + if err != nil { + return err + } + req := new(flashduty.TeamInfosRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Teams.ReadInfos(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "List of team IDs to look up. Max 100. (required)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genTeamsReadListCmd() *cobra.Command { + var dataJSON string + var fP int64 + var fLimit int64 + var fSearchAfterCtx string + var fAsc bool + var fOrderby string + var fPersonID int64 + var fQuery string + cmd := &cobra.Command{ + Use: "list", + Short: "List teams", + Long: `List teams. + +Return a paginated list of teams in the current account. + +API: POST /team/list (team-read-list) + +Request fields: + --page int — Page number. Default: 1. (min 1) + --limit int — Page size. Max: 100. Default: 20. (1-100) + --search-after-ctx string + --asc bool — Ascending sort order. + --orderby string — Sort field. [created_at, updated_at, team_name] + --person-id int — Filter by member ID — return only teams this person belongs to. + --query string — Substring match on team name. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - items (array) (required) + - account_id (integer) (required) — Owning account ID. + - created_at (integer) (required) — Unix epoch seconds the team was created. + - creator_id (integer) (required) — Member ID of the creator. + - creator_name (string) (required) — Display name of the creator. + - description (string) (required) — Free-form description. + - person_ids (array) (required) — Member IDs of team members. + - ref_id (string) (required) — External reference ID for third-party HR system integration. + - status (string) (required) — Team status. [enabled, disabled] + - team_id (integer) (required) — Unique team ID. + - team_name (string) (required) — Team display name. 1–39 characters, unique per account. + - updated_at (integer) (required) — Unix epoch seconds the team was last updated. + - updated_by (integer) (required) — Member ID of the last editor. + - updated_by_name (string) (required) — Display name of the last editor. + - limit (integer) (required) — Page size used. + - p (integer) (required) — Current page number. + - total (integer) (required) — Total number of teams matching the filter. +`, + Example: ` flashduty team list --data '{"asc":false,"limit":20,"orderby":"created_at","p":1}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("page") { + body["p"] = fP + } + if cmd.Flags().Changed("limit") { + body["limit"] = fLimit + } + if cmd.Flags().Changed("search-after-ctx") { + body["search_after_ctx"] = fSearchAfterCtx + } + if cmd.Flags().Changed("asc") { + body["asc"] = fAsc + } + if cmd.Flags().Changed("orderby") { + body["orderby"] = fOrderby + } + if cmd.Flags().Changed("person-id") { + body["person_id"] = fPersonID + } + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + }) + if err != nil { + return err + } + req := new(flashduty.TeamListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Teams.ReadList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().Int64Var(&fP, "page", 0, "Page number. Default: 1. (min 1)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Max: 100. Default: 20. (1-100)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending sort order.") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at, team_name]") + cmd.Flags().Int64Var(&fPersonID, "person-id", 0, "Filter by member ID — return only teams this person belongs to.") + cmd.Flags().StringVar(&fQuery, "query", "", "Substring match on team name.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genTeamsWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fRefID string + var fTeamID int64 + var fTeamName string + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a team", + Long: `Delete a team. + +Permanently delete a team by ID, name, or external reference ID. + +API: POST /team/delete (team-write-delete) + +Request fields: + --ref-id string — External reference ID. + --team-id int — Team ID. + --team-name string — Team name. +`, + Example: ` flashduty team delete --data '{"team_id":1001}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("ref-id") { + body["ref_id"] = fRefID + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("team-name") { + body["team_name"] = fTeamName + } + }) + if err != nil { + return err + } + req := new(flashduty.TeamDeleteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + resp, err := ctx.Client.Teams.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + if resp != nil && len(resp.Raw) > 0 { + return ctx.WriteRaw(resp.Raw) + } + ctx.WriteResult("OK: POST /team/delete") + return nil + }) + }, + } + cmd.Flags().StringVar(&fRefID, "ref-id", "", "External reference ID.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team ID.") + cmd.Flags().StringVar(&fTeamName, "team-name", "", "Team name.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func genTeamsWriteUpsertCmd() *cobra.Command { + var dataJSON string + var fCountryCode string + var fDescription string + var fEmails []string + var fPersonIDs []int + var fPhones []string + var fRefID string + var fResetIfNameExist bool + var fTeamID int64 + var fTeamName string + cmd := &cobra.Command{ + Use: "upsert", + Short: "Create or update a team", + Long: `Create or update a team. + +Create a new team or update an existing one. Pass 'team_id' to update. + +API: POST /team/upsert (team-write-upsert) + +Request fields: + --country-code string — Default country code applied to any 'phones' entries that are not in E.164 format. + --description string — Free-form description. (≤500 chars) + --emails []string — Email addresses to invite as members. + --person-ids []int — Member IDs to set as team members. Replaces the existing member list. + --phones []string — Phone numbers to invite as members. + --ref-id string — External reference ID for HR system integration. + --reset-if-name-exist bool — If true and a team with the same name already exists, reset its membership to the provided person_ids. + --team-id int — Team ID. Omit or set to 0 to create a new team. + --team-name string (required) — Team display name. 1–39 characters. (1-39 chars) + +Response fields ('data' envelope is unwrapped — these fields are at the top level): + - team_id (integer) (required) — Created or updated team ID. + - team_name (string) (required) — Team name echoed from the request. +`, + Example: ` flashduty team upsert --data '{"description":"Backend reliability engineering team","person_ids":[80011,80012],"team_name":"Backend SRE"}'`, + RunE: func(cmd *cobra.Command, args []string) error { + return runCommand(cmd, args, func(ctx *RunContext) error { + body, err := genAssembleBody(dataJSON, func(body map[string]any) { + if cmd.Flags().Changed("country-code") { + body["countryCode"] = fCountryCode + } + if cmd.Flags().Changed("description") { + body["description"] = fDescription + } + if cmd.Flags().Changed("emails") { + body["emails"] = fEmails + } + if cmd.Flags().Changed("person-ids") { + body["person_ids"] = fPersonIDs + } + if cmd.Flags().Changed("phones") { + body["phones"] = fPhones + } + if cmd.Flags().Changed("ref-id") { + body["ref_id"] = fRefID + } + if cmd.Flags().Changed("reset-if-name-exist") { + body["reset_if_name_exist"] = fResetIfNameExist + } + if cmd.Flags().Changed("team-id") { + body["team_id"] = fTeamID + } + if cmd.Flags().Changed("team-name") { + body["team_name"] = fTeamName + } + }) + if err != nil { + return err + } + req := new(flashduty.TeamUpsertRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Teams.WriteUpsert(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fCountryCode, "country-code", "", "Default country code applied to any 'phones' entries that are not in E.164 format.") + cmd.Flags().StringVar(&fDescription, "description", "", "Free-form description. (≤500 chars)") + cmd.Flags().StringSliceVar(&fEmails, "emails", nil, "Email addresses to invite as members.") + cmd.Flags().IntSliceVar(&fPersonIDs, "person-ids", nil, "Member IDs to set as team members. Replaces the existing member list.") + cmd.Flags().StringSliceVar(&fPhones, "phones", nil, "Phone numbers to invite as members.") + cmd.Flags().StringVar(&fRefID, "ref-id", "", "External reference ID for HR system integration.") + cmd.Flags().BoolVar(&fResetIfNameExist, "reset-if-name-exist", false, "If true and a team with the same name already exists, reset its membership to the provided person_ids.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team ID. Omit or set to 0 to create a new team.") + cmd.Flags().StringVar(&fTeamName, "team-name", "", "Team display name. 1–39 characters. (required) (1-39 chars)") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields") + return cmd +} + +func registerGeneratedTeams(root *cobra.Command) { + gTeam := genGroup(root, "team", "Platform/Teams API") + genAddLeaf(gTeam, genTeamsReadInfoCmd()) + genAddLeaf(gTeam, genTeamsReadInfosCmd()) + genAddLeaf(gTeam, genTeamsReadListCmd()) + genAddLeaf(gTeam, genTeamsWriteDeleteCmd()) + genAddLeaf(gTeam, genTeamsWriteUpsertCmd()) +} diff --git a/internal/cmd/cligen/main.go b/internal/cmd/cligen/main.go new file mode 100644 index 0000000..81a2436 --- /dev/null +++ b/internal/cmd/cligen/main.go @@ -0,0 +1,1314 @@ +// Command cligen generates cobra commands for every Flashduty OpenAPI operation +// not already owned by a hand-curated command, so the CLI covers the full API +// surface and every command's --help is sourced from the API description. +// +// It mirrors go-flashduty's own generator: it reads the SAME vendored spec (from +// the linked go-flashduty module) and reuses naming.go so each generated command +// targets the exact typed SDK method. Structure (method name, request type, data +// return) is read by REFLECTING the compiled SDK — authoritative and always in +// sync; help text (summaries, field descriptions, examples) is read from the +// spec. Run: `go run ./internal/cmd/cligen` from the repo root. +package main + +import ( + "encoding/json" + "fmt" + "go/format" + "os" + "os/exec" + "path/filepath" + "reflect" + "sort" + "strings" + + flashduty "github.com/flashcatcloud/go-flashduty" +) + +const genHeader = "// Code generated by internal/cmd/cligen; DO NOT EDIT.\n\n" + +func main() { + only := "" + for i, a := range os.Args[1:] { + if a == "--only" && i+1 < len(os.Args[1:]) { + only = os.Args[1:][i+1] + } + } + if err := run(only); err != nil { + fmt.Fprintln(os.Stderr, "cligen:", err) + os.Exit(1) + } +} + +func run(only string) error { + specPath, err := specPath() + if err != nil { + return err + } + spec, err := loadSpec(specPath) + if err != nil { + return err + } + sdk := reflectSDK() + + root, err := repoRoot() + if err != nil { + return err + } + genDir := filepath.Join(root, "internal", "cli") + + // On a full run, clear stale generated files first so a service that lost + // operations leaves no orphan zz_generated_*.go behind. + if only == "" { + if err := cleanGenerated(genDir); err != nil { + return err + } + } + + // Generate a path-derived command for EVERY operation. Curated commands win + // on an exact name match at registration (genAddLeaf), so a generated twin of + // a curated command is harmlessly dropped while the curated one keeps the + // path-name; curated commands at a different (ergonomic) name coexist as + // extras. This guarantees every API path has a command at its path-name. + services := collectServices(asMap(spec["paths"]), asMap(asMap(spec["components"])["schemas"])) + groupShorts := computeGroupShorts(services) + + var registered []string + var generatedOpIDs []string + emitted := 0 + for _, s := range services { + if only != "" && !strings.Contains(s.Tag, only) { + continue + } + src, n, opIDs := emitService(s, sdk, groupShorts) + if n == 0 { + continue + } + if err := writeGo(filepath.Join(genDir, "zz_generated_"+fileName(s.Name)+".go"), src); err != nil { + return err + } + registered = append(registered, s.Name) + generatedOpIDs = append(generatedOpIDs, opIDs...) + emitted += n + } + + if err := writeGo(filepath.Join(genDir, "zz_generated_register.go"), emitRegister(registered)); err != nil { + return err + } + // The coverage manifest is only authoritative on a full run. + if only == "" { + if err := writeGo(filepath.Join(genDir, "zz_generated_manifest.go"), emitManifest(generatedOpIDs)); err != nil { + return err + } + // Response-help table covers ALL services so curated commands can look up + // any method; only complete on a full run. + if err := writeGo(filepath.Join(genDir, "zz_generated_response_help.go"), emitResponseHelp(services)); err != nil { + return err + } + } + fmt.Printf("cligen: %d services, %d generated commands\n", len(registered), emitted) + return nil +} + +// cleanGenerated removes all zz_generated_*.go files so a full run starts from +// a clean slate (orphan files from removed services don't linger). +func cleanGenerated(genDir string) error { + entries, err := os.ReadDir(genDir) + if err != nil { + return err + } + for _, e := range entries { + if !e.IsDir() && strings.HasPrefix(e.Name(), "zz_generated_") && strings.HasSuffix(e.Name(), ".go") { + if err := os.Remove(filepath.Join(genDir, e.Name())); err != nil { + return err + } + } + } + return nil +} + +// ---- spec model ------------------------------------------------------------ + +type service struct { + Name string // Go service field on Client, e.g. "Calendars" + Tag string + Ops []specOp +} + +type specOp struct { + OpID string + Method string // Go SDK method name + HTTP string + Path string + Summary string + Desc string + Example string // compact request-body example JSON, "" if none + Fields []specField // flat top-level request fields (drives flag help) + ReqTree []schemaField // full nested request body tree (for --data expansion) + RespTree []schemaField // 200 response `data` field tree (output shape) + // RespArray is true when the 200 `data` payload is itself a bare top-level + // array (`type:array, items:$ref`) rather than an object or {items,total} + // envelope. RespTree then holds the ROW (array-element) fields, and help must + // document the output as a TOP-LEVEL array (`jq '.[]'`), not `under data`. + RespArray bool +} + +// schemaField is one node in a (possibly nested) request/response schema tree. +type schemaField struct { + Wire string + Type string // string,int,bool,number,object,array,... + Required bool + Desc string + Enum []string + Constraint string // compact bound, e.g. "max 100", "1-39 chars" + Children []schemaField +} + +type specField struct { + Wire string + Required bool + Desc string + Enum []string + Constraint string // compact bound, e.g. "max 100", "1-39 chars" +} + +func collectServices(paths, schemas map[string]any) []service { + walker := &specWalker{schemas: schemas} + byTag := map[string][]struct { + path, http string + op map[string]any + }{} + for p, item := range paths { + for method, raw := range asMap(item) { + m := strings.ToUpper(method) + if m != "POST" && m != "GET" { + continue + } + o := asMap(raw) + tags := asSlice(o["tags"]) + if len(tags) == 0 { + continue + } + tag, _ := tags[0].(string) + byTag[tag] = append(byTag[tag], struct { + path, http string + op map[string]any + }{p, m, o}) + } + } + + var tagNames []string + for t := range byTag { + tagNames = append(tagNames, t) + } + sort.Strings(tagNames) + + var out []service + for _, tag := range tagNames { + entries := byTag[tag] + sort.Slice(entries, func(i, j int) bool { + return str(entries[i].op, "operationId") < str(entries[j].op, "operationId") + }) + var opIDs []string + for _, e := range entries { + opIDs = append(opIDs, str(e.op, "operationId")) + } + names := methodNames(opIDs) + svc := service{Name: serviceName(tag), Tag: tag} + for _, e := range entries { + opID := str(e.op, "operationId") + respTree, respArray := walker.responseTree(e.op) + svc.Ops = append(svc.Ops, specOp{ + OpID: opID, + Method: names[opID], + HTTP: e.http, + Path: e.path, + Summary: str(e.op, "summary"), + Desc: str(e.op, "description"), + Example: walker.example(e.op), + Fields: walker.fields(e.op), + ReqTree: walker.requestTree(e.op), + RespTree: respTree, + RespArray: respArray, + }) + } + if len(svc.Ops) > 0 { + out = append(out, svc) + } + } + return out +} + +type specWalker struct{ schemas map[string]any } + +func (w *specWalker) deref(s map[string]any) map[string]any { + if ref := refName(s); ref != "" { + return asMap(w.schemas[ref]) + } + return s +} + +// merged returns the flattened properties + required set of a schema, resolving +// $ref and allOf. +func (w *specWalker) merged(s map[string]any) (map[string]any, map[string]bool) { + props := map[string]any{} + req := map[string]bool{} + s = w.deref(s) + for _, part := range asSlice(s["allOf"]) { + p, r := w.merged(asMap(part)) + for k, v := range p { + props[k] = v + } + for k := range r { + req[k] = true + } + } + for k, v := range asMap(s["properties"]) { + props[k] = v + } + for _, r := range asSlice(s["required"]) { + if name, ok := r.(string); ok { + req[name] = true + } + } + return props, req +} + +func (w *specWalker) fields(op map[string]any) []specField { + var fields []specField + if rb := asMap(op["requestBody"]); rb != nil { + sch := asMap(asMap(asMap(rb["content"])["application/json"])["schema"]) + props, req := w.merged(sch) + for wire, v := range props { + pv := w.deref(asMap(v)) + fields = append(fields, specField{ + Wire: wire, + Required: req[wire], + Desc: str(pv, "description"), + Enum: w.enumOf(pv), + Constraint: constraintOf(pv), + }) + } + } + for _, raw := range asSlice(op["parameters"]) { + pm := asMap(raw) + if str(pm, "in") != "query" { + continue + } + psch := w.deref(asMap(pm["schema"])) + fields = append(fields, specField{ + Wire: str(pm, "name"), + Required: boolOf(pm["required"]), + Desc: str(pm, "description"), + Enum: w.enumOf(psch), + Constraint: constraintOf(psch), + }) + } + sort.Slice(fields, func(i, j int) bool { return fields[i].Wire < fields[j].Wire }) + return fields +} + +func (w *specWalker) example(op map[string]any) string { + rb := asMap(op["requestBody"]) + if rb == nil { + return "" + } + ex, ok := asMap(asMap(rb["content"])["application/json"])["example"] + if !ok { + return "" + } + b, err := json.Marshal(ex) + if err != nil { + return "" + } + return string(b) +} + +// enumOf returns a schema's enum values, falling back to its array items' enum +// (so a `[]string` flag whose elements are constrained still advertises them). +func (w *specWalker) enumOf(s map[string]any) []string { + if e := enumStrings(s); len(e) > 0 { + return e + } + if str(s, "type") == "array" { + return enumStrings(w.deref(asMap(s["items"]))) + } + return nil +} + +// schemaType renders a compact type label for a (deref'd) property schema. +func schemaType(s map[string]any) string { + switch t := str(s, "type"); t { + case "array": + it := asMap(s["items"]) + et := str(it, "type") + if et == "" && (it["properties"] != nil || it["allOf"] != nil || it["$ref"] != nil) { + et = "object" + } + if et == "" { + et = "any" + } + return "array<" + et + ">" + case "": + if s["properties"] != nil || s["allOf"] != nil || s["$ref"] != nil { + return "object" + } + return "any" + default: + return t + } +} + +// constraintOf renders a compact bound string ("max 100", "1-39 chars", "1-100") +// from a property schema's numeric/length keywords. Agents exceeding a documented +// limit (e.g. --limit 200 when max is 100) is the single largest source of +// InvalidParameter 400s, so these bounds belong in --help alongside the type. +func constraintOf(s map[string]any) string { + var parts []string + mn, okMn := numStr(s["minimum"]) + mx, okMx := numStr(s["maximum"]) + switch { + case okMn && okMx: + parts = append(parts, mn+"-"+mx) + case okMx: + parts = append(parts, "max "+mx) + case okMn: + parts = append(parts, "min "+mn) + } + lmn, okLmn := numStr(s["minLength"]) + lmx, okLmx := numStr(s["maxLength"]) + switch { + case okLmn && okLmx: + parts = append(parts, lmn+"-"+lmx+" chars") + case okLmx: + parts = append(parts, "≤"+lmx+" chars") + case okLmn: + parts = append(parts, "≥"+lmn+" chars") + } + return strings.Join(parts, ", ") +} + +// numStr formats a JSON number (float64 / json.Number) without a trailing ".0", +// returning ok=false for nil/non-numeric so callers can omit absent bounds. +func numStr(v any) (string, bool) { + switch n := v.(type) { + case float64: + if n == float64(int64(n)) { + return fmt.Sprintf("%d", int64(n)), true + } + return fmt.Sprintf("%g", n), true + case json.Number: + return n.String(), true + default: + return "", false + } +} + +// maxSchemaDepth bounds how deep request/response trees are expanded in --help. +const maxSchemaDepth = 3 + +// tree walks an object schema (resolving $ref/allOf) into a sorted field tree, +// recursing into nested objects and array-element objects up to maxSchemaDepth. +func (w *specWalker) tree(schema map[string]any, depth int) []schemaField { + if depth > maxSchemaDepth { + return nil + } + props, req := w.merged(schema) + var out []schemaField + for wire, v := range props { + pv := w.deref(asMap(v)) + f := schemaField{ + Wire: wire, + Required: req[wire], + Desc: str(pv, "description"), + Enum: enumStrings(pv), + Type: schemaType(pv), + Constraint: constraintOf(pv), + } + switch { + case pv["properties"] != nil || pv["allOf"] != nil: + f.Children = w.tree(pv, depth+1) + case str(pv, "type") == "array": + it := w.deref(asMap(pv["items"])) + if it["properties"] != nil || it["allOf"] != nil { + f.Children = w.tree(it, depth+1) + } else if len(f.Enum) == 0 { + f.Enum = enumStrings(it) // array of constrained scalars + } + } + out = append(out, f) + } + sort.Slice(out, func(i, j int) bool { return out[i].Wire < out[j].Wire }) + return out +} + +// requestTree returns the full nested field tree of an operation's JSON body. +func (w *specWalker) requestTree(op map[string]any) []schemaField { + rb := asMap(op["requestBody"]) + if rb == nil { + return nil + } + sch := asMap(asMap(asMap(rb["content"])["application/json"])["schema"]) + if sch == nil { + return nil + } + return w.tree(sch, 0) +} + +// responseTree returns the field tree of an operation's 200 response `data` +// member (unwrapping the {request_id,error,data} envelope), so --help can show +// the output shape — including the {items[],total} pagination wrapper for lists. +// The bool is true when `data` is itself a bare top-level array +// (`type:array, items:$ref`): the returned tree then holds the array-element +// (row) fields, since an array schema has no `properties` of its own and tree() +// would otherwise yield nothing — leaving every such list endpoint (e.g. +// rule-list-basic) with an empty Response-fields block that forced agents to +// guess jq keys. Callers document this shape as a TOP-LEVEL array (`jq '.[]'`). +func (w *specWalker) responseTree(op map[string]any) ([]schemaField, bool) { + r200 := asMap(asMap(op["responses"])["200"]) + sch := asMap(asMap(asMap(r200["content"])["application/json"])["schema"]) + if sch == nil { + return nil, false + } + props, _ := w.merged(sch) + data := w.deref(asMap(props["data"])) + if data == nil { + return nil, false + } + if str(data, "type") == "array" { + it := w.deref(asMap(data["items"])) + if it["properties"] != nil || it["allOf"] != nil { + return w.tree(it, 0), true + } + return nil, false // array of bare scalars — no row field tree to document + } + return w.tree(data, 0), false +} + +// ---- reflection over the compiled SDK ------------------------------------- + +type methodInfo struct { + ReqType string // request struct name ("" => no body param) + HasData bool // method returns (*Data, *Response, error) +} + +// eachServiceField walks a Client value, descending into its embedded +// genServices struct, and invokes fn for every *XxxService handle with its +// field name (the service name, e.g. "Calendars"). +func eachServiceField(cv reflect.Value, fn func(name string, svcType reflect.Type)) { + ct := cv.Type() + for i := 0; i < ct.NumField(); i++ { + f := ct.Field(i) + if f.Anonymous && deref(f.Type).Kind() == reflect.Struct { + fv := cv.Field(i) + if fv.Kind() == reflect.Pointer { + fv = fv.Elem() + } + if fv.IsValid() { + eachServiceField(fv, fn) + } + continue + } + if f.Type.Kind() == reflect.Pointer && strings.HasSuffix(f.Type.Elem().Name(), "Service") { + fn(f.Name, f.Type) + } + } +} + +// reflectSDK introspects a real Client so the generator targets the exact +// method/request-type names the SDK exposes, regardless of how the spec maps to +// Go identifiers. +func reflectSDK() map[string]map[string]methodInfo { + client, err := flashduty.NewClient("cligen-dummy-key") + if err != nil { + panic("cligen: NewClient: " + err.Error()) + } + out := map[string]map[string]methodInfo{} + eachServiceField(reflect.ValueOf(client).Elem(), func(name string, svcType reflect.Type) { + methods := map[string]methodInfo{} + for m := 0; m < svcType.NumMethod(); m++ { + meth := svcType.Method(m) + mt := meth.Func.Type() // In(0)=receiver, In(1)=ctx, In(2)=*Req? + info := methodInfo{HasData: mt.NumOut() == 3} + if mt.NumIn() >= 3 { + rt := mt.In(2) + if rt.Kind() == reflect.Pointer { + rt = rt.Elem() + } + info.ReqType = rt.Name() + } + methods[meth.Name] = info + } + out[name] = methods + }) + return out +} + +// reqFields reflects a request struct's flag-able scalar fields (flattening +// embedded structs) and the names of complex fields (reachable only via --data). +func reqFields(reqType string) (scalars []scalarField, complex []string) { + if reqType == "" { + return nil, nil + } + t := typeByName(reqType) + if t == nil { + return nil, nil + } + // A request type whose body is a JSON array or scalar (not an object) has no + // flag-able fields; it is reachable only via --data. + if deref(t).Kind() != reflect.Struct { + return nil, nil + } + var walk func(t reflect.Type) + seen := map[string]bool{} + walk = func(t reflect.Type) { + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Anonymous && deref(f.Type).Kind() == reflect.Struct { + walk(deref(f.Type)) + continue + } + // POST bodies tag fields with `json`; GET query structs tag them with + // `url` and carry NO json tag. Fall back to the url wire-name so GET + // commands emit typed flags instead of a bare --data nobody can fill. + wire := strings.Split(f.Tag.Get("json"), ",")[0] + if wire == "" { + wire = strings.Split(f.Tag.Get("url"), ",")[0] + } + if wire == "" || wire == "-" || seen[wire] { + continue + } + seen[wire] = true + kind, ok := scalarKind(f.Type) + if !ok { + complex = append(complex, wire) + continue + } + scalars = append(scalars, scalarField{Wire: wire, Kind: kind}) + } + } + walk(deref(t)) + return scalars, complex +} + +type scalarField struct { + Wire string + Kind string // "string","bool","int","float","[]string","[]int" +} + +var typeRegistry = buildTypeRegistry() + +func buildTypeRegistry() map[string]reflect.Type { + reg := map[string]reflect.Type{} + client, err := flashduty.NewClient("cligen-dummy-key") + if err != nil { + panic("cligen: NewClient: " + err.Error()) + } + eachServiceField(reflect.ValueOf(client).Elem(), func(_ string, svcType reflect.Type) { + for m := 0; m < svcType.NumMethod(); m++ { + mt := svcType.Method(m).Func.Type() + if mt.NumIn() >= 3 { + rt := mt.In(2) + if rt.Kind() == reflect.Pointer { + rt = rt.Elem() + } + reg[rt.Name()] = rt + } + } + }) + return reg +} + +func typeByName(name string) reflect.Type { return typeRegistry[name] } + +func deref(t reflect.Type) reflect.Type { + for t.Kind() == reflect.Pointer { + t = t.Elem() + } + return t +} + +func scalarKind(t reflect.Type) (string, bool) { + t = deref(t) + switch t.Kind() { + case reflect.String: + return "string", true + case reflect.Bool: + return "bool", true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return "int", true + case reflect.Float32, reflect.Float64: + return "float", true + case reflect.Slice: + switch deref(t.Elem()).Kind() { + case reflect.String: + return "[]string", true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return "[]int", true + } + } + return "", false +} + +// ---- emission -------------------------------------------------------------- + +func emitService(s service, sdk map[string]map[string]methodInfo, groupShorts map[string]string) (string, int, []string) { + var cmds strings.Builder + methods := sdk[s.Name] + var register strings.Builder + fmt.Fprintf(®ister, "func registerGenerated%s(root *cobra.Command) {\n", s.Name) + + n := 0 + usesFlashduty := false + var opIDs []string + groups := map[string]string{} // group name -> Go var expr + for _, o := range s.Ops { + mi, ok := methods[o.Method] + if !ok { + // Method not found by reflection (naming drift); skip rather than emit broken code. + fmt.Fprintf(os.Stderr, "cligen: WARN %s.%s (%s) not found on SDK; skipped\n", s.Name, o.Method, o.OpID) + continue + } + if mi.ReqType != "" { + usesFlashduty = true + } + fn := "gen" + s.Name + o.Method + "Cmd" + cmds.WriteString(emitCmd(fn, s, o, mi)) + // Registration is a flat two-level tree: the first path segment is the + // top-level group (find-or-create — generated ops merge into the curated + // group of the same name), and the remaining segments hyphen-join into + // the leaf verb. Flattening guarantees no operation is both a runnable + // leaf and a parent group, so prefix-nested paths (e.g. /monit/rule/update + // and /monit/rule/update/fields) never collide. + group := cliGroup(o.Path) + parentVar, done := groups[group] + if !done { + parentVar = "g" + sanitize(group) + fmt.Fprintf(®ister, "\t%s := genGroup(root, %q, %q)\n", parentVar, group, groupShorts[group]) + groups[group] = parentVar + } + fmt.Fprintf(®ister, "\tgenAddLeaf(%s, %s())\n", parentVar, fn) + n++ + opIDs = append(opIDs, o.OpID) + } + register.WriteString("}\n") + + var b strings.Builder + b.WriteString(genHeader) + b.WriteString("package cli\n\n") + if usesFlashduty { + b.WriteString("import (\n\t\"github.com/spf13/cobra\"\n\n\tflashduty \"github.com/flashcatcloud/go-flashduty\"\n)\n\n") + } else { + b.WriteString("import \"github.com/spf13/cobra\"\n\n") + } + b.WriteString(cmds.String()) + b.WriteString(register.String()) + return b.String(), n, opIDs +} + +func emitCmd(fn string, s service, o specOp, mi methodInfo) string { + scalars, complexFields := reqFields(mi.ReqType) + // Spec metadata by wire for help + required annotation. + specByWire := map[string]specField{} + for _, f := range o.Fields { + specByWire[f.Wire] = f + } + + var b strings.Builder + fmt.Fprintf(&b, "func %s() *cobra.Command {\n", fn) + b.WriteString("\tvar dataJSON string\n") + for _, sf := range scalars { + fmt.Fprintf(&b, "\tvar %s %s\n", flagVar(sf.Wire), goFlagType(sf.Kind)) + } + + // Command literal. The leaf verb must match the name registered in + // emitService (cliVerb), so they share the same derivation. + verb := cliVerb(o.Path) + fmt.Fprintf(&b, "\tcmd := &cobra.Command{\n") + fmt.Fprintf(&b, "\t\tUse: %q,\n", verb) + fmt.Fprintf(&b, "\t\tShort: %q,\n", oneLine(o.Summary)) + fmt.Fprintf(&b, "\t\tLong: %s,\n", quoteMultiline(longHelp(o, scalars, complexFields, specByWire))) + if ex := exampleHelp(o, verb, s); ex != "" { + fmt.Fprintf(&b, "\t\tExample: %s,\n", quoteMultiline(ex)) + } + b.WriteString("\t\tRunE: func(cmd *cobra.Command, args []string) error {\n") + b.WriteString("\t\t\treturn runCommand(cmd, args, func(ctx *RunContext) error {\n") + // body assembly + b.WriteString("\t\t\t\tbody, err := genAssembleBody(dataJSON, func(body map[string]any) {\n") + for _, sf := range scalars { + fmt.Fprintf(&b, "\t\t\t\t\tif cmd.Flags().Changed(%q) {\n\t\t\t\t\t\tbody[%q] = %s\n\t\t\t\t\t}\n", + flagName(sf.Wire), sf.Wire, flagVar(sf.Wire)) + } + b.WriteString("\t\t\t\t})\n") + b.WriteString("\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n") + + call := fmt.Sprintf("ctx.Client.%s.%s(cmdContext(ctx.Cmd)", s.Name, o.Method) + if mi.ReqType != "" { + fmt.Fprintf(&b, "\t\t\t\treq := new(flashduty.%s)\n", mi.ReqType) + b.WriteString("\t\t\t\tif err := genBindBody(body, req); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n") + call += ", req)" + } else { + b.WriteString("\t\t\t\t_ = body\n") + call += ")" + } + + switch { + case mi.HasData: + fmt.Fprintf(&b, "\t\t\t\tout, _, err := %s\n", call) + b.WriteString("\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n") + b.WriteString("\t\t\t\treturn printGenericResult(ctx, out)\n") + default: + // (*Response, error): a mutation, OR an export/attachment endpoint + // whose CSV/file body the SDK surfaces on Response.Raw. Capture the + // response and stream Raw verbatim when present (so `> file.csv` + // works); otherwise fall back to the canned OK acknowledgment. + fmt.Fprintf(&b, "\t\t\t\tresp, err := %s\n", call) + b.WriteString("\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n") + b.WriteString("\t\t\t\tif resp != nil && len(resp.Raw) > 0 {\n\t\t\t\t\treturn ctx.WriteRaw(resp.Raw)\n\t\t\t\t}\n") + fmt.Fprintf(&b, "\t\t\t\tctx.WriteResult(%q)\n", "OK: "+o.HTTP+" "+o.Path) + b.WriteString("\t\t\t\treturn nil\n") + } + b.WriteString("\t\t\t})\n\t\t},\n\t}\n") + + // flags + for _, sf := range scalars { + usage := flagUsage(specByWire[sf.Wire]) + fmt.Fprintf(&b, "\tcmd.Flags().%s(&%s, %q, %s, %q)\n", + flagSetter(sf.Kind), flagVar(sf.Wire), flagName(sf.Wire), flagZero(sf.Kind), usage) + } + b.WriteString("\tcmd.Flags().StringVar(&dataJSON, \"data\", \"\", \"Full request body as JSON; typed flags override its fields\")\n") + b.WriteString("\treturn cmd\n}\n\n") + return b.String() +} + +func emitRegister(services []string) string { + var b strings.Builder + b.WriteString(genHeader) + b.WriteString("package cli\n\n") + b.WriteString("import \"github.com/spf13/cobra\"\n\n") + b.WriteString("// registerGenerated attaches every generated command group to root. Called\n// from root.go init() after curated commands so curated leaves win on conflict.\n") + b.WriteString("func registerGenerated(root *cobra.Command) {\n") + for _, s := range services { + fmt.Fprintf(&b, "\tregisterGenerated%s(root)\n", s) + } + b.WriteString("}\n") + return b.String() +} + +// emitManifest records every operationId that has a generated command, for the +// coverage test to assert the generator targets the full spec (one path-derived +// command per operation). +func emitManifest(generated []string) string { + sort.Strings(generated) + var b strings.Builder + b.WriteString(genHeader) + b.WriteString("package cli\n\n") + b.WriteString("// generatedOpIDs are the OpenAPI operationIds the generator emitted a\n// path-derived command for (every operation in the spec).\n") + b.WriteString("var generatedOpIDs = []string{\n") + for _, id := range generated { + fmt.Fprintf(&b, "\t%q,\n", id) + } + b.WriteString("}\n") + return b.String() +} + +// emitResponseHelp generates a map from SDK "Service.Method" to its rendered +// Response-fields block, so curated commands (which call the same SDK methods +// under ergonomic flags) can show the same output schema as generated commands. +func emitResponseHelp(services []service) string { + type kv struct{ k, v string } + var rows []kv + for _, s := range services { + for _, o := range s.Ops { + // Curated commands read this map. Every curated list command + // flattens its {items,total} envelope to a top-level array, so they + // need the flattened ("jq '.[]'") shape, not the wire envelope. An + // endpoint whose `data` is already a bare top-level array documents + // the same `.[]` shape directly from its row fields. + sec := responseSectionList(o.RespTree) + if o.RespArray { + sec = responseSectionTopArray(o.RespTree) + } + if sec == "" { + continue + } + rows = append(rows, kv{s.Name + "." + o.Method, sec}) + } + } + sort.Slice(rows, func(i, j int) bool { return rows[i].k < rows[j].k }) + + var b strings.Builder + b.WriteString(genHeader) + b.WriteString("package cli\n\n") + b.WriteString("// responseHelpBySDKMethod maps an SDK \"Service.Method\" to its rendered\n// Response-fields help block. Curated commands look this up via responseHelp()\n// so they document the same output shape as the generated commands.\n") + b.WriteString("var responseHelpBySDKMethod = map[string]string{\n") + for _, r := range rows { + fmt.Fprintf(&b, "\t%q: %q,\n", r.k, r.v) + } + b.WriteString("}\n") + return b.String() +} + +// ---- help text ------------------------------------------------------------- + +func longHelp(o specOp, scalars []scalarField, complexFields []string, byWire map[string]specField) string { + var b strings.Builder + if o.Summary != "" { + b.WriteString(strings.TrimSuffix(o.Summary, ".") + ".\n") + } + if o.Desc != "" && o.Desc != o.Summary { + b.WriteString("\n" + o.Desc + "\n") + } + fmt.Fprintf(&b, "\nAPI: %s %s (%s)\n", o.HTTP, o.Path, o.OpID) + if len(scalars) > 0 || len(complexFields) > 0 { + b.WriteString("\nRequest fields:\n") + for _, sf := range scalars { + f := byWire[sf.Wire] + req := "" + if f.Required { + req = " (required)" + } + line := fmt.Sprintf(" --%s %s%s", flagName(sf.Wire), sf.Kind, req) + if f.Desc != "" { + line += " — " + oneLine(f.Desc) + } + if len(f.Enum) > 0 { + line += " [" + strings.Join(f.Enum, ", ") + "]" + } + if f.Constraint != "" { + line += " (" + f.Constraint + ")" + } + b.WriteString(line + "\n") + } + reqByWire := map[string]schemaField{} + for _, n := range o.ReqTree { + reqByWire[n.Wire] = n + } + for _, wire := range complexFields { + f := byWire[wire] + node := reqByWire[wire] + typ := "JSON" + if node.Type != "" { + typ = node.Type + } + req := "" + if f.Required || node.Required { + req = " (required)" + } + line := fmt.Sprintf(" %s (%s, via --data)%s", wire, typ, req) + if f.Desc != "" { + line += " — " + oneLine(f.Desc) + } + b.WriteString(line + "\n") + // Expand the nested sub-fields so the agent doesn't guess the body shape. + renderTree(&b, node.Children, 2) + } + } + // Response shape — so the agent reads/extracts output fields without guessing. + s := responseSection(o.RespTree) + if o.RespArray { + s = responseSectionTopArray(o.RespTree) + } + if s != "" { + b.WriteString("\n" + s) + } + return b.String() +} + +// responseSection renders the "Response fields" help block for a response tree, +// or "" when the response has no documented schema. Shared by longHelp (embedded +// in generated commands) and emitResponseHelp (looked up by curated commands). +// listEnvelope returns the row (array-element) fields and true when resp is a +// paginated list envelope: exactly one object-array field named items/docs/list +// with only SCALAR siblings (total, p, limit, has_next_page, search_after_ctx … +// — all pagination metadata). This is the shape the backend returns under +// `data`; the CLI's curated list commands flatten it to a top-level array. The +// scalar-sibling test avoids enumerating every pagination field name. +func listEnvelope(resp []schemaField) ([]schemaField, bool) { + var rows []schemaField + found := false + for _, f := range resp { + if (f.Wire == "items" || f.Wire == "docs" || f.Wire == "list") && strings.HasPrefix(f.Type, "array") && len(f.Children) > 0 { + if found { + return nil, false + } + rows, found = f.Children, true + continue + } + // Any non-row sibling must be a scalar (pagination metadata). An array + // or object sibling means this is a richer response, not a flat list. + if strings.HasPrefix(f.Type, "array") || f.Type == "object" || len(f.Children) > 0 { + return nil, false + } + } + return rows, found +} + +// responseSection renders the Response-fields block. printGenericResult UNWRAPS +// the {request_id,error,data} envelope and prints `data`'s content at the top +// level (verified: `fduty schedule list --json | jq 'keys'` → [items,total], and +// `jq '.data'` → null). So the printed shape is `data`'s content directly — never +// nested under a `.data` key. A list envelope keeps its rows under items[]; the +// correct jq is `.items[]`, NOT `.data.items[]`. +func responseSection(resp []schemaField) string { + if len(resp) == 0 { + return "" + } + var b strings.Builder + if _, ok := listEnvelope(resp); ok { + b.WriteString("Response fields (`data` envelope is unwrapped — rows are nested under items[]; pipe `jq '.items[]'`, NOT `.data.items[]`):\n") + } else { + b.WriteString("Response fields (`data` envelope is unwrapped — these fields are at the top level):\n") + } + renderTree(&b, resp, 1) + return b.String() +} + +// responseSectionTopArray renders the Response-fields block for an endpoint whose +// `data` payload is itself a bare top-level array of row objects (no items[] +// wrapper) — both the generated command's raw print and a curated command emit a +// top-level array, so the documented jq path is `.[]`. rows are the +// array-element fields produced by responseTree's array descent. +func responseSectionTopArray(rows []schemaField) string { + if len(rows) == 0 { + return "" + } + var b strings.Builder + b.WriteString("Response fields (`data` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n") + renderTree(&b, rows, 1) + return b.String() +} + +// responseSectionList renders the Response-fields block for a command whose +// `--json` FLATTENS a list envelope to a top-level array of row objects (every +// curated list command does this via PrintList). It documents the flattened +// shape — `jq '.[]'`, not `.items[]` — so an agent that trusts the help writes a +// jq path that actually matches the output. Non-list responses fall through to +// responseSection unchanged. +func responseSectionList(resp []schemaField) string { + if rows, ok := listEnvelope(resp); ok { + var b strings.Builder + b.WriteString("Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n") + renderTree(&b, rows, 1) + return b.String() + } + return responseSection(resp) +} + +// renderTree writes a schema field tree as an indented "- name (type) — desc" +// list, recursing into nested object/array-element fields. indent is in 2-space +// units (so it nests under the parent line in longHelp). +func renderTree(b *strings.Builder, fields []schemaField, indent int) { + pad := strings.Repeat(" ", indent) + for _, f := range fields { + line := pad + "- " + f.Wire + " (" + f.Type + ")" + if f.Required { + line += " (required)" + } + if f.Desc != "" { + line += " — " + oneLine(f.Desc) + } + if len(f.Enum) > 0 { + line += " [" + strings.Join(f.Enum, ", ") + "]" + } + if f.Constraint != "" { + line += " (" + f.Constraint + ")" + } + b.WriteString(line + "\n") + renderTree(b, f.Children, indent+1) + } +} + +func exampleHelp(o specOp, verb string, s service) string { + cmdPath := "flashduty " + cliGroup(o.Path) + " " + cliVerb(o.Path) + if o.Example != "" { + return " " + cmdPath + " --data '" + o.Example + "'" + } + return "" +} + +func flagUsage(f specField) string { + // Spec descriptions use Markdown code-spans (`field`). pflag's UnquoteUsage + // treats the first backtick-quoted word as the flag's value-type placeholder, + // rendering nonsense like "--dql sql" / "--country-code phones". Convert + // backticks to single quotes so pflag falls back to the real type name. + u := strings.ReplaceAll(oneLine(f.Desc), "`", "'") + if u == "" { + u = "Request field " + f.Wire + } + if f.Required { + u += " (required)" + } + if len(f.Enum) > 0 { + u += " [" + strings.Join(f.Enum, ", ") + "]" + } + if f.Constraint != "" { + u += " (" + f.Constraint + ")" + } + return u +} + +// ---- small helpers --------------------------------------------------------- + +// flagDisplayName overrides the CLI surface name for a few unintuitive wire +// keys. The JSON wire key (what we put in the request body) is unchanged; only +// the flag an agent types differs. "p" → "page" because a bare "--p" is the +// single most-guessed-wrong list flag (audit), and no request field uses the +// wire "page", so the rename is collision-free. +var flagDisplayName = map[string]string{"p": "page"} + +func flagName(wire string) string { + if n, ok := flagDisplayName[wire]; ok { + return n + } + return kebab(wire) +} +func flagVar(wire string) string { return "f" + pascal(tokens(wire)) } + +func goFlagType(kind string) string { + switch kind { + case "string": + return "string" + case "bool": + return "bool" + case "int": + return "int64" + case "float": + return "float64" + case "[]string": + return "[]string" + case "[]int": + return "[]int" + } + return "string" +} + +func flagSetter(kind string) string { + switch kind { + case "string": + return "StringVar" + case "bool": + return "BoolVar" + case "int": + return "Int64Var" + case "float": + return "Float64Var" + case "[]string": + return "StringSliceVar" + case "[]int": + return "IntSliceVar" + } + return "StringVar" +} + +func flagZero(kind string) string { + switch kind { + case "string": + return `""` + case "bool": + return "false" + case "int": + return "0" + case "float": + return "0" + case "[]string": + return "nil" + case "[]int": + return "nil" + } + return `""` +} + +func pathSegs(p string) []string { + var out []string + for _, s := range strings.Split(p, "/") { + if s != "" { + out = append(out, s) + } + } + return out +} + +// cliGroup is the top-level command group for an operation: the kebab of its +// first path segment. Generated ops merge into the curated group of the same +// name (e.g. /incident/* joins the curated `incident` command). +func cliGroup(path string) string { return kebab(pathSegs(path)[0]) } + +// cliVerb is the leaf verb: the remaining path segments after the group, +// hyphen-joined. A single-segment path uses that segment as the verb. Because +// every operation becomes exactly one leaf (never a parent of another op), this +// flat shape is collision-free even for prefix-nested paths. +func cliVerb(path string) string { + segs := pathSegs(path) + if len(segs) == 1 { + return kebab(segs[0]) + } + return strings.Join(mapSlice(segs[1:], kebab), "-") +} + +// computeGroupShorts assigns each top-level group a one-line description. A group +// that holds a single tag's ops uses that tag; a group spanning several tags +// (e.g. `monit` = Alert rules + Data sources + Rule sets) uses their common +// tag-prefix ("Monitors"). Groups that merge into a curated command keep the +// curated short (genGroup reuses the existing command), so this only sets the +// short for generated-only groups. +func computeGroupShorts(services []service) map[string]string { + tagsByGroup := map[string]map[string]bool{} + for _, s := range services { + for _, o := range s.Ops { + g := cliGroup(o.Path) + if tagsByGroup[g] == nil { + tagsByGroup[g] = map[string]bool{} + } + tagsByGroup[g][s.Tag] = true + } + } + out := map[string]string{} + for g, set := range tagsByGroup { + var tags []string + for t := range set { + tags = append(tags, t) + } + sort.Strings(tags) + out[g] = groupShort(tags) + } + return out +} + +func groupShort(tags []string) string { + if len(tags) == 0 { + return "Flashduty API" + } + if len(tags) == 1 { + return tags[0] + " API" + } + common := strings.Split(tags[0], "/") + for _, t := range tags[1:] { + segs := strings.Split(t, "/") + i := 0 + for i < len(common) && i < len(segs) && common[i] == segs[i] { + i++ + } + common = common[:i] + } + if len(common) > 0 { + return strings.Join(common, "/") + " API" + } + return "Flashduty API" +} + +func sanitize(s string) string { + var b strings.Builder + for _, t := range tokens(s) { + b.WriteString(pascalToken(t)) + } + return b.String() +} + +func mapSlice(in []string, f func(string) string) []string { + out := make([]string, len(in)) + for i, s := range in { + out[i] = f(s) + } + return out +} + +func oneLine(s string) string { + s = strings.ReplaceAll(s, "\n", " ") + s = strings.ReplaceAll(s, "\r", " ") + return strings.Join(strings.Fields(s), " ") +} + +func quoteMultiline(s string) string { return "`" + strings.ReplaceAll(s, "`", "'") + "`" } + +func enumStrings(s map[string]any) []string { + var out []string + for _, v := range asSlice(s["enum"]) { + if str, ok := v.(string); ok && str != "" { + out = append(out, str) + } + } + return out +} + +func boolOf(v any) bool { b, _ := v.(bool); return b } + +func refName(s map[string]any) string { + ref, _ := s["$ref"].(string) + if ref == "" { + return "" + } + return ref[strings.LastIndex(ref, "/")+1:] +} + +func asMap(v any) map[string]any { m, _ := v.(map[string]any); return m } +func asSlice(v any) []any { s, _ := v.([]any); return s } +func str(m map[string]any, k string) string { + s, _ := m[k].(string) + return s +} + +func fileName(svc string) string { + var b strings.Builder + for i, r := range svc { + if r >= 'A' && r <= 'Z' { + if i > 0 { + b.WriteByte('_') + } + b.WriteRune(r - 'A' + 'a') + } else { + b.WriteRune(r) + } + } + return b.String() +} + +func writeGo(path, src string) error { + formatted, err := format.Source([]byte(src)) + if err != nil { + _ = os.WriteFile(path, []byte(src), 0o644) + return fmt.Errorf("format %s: %w", filepath.Base(path), err) + } + return os.WriteFile(path, formatted, 0o644) +} + +func loadSpec(path string) (map[string]any, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var spec map[string]any + if err := json.Unmarshal(data, &spec); err != nil { + return nil, fmt.Errorf("parse spec: %w", err) + } + return spec, nil +} + +// specPath resolves the openapi.en.json shipped in the linked go-flashduty +// module, so the CLI always generates against the spec its SDK was built from. +func specPath() (string, error) { + out, err := exec.Command("go", "list", "-m", "-f", "{{.Dir}}", "github.com/flashcatcloud/go-flashduty").Output() + if err != nil { + return "", fmt.Errorf("locate go-flashduty module: %w", err) + } + dir := strings.TrimSpace(string(out)) + return filepath.Join(dir, "openapi", "openapi.en.json"), nil +} + +func repoRoot() (string, error) { + wd, err := os.Getwd() + if err != nil { + return "", err + } + for dir := wd; ; { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir, nil + } + parent := filepath.Dir(dir) + if parent == dir { + return "", fmt.Errorf("go.mod not found from %s", wd) + } + dir = parent + } +} diff --git a/internal/cmd/cligen/naming.go b/internal/cmd/cligen/naming.go new file mode 100644 index 0000000..14466ba --- /dev/null +++ b/internal/cmd/cligen/naming.go @@ -0,0 +1,162 @@ +package main + +// Copied verbatim from go-flashduty/internal/cmd/gen/naming.go so the CLI +// generator derives the SAME service type and method names the SDK generator +// did. Keep in sync when the SDK's naming changes (rare). + +import ( + "strings" +) + +// initialisms are tokens forced to a canonical capitalization in Go identifiers. +var initialisms = map[string]string{ + "id": "ID", "ids": "IDs", "api": "API", "url": "URL", "uri": "URI", + "http": "HTTP", "https": "HTTPS", "json": "JSON", "html": "HTML", + "sls": "SLS", "csv": "CSV", "sms": "SMS", "md5": "MD5", "sha": "SHA", + "ip": "IP", "ai": "AI", "db": "DB", "os": "OS", "rum": "RUM", "mfa": "MFA", + "sso": "SSO", "saml": "SAML", "oidc": "OIDC", "ldap": "LDAP", "ts": "TS", + "ack": "Ack", "ok": "OK", "ttl": "TTL", "cpu": "CPU", "qps": "QPS", + "sla": "SLA", "mttr": "MTTR", "utc": "UTC", "tz": "TZ", "ui": "UI", +} + +// tokens splits an identifier (kebab, snake, or camelCase) into lowercase words. +func tokens(s string) []string { + var out []string + for _, part := range splitNonAlnum(s) { + out = append(out, splitCamel(part)...) + } + return out +} + +func splitNonAlnum(s string) []string { + return strings.FieldsFunc(s, func(r rune) bool { + //nolint:staticcheck // QF1001: keep the explicit !(isAlnum) form; De Morgan rewrite is less readable (matches go-flashduty .golangci.yml). + return !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) + }) +} + +func splitCamel(s string) []string { + runes := []rune(s) + n := len(runes) + if n == 0 { + return nil + } + isUpper := func(r rune) bool { return r >= 'A' && r <= 'Z' } + isLower := func(r rune) bool { return r >= 'a' && r <= 'z' } + + var words []string + start := 0 + for i := 1; i < n; i++ { + prev, cur := runes[i-1], runes[i] + boundary := false + switch { + case isLower(prev) && isUpper(cur): + boundary = true + case isUpper(prev) && isUpper(cur) && i+1 < n && isLower(runes[i+1]): + boundary = true + } + if boundary { + words = append(words, strings.ToLower(string(runes[start:i]))) + start = i + } + } + words = append(words, strings.ToLower(string(runes[start:]))) + return words +} + +func pascalToken(w string) string { + if v, ok := initialisms[strings.ToLower(w)]; ok { + return v + } + return strings.ToUpper(w[:1]) + strings.ToLower(w[1:]) +} + +func pascal(toks []string) string { + var b strings.Builder + for _, t := range toks { + b.WriteString(pascalToken(t)) + } + return b.String() +} + +// serviceName derives a service type prefix from a two-level tag's last segment. +func serviceName(tag string) string { + seg := tag + if i := strings.LastIndex(tag, "/"); i >= 0 { + seg = tag[i+1:] + } + return pascal(tokens(seg)) +} + +func commonPrefixLen(opTokens [][]string) int { + if len(opTokens) == 0 { + return 0 + } + n := 0 + for { + for _, t := range opTokens { + if len(t) <= n+1 { + return n + } + } + first := strings.ToLower(opTokens[0][n]) + for _, t := range opTokens[1:] { + if strings.ToLower(t[n]) != first { + return n + } + } + n++ + } +} + +// methodNames computes a unique Go method name for each operationId within a +// service by stripping the shared leading resource tokens, then deduping. +func methodNames(opIDs []string) map[string]string { + opTokens := make([][]string, len(opIDs)) + for i, id := range opIDs { + opTokens[i] = tokens(id) + } + n := commonPrefixLen(opTokens) + + result := make(map[string]string, len(opIDs)) + used := make(map[string]int) + for i, id := range opIDs { + toks := opTokens[i][n:] + if len(toks) == 0 { + toks = opTokens[i] + } + name := pascal(toks) + if name == "" { + name = pascal(opTokens[i]) + } + if c := used[name]; c > 0 { + full := pascal(opTokens[i]) + if used[full] == 0 { + name = full + } else { + name += itoa(c) + } + } + used[name]++ + result[id] = name + } + return result +} + +func itoa(n int) string { + if n == 0 { + return "0" + } + var b []byte + for n > 0 { + b = append([]byte{byte('0' + n%10)}, b...) + n /= 10 + } + return string(b) +} + +// kebab converts an identifier to kebab-case (lower words joined by '-'), used +// for CLI verb/flag names derived from wire names and path segments. +func kebab(s string) string { + return strings.Join(tokens(s), "-") +}