From 9227464c44de2fd0b91e7f23bef1fd8c74b42fe3 Mon Sep 17 00:00:00 2001 From: flashduty-bot Date: Thu, 10 Sep 2026 10:02:55 +0800 Subject: [PATCH 1/4] feat(monit): support datasource query tools in invoke command and skill cards --- go.mod | 2 +- go.sum | 10 +- internal/cli/datasource_tools_test.go | 45 + internal/cli/zz_generated_alert_rules.go | 931 +++++++++--------- internal/cli/zz_generated_applications.go | 405 ++++++++ internal/cli/zz_generated_data_sources.go | 11 +- internal/cli/zz_generated_manifest.go | 14 +- internal/cli/zz_generated_members.go | 6 + internal/cli/zz_generated_response_help.go | 409 ++++---- internal/cli/zz_generated_schedules.go | 64 ++ skills/flashduty/reference/member.md | 1 + .../flashduty/reference/monit-datasource.md | 18 +- skills/flashduty/reference/monit-query.md | 4 +- skills/flashduty/reference/monit-rule.md | 166 ++-- skills/flashduty/reference/rum.md | 38 + skills/flashduty/reference/schedule.md | 5 + 16 files changed, 1331 insertions(+), 798 deletions(-) diff --git a/go.mod b/go.mod index 603bbe5..9861816 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.15.1-0.20260908113851-5bf8f2902391 + github.com/flashcatcloud/go-flashduty v0.15.2-0.20260910014922-dce7c882be68 github.com/mattn/go-runewidth v0.0.28 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 732a29b..c20d3d6 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +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.15.1-0.20260908003559-2ac06de1601e h1:CW8D+jijv7S/oJqj/hVMjMxzCzlT93FJPBx902bg5Nk= -github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908003559-2ac06de1601e/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= -github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908025802-4fa9a76d8b57 h1:3g7059LyEJeIsLwT3qSng8BdV44GIOV9DQa+dLbalmo= -github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908025802-4fa9a76d8b57/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= -github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908030757-f478f34797be h1:F3+A0vVRICnEeBshac70P+VBtuo64P5hBmxcfbFxiXk= -github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908030757-f478f34797be/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= -github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908113851-5bf8f2902391 h1:u4IM9wE2/isYCAUMszeBsp4b9Na7Qh5mHlvPLpDgvkw= -github.com/flashcatcloud/go-flashduty v0.15.1-0.20260908113851-5bf8f2902391/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= +github.com/flashcatcloud/go-flashduty v0.15.2-0.20260910014922-dce7c882be68 h1:jPJA1NB+HcL2FcBZ+PbVZ0xejVvqELOMHEBDJYGUbQ0= +github.com/flashcatcloud/go-flashduty v0.15.2-0.20260910014922-dce7c882be68/go.mod h1:YpHiTYXR5NXBI/rGRZfUy537XMkhdCkwA8NW1QoRHwk= 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.28 h1:rPyg2ybwEKPebvpzVWe1gKBkH8EQFkxO4Y0hjBeLaBU= diff --git a/internal/cli/datasource_tools_test.go b/internal/cli/datasource_tools_test.go index b8bafb8..003072b 100644 --- a/internal/cli/datasource_tools_test.go +++ b/internal/cli/datasource_tools_test.go @@ -52,6 +52,51 @@ func TestDatasourceToolInvokeStdinPreservesJSON(t *testing.T) { } } +func TestDatasourceToolInvokeQueryParamsPreserveJSON(t *testing.T) { + saveAndResetGlobals(t) + requests := make(chan string, 1) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost || r.URL.Path != "/monit/datasource/tools/invoke" { + t.Errorf("unexpected endpoint: %s %s", r.Method, r.URL.Path) + } + raw, _ := io.ReadAll(r.Body) + requests <- string(raw) + w.Header().Set("Content-Type", "application/json") + _, _ = io.WriteString(w, `{"request_id":"query-test","data":{"datasource_id":42,"tool":"prometheus.query","data":{"format":"explore_result.v1","result":{"kind":"samples","samples":[]}}}}`) + })) + t.Cleanup(server.Close) + newClientFn = func() (*flashduty.Client, error) { + return flashduty.NewClient("test", flashduty.WithBaseURL(server.URL)) + } + out, err := execCommand("monit", "datasource-tools-invoke", "42", "--tool", "prometheus.query", + "--data", `{"params":{"expr":"sum by (job) (rate(http_requests_total[5m]))","execution":{"kind":"instant","to_ms":9007199254740993}}}`, + "--output-format", "json") + if err != nil { + t.Fatal(err) + } + raw := <-requests + var sent struct { + DatasourceID uint64 `json:"datasource_id"` + Tool string `json:"tool"` + Params json.RawMessage `json:"params"` + } + if err := json.Unmarshal([]byte(raw), &sent); err != nil { + t.Fatal(err) + } + if sent.DatasourceID != 42 || sent.Tool != "prometheus.query" { + t.Fatalf("request lost identity: %s", raw) + } + if !strings.Contains(string(sent.Params), `"to_ms":9007199254740993`) || strings.Contains(string(sent.Params), "9007199254740992") { + t.Fatalf("params lost numeric precision: %s", sent.Params) + } + if !strings.Contains(string(sent.Params), `"kind":"instant"`) || !strings.Contains(string(sent.Params), "rate(http_requests_total[5m])") { + t.Fatalf("params mangled: %s", sent.Params) + } + if !strings.Contains(out, "explore_result.v1") { + t.Fatalf("response lost query evidence: %s", out) + } +} + func TestDatasourceToolErrorsAreNotReplayed(t *testing.T) { for _, status := range []int{400, 429, 503, 504} { t.Run(fmt.Sprint(status), func(t *testing.T) { diff --git a/internal/cli/zz_generated_alert_rules.go b/internal/cli/zz_generated_alert_rules.go index a5f494b..ddf2dc9 100644 --- a/internal/cli/zz_generated_alert_rules.go +++ b/internal/cli/zz_generated_alert_rules.go @@ -149,39 +149,6 @@ API: POST /monit/rule/counter/channel (monit-rule-read-counter-channel) 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) error { - return nil - }) - 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; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") - return cmd -} - func genAlertRulesReadCounterTotalCmd() *cobra.Command { var dataJSON string cmd := &cobra.Command{ @@ -221,46 +188,6 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' 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) — ID of the datasource type record. - - 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) error { - return nil - }) - 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; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") - return cmd -} - func genAlertRulesReadExportCmd() *cobra.Command { var dataJSON string var fIDs []int @@ -369,96 +296,104 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' return cmd } -func genAlertRulesReadInfoCmd() *cobra.Command { +func genAlertRulesReadInfoV2Cmd() *cobra.Command { var dataJSON string var fID int64 cmd := &cobra.Command{ - Use: "rule-info", - Short: "Get alert rule detail", - Long: `Get alert rule detail. + Use: "rule-v2-info", + Short: "Get alert rule detail (V2)", + Long: `Get alert rule detail (V2). -Return the full configuration of an alert rule by its ID, including rule queries, thresholds, and notification settings. +Return the full V2 configuration of an alert rule by ID, including lifecycle v2 recovery and ending modes. -API: POST /monit/rule/info (monit-rule-read-info) +API: POST /monit/rule/v2/info (monit-rule-read-info-v2) Request fields: --id int (required) — Alert rule ID. Obtainable per folder via 'POST /monit/rule/list/basic'. Response fields ('data' envelope is unwrapped — these fields are at the top level): - - account_id (integer) (required) — Account ID. Filled by the server from the authenticated identity; do not provide. - - annotations (object) — Annotation key-value pairs delivered with alert events; keys must not start with '$' (reserved for query fields). - - channel_ids (array) — Channel IDs to send alerts to. - - created_at (string) (required) — Creation time as a Unix timestamp in seconds. Generated by the server; do not provide. CLI '--json' renders this as an RFC3339 string in the process's local timezone (NOT UTC, and NOT the wire integer); an unset value renders as null. - - creator_id (integer) (required) — Creator user ID. Filled by the server from the current user; do not provide. - - creator_name (string) (required) — Creator name. Filled by the server; do not provide. - - cron_pattern (string) (required) — Schedule expression: a 6-field cron (with seconds) or an '@every 30s' interval descriptor. Must not start with 'CRON_TZ=' or 'TZ='; use the 'timezone' field instead. - - debug_log_enabled (boolean) (required) — Whether to enable debug logging; the edge emits detailed evaluation logs, useful for troubleshooting rules that do not trigger as expected. - - delay_seconds (integer) (required) — Seconds to shift the evaluation query window backward, compensating for data ingestion latency. - - description (string) — Rule description, in Markdown. - - description_type (string) — Format for the description. Defaults to 'text' when omitted or empty. 'text' = plain text; 'markdown' = Markdown, rendered as Markdown in alert details. [text, markdown] - - ds_ids (array) — Datasource IDs, merged with 'ds_list' to decide which datasources the rule monitors; IDs survive datasource renames. At least one of 'ds_list' and 'ds_ids' must be provided. - - ds_list (array) — Data source name patterns (supports wildcards). At least one of 'ds_list' / 'ds_ids' must be non-empty; the two are merged to decide which datasources the rule monitors. - - ds_type (string) (required) — Datasource type identifier; allowed values are listed by 'POST /monit/rule/dstypes' (e.g. 'prometheus', 'elasticsearch'). - - enabled (boolean) (required) — Whether the rule is enabled. Updating to 'false' makes the server clean up the rule's active alerts. - - enabled_times (array) — Time windows when the rule is active. Defaults to all days from 00:00 to 23:59 when omitted or empty. + - account_id (integer) — Account ID, filled by the server from the authentication context; any client-supplied value is ignored. + - annotations (object) — Extra annotation key-value pairs delivered with alert events; keys must not start with '$' (reserved for query fields). + - channel_ids (array) — Collaboration space IDs alerts are sent to. May be empty; alerts then route through the global integration. + - created_at (integer) — Creation time as a Unix timestamp in seconds, generated by the server; any client-supplied value is ignored. + - creator_id (integer) — Creator member ID, filled by the server from the current user; any client-supplied value is ignored. + - creator_name (string) — Creator name, filled by the server; any client-supplied value is ignored. + - cron_pattern (string) (required) — Schedule expression: a 6-field cron (with seconds) or an '@every 30s' interval. Must not start with 'CRON_TZ=' or 'TZ='; set the timezone in the 'timezone' field instead. + - debug_log_enabled (boolean) — Enable debug logging; the edge then emits detailed evaluation logs for this rule, useful when the rule does not trigger as expected. + - delay_seconds (integer) — Seconds the evaluation query window is shifted back, compensating for data ingestion latency. + - description (string) — Rule description, Markdown format. + - description_type (string) — Format of the description content. Empty or omitted defaults to 'text'. 'text' = plain text; 'markdown' = Markdown, rendered as such in alert details. [text, markdown] + - ds_ids (array) — Datasource ID list, merged with 'ds_list' to decide the monitored datasources; IDs survive datasource renames. At least one of 'ds_list' / 'ds_ids' must be provided. + - ds_list (array) — Datasource name match patterns (wildcards supported). At least one of 'ds_list' / 'ds_ids' must be non-empty; both are merged to decide which datasources the rule monitors. + - ds_type (string) (required) — Datasource type identifier (e.g. 'prometheus', 'elasticsearch'). + - enabled (boolean) (required) — Whether the rule is enabled. Required — the server enforces an explicit value (including 'false') while decoding. Setting it to 'false' on update clears the rule's active alerts. + - enabled_times (array) — Time windows during which the rule is in effect. When omitted or empty, the rule is active 00:00–23:59 every day. - 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) — ID of the folder the rule belongs to. Obtainable via 'POST /monit/folder/list'. - - id (integer) (required) — Rule ID. Required for update; omit for create (assigned by the server). + - folder_id (integer) (required) — ID of the folder the rule belongs to; list folders via 'POST /monit/folder/list'. Cannot be changed through the update API — use '/monit/rule/move' instead. + - id (integer) — Rule ID. Required on update; omit on create (assigned by the server). + - investigation_targets (array) — Drill-down entries linked from the alert event detail page; at most 20 items, duplicates rejected. On update the field is presence-based: omit it to keep the current value, pass '[]' to clear. + - dashboard (object) — Configuration for the 'dashboard' kind; required when 'kind' is 'dashboard'. + - dashboard_id (string) (required) — Target dashboard ID; must be a canonical UUIDv7. + - target_id (string) — Panel ID inside the dashboard; must be a canonical UUIDv7. Optional. + - variable_bindings (object) — Dashboard variable bindings, keyed by dashboard variable name. + - kind (string) (required) — Entry type; currently only 'dashboard' is supported. [dashboard] - labels (object) — Custom labels. - - name (string) (required) — Rule name. Must be unique within the same folder. - - repeat_interval (integer) — Notification repeat interval in seconds. - - repeat_total (integer) — Max number of repeat notifications. - - rule_configs (object) (required) — Check configuration: query list plus trigger/recovery conditions. Structure see 'RuleConfigs'. - - check_anydata (object) — Any-data check configuration. Fires when the query returns any data rows. - - alerting_check_times (integer) — Number of consecutive evaluations that must satisfy the condition before alerting; minimum 1. - - enabled (boolean) — Whether any-data checking is enabled: any returned data row triggers an alert. - - push_recovery_event (boolean) — Whether to push a recovery event notification when the alert resolves. - - recovery (object) — Recovery condition for any-data check. If omitted or 'mode' is empty, treated as 'nodata'. - - args (object) — Datasource-specific options for the recovery query, same convention as 'queries[].args'; required for Elasticsearch datasources when 'mode' is 'ql'. - - 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) — Number of consecutive evaluations that must satisfy the recovery condition before resolving; minimum 1. - - severity (string) — Severity of any-data alert events; case-sensitive. [Critical, Warning, Info] - - check_nodata (object) — No-data check configuration. - - alert_on_empty_result (boolean) — Whether to trigger an alert when every query returns an empty result. - - alert_on_empty_result_severity (string) — Severity of empty-result alerts, case-sensitive; only effective when 'alert_on_empty_result' is enabled. [Critical, Warning, Info] - - alerting_check_times (integer) — Number of consecutive evaluations that must satisfy the condition before alerting; minimum 1. - - enabled (boolean) — Whether no-data checking is enabled: a previously-seen series that stops returning data triggers an alert. - - push_recovery_event (boolean) — Whether to push a recovery event notification when the alert resolves. - - recovery_check_times (integer) — Number of consecutive evaluations that must satisfy the recovery condition before resolving; minimum 1. - - resolve_timeout (integer) — Auto-resolve after N seconds. - - severity (string) — Severity of no-data alert events; case-sensitive. [Critical, Warning, Info] - - check_threshold (object) — Threshold check configuration. - - alerting_check_times (integer) — Number of consecutive evaluations that must satisfy the condition before alerting; minimum 1. - - critical (string) — Critical threshold expression referencing query results via '$' or '$.', e.g. '$A > 90'; at least one severity must be configured. - - enabled (boolean) — Whether threshold checking is enabled. - - info (string) — Info threshold expression, same syntax as 'critical'. - - push_recovery_event (boolean) — Whether to push a recovery event notification when the alert resolves. - - recovery (object) — Recovery evaluation configuration for threshold checks. - - args (object) — Datasource-specific extra parameters for the recovery query, using the same '.' key convention as query 'args'. Omitted when empty. - - condition (string) — Recovery condition expression; required when 'mode' is 'threshold' or 'ql', and must be empty for 'invert'. - - mode (string) — Recovery mode: 'invert' = resolve when the alert expression no longer holds ('condition' stays empty); 'threshold' = resolve when the 'condition' threshold expression holds; 'ql' = resolve when the 'condition' query expression evaluates true. [invert, threshold, ql] - - value_fields (array) — Numeric result fields the recovery 'condition' references as '$A.'; same semantics as the query's 'value_fields'. Omitted when empty. - - recovery_check_times (integer) — Number of consecutive evaluations that must satisfy the recovery condition before resolving; minimum 1. - - warning (string) — Warning threshold expression, same syntax as 'critical'. - - queries (array) (required) — Query list with at least one entry; each needs a unique 'name' ('R' and '__all__' are reserved) and a non-empty, non-duplicate 'expr'. - - args (object) — Datasource-specific query options keyed by the '.