From 814a2172039d9692f35010dc79dc5982408e762e Mon Sep 17 00:00:00 2001 From: shuangyu Date: Wed, 24 Jun 2026 22:50:27 +0800 Subject: [PATCH 01/11] =?UTF-8?q?chore(cli):=20regen=20against=20go-flashd?= =?UTF-8?q?uty=20=E2=80=94=20add=20session-delete,=20drop=20console-only?= =?UTF-8?q?=20skill-download=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(cli): regen against go-flashduty (adds session-delete, drops skill-download) Bump go-flashduty to the merged spec-sync commit (go-flashduty #14) and regenerate cligen. - adds `fduty safari session-delete` (POST /safari/session/delete) — was a public op with no cli command. - drops `fduty safari skill-download` — /safari/skill/download was reclassified jwt (console-only application/zip download; fc-pgy #570, flashduty-docs #106) and is no longer a public op. The old command was a broken, unused generated artifact (the SDK method couldn't decode a zip). - session.go: Sessions.List -> Sessions.ReadList (hand-written /insight list helper) following the SDK's read/write method rename. - regenerated zz_generated_* reflect the SDK type refresh (TimestampMilli, pointer scalars) and the a2a/mcp operationId renames. Build + full test suite green; the "every public op reachable" coverage invariant passes (251/251 non-streaming generated, session-export the only curated streaming op). * chore(cli): regenerate channel card fence after go-flashduty bump The go-flashduty bump adds the channel escalate-webhook-robot-list command (the cli was 8 days behind). Regenerate the channel skill card's GENERATED fence so `skilldoc check` (check-cards CI) stays green. --- go.mod | 2 +- go.sum | 4 +- internal/cli/session.go | 2 +- internal/cli/zz_generated_a2a_agents.go | 228 ++++++------ internal/cli/zz_generated_channels.go | 61 ++++ internal/cli/zz_generated_manifest.go | 3 +- internal/cli/zz_generated_mcp_servers.go | 404 +++++++++++---------- internal/cli/zz_generated_response_help.go | 27 +- internal/cli/zz_generated_sessions.go | 125 +++++-- internal/cli/zz_generated_skills.go | 303 +++++++--------- skills/flashduty/reference/channel.md | 5 + 11 files changed, 621 insertions(+), 543 deletions(-) diff --git a/go.mod b/go.mod index fcbe8d8..772adb8 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.4-0.20260616051811-54d4fc1065bb + github.com/flashcatcloud/go-flashduty v0.5.4-0.20260624143100-02193386c979 github.com/mattn/go-runewidth v0.0.24 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index c444e7d..1a8660f 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.4-0.20260616051811-54d4fc1065bb h1:V3SrTp2JSJ4MizuCfvFdEc4FFxial3uSYQZaCiCb6oI= -github.com/flashcatcloud/go-flashduty v0.5.4-0.20260616051811-54d4fc1065bb/go.mod h1:aA0RtZEs0AYOwwdNKdtVeD8YMOdnmVY1zAlVD+9Ovx8= +github.com/flashcatcloud/go-flashduty v0.5.4-0.20260624143100-02193386c979 h1:Pha9D3p8ofiMSGRv1AeEafBJ3JspLRFR0zVQFEXZo44= +github.com/flashcatcloud/go-flashduty v0.5.4-0.20260624143100-02193386c979/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.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= diff --git a/internal/cli/session.go b/internal/cli/session.go index bf16158..7f7580a 100644 --- a/internal/cli/session.go +++ b/internal/cli/session.go @@ -197,7 +197,7 @@ func fetchSessionsPaged( req.Page = page req.Limit = pageLimit - resp, _, err := client.Sessions.List(ctx, &req) + resp, _, err := client.Sessions.ReadList(ctx, &req) if err != nil { return nil, 0, err } diff --git a/internal/cli/zz_generated_a2a_agents.go b/internal/cli/zz_generated_a2a_agents.go index c218a57..7ec5620 100644 --- a/internal/cli/zz_generated_a2a_agents.go +++ b/internal/cli/zz_generated_a2a_agents.go @@ -16,38 +16,38 @@ func genA2aAgentsReadGetCmd() *cobra.Command { Short: "Get A2A agent detail", Long: `Get A2A agent detail. -Return the full configuration of a single A2A agent by ID. +Get one 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. + --agent-id string (required) — Target agent ID. 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. + - account_id (integer) (required) — Owning account ID. + - agent_card_name (string) — Agent name resolved from the remote card. + - agent_card_skills (array) — Skills advertised by the remote card. + - agent_id (string) (required) — Unique A2A agent ID (prefix 'a2a_'). + - agent_name (string) (required) — Agent display name. + - auth_config (object) — Authentication config; secret values are masked. + - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth] + - auth_type (string) (required) — Authentication type for reaching the remote agent. + - can_edit (boolean) (required) — Whether the caller may edit this agent. + - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds. + - card_url (string) (required) — URL of the remote agent card. + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the agent. + - description (string) (required) — Agent description. + - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). + - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). + - status (string) (required) — Agent status. [enabled, disabled] + - streaming (boolean) (required) — Whether the remote agent supports streaming responses. + - task_timeout (integer) (required) — Single-task execution timeout in seconds. + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. `, Args: requireExactArg("agent_id"), - Example: ` flashduty safari a2a-agent-get --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + Example: ` flashduty safari a2a-agent-get --data '{"agent_id":"a2a_6mWqZ2pK9nLcR3tY8uVb4D"}'`, 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 { @@ -74,7 +74,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Target agent ID. (required)") 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 } @@ -90,40 +90,40 @@ func genA2aAgentsReadListCmd() *cobra.Command { Short: "List A2A agents", Long: `List A2A agents. -List registered A2A agents visible to the caller across account and team scopes, with pagination. +List 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. + --include-account bool — Include account-scoped (team_id=0) rows. Defaults to true. + --limit int — Page size. + --offset int — Row offset for pagination. + --team-ids []int — Filter to these team IDs; empty = 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. + - items (array) (required) — A2A agents on this page. + - account_id (integer) (required) — Owning account ID. + - agent_card_name (string) — Agent name resolved from the remote card. + - agent_card_skills (array) — Skills advertised by the remote card. + - agent_id (string) (required) — Unique A2A agent ID (prefix 'a2a_'). + - agent_name (string) (required) — Agent display name. + - auth_config (object) — Authentication config; secret values are masked. + - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth] + - auth_type (string) (required) — Authentication type for reaching the remote agent. + - can_edit (boolean) (required) — Whether the caller may edit this agent. + - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds. + - card_url (string) (required) — URL of the remote agent card. + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the agent. + - description (string) (required) — Agent description. + - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). + - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). + - status (string) (required) — Agent status. [enabled, disabled] + - streaming (boolean) (required) — Whether the remote agent supports streaming responses. + - task_timeout (integer) (required) — Single-task execution timeout in seconds. + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - total (integer) (required) — Total number of matching agents. `, Example: ` flashduty safari a2a-agent-list --data '{"include_account":true,"limit":20,"offset":0}'`, RunE: func(cmd *cobra.Command, args []string) error { @@ -158,10 +158,10 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }) }, } - 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().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped (team_id=0) rows. Defaults to true.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size.") + cmd.Flags().Int64Var(&fOffset, "offset", 0, "Row offset for pagination.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter to these team IDs; empty = the caller's visible set.") 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 } @@ -182,26 +182,26 @@ func genA2aAgentsWriteCreateCmd() *cobra.Command { Short: "Create A2A agent", Long: `Create A2A agent. -Register a new A2A remote agent the SRE agent can delegate tasks to. +Register a new A2A remote agent from its agent-card URL. 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. + --agent-name string (required) — Agent display name. (≤128 chars) + --auth-mode string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. + --auth-type string — Authentication type for the remote agent. + --card-url string (required) — URL of the remote agent card. + --description string — Agent description. + --oauth-metadata string — JSON OAuth metadata; reserved for per_user_oauth. + --secret-schema string — JSON secret schema; required when auth_mode=per_user_secret. + --streaming bool — Whether the remote agent supports streaming. + --team-id int — Team scope: 0 = account-wide; >0 = team. + auth_config (object, via --data) — Authentication config key-values. Response fields ('data' envelope is unwrapped — these fields are at the top level): - - agent_id (string) (required) — Identifier of the created agent. + - agent_id (string) (required) — ID of the newly 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}'`, + Example: ` flashduty safari a2a-agent-create --data '{"agent_name":"deploy-bot","auth_type":"bearer","card_url":"https://agents.example.com/deploy-bot/card","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) error { @@ -249,15 +249,15 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - 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(&fAgentName, "agent-name", "", "Agent display name. (required) (≤128 chars)") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "Authentication mode: shared (default), per_user_secret, or per_user_oauth.") + cmd.Flags().StringVar(&fAuthType, "auth-type", "", "Authentication type for the remote agent.") + cmd.Flags().StringVar(&fCardURL, "card-url", "", "URL of the remote agent card. (required)") + cmd.Flags().StringVar(&fDescription, "description", "", "Agent description.") + cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "JSON OAuth metadata; reserved for per_user_oauth.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON secret schema; required when auth_mode=per_user_secret.") + cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Whether the remote agent supports streaming.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team scope: 0 = account-wide; >0 = team.") 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 } @@ -270,15 +270,15 @@ func genA2aAgentsWriteDeleteCmd() *cobra.Command { Short: "Delete A2A agent", Long: `Delete A2A agent. -Soft-delete an A2A agent registration so it can no longer be used. +Soft-delete an A2A agent by ID. API: POST /safari/a2a-agent/delete (remote-agent-write-delete) Request fields: - --agent-id string (required) — Identifier of the target agent. + --agent-id string (required) — Target agent ID. `, Args: requireExactArg("agent_id"), - Example: ` flashduty safari a2a-agent-delete --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + Example: ` flashduty safari a2a-agent-delete --data '{"agent_id":"a2a_6mWqZ2pK9nLcR3tY8uVb4D"}'`, 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 { @@ -305,7 +305,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Target agent ID. (required)") 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 } @@ -318,15 +318,15 @@ func genA2aAgentsWriteDisableCmd() *cobra.Command { Short: "Disable A2A agent", Long: `Disable A2A agent. -Deactivate an A2A agent so the SRE agent stops delegating to it. +Disable an enabled A2A agent. API: POST /safari/a2a-agent/disable (remote-agent-write-disable) Request fields: - --agent-id string (required) — Identifier of the target agent. + --agent-id string (required) — Target agent ID. `, Args: requireExactArg("agent_id"), - Example: ` flashduty safari a2a-agent-disable --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + Example: ` flashduty safari a2a-agent-disable --data '{"agent_id":"a2a_6mWqZ2pK9nLcR3tY8uVb4D"}'`, 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 { @@ -353,7 +353,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Target agent ID. (required)") 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 } @@ -366,15 +366,15 @@ func genA2aAgentsWriteEnableCmd() *cobra.Command { Short: "Enable A2A agent", Long: `Enable A2A agent. -Activate a disabled A2A agent so the SRE agent can delegate to it. +Enable a disabled A2A agent. API: POST /safari/a2a-agent/enable (remote-agent-write-enable) Request fields: - --agent-id string (required) — Identifier of the target agent. + --agent-id string (required) — Target agent ID. `, Args: requireExactArg("agent_id"), - Example: ` flashduty safari a2a-agent-enable --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, + Example: ` flashduty safari a2a-agent-enable --data '{"agent_id":"a2a_6mWqZ2pK9nLcR3tY8uVb4D"}'`, 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 { @@ -401,7 +401,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") + cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Target agent ID. (required)") 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 } @@ -423,25 +423,25 @@ func genA2aAgentsWriteUpdateCmd() *cobra.Command { Short: "Update A2A agent", Long: `Update A2A agent. -Edit an A2A agent's card URL, auth, streaming flag, or owning team. +Apply a partial update to an A2A agent. Omit a field to leave it unchanged. 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. + --agent-id string (required) — Target agent ID. + --agent-name string — New display name. Omit to leave unchanged. (≤128 chars) + --auth-mode string — New auth mode: shared, per_user_secret, or per_user_oauth. + --auth-type string — New auth type. Omit to leave unchanged. + --card-url string — New card URL. Omit to leave unchanged. + --description string — New description. Omit to leave unchanged. + --oauth-metadata string — New JSON OAuth metadata. + --secret-schema string — New JSON secret schema. + --streaming bool — Toggle streaming support. Omit to leave unchanged. + --team-id int — Reassign team scope. Omit to leave unchanged. + auth_config (object, via --data) — Replace the auth config. Omit to leave unchanged. `, Args: requireExactArg("agent_id"), - Example: ` flashduty safari a2a-agent-update --data '{"agent_id":"a2a_9d4c1f60b3a2","description":"Runs traceroute, BGP, and DNS analysis for network incidents."}'`, + Example: ` flashduty safari a2a-agent-update --data '{"agent_id":"a2a_6mWqZ2pK9nLcR3tY8uVb4D","description":"Inspects deployment pipelines and proposes rollbacks."}'`, 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 { @@ -495,16 +495,16 @@ Request fields: }) }, } - 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(&fAgentID, "agent-id", "", "Target agent ID. (required)") + cmd.Flags().StringVar(&fAgentName, "agent-name", "", "New display name. Omit to leave unchanged. (≤128 chars)") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "New auth mode: shared, per_user_secret, or per_user_oauth.") + cmd.Flags().StringVar(&fAuthType, "auth-type", "", "New auth type. Omit to leave unchanged.") + cmd.Flags().StringVar(&fCardURL, "card-url", "", "New card URL. Omit to leave unchanged.") + cmd.Flags().StringVar(&fDescription, "description", "", "New description. Omit to leave unchanged.") + cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "New JSON OAuth metadata.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "New JSON secret schema.") + cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Toggle streaming support. Omit to leave unchanged.") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign team scope. Omit to leave unchanged.") 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 } diff --git a/internal/cli/zz_generated_channels.go b/internal/cli/zz_generated_channels.go index 370b43a..9c13837 100644 --- a/internal/cli/zz_generated_channels.go +++ b/internal/cli/zz_generated_channels.go @@ -854,6 +854,66 @@ Request fields: return cmd } +func genChannelsChannelEscalateWebhookRobotListCmd() *cobra.Command { + var dataJSON string + var fQuery string + var fType string + cmd := &cobra.Command{ + Use: "escalate-webhook-robot-list", + Short: "List webhook robots in escalation rules", + Long: `List webhook robots in escalation rules. + +List all IM webhook robots configured in escalation rules across the account. Returns a deduplicated list of robots with references to which channels and escalation rules use them. + +API: POST /channel/escalate/webhook/robot/list (channelEscalateWebhookRobotList) + +Request fields: + --query string — Search keyword. Fuzzy matches against robot alias or token, case-insensitive. + --type string — Filter by robot type, e.g. 'feishu', 'dingtalk', 'wecom', 'slack', 'teams'. Omit to return all types. + +Response fields ('data' envelope is unwrapped — rows are nested under items[]; pipe 'jq '.items[]'', NOT '.data.items[]'): + - list (array) — Deduplicated list of webhook robots. + - referenced_by (array) — List of channels and escalation rules referencing this robot. + - channel_id (integer) — Channel ID. + - channel_name (string) — Channel name. + - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID). + - escalate_rule_name (string) — Escalation rule name. + - settings (object) — Robot configuration, including 'token' (webhook URL or secret) and 'alias' (robot display name) among other fields. + - type (string) — Robot type, e.g. 'feishu', 'dingtalk', 'wecom', 'slack', 'teams', etc. +`, + Example: ` flashduty channel escalate-webhook-robot-list --data '{"query":"ops","type":"feishu"}'`, + 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 { + if cmd.Flags().Changed("query") { + body["query"] = fQuery + } + if cmd.Flags().Changed("type") { + body["type"] = fType + } + return nil + }) + if err != nil { + return err + } + req := new(flashduty.ChannelsChannelEscalateWebhookRobotListRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Channels.ChannelEscalateWebhookRobotList(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fQuery, "query", "", "Search keyword. Fuzzy matches against robot alias or token, case-insensitive.") + cmd.Flags().StringVar(&fType, "type", "", "Filter by robot type, e.g. 'feishu', 'dingtalk', 'wecom', 'slack', 'teams'. Omit to return all types.") + 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 genChannelsChannelInfoCmd() *cobra.Command { var dataJSON string var fChannelID int64 @@ -2717,6 +2777,7 @@ func registerGeneratedChannels(root *cobra.Command) { genAddLeaf(gChannel, genChannelsChannelEscalateRuleInfoCmd()) genAddLeaf(gChannel, genChannelsChannelEscalateRuleListCmd()) genAddLeaf(gChannel, genChannelsChannelEscalateRuleUpdateCmd()) + genAddLeaf(gChannel, genChannelsChannelEscalateWebhookRobotListCmd()) genAddLeaf(gChannel, genChannelsChannelInfoCmd()) genAddLeaf(gChannel, genChannelsChannelInfosCmd()) genAddLeaf(gChannel, genChannelsChannelInhibitRuleCreateCmd()) diff --git a/internal/cli/zz_generated_manifest.go b/internal/cli/zz_generated_manifest.go index 384797f..5eeff71 100644 --- a/internal/cli/zz_generated_manifest.go +++ b/internal/cli/zz_generated_manifest.go @@ -38,6 +38,7 @@ var generatedOpIDs = []string{ "channelEscalateRuleInfo", "channelEscalateRuleList", "channelEscalateRuleUpdate", + "channelEscalateWebhookRobotList", "channelInfo", "channelInfos", "channelInhibitRuleCreate", @@ -216,7 +217,7 @@ var generatedOpIDs = []string{ "scheduleUpdate", "session-read-info", "session-read-list", - "skill-read-download", + "session-write-delete", "skill-read-enable", "skill-read-get", "skill-read-list", diff --git a/internal/cli/zz_generated_mcp_servers.go b/internal/cli/zz_generated_mcp_servers.go index dd765ff..e755d5b 100644 --- a/internal/cli/zz_generated_mcp_servers.go +++ b/internal/cli/zz_generated_mcp_servers.go @@ -16,46 +16,47 @@ func genMcpServersReadServerGetCmd() *cobra.Command { Short: "Get MCP server detail", Long: `Get MCP server detail. -Return the full configuration of a single MCP server, with a live tool catalogue. +Get one MCP server and run a live probe of its tool list. API: POST /safari/mcp/server/get (mcp-read-server-get) Request fields: - --server-id string (required) — Identifier of the server to fetch. + --server-id string (required) — Target MCP server ID. 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. + - account_id (integer) (required) — Owning account ID. + - ai_description (string) — LLM-generated description, preferred over 'description' when present. + - args (array) — Command arguments (stdio transport). + - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth] + - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s). + - can_edit (boolean) (required) — Whether the caller may edit this server. + - command (string) — Executable command (stdio transport only). + - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s). + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the server. + - description (string) (required) — Server description. + - env (object) — Environment variables (stdio transport). Secret values are masked. + - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked. + - list_error (string) — Error message when the live tool list failed. + - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). - 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. + - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). + - server_id (string) (required) — Unique MCP server ID (prefix 'mcp_'). + - server_name (string) (required) — MCP server name, unique within the account. + - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored. + - status (string) (required) — Server status. [enabled, disabled] + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tool_count (integer) — Number of tools in the live list. + - tools (array) — Live tool list; populated by the get/test endpoints. + - description (string) (required) — Tool description. + - input_schema (object) — JSON Schema describing the tool's input parameters. + - name (string) (required) — Tool name. + - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - url (string) — Server URL (sse / streamable-http transport). `, Args: requireExactArg("server_id"), - Example: ` flashduty safari mcp-server-get --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + Example: ` flashduty safari mcp-server-get --data '{"server_id":"mcp_4kP9wQ2nLceRtY7uVb3xA1"}'`, 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 { @@ -82,7 +83,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the server to fetch. (required)") + cmd.Flags().StringVar(&fServerID, "server-id", "", "Target MCP server ID. (required)") 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 } @@ -99,49 +100,50 @@ func genMcpServersReadServerListCmd() *cobra.Command { Short: "List MCP servers", Long: `List MCP servers. -List configured MCP servers visible to the caller across account and team scopes, with pagination. +List 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. + --page int — Page number, 1-based. + --limit int — Page size. --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. + --include-account bool — Include account-scoped (team_id=0) rows. Defaults to true. + --team-ids []int — Filter to these team IDs; empty = 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. + - servers (array) (required) — MCP servers on this page. + - account_id (integer) (required) — Owning account ID. + - ai_description (string) — LLM-generated description, preferred over 'description' when present. + - args (array) — Command arguments (stdio transport). + - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth] + - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s). + - can_edit (boolean) (required) — Whether the caller may edit this server. + - command (string) — Executable command (stdio transport only). + - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s). + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the server. + - description (string) (required) — Server description. + - env (object) — Environment variables (stdio transport). Secret values are masked. + - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked. + - list_error (string) — Error message when the live tool list failed. + - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). - 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. + - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). + - server_id (string) (required) — Unique MCP server ID (prefix 'mcp_'). + - server_name (string) (required) — MCP server name, unique within the account. + - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored. + - status (string) (required) — Server status. [enabled, disabled] + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tool_count (integer) — Number of tools in the live list. + - tools (array) — Live tool list; populated by the get/test endpoints. + - description (string) (required) — Tool description. + - input_schema (object) — JSON Schema describing the tool's input parameters. + - name (string) (required) — Tool name. + - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - url (string) — Server URL (sse / streamable-http transport). + - total (integer) (required) — Total number of matching servers. `, Example: ` flashduty safari mcp-server-list --data '{"include_account":true,"limit":20,"p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { @@ -179,11 +181,11 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1.") - cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size; defaults to 20.") + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, 1-based.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size.") 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().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped (team_id=0) rows. Defaults to true.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter to these team IDs; empty = the caller's visible set.") 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 } @@ -199,6 +201,7 @@ func genMcpServersWriteServerCreateCmd() *cobra.Command { var fOauthMetadata string var fSecretSchema string var fServerName string + var fSourceTemplateName string var fStatus string var fTeamID int64 var fTransport string @@ -208,59 +211,61 @@ func genMcpServersWriteServerCreateCmd() *cobra.Command { Short: "Create MCP server", Long: `Create MCP server. -Register a new MCP server the agent can call tools through. +Register a new MCP server (connector) on the account. 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. + --args []string — Command arguments (stdio transport). + --auth-mode string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. + --call-timeout int — Tool-call timeout in seconds. 0 = default (60s). + --command string — Executable command (stdio transport). + --connect-timeout int — Connection timeout in seconds. 0 = default (10s). + --description string (required) — Server description. (1-1024 chars) + --oauth-metadata string — JSON OAuth metadata; reserved for per_user_oauth. + --secret-schema string — JSON secret schema; required when auth_mode=per_user_secret. + --server-name string (required) — MCP server name, unique within the account. (1-255 chars) + --source-template-name string — Marketplace template name when created from a connector template. + --status string — Initial status. [enabled, disabled] + --team-id int — Team scope: 0 = account-wide; >0 = team. + --transport string (required) — Transport protocol. [stdio, sse, streamable-http] + --url string — Server URL (sse / streamable-http transport). + env (object, via --data) — Environment variables (stdio transport). + headers (object, via --data) — HTTP headers (sse / streamable-http). 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. + - account_id (integer) (required) — Owning account ID. + - ai_description (string) — LLM-generated description, preferred over 'description' when present. + - args (array) — Command arguments (stdio transport). + - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth] + - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s). + - can_edit (boolean) (required) — Whether the caller may edit this server. + - command (string) — Executable command (stdio transport only). + - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s). + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the server. + - description (string) (required) — Server description. + - env (object) — Environment variables (stdio transport). Secret values are masked. + - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked. + - list_error (string) — Error message when the live tool list failed. + - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). - 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. + - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). + - server_id (string) (required) — Unique MCP server ID (prefix 'mcp_'). + - server_name (string) (required) — MCP server name, unique within the account. + - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored. + - status (string) (required) — Server status. [enabled, disabled] + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tool_count (integer) — Number of tools in the live list. + - tools (array) — Live tool list; populated by the get/test endpoints. + - description (string) (required) — Tool description. + - input_schema (object) — JSON Schema describing the tool's input parameters. + - name (string) (required) — Tool name. + - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - url (string) — Server URL (sse / 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"}'`, + Example: ` flashduty safari mcp-server-create --data '{"description":"Query Prometheus metrics and alerts.","server_name":"prometheus","status":"enabled","transport":"streamable-http","url":"https://mcp.example.com/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) error { @@ -291,6 +296,9 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("server-name") { body["server_name"] = fServerName } + if cmd.Flags().Changed("source-template-name") { + body["source_template_name"] = fSourceTemplateName + } if cmd.Flags().Changed("status") { body["status"] = fStatus } @@ -320,19 +328,20 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - 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().StringSliceVar(&fArgs, "args", nil, "Command arguments (stdio transport).") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "Authentication mode: shared (default), per_user_secret, or per_user_oauth.") + cmd.Flags().Int64Var(&fCallTimeout, "call-timeout", 0, "Tool-call timeout in seconds. 0 = default (60s).") + cmd.Flags().StringVar(&fCommand, "command", "", "Executable command (stdio transport).") + cmd.Flags().Int64Var(&fConnectTimeout, "connect-timeout", 0, "Connection timeout in seconds. 0 = default (10s).") + cmd.Flags().StringVar(&fDescription, "description", "", "Server description. (required) (1-1024 chars)") + cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "JSON OAuth metadata; reserved for per_user_oauth.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON secret schema; required when auth_mode=per_user_secret.") + cmd.Flags().StringVar(&fServerName, "server-name", "", "MCP server name, unique within the account. (required) (1-255 chars)") + cmd.Flags().StringVar(&fSourceTemplateName, "source-template-name", "", "Marketplace template name when created from a connector template.") + cmd.Flags().StringVar(&fStatus, "status", "", "Initial status. [enabled, disabled]") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team scope: 0 = account-wide; >0 = team.") + cmd.Flags().StringVar(&fTransport, "transport", "", "Transport protocol. (required) [stdio, sse, streamable-http]") + cmd.Flags().StringVar(&fURL, "url", "", "Server URL (sse / streamable-http transport).") 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 } @@ -345,15 +354,15 @@ func genMcpServersWriteServerDeleteCmd() *cobra.Command { Short: "Delete MCP server", Long: `Delete MCP server. -Permanently remove an MCP server so agents can no longer use its tools. +Delete an MCP server by ID. API: POST /safari/mcp/server/delete (mcp-write-server-delete) Request fields: - --server-id string (required) — Identifier of the server to delete. + --server-id string (required) — Target MCP server ID. `, Args: requireExactArg("server_id"), - Example: ` flashduty safari mcp-server-delete --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + Example: ` flashduty safari mcp-server-delete --data '{"server_id":"mcp_4kP9wQ2nLceRtY7uVb3xA1"}'`, 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 { @@ -380,7 +389,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the server to delete. (required)") + cmd.Flags().StringVar(&fServerID, "server-id", "", "Target MCP server ID. (required)") 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 } @@ -393,15 +402,15 @@ func genMcpServersWriteServerDisableCmd() *cobra.Command { Short: "Disable MCP server", Long: `Disable MCP server. -Deactivate an MCP server so agents stop calling its tools. +Disable an enabled MCP server. API: POST /safari/mcp/server/disable (mcp-write-server-disable) Request fields: - --server-id string (required) — Identifier of the target server. + --server-id string (required) — Target MCP server ID. `, Args: requireExactArg("server_id"), - Example: ` flashduty safari mcp-server-disable --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + Example: ` flashduty safari mcp-server-disable --data '{"server_id":"mcp_4kP9wQ2nLceRtY7uVb3xA1"}'`, 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 { @@ -428,7 +437,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the target server. (required)") + cmd.Flags().StringVar(&fServerID, "server-id", "", "Target MCP server ID. (required)") 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 } @@ -441,15 +450,15 @@ func genMcpServersWriteServerEnableCmd() *cobra.Command { Short: "Enable MCP server", Long: `Enable MCP server. -Activate a disabled MCP server so agents can call its tools. +Enable a disabled MCP server. API: POST /safari/mcp/server/enable (mcp-write-server-enable) Request fields: - --server-id string (required) — Identifier of the target server. + --server-id string (required) — Target MCP server ID. `, Args: requireExactArg("server_id"), - Example: ` flashduty safari mcp-server-enable --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, + Example: ` flashduty safari mcp-server-enable --data '{"server_id":"mcp_4kP9wQ2nLceRtY7uVb3xA1"}'`, 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 { @@ -476,7 +485,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the target server. (required)") + cmd.Flags().StringVar(&fServerID, "server-id", "", "Target MCP server ID. (required)") 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 } @@ -501,60 +510,61 @@ func genMcpServersWriteServerUpdateCmd() *cobra.Command { Short: "Update MCP server", Long: `Update MCP server. -Edit an MCP server's connection settings or reassign its owning team. +Update an MCP server's configuration. Omit a field to leave it unchanged. 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. + --args []string — Command arguments (stdio transport). + --auth-mode string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. + --call-timeout int — Tool-call timeout in seconds. 0 = default (60s). + --command string — Executable command (stdio transport). + --connect-timeout int — Connection timeout in seconds. 0 = default (10s). --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. + --oauth-metadata string — JSON OAuth metadata; reserved for per_user_oauth. + --secret-schema string — JSON secret schema; required when auth_mode=per_user_secret. + --server-id string (required) — Target MCP server ID. + --server-name string — New name. (1-255 chars) + --team-id int — Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged. + --transport string — Transport protocol. [stdio, sse, streamable-http] + --url string — Server URL (sse / streamable-http transport). + env (object, via --data) — Environment variables (stdio transport). + headers (object, via --data) — HTTP headers (sse / streamable-http). 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. + - account_id (integer) (required) — Owning account ID. + - ai_description (string) — LLM-generated description, preferred over 'description' when present. + - args (array) — Command arguments (stdio transport). + - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth] + - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s). + - can_edit (boolean) (required) — Whether the caller may edit this server. + - command (string) — Executable command (stdio transport only). + - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s). + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the server. + - description (string) (required) — Server description. + - env (object) — Environment variables (stdio transport). Secret values are masked. + - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked. + - list_error (string) — Error message when the live tool list failed. + - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). - 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. + - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). + - server_id (string) (required) — Unique MCP server ID (prefix 'mcp_'). + - server_name (string) (required) — MCP server name, unique within the account. + - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored. + - status (string) (required) — Server status. [enabled, disabled] + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tool_count (integer) — Number of tools in the live list. + - tools (array) — Live tool list; populated by the get/test endpoints. + - description (string) (required) — Tool description. + - input_schema (object) — JSON Schema describing the tool's input parameters. + - name (string) (required) — Tool name. + - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http] + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - url (string) — Server URL (sse / streamable-http transport). `, Args: requireExactArg("server_id"), - Example: ` flashduty safari mcp-server-update --data '{"description":"Read issues, PRs, and commits from GitHub.","server_id":"mcp-2b5e8d14a7c9"}'`, + Example: ` flashduty safari mcp-server-update --data '{"description":"Query Prometheus metrics, alerts, and rules.","server_id":"mcp_4kP9wQ2nLceRtY7uVb3xA1"}'`, 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 { @@ -617,19 +627,19 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - 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().StringSliceVar(&fArgs, "args", nil, "Command arguments (stdio transport).") + cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "Authentication mode: shared (default), per_user_secret, or per_user_oauth.") + cmd.Flags().Int64Var(&fCallTimeout, "call-timeout", 0, "Tool-call timeout in seconds. 0 = default (60s).") + cmd.Flags().StringVar(&fCommand, "command", "", "Executable command (stdio transport).") + cmd.Flags().Int64Var(&fConnectTimeout, "connect-timeout", 0, "Connection timeout in seconds. 0 = default (10s).") 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(&fOauthMetadata, "oauth-metadata", "", "JSON OAuth metadata; reserved for per_user_oauth.") + cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON secret schema; required when auth_mode=per_user_secret.") + cmd.Flags().StringVar(&fServerID, "server-id", "", "Target MCP server ID. (required)") + cmd.Flags().StringVar(&fServerName, "server-name", "", "New name. (1-255 chars)") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged.") + cmd.Flags().StringVar(&fTransport, "transport", "", "Transport protocol. [stdio, sse, streamable-http]") + cmd.Flags().StringVar(&fURL, "url", "", "Server URL (sse / streamable-http transport).") 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 } diff --git a/internal/cli/zz_generated_response_help.go b/internal/cli/zz_generated_response_help.go index bec4a75..00f7d78 100644 --- a/internal/cli/zz_generated_response_help.go +++ b/internal/cli/zz_generated_response_help.go @@ -6,9 +6,9 @@ package cli // 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", + "A2aAgents.ReadGet": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - agent_card_name (string) — Agent name resolved from the remote card.\n - agent_card_skills (array) — Skills advertised by the remote card.\n - agent_id (string) (required) — Unique A2A agent ID (prefix `a2a_`).\n - agent_name (string) (required) — Agent display name.\n - auth_config (object) — Authentication config; secret values are masked.\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - auth_type (string) (required) — Authentication type for reaching the remote agent.\n - can_edit (boolean) (required) — Whether the caller may edit this agent.\n - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds.\n - card_url (string) (required) — URL of the remote agent card.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the agent.\n - description (string) (required) — Agent description.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - status (string) (required) — Agent status. [enabled, disabled]\n - streaming (boolean) (required) — Whether the remote agent supports streaming responses.\n - task_timeout (integer) (required) — Single-task execution timeout in seconds.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\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 ID.\n - agent_card_name (string) — Agent name resolved from the remote card.\n - agent_card_skills (array) — Skills advertised by the remote card.\n - agent_id (string) (required) — Unique A2A agent ID (prefix `a2a_`).\n - agent_name (string) (required) — Agent display name.\n - auth_config (object) — Authentication config; secret values are masked.\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - auth_type (string) (required) — Authentication type for reaching the remote agent.\n - can_edit (boolean) (required) — Whether the caller may edit this agent.\n - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds.\n - card_url (string) (required) — URL of the remote agent card.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the agent.\n - description (string) (required) — Agent description.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - status (string) (required) — Agent status. [enabled, disabled]\n - streaming (boolean) (required) — Whether the remote agent supports streaming responses.\n - task_timeout (integer) (required) — Single-task execution timeout in seconds.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n", + "A2aAgents.WriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - agent_id (string) (required) — ID of the newly 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", @@ -67,6 +67,7 @@ var responseHelpBySDKMethod = map[string]string{ "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.ChannelEscalateWebhookRobotList": "Response fields (this command's `--json` is a TOP-LEVEL array of these row objects — pipe `jq '.[]'`, NOT `.items[]`):\n - referenced_by (array) — List of channels and escalation rules referencing this robot.\n - channel_id (integer) — Channel ID.\n - channel_name (string) — Channel name.\n - escalate_rule_id (string) — Escalation rule ID (MongoDB ObjectID).\n - escalate_rule_name (string) — Escalation rule name.\n - settings (object) — Robot configuration, including `token` (webhook URL or secret) and `alias` (robot display name) among other fields.\n - type (string) — Robot type, e.g. `feishu`, `dingtalk`, `wecom`, `slack`, `teams`, etc.\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 - creator_name (string) — Name of the member who created the channel (resolved from the member directory; empty when unavailable).\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 - team_name (string) — Owning team name (resolved from the team directory; empty when unavailable).\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", @@ -107,10 +108,10 @@ var responseHelpBySDKMethod = map[string]string{ "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", + "McpServers.ReadServerGet": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - ai_description (string) — LLM-generated description, preferred over `description` when present.\n - args (array) — Command arguments (stdio transport).\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s).\n - can_edit (boolean) (required) — Whether the caller may edit this server.\n - command (string) — Executable command (stdio transport only).\n - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s).\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the server.\n - description (string) (required) — Server description.\n - env (object) — Environment variables (stdio transport). Secret values are masked.\n - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked.\n - list_error (string) — Error message when the live tool list failed.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - server_id (string) (required) — Unique MCP server ID (prefix `mcp_`).\n - server_name (string) (required) — MCP server name, unique within the account.\n - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored.\n - status (string) (required) — Server status. [enabled, disabled]\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tool_count (integer) — Number of tools in the live list.\n - tools (array) — Live tool list; populated by the get/test endpoints.\n - description (string) (required) — Tool description.\n - input_schema (object) — JSON Schema describing the tool's input parameters.\n - name (string) (required) — Tool name.\n - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - url (string) — Server URL (sse / 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 this page.\n - account_id (integer) (required) — Owning account ID.\n - ai_description (string) — LLM-generated description, preferred over `description` when present.\n - args (array) — Command arguments (stdio transport).\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s).\n - can_edit (boolean) (required) — Whether the caller may edit this server.\n - command (string) — Executable command (stdio transport only).\n - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s).\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the server.\n - description (string) (required) — Server description.\n - env (object) — Environment variables (stdio transport). Secret values are masked.\n - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked.\n - list_error (string) — Error message when the live tool list failed.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - server_id (string) (required) — Unique MCP server ID (prefix `mcp_`).\n - server_name (string) (required) — MCP server name, unique within the account.\n - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored.\n - status (string) (required) — Server status. [enabled, disabled]\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tool_count (integer) — Number of tools in the live list.\n - tools (array) — Live tool list; populated by the get/test endpoints.\n - description (string) (required) — Tool description.\n - input_schema (object) — JSON Schema describing the tool's input parameters.\n - name (string) (required) — Tool name.\n - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - url (string) — Server URL (sse / streamable-http transport).\n - total (integer) (required) — Total number of matching servers.\n", + "McpServers.WriteServerCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - ai_description (string) — LLM-generated description, preferred over `description` when present.\n - args (array) — Command arguments (stdio transport).\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s).\n - can_edit (boolean) (required) — Whether the caller may edit this server.\n - command (string) — Executable command (stdio transport only).\n - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s).\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the server.\n - description (string) (required) — Server description.\n - env (object) — Environment variables (stdio transport). Secret values are masked.\n - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked.\n - list_error (string) — Error message when the live tool list failed.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - server_id (string) (required) — Unique MCP server ID (prefix `mcp_`).\n - server_name (string) (required) — MCP server name, unique within the account.\n - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored.\n - status (string) (required) — Server status. [enabled, disabled]\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tool_count (integer) — Number of tools in the live list.\n - tools (array) — Live tool list; populated by the get/test endpoints.\n - description (string) (required) — Tool description.\n - input_schema (object) — JSON Schema describing the tool's input parameters.\n - name (string) (required) — Tool name.\n - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - url (string) — Server URL (sse / 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 ID.\n - ai_description (string) — LLM-generated description, preferred over `description` when present.\n - args (array) — Command arguments (stdio transport).\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - call_timeout (integer) (required) — Tool-call timeout in seconds (0 = server default, 60s).\n - can_edit (boolean) (required) — Whether the caller may edit this server.\n - command (string) — Executable command (stdio transport only).\n - connect_timeout (integer) (required) — Connection timeout in seconds (0 = server default, 10s).\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the server.\n - description (string) (required) — Server description.\n - env (object) — Environment variables (stdio transport). Secret values are masked.\n - headers (object) — HTTP headers (sse / streamable-http). Secret values are masked.\n - list_error (string) — Error message when the live tool list failed.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - proxy_url (string) — Outbound proxy URL used to reach the server.\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - server_id (string) (required) — Unique MCP server ID (prefix `mcp_`).\n - server_name (string) (required) — MCP server name, unique within the account.\n - source_template_name (string) — Marketplace template this connector was installed from; empty for user-authored.\n - status (string) (required) — Server status. [enabled, disabled]\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tool_count (integer) — Number of tools in the live list.\n - tools (array) — Live tool list; populated by the get/test endpoints.\n - description (string) (required) — Tool description.\n - input_schema (object) — JSON Schema describing the tool's input parameters.\n - name (string) (required) — Tool name.\n - transport (string) (required) — Transport protocol. [stdio, sse, streamable-http]\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - url (string) — Server URL (sse / 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", @@ -134,12 +135,12 @@ var responseHelpBySDKMethod = map[string]string{ "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", - "Sessions.Info": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - events (array) — Recent events, ascending by (created_at, event_id).\n - actions (object) — ADK actions envelope (state deltas, transfers, escalation).\n - author (string) — Event author (e.g. user, the agent name).\n - branch (string) — ADK branch path for nested agents.\n - content (object) — ADK content envelope {role, parts:[...]}.\n - created_at (integer) — Unix timestamp in milliseconds when the event was written.\n - error_code (string) — Error code when the event represents a failure.\n - error_message (string) — Human-readable error message, when present.\n - event_id (string) — Event identifier.\n - invocation_id (string) — ADK invocation id grouping a turn.\n - partial (boolean) — True for a streaming partial chunk.\n - session_id (string) — Owning session id.\n - status (string) — Event status. [normal, compressed]\n - turn_complete (boolean) — True on the terminal event of a turn.\n - usage_metadata (object) — Per-turn token usage metadata.\n - has_more_older (boolean) — True when older events remain beyond this page.\n - search_after_ctx (string) — Opaque keyset cursor; pass back as search_after_ctx to fetch the next older page. Omitted when has_more_older is false.\n - session (object) — One agent session row.\n - app_name (string) — Agent app that owns the session.\n - archived_at (integer) — Unix timestamp in milliseconds when archived; 0 means not archived.\n - bound_environment (object) — The runner or cloud sandbox the session is bound to. Null until the first message.\n - id (string) — Environment identifier.\n - kind (string) — Environment kind (e.g. runner, sandbox).\n - name (string) — Human-readable environment name.\n - status (string) — Binding status.\n - can_manage (boolean) — True when the caller may rename/archive/delete the session.\n - context_resolved (object) — Snapshot of the three-tier knowledge-pack resolution for this session.\n - account_pack_id (string) — Resolved account-scoped pack id.\n - incident_id (string) — Bound incident id, when war-room originated.\n - resolved_at_ms (integer) — Unix timestamp in milliseconds when the packs were resolved.\n - team_pack_id (string) — Resolved team-scoped pack id.\n - versions (object) — Per-pack resolved version map.\n - context_window (integer) — The bound model's max context size in tokens. 0 means unknown.\n - created_at (integer) — Unix timestamp in milliseconds when the session was created.\n - current_context_tokens (integer) — Size in tokens of the LLM context window as of the most recent turn. 0 means no turn has completed.\n - entry_kind (string) — Surface that created the session. [web, im, api, scheduled, subagent]\n - has_unread (boolean) — True when there is assistant output the caller has not yet viewed.\n - incognito (boolean) — True for incognito (non-persisted-memory) sessions.\n - is_mine (boolean) — True when the caller created this session.\n - is_running (boolean) — True when an agent turn is currently in flight for this session.\n - last_event_at (integer) — Unix timestamp in milliseconds of the most recent assistant-side event.\n - parent_session_id (string) — Parent session id for subagent (child) sessions; empty otherwise.\n - person_id (string) — Creator person id.\n - pinned_at (integer) — Caller's per-user pin timestamp in milliseconds; 0 means not pinned.\n - session_id (string) — Session identifier.\n - session_name (string) — Session title; may be empty for untitled sessions.\n - state (object) — Raw session-state bag (session-scoped keys). Omitted when empty.\n - status (string) — Lifecycle status. [enabled, deleted]\n - team_id (integer) — Owning team id; 0 means no team is bound. Immutable after create.\n - team_name (string) — Resolved team name; empty for unbound rows or deleted teams.\n - template_staging_round_id (string) — Current save→validate round id (template-assistant only); empty otherwise.\n - token_usage (object) — Cumulative session-level token rollup across all turns. The account-billing source of truth.\n - cached_tokens (integer) — Portion of input_tokens served from the prompt cache.\n - input_tokens (integer) — Total prompt (input) tokens, including the cached portion.\n - output_tokens (integer) — Total generated (output) tokens.\n - reasoning_tokens (integer) — Total reasoning/thinking tokens.\n - updated_at (integer) — Unix timestamp in milliseconds of the last session update.\n", - "Sessions.List": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - sessions (array) — The page of sessions.\n - app_name (string) — Agent app that owns the session.\n - archived_at (integer) — Unix timestamp in milliseconds when archived; 0 means not archived.\n - bound_environment (object) — The runner or cloud sandbox the session is bound to. Null until the first message.\n - id (string) — Environment identifier.\n - kind (string) — Environment kind (e.g. runner, sandbox).\n - name (string) — Human-readable environment name.\n - status (string) — Binding status.\n - can_manage (boolean) — True when the caller may rename/archive/delete the session.\n - context_resolved (object) — Snapshot of the three-tier knowledge-pack resolution for this session.\n - account_pack_id (string) — Resolved account-scoped pack id.\n - incident_id (string) — Bound incident id, when war-room originated.\n - resolved_at_ms (integer) — Unix timestamp in milliseconds when the packs were resolved.\n - team_pack_id (string) — Resolved team-scoped pack id.\n - versions (object) — Per-pack resolved version map.\n - context_window (integer) — The bound model's max context size in tokens. 0 means unknown.\n - created_at (integer) — Unix timestamp in milliseconds when the session was created.\n - current_context_tokens (integer) — Size in tokens of the LLM context window as of the most recent turn. 0 means no turn has completed.\n - entry_kind (string) — Surface that created the session. [web, im, api, scheduled, subagent]\n - has_unread (boolean) — True when there is assistant output the caller has not yet viewed.\n - incognito (boolean) — True for incognito (non-persisted-memory) sessions.\n - is_mine (boolean) — True when the caller created this session.\n - is_running (boolean) — True when an agent turn is currently in flight for this session.\n - last_event_at (integer) — Unix timestamp in milliseconds of the most recent assistant-side event.\n - parent_session_id (string) — Parent session id for subagent (child) sessions; empty otherwise.\n - person_id (string) — Creator person id.\n - pinned_at (integer) — Caller's per-user pin timestamp in milliseconds; 0 means not pinned.\n - session_id (string) — Session identifier.\n - session_name (string) — Session title; may be empty for untitled sessions.\n - state (object) — Raw session-state bag (session-scoped keys). Omitted when empty.\n - status (string) — Lifecycle status. [enabled, deleted]\n - team_id (integer) — Owning team id; 0 means no team is bound. Immutable after create.\n - team_name (string) — Resolved team name; empty for unbound rows or deleted teams.\n - template_staging_round_id (string) — Current save→validate round id (template-assistant only); empty otherwise.\n - token_usage (object) — Cumulative session-level token rollup across all turns. The account-billing source of truth.\n - cached_tokens (integer) — Portion of input_tokens served from the prompt cache.\n - input_tokens (integer) — Total prompt (input) tokens, including the cached portion.\n - output_tokens (integer) — Total generated (output) tokens.\n - reasoning_tokens (integer) — Total reasoning/thinking tokens.\n - updated_at (integer) — Unix timestamp in milliseconds of the last session update.\n - total (integer) — Total number of sessions matching the filter (ignoring pagination).\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", + "Sessions.ReadInfo": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - events (array) — Recent events, ascending by (created_at, event_id).\n - actions (object) — ADK actions envelope (state deltas, transfers, escalation).\n - author (string) — Event author (e.g. user, the agent name).\n - branch (string) — ADK branch path for nested agents.\n - content (object) — ADK content envelope {role, parts:[...]}.\n - created_at (integer) — Unix timestamp in milliseconds when the event was written.\n - error_code (string) — Error code when the event represents a failure.\n - error_message (string) — Human-readable error message, when present.\n - event_id (string) — Event identifier.\n - invocation_id (string) — ADK invocation id grouping a turn.\n - partial (boolean) — True for a streaming partial chunk.\n - session_id (string) — Owning session id.\n - status (string) — Event status. [normal, compressed]\n - turn_complete (boolean) — True on the terminal event of a turn.\n - usage_metadata (object) — Per-turn token usage metadata.\n - has_more_older (boolean) — True when older events remain beyond this page.\n - search_after_ctx (string) — Opaque keyset cursor; pass back as search_after_ctx to fetch the next older page. Omitted when has_more_older is false.\n - session (object) — One agent session row.\n - app_name (string) — Agent app that owns the session.\n - archived_at (integer) — Unix timestamp in milliseconds when archived; 0 means not archived.\n - bound_environment (object) — The runner or cloud sandbox the session is bound to. Null until the first message.\n - id (string) — Environment identifier.\n - kind (string) — Environment kind (e.g. runner, sandbox).\n - name (string) — Human-readable environment name.\n - status (string) — Binding status.\n - can_manage (boolean) — True when the caller may rename/archive/delete the session.\n - context_resolved (object) — Snapshot of the three-tier knowledge-pack resolution for this session.\n - account_pack_id (string) — Resolved account-scoped pack id.\n - incident_id (string) — Bound incident id, when war-room originated.\n - resolved_at_ms (integer) — Unix timestamp in milliseconds when the packs were resolved.\n - team_pack_id (string) — Resolved team-scoped pack id.\n - versions (object) — Per-pack resolved version map.\n - context_window (integer) — The bound model's max context size in tokens. 0 means unknown.\n - created_at (integer) — Unix timestamp in milliseconds when the session was created.\n - current_context_tokens (integer) — Size in tokens of the LLM context window as of the most recent turn. 0 means no turn has completed.\n - entry_kind (string) — Surface that created the session. [web, im, api, scheduled, subagent]\n - has_unread (boolean) — True when there is assistant output the caller has not yet viewed.\n - incognito (boolean) — True for incognito (non-persisted-memory) sessions.\n - is_mine (boolean) — True when the caller created this session.\n - is_running (boolean) — True when an agent turn is currently in flight for this session.\n - last_event_at (integer) — Unix timestamp in milliseconds of the most recent assistant-side event.\n - parent_session_id (string) — Parent session id for subagent (child) sessions; empty otherwise.\n - person_id (string) — Creator person id.\n - pinned_at (integer) — Caller's per-user pin timestamp in milliseconds; 0 means not pinned.\n - session_id (string) — Session identifier.\n - session_name (string) — Session title; may be empty for untitled sessions.\n - state (object) — Raw session-state bag (session-scoped keys). Omitted when empty.\n - status (string) — Lifecycle status. [enabled, deleted]\n - team_id (integer) — Owning team id; 0 means no team is bound. Immutable after create.\n - team_name (string) — Resolved team name; empty for unbound rows or deleted teams.\n - template_staging_round_id (string) — Current save→validate round id (template-assistant only); empty otherwise.\n - token_usage (object) — Cumulative session-level token rollup across all turns. The account-billing source of truth.\n - cached_tokens (integer) — Portion of input_tokens served from the prompt cache.\n - input_tokens (integer) — Total prompt (input) tokens, including the cached portion.\n - output_tokens (integer) — Total generated (output) tokens.\n - reasoning_tokens (integer) — Total reasoning/thinking tokens.\n - updated_at (integer) — Unix timestamp in milliseconds of the last session update.\n", + "Sessions.ReadList": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - sessions (array) — The page of sessions.\n - app_name (string) — Agent app that owns the session.\n - archived_at (integer) — Unix timestamp in milliseconds when archived; 0 means not archived.\n - bound_environment (object) — The runner or cloud sandbox the session is bound to. Null until the first message.\n - id (string) — Environment identifier.\n - kind (string) — Environment kind (e.g. runner, sandbox).\n - name (string) — Human-readable environment name.\n - status (string) — Binding status.\n - can_manage (boolean) — True when the caller may rename/archive/delete the session.\n - context_resolved (object) — Snapshot of the three-tier knowledge-pack resolution for this session.\n - account_pack_id (string) — Resolved account-scoped pack id.\n - incident_id (string) — Bound incident id, when war-room originated.\n - resolved_at_ms (integer) — Unix timestamp in milliseconds when the packs were resolved.\n - team_pack_id (string) — Resolved team-scoped pack id.\n - versions (object) — Per-pack resolved version map.\n - context_window (integer) — The bound model's max context size in tokens. 0 means unknown.\n - created_at (integer) — Unix timestamp in milliseconds when the session was created.\n - current_context_tokens (integer) — Size in tokens of the LLM context window as of the most recent turn. 0 means no turn has completed.\n - entry_kind (string) — Surface that created the session. [web, im, api, scheduled, subagent]\n - has_unread (boolean) — True when there is assistant output the caller has not yet viewed.\n - incognito (boolean) — True for incognito (non-persisted-memory) sessions.\n - is_mine (boolean) — True when the caller created this session.\n - is_running (boolean) — True when an agent turn is currently in flight for this session.\n - last_event_at (integer) — Unix timestamp in milliseconds of the most recent assistant-side event.\n - parent_session_id (string) — Parent session id for subagent (child) sessions; empty otherwise.\n - person_id (string) — Creator person id.\n - pinned_at (integer) — Caller's per-user pin timestamp in milliseconds; 0 means not pinned.\n - session_id (string) — Session identifier.\n - session_name (string) — Session title; may be empty for untitled sessions.\n - state (object) — Raw session-state bag (session-scoped keys). Omitted when empty.\n - status (string) — Lifecycle status. [enabled, deleted]\n - team_id (integer) — Owning team id; 0 means no team is bound. Immutable after create.\n - team_name (string) — Resolved team name; empty for unbound rows or deleted teams.\n - template_staging_round_id (string) — Current save→validate round id (template-assistant only); empty otherwise.\n - token_usage (object) — Cumulative session-level token rollup across all turns. The account-billing source of truth.\n - cached_tokens (integer) — Portion of input_tokens served from the prompt cache.\n - input_tokens (integer) — Total prompt (input) tokens, including the cached portion.\n - output_tokens (integer) — Total generated (output) tokens.\n - reasoning_tokens (integer) — Total reasoning/thinking tokens.\n - updated_at (integer) — Unix timestamp in milliseconds of the last session update.\n - total (integer) — Total number of sessions matching the filter (ignoring pagination).\n", + "Skills.ReadGet": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - author (string) — Skill author.\n - can_edit (boolean) (required) — Whether the caller may edit this skill.\n - checksum (string) — SHA-256 checksum of the skill zip.\n - content (string) — Full SKILL.md content. Omitted in list responses.\n - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the skill.\n - description (string) (required) — Human-readable description from the SKILL.md frontmatter.\n - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it).\n - license (string) — Skill license.\n - s3_key (string) — Object-storage key of the skill zip.\n - skill_id (string) (required) — Unique skill ID (prefix `skill_`).\n - skill_name (string) (required) — Skill name, unique within the account.\n - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored.\n - source_template_version (string) — Template version at install time.\n - status (string) (required) — Skill status. [enabled, disabled]\n - tags (array) — Tags parsed from the frontmatter.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tools (array) — Required tools (builtin or `mcp:server/tool`).\n - update_available (boolean) (required) — True when the marketplace has a newer template version.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - version (string) — Skill version from the frontmatter.\n", + "Skills.ReadList": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - skills (array) (required) — Skills on this page.\n - account_id (integer) (required) — Owning account ID.\n - author (string) — Skill author.\n - can_edit (boolean) (required) — Whether the caller may edit this skill.\n - checksum (string) — SHA-256 checksum of the skill zip.\n - content (string) — Full SKILL.md content. Omitted in list responses.\n - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the skill.\n - description (string) (required) — Human-readable description from the SKILL.md frontmatter.\n - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it).\n - license (string) — Skill license.\n - s3_key (string) — Object-storage key of the skill zip.\n - skill_id (string) (required) — Unique skill ID (prefix `skill_`).\n - skill_name (string) (required) — Skill name, unique within the account.\n - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored.\n - source_template_version (string) — Template version at install time.\n - status (string) (required) — Skill status. [enabled, disabled]\n - tags (array) — Tags parsed from the frontmatter.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tools (array) — Required tools (builtin or `mcp:server/tool`).\n - update_available (boolean) (required) — True when the marketplace has a newer template version.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - version (string) — Skill version from the frontmatter.\n - total (integer) (required) — Total number of matching skills.\n", + "Skills.WriteUpdate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - author (string) — Skill author.\n - can_edit (boolean) (required) — Whether the caller may edit this skill.\n - checksum (string) — SHA-256 checksum of the skill zip.\n - content (string) — Full SKILL.md content. Omitted in list responses.\n - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the skill.\n - description (string) (required) — Human-readable description from the SKILL.md frontmatter.\n - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it).\n - license (string) — Skill license.\n - s3_key (string) — Object-storage key of the skill zip.\n - skill_id (string) (required) — Unique skill ID (prefix `skill_`).\n - skill_name (string) (required) — Skill name, unique within the account.\n - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored.\n - source_template_version (string) — Template version at install time.\n - status (string) (required) — Skill status. [enabled, disabled]\n - tags (array) — Tags parsed from the frontmatter.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tools (array) — Required tools (builtin or `mcp:server/tool`).\n - update_available (boolean) (required) — True when the marketplace has a newer template version.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - version (string) — Skill version from the frontmatter.\n", + "Skills.WriteUpload": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - author (string) — Skill author.\n - can_edit (boolean) (required) — Whether the caller may edit this skill.\n - checksum (string) — SHA-256 checksum of the skill zip.\n - content (string) — Full SKILL.md content. Omitted in list responses.\n - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the skill.\n - description (string) (required) — Human-readable description from the SKILL.md frontmatter.\n - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it).\n - license (string) — Skill license.\n - s3_key (string) — Object-storage key of the skill zip.\n - skill_id (string) (required) — Unique skill ID (prefix `skill_`).\n - skill_name (string) (required) — Skill name, unique within the account.\n - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored.\n - source_template_version (string) — Template version at install time.\n - status (string) (required) — Skill status. [enabled, disabled]\n - tags (array) — Tags parsed from the frontmatter.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - tools (array) — Required tools (builtin or `mcp:server/tool`).\n - update_available (boolean) (required) — True when the marketplace has a newer template version.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n - version (string) — Skill version from the 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", diff --git a/internal/cli/zz_generated_sessions.go b/internal/cli/zz_generated_sessions.go index 2702009..995e99c 100644 --- a/internal/cli/zz_generated_sessions.go +++ b/internal/cli/zz_generated_sessions.go @@ -8,7 +8,7 @@ import ( flashduty "github.com/flashcatcloud/go-flashduty" ) -func genSessionsInfoCmd() *cobra.Command { +func genSessionsReadInfoCmd() *cobra.Command { var dataJSON string var fLimit int64 var fNumRecentEvents int64 @@ -19,15 +19,15 @@ func genSessionsInfoCmd() *cobra.Command { Short: "Get session detail", Long: `Get session detail. -Fetch one session plus a backward-paged window of its most recent events. Use search_after_ctx to page through older history. +Fetch one session plus a backward-paged window of its most recent events. API: POST /safari/session/get (session-read-info) Request fields: - --limit int — Alias for num_recent_events; takes precedence when both are set. (0-1000) - --num-recent-events int — Number of most-recent events to return; 0 uses the server default. (0-1000) - --search-after-ctx string — Opaque keyset cursor from a previous response's search_after_ctx, to page backward through older events. (≤4096 chars) - --session-id string (required) — Session identifier. (≥1 chars) + --limit int — Page size for events; takes precedence over 'num_recent_events'. 0 uses the server default (100). (0-1000) + --num-recent-events int — Legacy page size: number of most-recent events to return. Superseded by 'limit' when both are set; 0 uses the server default (100). (0-1000) + --search-after-ctx string — Opaque keyset cursor from a previous response; pass it back to fetch the next older page. (≤4096 chars) + --session-id string (required) — Target session ID. (≥1 chars) Response fields ('data' envelope is unwrapped — these fields are at the top level): - events (array) — Recent events, ascending by (created_at, event_id). @@ -117,7 +117,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if err := genBindBody(body, req); err != nil { return err } - out, _, err := ctx.Client.Sessions.Info(cmdContext(ctx.Cmd), req) + out, _, err := ctx.Client.Sessions.ReadInfo(cmdContext(ctx.Cmd), req) if err != nil { return err } @@ -125,15 +125,15 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().Int64Var(&fLimit, "limit", 0, "Alias for num_recent_events; takes precedence when both are set. (0-1000)") - cmd.Flags().Int64Var(&fNumRecentEvents, "num-recent-events", 0, "Number of most-recent events to return; 0 uses the server default. (0-1000)") - cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque keyset cursor from a previous response's search_after_ctx, to page backward through older events. (≤4096 chars)") - cmd.Flags().StringVar(&fSessionID, "session-id", "", "Session identifier. (required) (≥1 chars)") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size for events; takes precedence over 'num_recent_events'. 0 uses the server default (100). (0-1000)") + cmd.Flags().Int64Var(&fNumRecentEvents, "num-recent-events", 0, "Legacy page size: number of most-recent events to return. Superseded by 'limit' when both are set; 0 uses the server default (100). (0-1000)") + cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque keyset cursor from a previous response; pass it back to fetch the next older page. (≤4096 chars)") + cmd.Flags().StringVar(&fSessionID, "session-id", "", "Target session ID. (required) (≥1 chars)") 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 genSessionsListCmd() *cobra.Command { +func genSessionsReadListCmd() *cobra.Command { var dataJSON string var fP int64 var fLimit int64 @@ -152,23 +152,23 @@ func genSessionsListCmd() *cobra.Command { Short: "List sessions", Long: `List sessions. -List agent sessions visible to the caller within the resolved account, filtered by app, entry surface, archive status, and team scope, with pagination. Reads are scoped to the person the app_key resolves to. +List agent sessions visible to the caller, filtered by app, surface, archive status, and team. API: POST /safari/session/list (session-read-list) Request fields: - --page int — 1-based page number; defaults to 1. - --limit int — Page size, 1..100; defaults to 20. (1-100) + --page int — Page number, 1-based. (min 1) + --limit int — Page size, 1–100. (1-100) --search-after-ctx string - --app-name string (required) — Agent app whose sessions to list. [ask-ai, support, support-website, support-flashcat, ai-sre, template-assistant] - --asc bool — Ascending sort when true; defaults to false (descending). Only honored when orderby is set. - --entry-kinds []string — Restrict to sessions produced by these entry surfaces. Empty returns every kind. [web, im, api, scheduled] - --include-subagent-sessions bool — Include subagent (child) sessions in the result; defaults to false. - --keyword string — Case-insensitive substring match against session name. (≤64 chars) - --orderby string — Sort column. [created_at, updated_at] - --scope string — Visibility scope: all (own + member-of-team rows, the default), personal (own only), or team (member teams only). [all, personal, team] - --status string — Archive bucket: active (default, not archived), archived, or all. [active, archived, all] - --team-ids []int — Optional explicit team filter; intersected with the caller's visible set / scope. + --app-name string (required) — Agent app whose sessions to list. [ask-ai, support, support-website, support-flashcat, ai-sre, template-assistant, swe] + --asc bool — Ascending order when true; applies only when 'orderby' is set. + --entry-kinds []string — Restrict to sessions produced by these surfaces; empty returns every kind. [web, im, api, automation] + --include-subagent-sessions bool — Include subagent-dispatched sessions in the list. + --keyword string — Filter by session-name keyword. (≤64 chars) + --orderby string — Sort field. [created_at, updated_at] + --scope string — Visibility scope: all (own + member-of-team rows, default), personal, or team. [all, personal, team] + --status string — Archive bucket: active (default) returns un-archived, archived returns archived, all returns both. [active, archived, all] + --team-ids []int — Optional explicit team filter; intersects with 'scope'. Response fields ('data' envelope is unwrapped — these fields are at the top level): - sessions (array) — The page of sessions. @@ -262,7 +262,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if err := genBindBody(body, req); err != nil { return err } - out, _, err := ctx.Client.Sessions.List(cmdContext(ctx.Cmd), req) + out, _, err := ctx.Client.Sessions.ReadList(cmdContext(ctx.Cmd), req) if err != nil { return err } @@ -270,24 +270,73 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().Int64Var(&fP, "page", 0, "1-based page number; defaults to 1.") - cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size, 1..100; defaults to 20. (1-100)") + 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(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") - cmd.Flags().StringVar(&fAppName, "app-name", "", "Agent app whose sessions to list. (required) [ask-ai, support, support-website, support-flashcat, ai-sre, template-assistant]") - cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending sort when true; defaults to false (descending). Only honored when orderby is set.") - cmd.Flags().StringSliceVar(&fEntryKinds, "entry-kinds", nil, "Restrict to sessions produced by these entry surfaces. Empty returns every kind. [web, im, api, scheduled]") - cmd.Flags().BoolVar(&fIncludeSubagentSessions, "include-subagent-sessions", false, "Include subagent (child) sessions in the result; defaults to false.") - cmd.Flags().StringVar(&fKeyword, "keyword", "", "Case-insensitive substring match against session name. (≤64 chars)") - cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort column. [created_at, updated_at]") - cmd.Flags().StringVar(&fScope, "scope", "", "Visibility scope: all (own + member-of-team rows, the default), personal (own only), or team (member teams only). [all, personal, team]") - cmd.Flags().StringVar(&fStatus, "status", "", "Archive bucket: active (default, not archived), archived, or all. [active, archived, all]") - cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Optional explicit team filter; intersected with the caller's visible set / scope.") + cmd.Flags().StringVar(&fAppName, "app-name", "", "Agent app whose sessions to list. (required) [ask-ai, support, support-website, support-flashcat, ai-sre, template-assistant, swe]") + cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending order when true; applies only when 'orderby' is set.") + cmd.Flags().StringSliceVar(&fEntryKinds, "entry-kinds", nil, "Restrict to sessions produced by these surfaces; empty returns every kind. [web, im, api, automation]") + cmd.Flags().BoolVar(&fIncludeSubagentSessions, "include-subagent-sessions", false, "Include subagent-dispatched sessions in the list.") + cmd.Flags().StringVar(&fKeyword, "keyword", "", "Filter by session-name keyword. (≤64 chars)") + cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") + cmd.Flags().StringVar(&fScope, "scope", "", "Visibility scope: all (own + member-of-team rows, default), personal, or team. [all, personal, team]") + cmd.Flags().StringVar(&fStatus, "status", "", "Archive bucket: active (default) returns un-archived, archived returns archived, all returns both. [active, archived, all]") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Optional explicit team filter; intersects with 'scope'.") + 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 genSessionsWriteDeleteCmd() *cobra.Command { + var dataJSON string + var fSessionID string + cmd := &cobra.Command{ + Use: "session-delete ", + Short: "Delete session", + Long: `Delete session. + +Delete a session by ID. + +API: POST /safari/session/delete (session-write-delete) + +Request fields: + --session-id string (required) — Target session ID. (≥1 chars) +`, + Args: requireExactArg("session_id"), + Example: ` flashduty safari session-delete --data '{"session_id":"sess_f8oDvqiG64uur6sBNsTc4u"}'`, + 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 { + if err := genFoldPositional(args, body, "session_id", "string"); err != nil { + return err + } + if cmd.Flags().Changed("session-id") { + body["session_id"] = fSessionID + } + return nil + }) + if err != nil { + return err + } + req := new(flashduty.SessionDeleteRequest) + if err := genBindBody(body, req); err != nil { + return err + } + out, _, err := ctx.Client.Sessions.WriteDelete(cmdContext(ctx.Cmd), req) + if err != nil { + return err + } + return printGenericResult(ctx, out) + }) + }, + } + cmd.Flags().StringVar(&fSessionID, "session-id", "", "Target session ID. (required) (≥1 chars)") 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 registerGeneratedSessions(root *cobra.Command) { gSafari := genGroup(root, "safari", "AI SRE API") - genAddLeaf(gSafari, genSessionsInfoCmd()) - genAddLeaf(gSafari, genSessionsListCmd()) + genAddLeaf(gSafari, genSessionsReadInfoCmd()) + genAddLeaf(gSafari, genSessionsReadListCmd()) + genAddLeaf(gSafari, genSessionsWriteDeleteCmd()) } diff --git a/internal/cli/zz_generated_skills.go b/internal/cli/zz_generated_skills.go index 5b4f642..17e59c9 100644 --- a/internal/cli/zz_generated_skills.go +++ b/internal/cli/zz_generated_skills.go @@ -8,54 +8,6 @@ import ( 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. -`, - Args: requireExactArg("skill_id"), - 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) error { - if err := genFoldPositional(args, body, "skill_id", "string"); err != nil { - return err - } - if cmd.Flags().Changed("skill-id") { - body["skill_id"] = fSkillID - } - return nil - }) - 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; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") - return cmd -} - func genSkillsReadEnableCmd() *cobra.Command { var dataJSON string var fSkillID string @@ -64,15 +16,15 @@ func genSkillsReadEnableCmd() *cobra.Command { Short: "Enable skill", Long: `Enable skill. -Activate a disabled skill so agents can load it at session start. +Enable a disabled skill so the agent can load it. API: POST /safari/skill/enable (skill-read-enable) Request fields: - --skill-id string (required) — Identifier of the target skill. + --skill-id string (required) — Target skill ID. `, Args: requireExactArg("skill_id"), - Example: ` flashduty safari skill-enable --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + Example: ` flashduty safari skill-enable --data '{"skill_id":"skill_8s7Hn2kLpQ3xYbVc4Wd2m"}'`, 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 { @@ -99,7 +51,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the target skill. (required)") + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Target skill ID. (required)") 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 } @@ -112,40 +64,40 @@ func genSkillsReadGetCmd() *cobra.Command { Short: "Get skill detail", Long: `Get skill detail. -Return the full configuration and SKILL.md body of a single skill by ID. +Get one skill including its full SKILL.md content. API: POST /safari/skill/get (skill-read-get) Request fields: - --skill-id string (required) — Identifier of the skill to fetch. + --skill-id string (required) — Target skill ID. 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. + - account_id (integer) (required) — Owning account ID. + - author (string) — Skill author. + - can_edit (boolean) (required) — Whether the caller may edit this skill. + - checksum (string) — SHA-256 checksum of the skill zip. + - content (string) — Full SKILL.md content. Omitted in list responses. + - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update. + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the skill. + - description (string) (required) — Human-readable description from the SKILL.md frontmatter. + - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it). + - license (string) — Skill license. + - s3_key (string) — Object-storage key of the skill zip. + - skill_id (string) (required) — Unique skill ID (prefix 'skill_'). + - skill_name (string) (required) — Skill name, unique within the account. + - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored. + - source_template_version (string) — Template version at install time. + - status (string) (required) — Skill status. [enabled, disabled] + - tags (array) — Tags parsed from the frontmatter. + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tools (array) — Required tools (builtin or 'mcp:server/tool'). + - update_available (boolean) (required) — True when the marketplace has a newer template version. + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - version (string) — Skill version from the frontmatter. `, Args: requireExactArg("skill_id"), - Example: ` flashduty safari skill-get --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + Example: ` flashduty safari skill-get --data '{"skill_id":"skill_8s7Hn2kLpQ3xYbVc4Wd2m"}'`, 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 { @@ -172,7 +124,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to fetch. (required)") + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Target skill ID. (required)") 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 } @@ -194,38 +146,38 @@ List AI SRE skills visible to the caller across account and team scopes, with pa API: POST /safari/skill/list (skill-read-list) Request fields: - --page int — Page number, starting at 1. - --limit int — Page size; defaults to 20. + --page int — Page number, 1-based. + --limit int — Page size. --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. + --include-account bool — Include account-scoped (team_id=0) rows. Defaults to true. + --team-ids []int — Filter to these team IDs; empty = 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. + - skills (array) (required) — Skills on this page. + - account_id (integer) (required) — Owning account ID. + - author (string) — Skill author. + - can_edit (boolean) (required) — Whether the caller may edit this skill. + - checksum (string) — SHA-256 checksum of the skill zip. + - content (string) — Full SKILL.md content. Omitted in list responses. + - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update. + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the skill. + - description (string) (required) — Human-readable description from the SKILL.md frontmatter. + - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it). + - license (string) — Skill license. + - s3_key (string) — Object-storage key of the skill zip. + - skill_id (string) (required) — Unique skill ID (prefix 'skill_'). + - skill_name (string) (required) — Skill name, unique within the account. + - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored. + - source_template_version (string) — Template version at install time. + - status (string) (required) — Skill status. [enabled, disabled] + - tags (array) — Tags parsed from the frontmatter. + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tools (array) — Required tools (builtin or 'mcp:server/tool'). + - update_available (boolean) (required) — True when the marketplace has a newer template version. + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - version (string) — Skill version from the frontmatter. + - total (integer) (required) — Total number of matching skills. `, Example: ` flashduty safari skill-list --data '{"include_account":true,"limit":20,"p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { @@ -263,11 +215,11 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().Int64Var(&fP, "page", 0, "Page number, starting at 1.") - cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size; defaults to 20.") + cmd.Flags().Int64Var(&fP, "page", 0, "Page number, 1-based.") + cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size.") 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().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped (team_id=0) rows. Defaults to true.") + cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter to these team IDs; empty = the caller's visible set.") 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 } @@ -280,15 +232,15 @@ func genSkillsWriteDeleteCmd() *cobra.Command { Short: "Delete skill", Long: `Delete skill. -Permanently remove a skill so agents can no longer load it. +Delete a skill by ID. API: POST /safari/skill/delete (skill-write-delete) Request fields: - --skill-id string (required) — Identifier of the skill to delete. + --skill-id string (required) — Target skill ID. `, Args: requireExactArg("skill_id"), - Example: ` flashduty safari skill-delete --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + Example: ` flashduty safari skill-delete --data '{"skill_id":"skill_8s7Hn2kLpQ3xYbVc4Wd2m"}'`, 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 { @@ -315,7 +267,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to delete. (required)") + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Target skill ID. (required)") 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 } @@ -328,15 +280,15 @@ func genSkillsWriteDisableCmd() *cobra.Command { Short: "Disable skill", Long: `Disable skill. -Deactivate an enabled skill so agents no longer load it. +Disable an enabled skill so the agent stops loading it. API: POST /safari/skill/disable (skill-write-disable) Request fields: - --skill-id string (required) — Identifier of the target skill. + --skill-id string (required) — Target skill ID. `, Args: requireExactArg("skill_id"), - Example: ` flashduty safari skill-disable --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, + Example: ` flashduty safari skill-disable --data '{"skill_id":"skill_8s7Hn2kLpQ3xYbVc4Wd2m"}'`, 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 { @@ -363,7 +315,7 @@ Request fields: }) }, } - cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the target skill. (required)") + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Target skill ID. (required)") 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 } @@ -378,42 +330,42 @@ func genSkillsWriteUpdateCmd() *cobra.Command { Short: "Update skill", Long: `Update skill. -Edit a skill's description or reassign its owning team. +Update a skill's description or reassign its team scope. 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. + --description string — New description. (≤1024 chars) + --skill-id string (required) — Target skill ID. + --team-id int — Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged. 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. + - account_id (integer) (required) — Owning account ID. + - author (string) — Skill author. + - can_edit (boolean) (required) — Whether the caller may edit this skill. + - checksum (string) — SHA-256 checksum of the skill zip. + - content (string) — Full SKILL.md content. Omitted in list responses. + - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update. + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the skill. + - description (string) (required) — Human-readable description from the SKILL.md frontmatter. + - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it). + - license (string) — Skill license. + - s3_key (string) — Object-storage key of the skill zip. + - skill_id (string) (required) — Unique skill ID (prefix 'skill_'). + - skill_name (string) (required) — Skill name, unique within the account. + - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored. + - source_template_version (string) — Template version at install time. + - status (string) (required) — Skill status. [enabled, disabled] + - tags (array) — Tags parsed from the frontmatter. + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tools (array) — Required tools (builtin or 'mcp:server/tool'). + - update_available (boolean) (required) — True when the marketplace has a newer template version. + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - version (string) — Skill version from the frontmatter. `, Args: requireExactArg("skill_id"), - Example: ` flashduty safari skill-update --data '{"description":"Diagnose unhealthy Kubernetes workloads.","skill_id":"skl-7f3a9c21b8e0"}'`, + Example: ` flashduty safari skill-update --data '{"description":"Updated triage runbook.","skill_id":"skill_8s7Hn2kLpQ3xYbVc4Wd2m"}'`, 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 { @@ -446,9 +398,9 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - 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(&fDescription, "description", "", "New description. (≤1024 chars)") + cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Target skill ID. (required)") + cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged.") 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 } @@ -460,34 +412,34 @@ func genSkillsWriteUploadCmd() *cobra.Command { Short: "Upload skill", Long: `Upload skill. -Upload a skill zip package and register it as a new account or team skill. +Upload a skill archive (.skill/.zip/.tar.gz/.tgz) to create or replace a 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. + - account_id (integer) (required) — Owning account ID. + - author (string) — Skill author. + - can_edit (boolean) (required) — Whether the caller may edit this skill. + - checksum (string) — SHA-256 checksum of the skill zip. + - content (string) — Full SKILL.md content. Omitted in list responses. + - created (boolean) — Set only on install-from-session responses: true = fresh install, false = in-place update. + - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. + - created_by (integer) (required) — Member ID that created the skill. + - description (string) (required) — Human-readable description from the SKILL.md frontmatter. + - is_modified (boolean) (required) — True when a marketplace-sourced skill was edited locally (auto-update skips it). + - license (string) — Skill license. + - s3_key (string) — Object-storage key of the skill zip. + - skill_id (string) (required) — Unique skill ID (prefix 'skill_'). + - skill_name (string) (required) — Skill name, unique within the account. + - source_template_name (string) — Marketplace template this skill was installed from; empty for user-authored. + - source_template_version (string) — Template version at install time. + - status (string) (required) — Skill status. [enabled, disabled] + - tags (array) — Tags parsed from the frontmatter. + - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team. + - tools (array) — Required tools (builtin or 'mcp:server/tool'). + - update_available (boolean) (required) — True when the marketplace has a newer template version. + - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds. + - version (string) — Skill version from the frontmatter. `, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -512,7 +464,6 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le func registerGeneratedSkills(root *cobra.Command) { gSafari := genGroup(root, "safari", "AI SRE API") - genAddLeaf(gSafari, genSkillsReadDownloadCmd()) genAddLeaf(gSafari, genSkillsReadEnableCmd()) genAddLeaf(gSafari, genSkillsReadGetCmd()) genAddLeaf(gSafari, genSkillsReadListCmd()) diff --git a/skills/flashduty/reference/channel.md b/skills/flashduty/reference/channel.md index 716bc84..6b6f5b1 100644 --- a/skills/flashduty/reference/channel.md +++ b/skills/flashduty/reference/channel.md @@ -129,6 +129,11 @@ Update escalation rule - `--template-id` string (required) — Notification template ID (MongoDB ObjectID). - body-only (`--data`): filters (object); layers (array) (required); time_filters (array) +### escalate-webhook-robot-list +List webhook robots in escalation rules +- `--query` string — Search keyword. Fuzzy matches against robot alias or token, case-insensitive. +- `--type` string — Filter by robot type, e.g. 'feishu', 'dingtalk', 'wecom', 'slack', 'teams'. Omit to return all types. + ### info Get channel detail - `` (positional, required) int64 — Channel ID to fetch. From 78b3361bbea28619c48adbbc04442f7fc62d875f Mon Sep 17 00:00:00 2001 From: shuangyu Date: Thu, 25 Jun 2026 10:30:38 +0800 Subject: [PATCH 02/11] feat(skill): add `safari` reference card for AI-SRE Customize self-management (#60) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(skill): add `safari` reference card for AI-SRE Customize self-management The `fduty safari` group (mcp-server-*, skill-*, a2a-agent-*, session-*) had no reference card, so the flashduty skill could not route install/configure intents to it — agents fell back to a tool search (wrong tool) and --help-dance. Seed `reference/safari.md` (skilldoc fence auto-filled from the cobra tree) + a SKILL.md domain-index row. Hot flows cover stdio and remote MCP install; key concepts document transport/scope/auth_mode and the per_user_oauth model: empty oauth_metadata is normal — the runtime auto-discovers + DCRs on first authorization; no OAuth client config is collected up front. `skilldoc check` passes (fence in sync; examples valid). * card(safari): drop skill-download, add session-delete (sync to current safari surface) Merge feat/ai-sre and regenerate the safari command card against the current cli: /safari/skill/download was reclassified jwt (console-only) and dropped from the public surface, while session-delete was added. The GENERATED fence syncs automatically; also update the hand-written intent table (remove the skill-download routing line, add session-delete to the session line). --- skills/flashduty/SKILL.md | 1 + skills/flashduty/reference/safari.md | 231 +++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 skills/flashduty/reference/safari.md diff --git a/skills/flashduty/SKILL.md b/skills/flashduty/SKILL.md index 093b655..e0fdbea 100644 --- a/skills/flashduty/SKILL.md +++ b/skills/flashduty/SKILL.md @@ -71,3 +71,4 @@ Some asks span several commands. For those the skill ships a script that fetches | route / 分派路由 / alert routing 告警路由 / integration routing 集成路由 / routing case 路由用例 | **`reference/route.md`** | | RUM / real user monitoring / 真实用户监控 / frontend 前端 / application 应用 / issue | **`reference/rum.md`** | | status page / 状态页 / public incident 公开事件 / public timeline 公开时间线 / maintenance window 维护窗口 / subscriber 订阅者 | **`reference/status-page.md`** | +| AI-SRE platform / customize / 安装配置 MCP server (connector) 连接器 / install mcp / skill upload 上传技能 / A2A agent / session export 会话导出 | **`reference/safari.md`** | diff --git a/skills/flashduty/reference/safari.md b/skills/flashduty/reference/safari.md new file mode 100644 index 0000000..b94ed7e --- /dev/null +++ b/skills/flashduty/reference/safari.md @@ -0,0 +1,231 @@ +# fduty safari — command card + +Prereq: `SKILL.md` read. This is the **AI-SRE platform self-management** group: install/configure the account's own **MCP servers (connectors)**, **skills**, and **A2A agents**, plus inspect **sessions**. Mutating verbs (`create`, `update`, `delete`, `upload`) change account configuration — confirm before running. `delete` is **irreversible**. + +> Registering an MCP server is THIS group (`fduty safari mcp-server-create`) — **not** a tool search. A tool search only discovers callable tools on servers already connected to you; it can neither register nor configure one. + +## Route here when + +"安装 / 添加 / 配置 MCP / connector / 连接器 / install mcp / add mcp server / 上传 skill / 自定义 skill / skill upload / A2A agent / customize / AI-SRE 平台配置 / session 导出" → **safari**. Key IDs: **`server_id`** (`mcp_…`) from `mcp-server-list`; **`skill_id`** from `skill-list`; **`agent_id`** from `a2a-agent-list`; **`session_id`** (`sess_…`). + +## Intent → verb + +| want | verb | +|---|---| +| list MCP servers / connectors | `mcp-server-list` | +| install / register an MCP server | `mcp-server-create` | +| change an MCP server's config | `mcp-server-update` | +| turn an MCP server on / off | `mcp-server-enable` / `mcp-server-disable` | +| inspect one MCP server (+ live tool probe) | `mcp-server-get` | +| remove an MCP server | `mcp-server-delete` | +| list / upload / update a skill | `skill-list` / `skill-upload` / `skill-update` | +| enable / disable / delete a skill | `skill-enable` / `skill-disable` / `skill-delete` | +| list / create / update an A2A agent | `a2a-agent-list` / `a2a-agent-create` / `a2a-agent-update` | +| enable / disable / delete an A2A agent | `a2a-agent-enable` / `a2a-agent-disable` / `a2a-agent-delete` | +| list / get / export / delete a session | `session-list` / `session-get` / `session-export` / `session-delete` | + +## Hot flow — install an MCP server + +Pass the nested `env` / `headers` objects through `--data` (they have no scalar flags); `args` has a repeatable `--args` flag but is shown via `--data` below for one-line copy-paste. + +```bash +# stdio (local process): command + args + secrets via env +fduty safari mcp-server-create --data '{"server_name":"GitHub Tools","transport":"stdio","description":"Read issues and pull requests from GitHub.","command":"npx","args":["-y","@modelcontextprotocol/server-github"],"env":{"GITHUB_TOKEN":"ghp_xxx"},"team_id":0,"status":"enabled"}' + +# remote (streamable-http) with per-user OAuth — oauth_metadata stays empty (auto-discovered + DCR at runtime) +fduty safari mcp-server-create --data '{"server_name":"Aliyun OpenAPI","transport":"streamable-http","description":"Alibaba Cloud OpenAPI MCP.","url":"https://openapi-mcp.example.com/mcp","auth_mode":"per_user_oauth","team_id":0,"status":"enabled"}' + +# confirm it registered, then inspect its live tool catalogue +fduty safari mcp-server-list --output-format toon +fduty safari mcp-server-get --data '{"server_id":"mcp_xxx"}' +``` + + + +### a2a-agent-create +Create A2A agent +- `--agent-name` string (required) — Agent display name. (≤128 chars) +- `--auth-mode` string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. +- `--auth-type` string — Authentication type for the remote agent. +- `--card-url` string (required) — URL of the remote agent card. +- `--description` string — Agent description. +- `--oauth-metadata` string — JSON OAuth metadata; reserved for per_user_oauth. +- `--secret-schema` string — JSON secret schema; required when auth_mode=per_user_secret. +- `--streaming` bool — Whether the remote agent supports streaming. +- `--team-id` int64 — Team scope: 0 = account-wide; >0 = team. +- body-only (`--data`): auth_config (object) + +### a2a-agent-delete +Delete A2A agent +- `` (positional, required) string — Target agent ID. + +### a2a-agent-disable +Disable A2A agent +- `` (positional, required) string — Target agent ID. + +### a2a-agent-enable +Enable A2A agent +- `` (positional, required) string — Target agent ID. + +### a2a-agent-get +Get A2A agent detail +- `` (positional, required) string — Target agent ID. + +### a2a-agent-list +List A2A agents +- `--include-account` bool — Include account-scoped (team_id=0) rows. Defaults to true. +- `--limit` int64 — Page size. +- `--offset` int64 — Row offset for pagination. +- `--team-ids` intSlice — Filter to these team IDs; empty = the caller's visible set. + +### a2a-agent-update +Update A2A agent +- `` (positional, required) string — Target agent ID. +- `--agent-name` string — New display name. Omit to leave unchanged. (≤128 chars) +- `--auth-mode` string — New auth mode: shared, per_user_secret, or per_user_oauth. +- `--auth-type` string — New auth type. Omit to leave unchanged. +- `--card-url` string — New card URL. Omit to leave unchanged. +- `--description` string — New description. Omit to leave unchanged. +- `--oauth-metadata` string — New JSON OAuth metadata. +- `--secret-schema` string — New JSON secret schema. +- `--streaming` bool — Toggle streaming support. Omit to leave unchanged. +- `--team-id` int64 — Reassign team scope. Omit to leave unchanged. +- body-only (`--data`): auth_config (object) + +### mcp-server-create +Create MCP server +- `--args` stringSlice — Command arguments (stdio transport). +- `--auth-mode` string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. +- `--call-timeout` int64 — Tool-call timeout in seconds. 0 = default (60s). +- `--command` string — Executable command (stdio transport). +- `--connect-timeout` int64 — Connection timeout in seconds. 0 = default (10s). +- `--description` string (required) — Server description. (1-1024 chars) +- `--oauth-metadata` string — JSON OAuth metadata; reserved for per_user_oauth. +- `--secret-schema` string — JSON secret schema; required when auth_mode=per_user_secret. +- `--server-name` string (required) — MCP server name, unique within the account. (1-255 chars) +- `--source-template-name` string — Marketplace template name when created from a connector template. +- `--status` string — Initial status. · enum: enabled | disabled +- `--team-id` int64 — Team scope: 0 = account-wide; >0 = team. +- `--transport` string (required) — Transport protocol. · enum: stdio | sse | streamable-http +- `--url` string — Server URL (sse / streamable-http transport). +- body-only (`--data`): env (object); headers (object) + +### mcp-server-delete +Delete MCP server +- `` (positional, required) string — Target MCP server ID. + +### mcp-server-disable +Disable MCP server +- `` (positional, required) string — Target MCP server ID. + +### mcp-server-enable +Enable MCP server +- `` (positional, required) string — Target MCP server ID. + +### mcp-server-get +Get MCP server detail +- `` (positional, required) string — Target MCP server ID. + +### mcp-server-list +List MCP servers +- `--include-account` bool — Include account-scoped (team_id=0) rows. Defaults to true. +- `--limit` int64 — Page size. +- `--page` int64 — Page number, 1-based. +- `--search-after-ctx` string +- `--team-ids` intSlice — Filter to these team IDs; empty = the caller's visible set. + +### mcp-server-update +Update MCP server +- `--args` stringSlice — Command arguments (stdio transport). +- `--auth-mode` string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. +- `--call-timeout` int64 — Tool-call timeout in seconds. 0 = default (60s). +- `--command` string — Executable command (stdio transport). +- `--connect-timeout` int64 — Connection timeout in seconds. 0 = default (10s). +- `--description` string — New description. (1-1024 chars) +- `--oauth-metadata` string — JSON OAuth metadata; reserved for per_user_oauth. +- `--secret-schema` string — JSON secret schema; required when auth_mode=per_user_secret. +- `` (positional, required) string — Target MCP server ID. +- `--server-name` string — New name. (1-255 chars) +- `--team-id` int64 — Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged. +- `--transport` string — Transport protocol. · enum: stdio | sse | streamable-http +- `--url` string — Server URL (sse / streamable-http transport). +- body-only (`--data`): env (object); headers (object) + +### session-delete +Delete session +- `` (positional, required) string — Target session ID. (≥1 chars) + +### session-export +Stream a session's full event transcript as NDJSON +- `--include-subagents` bool + +### session-get +Get session detail +- `--limit` int64 — Page size for events; takes precedence over 'num_recent_events'. 0 uses the server default (100). (0-1000) +- `--num-recent-events` int64 — Legacy page size: number of most-recent events to return. Superseded by 'limit' when both are set; 0 uses the server default (100). (0-1000) +- `--search-after-ctx` string — Opaque keyset cursor from a previous response; pass it back to fetch the next older page. (≤4096 chars) +- `` (positional, required) string — Target session ID. (≥1 chars) + +### session-list +List sessions +- `--app-name` string (required) — Agent app whose sessions to list. · enum: ask-ai | support | support-website | support-flashcat | ai-sre | template-assistant | swe +- `--asc` bool — Ascending order when true; applies only when 'orderby' is set. +- `--entry-kinds` stringSlice — Restrict to sessions produced by these surfaces; empty returns every kind. · enum: web | im | api | automation +- `--include-subagent-sessions` bool — Include subagent-dispatched sessions in the list. +- `--keyword` string — Filter by session-name keyword. (≤64 chars) +- `--limit` int64 — Page size, 1–100. (1-100) +- `--orderby` string — Sort field. · enum: created_at | updated_at +- `--page` int64 — Page number, 1-based. (min 1) +- `--scope` string — Visibility scope: all (own + member-of-team rows, default), personal, or team. · enum: all | personal | team +- `--search-after-ctx` string +- `--status` string — Archive bucket: active (default) returns un-archived, archived returns archived, all returns both. · enum: active | archived | all +- `--team-ids` intSlice — Optional explicit team filter; intersects with 'scope'. + +### skill-delete +Delete skill +- `` (positional, required) string — Target skill ID. + +### skill-disable +Disable skill +- `` (positional, required) string — Target skill ID. + +### skill-enable +Enable skill +- `` (positional, required) string — Target skill ID. + +### skill-get +Get skill detail +- `` (positional, required) string — Target skill ID. + +### skill-list +List skills +- `--include-account` bool — Include account-scoped (team_id=0) rows. Defaults to true. +- `--limit` int64 — Page size. +- `--page` int64 — Page number, 1-based. +- `--search-after-ctx` string +- `--team-ids` intSlice — Filter to these team IDs; empty = the caller's visible set. + +### skill-update +Update skill +- `--description` string — New description. (≤1024 chars) +- `` (positional, required) string — Target skill ID. +- `--team-id` int64 — Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged. + +### skill-upload +Upload skill + + + +## Key concepts + +- **Transport ⇒ which fields matter.** `stdio` uses `command` + `args` + `env`; `sse` / `streamable-http` use `url` + `headers`. The nested `env` / `headers` objects have no scalar flags — pass them through `--data '{...}'`; typed scalar flags (`--server-name`, `--url`, `--args`, …) override matching `--data` keys. +- **Scope (`team_id`).** `0` = account-wide (every team sees it); `>0` = that team only. Same field on every safari verb. +- **Auth mode (`auth_mode`).** `shared` (default) = one credential for everyone, stored on the server. `per_user_secret` = each user supplies a secret matching `secret_schema` (which must carry a `header_name`). `per_user_oauth` = each user authorizes the server via OAuth. +- **`per_user_oauth` needs no OAuth config up front.** Create it with an **empty `oauth_metadata`** — that is the normal, complete state, not a missing prerequisite. The runtime **auto-discovers the OAuth server and dynamically registers a client (DCR)** the first time a user authorizes; you do **not** collect `authorization_url` / `client_id` / `client_secret` / `scopes`. Only pass `oauth_metadata` as a rare fallback, when the endpoint advertises no discovery document. + +## Gotchas + +- **`mcp-server-create` requires `server_name`, `description`, `transport`.** A `stdio` server also needs `command`; a remote (`sse` / `streamable-http`) server needs `url`. +- **`env` / `headers` go through `--data`** — there are no `--env` / `--header` scalar flags for them (`args` does have a repeatable `--args` flag). +- **Don't reach for a tool search to install or configure a server** — that only finds tools on already-connected servers. Registration and configuration are `mcp-server-create` / `mcp-server-update`. +- **`delete` is irreversible** — prefer `disable` to park a server / skill / agent without destroying it. `list` first to confirm the id. From 7d621fa57b9e91ba07e645053e695d4a709c9698 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Mon, 29 Jun 2026 13:28:00 +0800 Subject: [PATCH 03/11] docs(skills): tighten fduty counting guidance --- skills/flashduty/SKILL.md | 5 +++++ skills/flashduty/reference/alert.md | 2 ++ skills/flashduty/reference/incident.md | 2 ++ skills/flashduty/reference/monit.md | 23 ++++++++++++++++++++--- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/skills/flashduty/SKILL.md b/skills/flashduty/SKILL.md index 093b655..7e7f656 100644 --- a/skills/flashduty/SKILL.md +++ b/skills/flashduty/SKILL.md @@ -15,6 +15,7 @@ hidden: true # internal-only: withheld from skills.sh public discovery (Safari - **Auth.** Set your Flashduty app key once — `export FLASHDUTY_APP_KEY=` — or pass `--app-key ` per call. Then just call the verb. - **No curl for the API.** The CLI is the only supported path to Flashduty — never hand-roll an HTTP call. - **If `fduty: command not found`** (rare — it is normally on PATH at startup): install from the Flashduty CDN into a user-writable dir (no sudo, no hang), then tell the user — don't work around it: `curl -sSL https://static.flashcat.cloud/flashduty-cli/install.sh | FLASHDUTY_INSTALL_DIR="$HOME/.local/bin" INSTALLED_NAME=fduty sh && export PATH="$HOME/.local/bin:$PATH"`. +- **If `jq` is missing** and you genuinely need JSON filtering, it is fine to install it into a user-writable dir the same way (`$HOME/.local/bin`) and continue. But first ask whether you can avoid `jq` entirely by using `--output-format toon`, `--fields`, a returned `total`, or a server-side aggregation verb (`insight`, `rule-counter-*`, etc.). ## Data model — 3 layers @@ -24,6 +25,10 @@ hidden: true # internal-only: withheld from skills.sh public discovery (Safari Append `--output-format toon` to read commands: it drops the per-row repeated keys that JSON emits, so lists cost far fewer tokens. Use `--json` only to pipe into `jq`. Bare output is a human table — don't parse it. +**Count the cheap way.** If a list response already returns `total`, trust that authoritative server count — do not page N times just to count rows. When the question is "how many by status/progress/severity", issue the narrowest server-side filter per bucket and read each response's `total`; for account-wide trends switch to aggregation verbs such as `insight *`, `rule-counter-status`, `rule-counter-node`, or `rule-counter-total`. + +**Shape the payload before you fetch it.** For ID scans, counts, or "find the matching row" tasks, prefer `--fields` projections and compact list verbs over full detail dumps. Huge raw JSON dumps are a last resort, not a default. + **Empty result = authoritative not-found.** A filter returning `[]` means no such entity in scope — report it (optionally the 1–2 closest names) and stop. Do **not** brute-force (no shifted-keyword re-queries, no widening past caps, no full-dump grep). Never infer "feature not enabled" from an empty list, and never fabricate data absent from tool output. **A result you did not fetch is "unknown", never "empty".** You may report a command's result — including "returned empty" or any count/list/finding — **only if that exact command appears in your tool-call history this turn**. If you did not run it, the honest answer is "未查询 / not queried", followed by the command to run. Writing "`incident similar` 返回空" or "无变更" for a command you never executed is fabrication, not a summary. diff --git a/skills/flashduty/reference/alert.md b/skills/flashduty/reference/alert.md index 6c63ec8..ef91058 100644 --- a/skills/flashduty/reference/alert.md +++ b/skills/flashduty/reference/alert.md @@ -130,6 +130,8 @@ View alert timeline - **All alert verbs are positional except `list` and the two-ID `merge` flag.** Every verb with `` in its `use` form takes that ID as the first bare argument — do NOT pass `--alert-id`. The single exception: `merge` takes the first alert ID positionally AND requires `--incident-id` as a flag (two different IDs, different roles). - **`alert get` vs `alert info`, `alert events` vs `alert-event list`:** both pairs exist; prefer `get`/`events` (shorter, no extra flag); `info`/`event-list` accept `--alert-id` as a flag override for scripting. - **No server-side title filter on `list`.** To search by title, use `--json` and pipe to `jq`: `fduty alert list --json | jq '.[] | select(.title | test("disk";"i"))'` +- **If `list` returns a `total`, use it.** Do not paginate page 1/2/3... just to count alerts. Ask the narrowest question (`--active`, `--recovered`, `--severity`, `--channel`, `--since`) and read the server-reported total for that bucket. +- **Use `--fields` when hunting IDs, not full rows.** If the task is "find alert IDs / titles / channels / severities", project only those fields first, then drill into one alert with `get` / `events`. Dumping every field for 100 alerts wastes tokens and hides the one row you need. - **`list` time window cap is 31 days**; `--limit` max is 100. For broader queries use `insight` domain. - **`pipeline-upsert` fully replaces** the existing pipeline — always fetch current config with `pipeline-info` first and include unchanged rules in the new body. - **Empty `list` result is authoritative** — report "no alerts match" and stop; do not widen filters or retry with alternate keywords. diff --git a/skills/flashduty/reference/incident.md b/skills/flashduty/reference/incident.md index 743794b..4063413 100644 --- a/skills/flashduty/reference/incident.md +++ b/skills/flashduty/reference/incident.md @@ -362,6 +362,8 @@ List war rooms - **24-char `incident_id` vs 6-char `num`**: positional-id verbs (`ack`, `close`, `resolve`, `detail`, `alerts`, `timeline`, `merge`, `reassign`, `comment`, `reset`, …) require the full ObjectID. Passing a 6-char num 400s. Use `incident info --num ` to resolve, or `incident list --query ` and read `incident_id`. - **`similar` only works on channel-backed incidents** (those with a real `channel_id`). Manually created incidents with no channel return HTTP 400 "Channel not found" — this is expected, not transient. Fall back to `incident list --query ""` for text search. - **`update` vs `reset`**: `update ` edits title/description/severity/custom fields. `reset ` additionally supports `--impact`, `--root-cause`, `--resolution` (the AI narrative fields). Use `reset` for post-incident write-back. +- **If `list` returns a `total`, use it instead of page-walking.** For "how many incidents are Triggered / Processing / Closed", run one filtered `incident list --progress ...` per bucket and read the returned `total`. Do not fetch page 1/2/3 just to derive counts the server already computed. +- **Use `--fields` to keep list scans compact.** When the goal is to identify matching incidents or collect IDs/numbers/titles, project only the needed columns first, then fetch one target incident with `detail` / `alerts` / `timeline`. - **`--list` window cap**: `--since`/`--until` window must be < 31 days; `--limit` max 100. Empty result is authoritative — do not widen filters or retry. - **`merge` is irreversible**: source incidents are absorbed into target permanently. Always list and confirm both IDs before running. - **`remove --force`** bypasses the interactive confirmation prompt — never pass `--force` unless the user has explicitly said so. diff --git a/skills/flashduty/reference/monit.md b/skills/flashduty/reference/monit.md index b18b785..7d59779 100644 --- a/skills/flashduty/reference/monit.md +++ b/skills/flashduty/reference/monit.md @@ -68,6 +68,19 @@ fduty monit tools-invoke --target-locator --output-format toon EOF ``` +## Hot flow — inventory all monitor alert rules without guessing folders + +```bash +# 1. get the distribution first; this is the cheap way to answer "how many rules are where / in what status" +fduty monit rule-counter-status --output-format toon + +# 2. list rules only for the concrete folder you care about +fduty monit rule-list-basic --folder-id --output-format toon + +# 3. export the exact rules you selected +fduty monit rule-export --ids ,, --output-format toon +``` + ### datasource-create @@ -327,6 +340,9 @@ Invoke target tools - **`query-rows` has no time flags.** There is no `--time-start` / `--time-end` / `--operation`. Embed all time range and bucketing inside `--expr`. Passing those flags is a silent no-op or error. - **`query-diagnose` time window via `--data`**, not flags. Pass `{"time_range":{"start":,"end":},...}`. Window wider than 6 hours is rejected server-side. Omitting `time_range` defaults to the last 15 minutes. - **`rule_configs` and nested arrays require `--data`.** The queries, thresholds, enabled_times, and labels objects cannot be expressed as flat flags — pass them as inline JSON via `--data '{"rule_configs":{...}}'`. Typed scalar flags (`--name`, `--enabled`, `--cron-pattern`, `--ds-type`) override matching `--data` keys. +- **`folder-id 0` is not a universal "all rules" sentinel.** If the API says "Folder not found", believe it. For global inventory use `rule-counter-status` / `rule-counter-node` first, then run `rule-list-basic` against real folder IDs only. +- **"全量规则 / full rules" means exported monitor alert-rule definitions.** The concrete verb is `rule-export --ids ...`, usually after `rule-list-basic` selected the IDs. It does not mean dumping incidents or alerts. +- **For rule counts, prefer the counter verbs over list pagination.** `rule-counter-status`, `rule-counter-node`, and `rule-counter-total` are the authoritative aggregation surfaces; do not infer counts by walking `rule-list-basic` pages. - **`tools-catalog` / `tools-invoke` `--target-locator` is required and not guessable.** If the user has not provided a host or IP, ask — do not invent one. Tool names in `invoke` must come from the `tools-catalog` response — never hallucinate them. - **`rule-delete-batch` and `datasource-delete` are irreversible.** Confirm IDs with `rule-list-basic` / `datasource-info` first. - **`rule-audit-detail --id` takes the audit record ID**, not the rule ID. Get audit record IDs from `rule-audits --id ` first; passing the rule ID returns HTTP 400. @@ -334,9 +350,10 @@ Invoke target tools ## Worked example — inspect a firing rule then batch-disable it ```bash -# 1. find triggered rules in folder 0 (all accessible) -fduty monit rule-list-basic --folder-id 0 --output-format toon -# look at triggered=true rows; note their ids +# 1. inspect top-level distribution, then choose a real folder id +fduty monit rule-counter-status --output-format toon +fduty monit rule-list-basic --folder-id --output-format toon +# look at the rows you actually care about; note their ids # 2. get full config of one rule fduty monit rule-info --id --output-format toon From fd1019bde27fee188c021331f8ac2082903ad430 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Mon, 29 Jun 2026 17:09:46 +0800 Subject: [PATCH 04/11] feat: use A2A instructions field --- go.mod | 2 +- go.sum | 4 ++-- internal/cli/zz_generated_a2a_agents.go | 28 +++++++++++----------- internal/cli/zz_generated_response_help.go | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 772adb8..e9a0134 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.4-0.20260624143100-02193386c979 + github.com/flashcatcloud/go-flashduty v0.5.4-0.20260629090534-8f1a398d02df github.com/mattn/go-runewidth v0.0.24 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 1a8660f..462a38b 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.4-0.20260624143100-02193386c979 h1:Pha9D3p8ofiMSGRv1AeEafBJ3JspLRFR0zVQFEXZo44= -github.com/flashcatcloud/go-flashduty v0.5.4-0.20260624143100-02193386c979/go.mod h1:aA0RtZEs0AYOwwdNKdtVeD8YMOdnmVY1zAlVD+9Ovx8= +github.com/flashcatcloud/go-flashduty v0.5.4-0.20260629090534-8f1a398d02df h1:1/4t9DDRjjL+N8o90ioDBJ4CvU7ox/1rrAl/OxF8LhQ= +github.com/flashcatcloud/go-flashduty v0.5.4-0.20260629090534-8f1a398d02df/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.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= diff --git a/internal/cli/zz_generated_a2a_agents.go b/internal/cli/zz_generated_a2a_agents.go index 7ec5620..85f150d 100644 --- a/internal/cli/zz_generated_a2a_agents.go +++ b/internal/cli/zz_generated_a2a_agents.go @@ -37,7 +37,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - card_url (string) (required) — URL of the remote agent card. - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. - created_by (integer) (required) — Member ID that created the agent. - - description (string) (required) — Agent description. + - instructions (string) (required) — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). - status (string) (required) — Agent status. [enabled, disabled] @@ -115,7 +115,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - card_url (string) (required) — URL of the remote agent card. - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds. - created_by (integer) (required) — Member ID that created the agent. - - description (string) (required) — Agent description. + - instructions (string) (required) — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode). - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode). - status (string) (required) — Agent status. [enabled, disabled] @@ -172,7 +172,7 @@ func genA2aAgentsWriteCreateCmd() *cobra.Command { var fAuthMode string var fAuthType string var fCardURL string - var fDescription string + var fInstructions string var fOauthMetadata string var fSecretSchema string var fStreaming bool @@ -191,7 +191,7 @@ Request fields: --auth-mode string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. --auth-type string — Authentication type for the remote agent. --card-url string (required) — URL of the remote agent card. - --description string — Agent description. + --instructions string — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank. --oauth-metadata string — JSON OAuth metadata; reserved for per_user_oauth. --secret-schema string — JSON secret schema; required when auth_mode=per_user_secret. --streaming bool — Whether the remote agent supports streaming. @@ -201,7 +201,7 @@ Request fields: Response fields ('data' envelope is unwrapped — these fields are at the top level): - agent_id (string) (required) — ID of the newly created agent. `, - Example: ` flashduty safari a2a-agent-create --data '{"agent_name":"deploy-bot","auth_type":"bearer","card_url":"https://agents.example.com/deploy-bot/card","streaming":true,"team_id":0}'`, + Example: ` flashduty safari a2a-agent-create --data '{"agent_name":"deploy-bot","auth_type":"bearer","card_url":"https://agents.example.com/deploy-bot/card","instructions":"Use when deployment pipelines need inspection or rollback advice.","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) error { @@ -217,8 +217,8 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("card-url") { body["card_url"] = fCardURL } - if cmd.Flags().Changed("description") { - body["description"] = fDescription + if cmd.Flags().Changed("instructions") { + body["instructions"] = fInstructions } if cmd.Flags().Changed("oauth-metadata") { body["oauth_metadata"] = fOauthMetadata @@ -253,7 +253,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "Authentication mode: shared (default), per_user_secret, or per_user_oauth.") cmd.Flags().StringVar(&fAuthType, "auth-type", "", "Authentication type for the remote agent.") cmd.Flags().StringVar(&fCardURL, "card-url", "", "URL of the remote agent card. (required)") - cmd.Flags().StringVar(&fDescription, "description", "", "Agent description.") + cmd.Flags().StringVar(&fInstructions, "instructions", "", "Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank.") cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "JSON OAuth metadata; reserved for per_user_oauth.") cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON secret schema; required when auth_mode=per_user_secret.") cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Whether the remote agent supports streaming.") @@ -413,7 +413,7 @@ func genA2aAgentsWriteUpdateCmd() *cobra.Command { var fAuthMode string var fAuthType string var fCardURL string - var fDescription string + var fInstructions string var fOauthMetadata string var fSecretSchema string var fStreaming bool @@ -433,7 +433,7 @@ Request fields: --auth-mode string — New auth mode: shared, per_user_secret, or per_user_oauth. --auth-type string — New auth type. Omit to leave unchanged. --card-url string — New card URL. Omit to leave unchanged. - --description string — New description. Omit to leave unchanged. + --instructions string — New invocation instructions. Omit to leave unchanged; when supplied, must be nonblank. --oauth-metadata string — New JSON OAuth metadata. --secret-schema string — New JSON secret schema. --streaming bool — Toggle streaming support. Omit to leave unchanged. @@ -441,7 +441,7 @@ Request fields: auth_config (object, via --data) — Replace the auth config. Omit to leave unchanged. `, Args: requireExactArg("agent_id"), - Example: ` flashduty safari a2a-agent-update --data '{"agent_id":"a2a_6mWqZ2pK9nLcR3tY8uVb4D","description":"Inspects deployment pipelines and proposes rollbacks."}'`, + Example: ` flashduty safari a2a-agent-update --data '{"agent_id":"a2a_6mWqZ2pK9nLcR3tY8uVb4D","instructions":"Inspect deployment pipelines and propose rollback steps."}'`, 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 { @@ -463,8 +463,8 @@ Request fields: if cmd.Flags().Changed("card-url") { body["card_url"] = fCardURL } - if cmd.Flags().Changed("description") { - body["description"] = fDescription + if cmd.Flags().Changed("instructions") { + body["instructions"] = fInstructions } if cmd.Flags().Changed("oauth-metadata") { body["oauth_metadata"] = fOauthMetadata @@ -500,7 +500,7 @@ Request fields: cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "New auth mode: shared, per_user_secret, or per_user_oauth.") cmd.Flags().StringVar(&fAuthType, "auth-type", "", "New auth type. Omit to leave unchanged.") cmd.Flags().StringVar(&fCardURL, "card-url", "", "New card URL. Omit to leave unchanged.") - cmd.Flags().StringVar(&fDescription, "description", "", "New description. Omit to leave unchanged.") + cmd.Flags().StringVar(&fInstructions, "instructions", "", "New invocation instructions. Omit to leave unchanged; when supplied, must be nonblank.") cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "New JSON OAuth metadata.") cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "New JSON secret schema.") cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Toggle streaming support. Omit to leave unchanged.") diff --git a/internal/cli/zz_generated_response_help.go b/internal/cli/zz_generated_response_help.go index 00f7d78..b361820 100644 --- a/internal/cli/zz_generated_response_help.go +++ b/internal/cli/zz_generated_response_help.go @@ -6,8 +6,8 @@ package cli // 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 ID.\n - agent_card_name (string) — Agent name resolved from the remote card.\n - agent_card_skills (array) — Skills advertised by the remote card.\n - agent_id (string) (required) — Unique A2A agent ID (prefix `a2a_`).\n - agent_name (string) (required) — Agent display name.\n - auth_config (object) — Authentication config; secret values are masked.\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - auth_type (string) (required) — Authentication type for reaching the remote agent.\n - can_edit (boolean) (required) — Whether the caller may edit this agent.\n - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds.\n - card_url (string) (required) — URL of the remote agent card.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the agent.\n - description (string) (required) — Agent description.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - status (string) (required) — Agent status. [enabled, disabled]\n - streaming (boolean) (required) — Whether the remote agent supports streaming responses.\n - task_timeout (integer) (required) — Single-task execution timeout in seconds.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\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 ID.\n - agent_card_name (string) — Agent name resolved from the remote card.\n - agent_card_skills (array) — Skills advertised by the remote card.\n - agent_id (string) (required) — Unique A2A agent ID (prefix `a2a_`).\n - agent_name (string) (required) — Agent display name.\n - auth_config (object) — Authentication config; secret values are masked.\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - auth_type (string) (required) — Authentication type for reaching the remote agent.\n - can_edit (boolean) (required) — Whether the caller may edit this agent.\n - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds.\n - card_url (string) (required) — URL of the remote agent card.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the agent.\n - description (string) (required) — Agent description.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - status (string) (required) — Agent status. [enabled, disabled]\n - streaming (boolean) (required) — Whether the remote agent supports streaming responses.\n - task_timeout (integer) (required) — Single-task execution timeout in seconds.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n", + "A2aAgents.ReadGet": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - account_id (integer) (required) — Owning account ID.\n - agent_card_name (string) — Agent name resolved from the remote card.\n - agent_card_skills (array) — Skills advertised by the remote card.\n - agent_id (string) (required) — Unique A2A agent ID (prefix `a2a_`).\n - agent_name (string) (required) — Agent display name.\n - auth_config (object) — Authentication config; secret values are masked.\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - auth_type (string) (required) — Authentication type for reaching the remote agent.\n - can_edit (boolean) (required) — Whether the caller may edit this agent.\n - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds.\n - card_url (string) (required) — URL of the remote agent card.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the agent.\n - instructions (string) (required) — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - status (string) (required) — Agent status. [enabled, disabled]\n - streaming (boolean) (required) — Whether the remote agent supports streaming responses.\n - task_timeout (integer) (required) — Single-task execution timeout in seconds.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\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 ID.\n - agent_card_name (string) — Agent name resolved from the remote card.\n - agent_card_skills (array) — Skills advertised by the remote card.\n - agent_id (string) (required) — Unique A2A agent ID (prefix `a2a_`).\n - agent_name (string) (required) — Agent display name.\n - auth_config (object) — Authentication config; secret values are masked.\n - auth_mode (string) — Authentication mode. [shared, per_user_secret, per_user_oauth]\n - auth_type (string) (required) — Authentication type for reaching the remote agent.\n - can_edit (boolean) (required) — Whether the caller may edit this agent.\n - card_resolve_timeout (integer) (required) — Card-resolution timeout in seconds.\n - card_url (string) (required) — URL of the remote agent card.\n - created_at (integer) (required) — Creation time. Unix timestamp in milliseconds.\n - created_by (integer) (required) — Member ID that created the agent.\n - instructions (string) (required) — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent.\n - oauth_metadata (string) — JSON-encoded OAuth metadata (per_user_oauth mode).\n - secret_schema (string) — JSON-encoded secret schema (per_user_secret mode).\n - status (string) (required) — Agent status. [enabled, disabled]\n - streaming (boolean) (required) — Whether the remote agent supports streaming responses.\n - task_timeout (integer) (required) — Single-task execution timeout in seconds.\n - team_id (integer) (required) — Team scope: 0 = account-wide; >0 = the owning team.\n - updated_at (integer) (required) — Last update time. Unix timestamp in milliseconds.\n", "A2aAgents.WriteCreate": "Response fields (`data` envelope is unwrapped — these fields are at the top level):\n - agent_id (string) (required) — ID of the newly 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", From 58c726ab57cd5c8be4feee1e68c7354c37c8f233 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Mon, 29 Jun 2026 17:14:17 +0800 Subject: [PATCH 05/11] docs: refresh Safari skill card --- skills/flashduty/reference/safari.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/flashduty/reference/safari.md b/skills/flashduty/reference/safari.md index b94ed7e..f8e7dfb 100644 --- a/skills/flashduty/reference/safari.md +++ b/skills/flashduty/reference/safari.md @@ -48,7 +48,7 @@ Create A2A agent - `--auth-mode` string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. - `--auth-type` string — Authentication type for the remote agent. - `--card-url` string (required) — URL of the remote agent card. -- `--description` string — Agent description. +- `--instructions` string — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank. - `--oauth-metadata` string — JSON OAuth metadata; reserved for per_user_oauth. - `--secret-schema` string — JSON secret schema; required when auth_mode=per_user_secret. - `--streaming` bool — Whether the remote agent supports streaming. @@ -85,7 +85,7 @@ Update A2A agent - `--auth-mode` string — New auth mode: shared, per_user_secret, or per_user_oauth. - `--auth-type` string — New auth type. Omit to leave unchanged. - `--card-url` string — New card URL. Omit to leave unchanged. -- `--description` string — New description. Omit to leave unchanged. +- `--instructions` string — New invocation instructions. Omit to leave unchanged; when supplied, must be nonblank. - `--oauth-metadata` string — New JSON OAuth metadata. - `--secret-schema` string — New JSON secret schema. - `--streaming` bool — Toggle streaming support. Omit to leave unchanged. From 286ddf598e81f8b4b04a09b986600dc03e80d6fa Mon Sep 17 00:00:00 2001 From: ysyneu Date: Mon, 29 Jun 2026 17:21:30 +0800 Subject: [PATCH 06/11] fix: require A2A instructions flag --- go.mod | 2 +- go.sum | 4 ++-- internal/cli/zz_generated_a2a_agents.go | 4 ++-- skills/flashduty/reference/safari.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e9a0134..8f79eb8 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.4-0.20260629090534-8f1a398d02df + github.com/flashcatcloud/go-flashduty v0.5.4-0.20260629091912-04a0bfaf665d github.com/mattn/go-runewidth v0.0.24 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 462a38b..866fa1b 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.4-0.20260629090534-8f1a398d02df h1:1/4t9DDRjjL+N8o90ioDBJ4CvU7ox/1rrAl/OxF8LhQ= -github.com/flashcatcloud/go-flashduty v0.5.4-0.20260629090534-8f1a398d02df/go.mod h1:aA0RtZEs0AYOwwdNKdtVeD8YMOdnmVY1zAlVD+9Ovx8= +github.com/flashcatcloud/go-flashduty v0.5.4-0.20260629091912-04a0bfaf665d h1:jWzPzsWn46dCWC/PL5nncOTL1Zid4UMynbPsbL1mGdU= +github.com/flashcatcloud/go-flashduty v0.5.4-0.20260629091912-04a0bfaf665d/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.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= diff --git a/internal/cli/zz_generated_a2a_agents.go b/internal/cli/zz_generated_a2a_agents.go index 85f150d..8fadf53 100644 --- a/internal/cli/zz_generated_a2a_agents.go +++ b/internal/cli/zz_generated_a2a_agents.go @@ -191,7 +191,7 @@ Request fields: --auth-mode string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. --auth-type string — Authentication type for the remote agent. --card-url string (required) — URL of the remote agent card. - --instructions string — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank. + --instructions string (required) — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank. --oauth-metadata string — JSON OAuth metadata; reserved for per_user_oauth. --secret-schema string — JSON secret schema; required when auth_mode=per_user_secret. --streaming bool — Whether the remote agent supports streaming. @@ -253,7 +253,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fAuthMode, "auth-mode", "", "Authentication mode: shared (default), per_user_secret, or per_user_oauth.") cmd.Flags().StringVar(&fAuthType, "auth-type", "", "Authentication type for the remote agent.") cmd.Flags().StringVar(&fCardURL, "card-url", "", "URL of the remote agent card. (required)") - cmd.Flags().StringVar(&fInstructions, "instructions", "", "Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank.") + cmd.Flags().StringVar(&fInstructions, "instructions", "", "Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank. (required)") cmd.Flags().StringVar(&fOauthMetadata, "oauth-metadata", "", "JSON OAuth metadata; reserved for per_user_oauth.") cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON secret schema; required when auth_mode=per_user_secret.") cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Whether the remote agent supports streaming.") diff --git a/skills/flashduty/reference/safari.md b/skills/flashduty/reference/safari.md index f8e7dfb..054d3b7 100644 --- a/skills/flashduty/reference/safari.md +++ b/skills/flashduty/reference/safari.md @@ -48,7 +48,7 @@ Create A2A agent - `--auth-mode` string — Authentication mode: shared (default), per_user_secret, or per_user_oauth. - `--auth-type` string — Authentication type for the remote agent. - `--card-url` string (required) — URL of the remote agent card. -- `--instructions` string — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank. +- `--instructions` string (required) — Invocation instructions included in AI SRE's system prompt to decide when to call this A2A agent. Must be nonblank. - `--oauth-metadata` string — JSON OAuth metadata; reserved for per_user_oauth. - `--secret-schema` string — JSON secret schema; required when auth_mode=per_user_secret. - `--streaming` bool — Whether the remote agent supports streaming. From 2cd40211c2b1a9af4426e42014537a1472199f9b Mon Sep 17 00:00:00 2001 From: ysyneu Date: Tue, 21 Jul 2026 21:52:54 -0700 Subject: [PATCH 07/11] fix(cli): compact incident audit outputs --- internal/cli/alert_event.go | 21 ++- internal/cli/fieldproject_test.go | 155 ++++++++++++++++++ internal/cli/incident.go | 35 +++- internal/cli/incident_summary_script_test.go | 45 +++++ .../incident_projection_guidance_test.go | 39 +++++ skills/flashduty/reference/alert.md | 2 + skills/flashduty/reference/incident.md | 22 +-- skills/flashduty/scripts/incident-summary.sh | 14 +- 8 files changed, 316 insertions(+), 17 deletions(-) create mode 100644 internal/cli/incident_summary_script_test.go create mode 100644 internal/skilldoc/incident_projection_guidance_test.go diff --git a/internal/cli/alert_event.go b/internal/cli/alert_event.go index b8b0283..ee5e376 100644 --- a/internal/cli/alert_event.go +++ b/internal/cli/alert_event.go @@ -21,15 +21,19 @@ func newAlertEventCmd() *cobra.Command { } func newAlertEventListCmd() *cobra.Command { - var severity, channel, integrationType, since, until string + var severity, channel, integrationType, since, until, fields string var limit, page int 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"), + Long: curatedLong("List alert events across all alerts within a time window, optionally filtered by severity, channel, or integration type. In json/toon mode, output defaults to compact event fields; use --fields to choose a different projection.", "Alerts", "EventReadList"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { + if ctx.Structured() && cmd.Flags().Changed("fields") && len(parseStringSlice(fields)) == 0 { + return fmt.Errorf("--fields must name at least one field") + } + startTime, err := timeutil.Parse(since) if err != nil { return fmt.Errorf("invalid --since: %w", err) @@ -77,6 +81,18 @@ func newAlertEventListCmd() *cobra.Command { {Header: "TITLE", MaxWidth: 50, Field: func(v any) string { return v.(flashduty.AlertEventItem).Title }}, } + if ctx.Structured() { + fieldNames := []string{"event_id", "alert_id", "event_severity", "event_status", "event_time", "title"} + if fields != "" { + fieldNames = parseStringSlice(fields) + } + proj, err := projectFields(result.Items, fieldNames) + if err != nil { + return err + } + return ctx.PrintList(proj, nil, len(result.Items), page, int(result.Total)) + } + return ctx.PrintList(result.Items, cols, len(result.Items), page, int(result.Total)) }) }, @@ -90,6 +106,7 @@ func newAlertEventListCmd() *cobra.Command { cmd.Flags().StringVar(&until, "until", "now", "End time") cmd.Flags().IntVar(&limit, "limit", 20, "Max results") cmd.Flags().IntVar(&page, "page", 1, "Page number") + cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. event_id,alert_id,event_severity,event_status,event_time,title); ignored in table mode. Defaults to these compact event fields.") return cmd } diff --git a/internal/cli/fieldproject_test.go b/internal/cli/fieldproject_test.go index 215fdc0..100d95c 100644 --- a/internal/cli/fieldproject_test.go +++ b/internal/cli/fieldproject_test.go @@ -1,9 +1,12 @@ package cli import ( + "bytes" "encoding/json" "strings" "testing" + + "github.com/spf13/cobra" ) // incidentRow / alertRow are multi-field stub payloads with the nested blobs @@ -235,3 +238,155 @@ func TestFieldsUnknownFieldErrors(t *testing.T) { }) } } + +func TestIncidentSimilarStructuredProjection(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + items := make([]any, 20) + for i := range items { + row := incidentRow() + row["incident_id"] = "similar-" + string(rune('a'+i)) + row["close_time"] = 1712000060 + row["ack_time"] = 1712000030 + row["alert_cnt"] = 3 + row["root_cause"] = "disk exhaustion" + row["score"] = 0.9 + row["description"] = strings.Repeat("large description ", 100) + row["images"] = []map[string]any{{"src": strings.Repeat("https://example.test/image/", 100)}} + items[i] = row + } + stub.data = map[string]any{"items": items, "total": len(items)} + + out, err := execCommand("incident", "similar", "inc-1", "--limit", "20", "--output-format", "json") + if err != nil { + t.Fatalf("execCommand: %v", err) + } + if len(out) >= 16*1024 { + t.Fatalf("compact similar output is %d bytes, want <16 KiB", len(out)) + } + + var rows []map[string]json.RawMessage + if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &rows); err != nil { + t.Fatalf("parse compact similar json: %v\n%s", err, out) + } + want := []string{"incident_id", "title", "incident_severity", "progress", "start_time", "close_time", "ack_time", "alert_cnt", "root_cause", "score"} + if len(rows) != len(items) { + t.Fatalf("got %d rows, want %d", len(rows), len(items)) + } + for _, row := range rows { + if len(row) != len(want) { + t.Fatalf("got keys %v, want exactly %v", row, want) + } + for _, field := range want { + if _, ok := row[field]; !ok { + t.Errorf("projected row missing %q", field) + } + } + if _, ok := row["description"]; ok { + t.Errorf("projected row includes description: %v", row) + } + } + +} + +func TestIncidentDetailFieldsProjection(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + row := incidentRow() + row["description"] = strings.Repeat("large description ", 500) + row["images"] = []map[string]any{{"src": strings.Repeat("https://example.test/image/", 100)}} + stub.data = row + + out, err := execCommand("incident", "detail", "inc-1", "--fields", "incident_id,title,root_cause", "--output-format", "json") + if err != nil { + t.Fatalf("execCommand: %v", err) + } + if len(out) >= 8*1024 { + t.Fatalf("projected detail output is %d bytes, want <8 KiB", len(out)) + } + var detail map[string]json.RawMessage + if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &detail); err != nil { + t.Fatalf("parse projected detail json: %v\n%s", err, out) + } + if len(detail) != 3 || detail["incident_id"] == nil || detail["title"] == nil || detail["root_cause"] == nil { + t.Fatalf("projected detail = %v, want exactly incident_id,title,root_cause", detail) + } + if _, ok := detail["description"]; ok { + t.Errorf("projected detail includes description: %v", detail) + } + +} + +func TestAlertEventListStructuredProjection(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + items := make([]any, 30) + for i := range items { + items[i] = map[string]any{ + "event_id": "event-" + string(rune('a'+i)), + "alert_id": "alert-1", + "event_severity": "Warning", + "event_status": "Triggered", + "event_time": 1712000000, + "title": "CPU high", + "description": strings.Repeat("large description ", 100), + "labels": map[string]any{"host": "web-01"}, + "images": []map[string]any{{"src": strings.Repeat("https://example.test/image/", 100)}}, + } + } + stub.data = map[string]any{"items": items, "total": len(items)} + + out, err := execCommand("alert-event", "list", "--limit", "30", "--output-format", "json") + if err != nil { + t.Fatalf("execCommand: %v", err) + } + if len(out) >= 16*1024 { + t.Fatalf("compact alert-event output is %d bytes, want <16 KiB", len(out)) + } + var rows []map[string]json.RawMessage + if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &rows); err != nil { + t.Fatalf("parse compact alert-event json: %v\n%s", err, out) + } + want := []string{"event_id", "alert_id", "event_severity", "event_status", "event_time", "title"} + if len(rows) != len(items) { + t.Fatalf("got %d rows, want %d", len(rows), len(items)) + } + for _, row := range rows { + if len(row) != len(want) { + t.Fatalf("got keys %v, want exactly %v", row, want) + } + for _, field := range want { + if _, ok := row[field]; !ok { + t.Errorf("projected row missing %q", field) + } + } + } + +} + +func TestStructuredFieldsEmptyErrors(t *testing.T) { + cases := []struct { + name string + cmd func() *cobra.Command + args []string + }{ + {"incident similar", newIncidentSimilarCmd, []string{"inc-1", "--fields", ""}}, + {"incident detail", newIncidentDetailCmd, []string{"inc-1", "--fields", ""}}, + {"alert-event list", newAlertEventListCmd, []string{"--fields", ""}}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + saveAndResetGlobals(t) + newGFStub(t) + flagOutputFormat = "json" + cmd := tc.cmd() + cmd.SetOut(new(bytes.Buffer)) + cmd.SetErr(cmd.OutOrStderr()) + cmd.SetArgs(tc.args) + err := cmd.Execute() + if err == nil || !strings.Contains(err.Error(), "--fields") { + t.Fatalf("empty --fields error = %v, want --fields validation", err) + } + }) + } +} diff --git a/internal/cli/incident.go b/internal/cli/incident.go index 948aa8a..0ff55fb 100644 --- a/internal/cli/incident.go +++ b/internal/cli/incident.go @@ -564,6 +564,7 @@ func newIncidentAlertsCmd() *cobra.Command { } func newIncidentSimilarCmd() *cobra.Command { + var fields string var limit int cmd := &cobra.Command{ @@ -573,6 +574,10 @@ func newIncidentSimilarCmd() *cobra.Command { Args: requireArgs("incident_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { + if ctx.Structured() && cmd.Flags().Changed("fields") && len(parseStringSlice(fields)) == 0 { + return errors.New("--fields must name at least one field") + } + result, _, err := ctx.Client.Incidents.PastList(cmdContext(ctx.Cmd), &flashduty.ListPastIncidentsRequest{ IncidentID: ctx.Args[0], Limit: flashduty.Int64(int64(limit)), @@ -586,12 +591,25 @@ func newIncidentSimilarCmd() *cobra.Command { return nil } + if ctx.Structured() { + fieldNames := []string{"incident_id", "title", "incident_severity", "progress", "start_time", "close_time", "ack_time", "alert_cnt", "root_cause", "score"} + if fields != "" { + fieldNames = parseStringSlice(fields) + } + proj, err := projectFields(result.Items, fieldNames) + if err != nil { + return err + } + return ctx.Printer.Print(proj, nil) + } + return ctx.Printer.Print(result.Items, pastIncidentColumns()) }) }, } cmd.Flags().IntVar(&limit, "limit", 5, "Max results") + cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. incident_id,title,incident_severity,progress,start_time); ignored in table mode. Defaults to a compact incident summary.") return cmd } @@ -1301,13 +1319,19 @@ func resolveFeedOperators(rc *RunContext, items []flashduty.IncidentFeedItem) ma } func newIncidentDetailCmd() *cobra.Command { - return &cobra.Command{ + var fields string + + cmd := &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 { + if ctx.Structured() && cmd.Flags().Changed("fields") && len(parseStringSlice(fields)) == 0 { + return errors.New("--fields must name at least one field") + } + fullID, candidates, err := resolveIncidentArg(ctx, ctx.Args[0]) if err != nil { return err @@ -1324,6 +1348,13 @@ func newIncidentDetailCmd() *cobra.Command { } if ctx.Structured() { + if fields != "" { + proj, err := projectFields([]flashduty.IncidentInfo{*result}, parseStringSlice(fields)) + if err != nil { + return err + } + return ctx.Printer.Print(proj[0], nil) + } return ctx.Printer.Print(result, nil) } @@ -1332,6 +1363,8 @@ func newIncidentDetailCmd() *cobra.Command { }) }, } + cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. incident_id,title,incident_severity,progress,root_cause); ignored in table mode. Omit for full detail.") + return cmd } func printIncidentFullDetail(w io.Writer, inc *flashduty.IncidentInfo) { diff --git a/internal/cli/incident_summary_script_test.go b/internal/cli/incident_summary_script_test.go new file mode 100644 index 0000000..dbf65df --- /dev/null +++ b/internal/cli/incident_summary_script_test.go @@ -0,0 +1,45 @@ +package cli + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestIncidentSummaryScriptCompactOutput(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Bash fixture is unavailable on Windows") + } + + root, err := filepath.Abs("../..") + if err != nil { + t.Fatalf("resolve repository root: %v", err) + } + script := filepath.Join(root, "skills", "flashduty", "scripts", "incident-summary.sh") + log := filepath.Join(t.TempDir(), "fduty.log") + bin := filepath.Join(t.TempDir(), "fduty") + if err := os.WriteFile(bin, []byte("#!/usr/bin/env bash\nprintf '%s\\n' \"$*\" >> \"$FDUTY_LOG\"\nprintf 'compact result\\n'\n"), 0o755); err != nil { + t.Fatalf("write fake fduty: %v", err) + } + t.Setenv("FDUTY_LOG", log) + t.Setenv("PATH", filepath.Dir(bin)+string(os.PathListSeparator)+os.Getenv("PATH")) + + output, err := exec.Command("bash", script, "inc-1").CombinedOutput() + if err != nil { + t.Fatalf("run incident summary: %v\n%s", err, output) + } + invocations, err := os.ReadFile(log) + if err != nil { + t.Fatalf("read fake fduty log: %v", err) + } + lines := strings.FieldsFunc(strings.TrimSpace(string(invocations)), func(r rune) bool { return r == '\n' }) + if len(lines) != 6 { + t.Fatalf("fduty calls = %d, want 6:\n%s", len(lines), invocations) + } + if strings.Contains(string(invocations), "--output-format toon") { + t.Fatalf("summary forces toon instead of each command's compact default:\n%s", invocations) + } +} diff --git a/internal/skilldoc/incident_projection_guidance_test.go b/internal/skilldoc/incident_projection_guidance_test.go new file mode 100644 index 0000000..fc04b09 --- /dev/null +++ b/internal/skilldoc/incident_projection_guidance_test.go @@ -0,0 +1,39 @@ +package skilldoc + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestIncidentCardAvoidsUnboundedStructuredHotFlows(t *testing.T) { + card, err := os.ReadFile(filepath.Join("..", "..", "skills", "flashduty", "reference", "incident.md")) + if err != nil { + t.Fatal(err) + } + + body := string(card) + for description, command := range map[string]string{ + "triage list": "incident list --severity Critical --progress Triggered --since 4h --fields incident_id,title,incident_severity,progress,start_time,channel_id --output-format toon", + "triage detail": "incident detail --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon", + "summary detail": `incident detail "$ID" --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon`, + } { + if !strings.Contains(body, command) { + t.Errorf("incident %s must project structured output to the fields needed by the workflow", description) + } + } + + for _, command := range []string{ + "incident alerts --output-format toon", + `incident alerts "$ID" --output-format toon`, + `incident timeline "$ID" --output-format toon`, + "incident post-mortem-list --channel-ids --output-format toon", + "change list --since 24h --output-format toon", + "incident timeline --output-format toon", + } { + if strings.Contains(body, command) { + t.Errorf("incident hot flow must use the compact default instead of %q", command) + } + } +} diff --git a/skills/flashduty/reference/alert.md b/skills/flashduty/reference/alert.md index ef91058..93bc424 100644 --- a/skills/flashduty/reference/alert.md +++ b/skills/flashduty/reference/alert.md @@ -34,6 +34,8 @@ fduty alert get --output-format toon fduty alert events --output-format toon # 4. view state transitions (mute/severity changes/operator actions) fduty alert feed --output-format toon +# 5. for a time-window view across alerts, alert-event list is compact by default +fduty alert-event list --channel --since 1h --limit 30 --output-format toon ``` ## Hot flow — merge noisy alerts into an existing incident diff --git a/skills/flashduty/reference/incident.md b/skills/flashduty/reference/incident.md index 4063413..903431e 100644 --- a/skills/flashduty/reference/incident.md +++ b/skills/flashduty/reference/incident.md @@ -42,13 +42,13 @@ Prereq: `SKILL.md` read. Read verbs are free. **Mutating verbs notify responders ```bash # 1. Find unacknowledged critical incidents (last 4h) -fduty incident list --severity Critical --progress Triggered --since 4h --output-format toon +fduty incident list --severity Critical --progress Triggered --since 4h --fields incident_id,title,incident_severity,progress,start_time,channel_id --output-format toon # 2. Get AI summary + full detail (use the 24-char incident_id from step 1) -fduty incident detail --output-format toon +fduty incident detail --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon # 3. See contributing alerts -fduty incident alerts --output-format toon +fduty incident alerts # 4. Check for prior similar incidents (channel-backed only; see Gotchas) fduty incident similar --limit 5 --output-format toon @@ -77,12 +77,12 @@ If you fetch the pieces by hand instead, run **all six** — they are cheap read ```bash ID= # 24-char id from `incident list` -fduty incident detail "$ID" --output-format toon # ① 详情 + AI summary + alert counts + channel_id -fduty incident alerts "$ID" --output-format toon # ② contributing alerts (detail's embedded alerts are empty here) -fduty incident timeline "$ID" --output-format toon # ④ timeline (or `incident feed "$ID"` for the paginated view) -fduty incident similar "$ID" --limit 5 --output-format toon # ⑤ similar past incidents (channel-backed; see Gotchas) -fduty incident post-mortem-list --channel-ids --output-format toon # ⑥ post-mortems for this incident's channel -fduty change list --since 24h --output-format toon # ③ correlated changes — by shared labels + time; see reference/change.md +fduty incident detail "$ID" --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon # ① 详情 + AI summary + alert counts + channel +fduty incident alerts "$ID" # ② contributing alerts (detail's embedded alerts are empty here) +fduty incident timeline "$ID" # ④ timeline (or `incident feed "$ID"` for the paginated view) +fduty incident similar "$ID" --limit 5 --output-format toon # ⑤ similar past incidents (channel-backed; see Gotchas; compact by default) +fduty incident post-mortem-list --channel-ids # ⑥ post-mortems for this incident's channel +fduty change list --since 24h # ③ correlated changes — by shared labels + time; see reference/change.md ``` > **Never report a result you didn't fetch.** Do not write "返回空" / "无" / a count for any aspect whose command is **absent from your tool-call history this turn** — write `未查询 — 可运行 ` instead. "Empty" is a claim only a command you actually ran can make; inventing it is the worst failure mode of a fault summary. @@ -100,7 +100,7 @@ fduty incident reset \ --resolution "Increased memory limit; deployed hot patch" # Review the event timeline -fduty incident timeline --output-format toon +fduty incident timeline ``` @@ -158,6 +158,7 @@ Execute custom action ### detail View full incident detail with AI summary +- `--fields` string ### disable-merge [...] Disable incident merge @@ -266,6 +267,7 @@ Add incident responder ### similar Find similar incidents +- `--fields` string - `--limit` int ### snooze [ ...] diff --git a/skills/flashduty/scripts/incident-summary.sh b/skills/flashduty/scripts/incident-summary.sh index 8288100..f9a52ac 100644 --- a/skills/flashduty/scripts/incident-summary.sh +++ b/skills/flashduty/scripts/incident-summary.sh @@ -7,8 +7,9 @@ # # usage: bash incident-summary.sh # -# To tie post-mortems to this incident specifically, re-run the last section with the -# channel_id from "incident detail": fduty incident post-mortem-list --channel-ids +# Section ⑥ lists recent post-mortems account-wide. To scope them to THIS incident's +# channel, read its channel_id (fduty incident info --incident-id --output-format +# toon | grep '^channel_id:') and re-run: fduty incident post-mortem-list --channel-ids # # Note: errexit (-e) is intentionally NOT set — every section must run even if one # command fails, so the summary stays as complete as possible. Each command's own @@ -21,9 +22,14 @@ if [ -z "$ID" ]; then exit 2 fi -run() { echo "===== fduty $* ====="; fduty "$@" --output-format toon 2>&1; echo; } +# Print each command's DEFAULT renderer (a curated table/summary that projects the +# summary-relevant fields), NOT --output-format toon: toon dumps the full raw objects +# — every empty field plus heavy blobs like a change's labels.steps — which overflowed +# the output cap and forced repeated paging. For these read verbs the lean default IS +# the field projection a fault summary needs (id/severity/status/title/channel/times/…). +run() { echo "===== fduty $* ====="; fduty "$@" 2>&1; echo; } -run incident detail "$ID" # ① 详情 + AI summary + alert counts + channel_id +run incident detail "$ID" # ① 详情 + AI summary + alert counts + channel run incident alerts "$ID" # ② contributing alerts run incident timeline "$ID" # ④ timeline run incident similar "$ID" --limit 5 # ⑤ similar past incidents (channel-backed) From 7af84fc1b3770807cb1c96f98e825613cebf5353 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Tue, 21 Jul 2026 21:52:54 -0700 Subject: [PATCH 08/11] fix(skill): preserve incident comment content --- .../incident_comment_guidance_test.go | 72 +++++++++++++++++++ skills/flashduty/reference/incident.md | 13 +++- 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 internal/skilldoc/incident_comment_guidance_test.go diff --git a/internal/skilldoc/incident_comment_guidance_test.go b/internal/skilldoc/incident_comment_guidance_test.go new file mode 100644 index 0000000..b1d7393 --- /dev/null +++ b/internal/skilldoc/incident_comment_guidance_test.go @@ -0,0 +1,72 @@ +package skilldoc + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestIncidentCardCommentWorkflow(t *testing.T) { + card, err := os.ReadFile(filepath.Join("..", "..", "skills", "flashduty", "reference", "incident.md")) + if err != nil { + t.Fatal(err) + } + + body := string(card) + quotedHeredoc := "COMMENT=$(cat <<'COMMENT_EOF'" + commentCommand := `fduty incident comment "$ID" --comment "$COMMENT"` + timelineCommand := `fduty incident timeline "$ID" --output-format toon` + + previous := -1 + for _, requirement := range []string{quotedHeredoc, commentCommand, timelineCommand} { + position := strings.Index(body, requirement) + if position == -1 { + t.Errorf("incident card is missing %q", requirement) + continue + } + if position <= previous { + t.Errorf("incident card must place %q after the previous workflow step", requirement) + } + previous = position + } + + if !strings.Contains(body, "After every comment write, read back every target and verify the intended comment is present before reporting success.") { + t.Error("incident card must require content-fidelity verification after every comment write") + } + if strings.Contains(body, `fduty incident comment --comment "Root cause identified: DB failover. Fix deploying."`) { + t.Error("incident card must not retain the unsafe inline comment example") + } +} + +func TestIncidentCommentQuotedHeredocPreservesMarkdown(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Bash fixture is unavailable on Windows") + } + + script := `fduty() { + printf '%s' "$5" +} + +ID=64b64ca26f84f00000000000 +COMMENT=$(cat <<'COMMENT_EOF' +## Investigation +Use ` + "`kubectl get pod`" + ` to inspect the restart. +The follow-up is still pending. +COMMENT_EOF +) +fduty incident comment "$ID" --comment "$COMMENT" +` + + output, err := exec.Command("bash", "-c", script).CombinedOutput() + if err != nil { + t.Fatalf("Bash fixture failed: %v\n%s", err, output) + } + + want := "## Investigation\nUse `kubectl get pod` to inspect the restart.\nThe follow-up is still pending." + if got := string(output); got != want { + t.Errorf("comment content changed:\nwant: %q\n got: %q", want, got) + } +} diff --git a/skills/flashduty/reference/incident.md b/skills/flashduty/reference/incident.md index 4063413..ca0f6f4 100644 --- a/skills/flashduty/reference/incident.md +++ b/skills/flashduty/reference/incident.md @@ -56,13 +56,22 @@ fduty incident similar --limit 5 --output-format toon # 5. Acknowledge ownership fduty incident ack -# 6. Post a status comment -fduty incident comment --comment "Root cause identified: DB failover. Fix deploying." +# 6. Post a status comment safely, then read it back +ID= +COMMENT=$(cat <<'COMMENT_EOF' +Root cause identified: DB failover. +Fix deploying. +COMMENT_EOF +) +fduty incident comment "$ID" --comment "$COMMENT" +fduty incident timeline "$ID" --output-format toon # 7. Resolve with root-cause note fduty incident resolve --root-cause "DB primary failover delay" --resolution "Failover completed; latency normal." ``` +After every comment write, read back every target and verify the intended comment is present before reporting success. `Commented on ...` proves acceptance, not content fidelity. + ## Hot flow — full fault analysis (read-only summary) When asked to **summarize / analyze** an incident — 详情 + 关联告警 + 变更 + 时间线 + 相似故障 + 复盘 — `incident detail` does **not** contain the alerts / timeline / similar / post-mortem / change data; each is its own command. **Your first action must be the bundled script** — do not hand-pick one or two commands and write the rest from memory. One call fetches all six aspects: From 3b6f5b56e3424bf27611d2f2fd61473d5158da86 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Tue, 21 Jul 2026 21:56:58 -0700 Subject: [PATCH 09/11] fix(cli): bound incident summary detail --- internal/cli/fieldproject_test.go | 12 +++++++++--- internal/cli/incident_summary_script_test.go | 8 ++++++-- .../skilldoc/incident_projection_guidance_test.go | 4 ++-- skills/flashduty/reference/incident.md | 4 ++-- skills/flashduty/scripts/incident-summary.sh | 14 ++++++-------- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/internal/cli/fieldproject_test.go b/internal/cli/fieldproject_test.go index 100d95c..84277bc 100644 --- a/internal/cli/fieldproject_test.go +++ b/internal/cli/fieldproject_test.go @@ -297,7 +297,8 @@ func TestIncidentDetailFieldsProjection(t *testing.T) { row["images"] = []map[string]any{{"src": strings.Repeat("https://example.test/image/", 100)}} stub.data = row - out, err := execCommand("incident", "detail", "inc-1", "--fields", "incident_id,title,root_cause", "--output-format", "json") + fields := []string{"incident_id", "title", "incident_severity", "progress", "ai_summary", "root_cause", "resolution", "alert_cnt", "start_time", "channel_id"} + out, err := execCommand("incident", "detail", "inc-1", "--fields", strings.Join(fields, ","), "--output-format", "json") if err != nil { t.Fatalf("execCommand: %v", err) } @@ -308,8 +309,13 @@ func TestIncidentDetailFieldsProjection(t *testing.T) { if err := json.Unmarshal([]byte(strings.TrimSpace(out)), &detail); err != nil { t.Fatalf("parse projected detail json: %v\n%s", err, out) } - if len(detail) != 3 || detail["incident_id"] == nil || detail["title"] == nil || detail["root_cause"] == nil { - t.Fatalf("projected detail = %v, want exactly incident_id,title,root_cause", detail) + if len(detail) != len(fields) { + t.Fatalf("projected detail keys = %v, want exactly %v", detail, fields) + } + for _, field := range fields { + if detail[field] == nil { + t.Errorf("projected detail missing %q: %v", field, detail) + } } if _, ok := detail["description"]; ok { t.Errorf("projected detail includes description: %v", detail) diff --git a/internal/cli/incident_summary_script_test.go b/internal/cli/incident_summary_script_test.go index dbf65df..2eeab13 100644 --- a/internal/cli/incident_summary_script_test.go +++ b/internal/cli/incident_summary_script_test.go @@ -39,7 +39,11 @@ func TestIncidentSummaryScriptCompactOutput(t *testing.T) { if len(lines) != 6 { t.Fatalf("fduty calls = %d, want 6:\n%s", len(lines), invocations) } - if strings.Contains(string(invocations), "--output-format toon") { - t.Fatalf("summary forces toon instead of each command's compact default:\n%s", invocations) + wantDetail := "incident detail inc-1 --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time,channel_id --output-format toon" + if lines[0] != wantDetail { + t.Fatalf("detail call = %q, want compact projection %q", lines[0], wantDetail) + } + if strings.Contains(strings.Join(lines[1:], "\n"), "--output-format toon") { + t.Fatalf("non-detail reads force raw toon instead of their compact defaults:\n%s", invocations) } } diff --git a/internal/skilldoc/incident_projection_guidance_test.go b/internal/skilldoc/incident_projection_guidance_test.go index fc04b09..bb2867e 100644 --- a/internal/skilldoc/incident_projection_guidance_test.go +++ b/internal/skilldoc/incident_projection_guidance_test.go @@ -16,8 +16,8 @@ func TestIncidentCardAvoidsUnboundedStructuredHotFlows(t *testing.T) { body := string(card) for description, command := range map[string]string{ "triage list": "incident list --severity Critical --progress Triggered --since 4h --fields incident_id,title,incident_severity,progress,start_time,channel_id --output-format toon", - "triage detail": "incident detail --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon", - "summary detail": `incident detail "$ID" --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon`, + "triage detail": "incident detail --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time,channel_id --output-format toon", + "summary detail": `incident detail "$ID" --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time,channel_id --output-format toon`, } { if !strings.Contains(body, command) { t.Errorf("incident %s must project structured output to the fields needed by the workflow", description) diff --git a/skills/flashduty/reference/incident.md b/skills/flashduty/reference/incident.md index 903431e..d26c7ef 100644 --- a/skills/flashduty/reference/incident.md +++ b/skills/flashduty/reference/incident.md @@ -45,7 +45,7 @@ Prereq: `SKILL.md` read. Read verbs are free. **Mutating verbs notify responders fduty incident list --severity Critical --progress Triggered --since 4h --fields incident_id,title,incident_severity,progress,start_time,channel_id --output-format toon # 2. Get AI summary + full detail (use the 24-char incident_id from step 1) -fduty incident detail --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon +fduty incident detail --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time,channel_id --output-format toon # 3. See contributing alerts fduty incident alerts @@ -77,7 +77,7 @@ If you fetch the pieces by hand instead, run **all six** — they are cheap read ```bash ID= # 24-char id from `incident list` -fduty incident detail "$ID" --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time --output-format toon # ① 详情 + AI summary + alert counts + channel +fduty incident detail "$ID" --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time,channel_id --output-format toon # ① 详情 + AI summary + alert counts + channel fduty incident alerts "$ID" # ② contributing alerts (detail's embedded alerts are empty here) fduty incident timeline "$ID" # ④ timeline (or `incident feed "$ID"` for the paginated view) fduty incident similar "$ID" --limit 5 --output-format toon # ⑤ similar past incidents (channel-backed; see Gotchas; compact by default) diff --git a/skills/flashduty/scripts/incident-summary.sh b/skills/flashduty/scripts/incident-summary.sh index f9a52ac..ebcc937 100644 --- a/skills/flashduty/scripts/incident-summary.sh +++ b/skills/flashduty/scripts/incident-summary.sh @@ -8,8 +8,8 @@ # usage: bash incident-summary.sh # # Section ⑥ lists recent post-mortems account-wide. To scope them to THIS incident's -# channel, read its channel_id (fduty incident info --incident-id --output-format -# toon | grep '^channel_id:') and re-run: fduty incident post-mortem-list --channel-ids +# channel, read channel_id from the projected detail section and re-run: +# fduty incident post-mortem-list --channel-ids # # Note: errexit (-e) is intentionally NOT set — every section must run even if one # command fails, so the summary stays as complete as possible. Each command's own @@ -22,14 +22,12 @@ if [ -z "$ID" ]; then exit 2 fi -# Print each command's DEFAULT renderer (a curated table/summary that projects the -# summary-relevant fields), NOT --output-format toon: toon dumps the full raw objects -# — every empty field plus heavy blobs like a change's labels.steps — which overflowed -# the output cap and forced repeated paging. For these read verbs the lean default IS -# the field projection a fault summary needs (id/severity/status/title/channel/times/…). +# Project detail explicitly because its default table includes unbounded narrative +# fields. The other read verbs use their compact default renderers; raw toon dumps +# every empty field plus heavy blobs like a change's labels.steps. run() { echo "===== fduty $* ====="; fduty "$@" 2>&1; echo; } -run incident detail "$ID" # ① 详情 + AI summary + alert counts + channel +run incident detail "$ID" --fields incident_id,title,incident_severity,progress,ai_summary,root_cause,resolution,alert_cnt,start_time,channel_id --output-format toon # ① 详情 + AI summary + alert counts + channel run incident alerts "$ID" # ② contributing alerts run incident timeline "$ID" # ④ timeline run incident similar "$ID" --limit 5 # ⑤ similar past incidents (channel-backed) From 8373bce9c1f0128785cbefe006c10d4ed8da5df8 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Tue, 21 Jul 2026 22:12:22 -0700 Subject: [PATCH 10/11] fix(skill): harden heredoc delimiter --- internal/skilldoc/incident_comment_guidance_test.go | 12 ++++++++---- skills/flashduty/reference/incident.md | 5 +++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/internal/skilldoc/incident_comment_guidance_test.go b/internal/skilldoc/incident_comment_guidance_test.go index b1d7393..2dc7062 100644 --- a/internal/skilldoc/incident_comment_guidance_test.go +++ b/internal/skilldoc/incident_comment_guidance_test.go @@ -16,7 +16,7 @@ func TestIncidentCardCommentWorkflow(t *testing.T) { } body := string(card) - quotedHeredoc := "COMMENT=$(cat <<'COMMENT_EOF'" + quotedHeredoc := "COMMENT=$(cat <<'FDUTY_COMMENT_7F3A9C2E_EOF'" commentCommand := `fduty incident comment "$ID" --comment "$COMMENT"` timelineCommand := `fduty incident timeline "$ID" --output-format toon` @@ -36,6 +36,9 @@ func TestIncidentCardCommentWorkflow(t *testing.T) { if !strings.Contains(body, "After every comment write, read back every target and verify the intended comment is present before reporting success.") { t.Error("incident card must require content-fidelity verification after every comment write") } + if !strings.Contains(body, "choose a fresh delimiter that is absent as a full line in the intended comment") { + t.Error("incident card must require a collision-free heredoc delimiter") + } if strings.Contains(body, `fduty incident comment --comment "Root cause identified: DB failover. Fix deploying."`) { t.Error("incident card must not retain the unsafe inline comment example") } @@ -51,11 +54,12 @@ func TestIncidentCommentQuotedHeredocPreservesMarkdown(t *testing.T) { } ID=64b64ca26f84f00000000000 -COMMENT=$(cat <<'COMMENT_EOF' +COMMENT=$(cat <<'FDUTY_COMMENT_7F3A9C2E_EOF' ## Investigation Use ` + "`kubectl get pod`" + ` to inspect the restart. -The follow-up is still pending. COMMENT_EOF +The follow-up is still pending. +FDUTY_COMMENT_7F3A9C2E_EOF ) fduty incident comment "$ID" --comment "$COMMENT" ` @@ -65,7 +69,7 @@ fduty incident comment "$ID" --comment "$COMMENT" t.Fatalf("Bash fixture failed: %v\n%s", err, output) } - want := "## Investigation\nUse `kubectl get pod` to inspect the restart.\nThe follow-up is still pending." + want := "## Investigation\nUse `kubectl get pod` to inspect the restart.\nCOMMENT_EOF\nThe follow-up is still pending." if got := string(output); got != want { t.Errorf("comment content changed:\nwant: %q\n got: %q", want, got) } diff --git a/skills/flashduty/reference/incident.md b/skills/flashduty/reference/incident.md index ca0f6f4..8bafdae 100644 --- a/skills/flashduty/reference/incident.md +++ b/skills/flashduty/reference/incident.md @@ -58,10 +58,11 @@ fduty incident ack # 6. Post a status comment safely, then read it back ID= -COMMENT=$(cat <<'COMMENT_EOF' +# Before running, choose a fresh delimiter that is absent as a full line in the intended comment. +COMMENT=$(cat <<'FDUTY_COMMENT_7F3A9C2E_EOF' Root cause identified: DB failover. Fix deploying. -COMMENT_EOF +FDUTY_COMMENT_7F3A9C2E_EOF ) fduty incident comment "$ID" --comment "$COMMENT" fduty incident timeline "$ID" --output-format toon From 97aedf3ae097e4590d13ff4142440e7ebbb7447b Mon Sep 17 00:00:00 2001 From: ysyneu Date: Tue, 21 Jul 2026 22:12:22 -0700 Subject: [PATCH 11/11] fix(cli): cap compact structured output --- internal/cli/alert_event.go | 5 +- internal/cli/fieldproject.go | 89 ++++++++++++++++++++++++++ internal/cli/fieldproject_test.go | 51 ++++++++++++++- internal/cli/incident.go | 10 ++- skills/flashduty/reference/alert.md | 2 + skills/flashduty/reference/incident.md | 2 + 6 files changed, 154 insertions(+), 5 deletions(-) diff --git a/internal/cli/alert_event.go b/internal/cli/alert_event.go index ee5e376..8d05b15 100644 --- a/internal/cli/alert_event.go +++ b/internal/cli/alert_event.go @@ -90,6 +90,9 @@ func newAlertEventListCmd() *cobra.Command { if err != nil { return err } + if err := boundProjectedOutput(proj, compactListOutputLimit); err != nil { + return err + } return ctx.PrintList(proj, nil, len(result.Items), page, int(result.Total)) } @@ -106,7 +109,7 @@ func newAlertEventListCmd() *cobra.Command { cmd.Flags().StringVar(&until, "until", "now", "End time") cmd.Flags().IntVar(&limit, "limit", 20, "Max results") cmd.Flags().IntVar(&page, "page", 1, "Page number") - cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. event_id,alert_id,event_severity,event_status,event_time,title); ignored in table mode. Defaults to these compact event fields.") + cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. event_id,alert_id,event_severity,event_status,event_time,title); ignored in table mode. Defaults to these compact event fields. Long strings are truncated as needed to keep structured output below 16 KiB.") return cmd } diff --git a/internal/cli/fieldproject.go b/internal/cli/fieldproject.go index e421397..7bcb12c 100644 --- a/internal/cli/fieldproject.go +++ b/internal/cli/fieldproject.go @@ -5,6 +5,12 @@ import ( "reflect" "sort" "strings" + "unicode/utf8" +) + +const ( + compactListOutputLimit = 16 * 1024 + compactDetailOutputLimit = 8 * 1024 ) // projectFields reduces each struct element of items to a map containing only @@ -63,6 +69,89 @@ func projectFields(items any, fields []string) ([]map[string]any, error) { return out, nil } +// boundProjectedOutput keeps the new agent-oriented projections below their +// command budget without changing the selected keys. Short values remain byte +// identical; when retained strings alone would overflow the actual JSON/TOON +// encoding, they are shortened fairly and marked with "...". If keys and +// non-string values alone exceed the budget, the command fails with a small +// error instead of emitting an oversized payload. +func boundProjectedOutput(data any, maxBytes int) error { + var rows []map[string]any + switch value := data.(type) { + case map[string]any: + rows = []map[string]any{value} + case []map[string]any: + rows = value + default: + return fmt.Errorf("internal error: unsupported projected output %T", data) + } + + encoded, err := marshalStructured(data) + if err != nil { + return err + } + if len(encoded)+1 < maxBytes { + return nil + } + + stringCount := 0 + for _, row := range rows { + for _, value := range row { + if _, ok := value.(string); ok { + stringCount++ + } + } + } + if stringCount == 0 { + return fmt.Errorf("structured projection exceeds %d-byte limit; request fewer rows or fields", maxBytes) + } + + fieldLimit := maxBytes / stringCount + for { + for _, row := range rows { + for key, value := range row { + if text, ok := value.(string); ok { + row[key] = truncateUTF8Bytes(text, fieldLimit) + } + } + } + + encoded, err = marshalStructured(data) + if err != nil { + return err + } + if len(encoded)+1 < maxBytes { + return nil + } + if fieldLimit == 0 { + return fmt.Errorf("structured projection exceeds %d-byte limit; request fewer rows or fields", maxBytes) + } + fieldLimit /= 2 + } +} + +func truncateUTF8Bytes(value string, maxBytes int) string { + if len(value) <= maxBytes { + return value + } + if maxBytes <= 0 { + return "" + } + if maxBytes <= 3 { + end := maxBytes + for end > 0 && !utf8.ValidString(value[:end]) { + end-- + } + return value[:end] + } + + end := maxBytes - 3 + for end > 0 && !utf8.ValidString(value[:end]) { + end-- + } + return value[:end] + "..." +} + // jsonTagIndex maps each exported field's json tag name (leading component, sans // `,omitempty`) to its index in the struct. Fields tagged `json:"-"`, untagged // fields, and embedded/anonymous fields are skipped — only declared, named, diff --git a/internal/cli/fieldproject_test.go b/internal/cli/fieldproject_test.go index 84277bc..c9de19f 100644 --- a/internal/cli/fieldproject_test.go +++ b/internal/cli/fieldproject_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "strings" "testing" + "unicode/utf8" "github.com/spf13/cobra" ) @@ -28,6 +29,48 @@ func incidentRow() map[string]any { } } +func TestBoundProjectedOutputCapsStructuredFormats(t *testing.T) { + for _, format := range []string{"json", "toon"} { + t.Run(format, func(t *testing.T) { + saveAndResetGlobals(t) + flagOutputFormat = format + rows := []map[string]any{{ + "incident_id": "inc-1", + "title": strings.Repeat("数据库故障", 2000), + }} + + if err := boundProjectedOutput(rows, 512); err != nil { + t.Fatalf("bound projected output: %v", err) + } + encoded, err := marshalStructured(rows) + if err != nil { + t.Fatalf("marshal bounded output: %v", err) + } + if len(encoded)+1 >= 512 { + t.Fatalf("bounded %s output is %d bytes, want <512", format, len(encoded)+1) + } + title := rows[0]["title"].(string) + if !utf8.ValidString(title) || !strings.HasSuffix(title, "...") { + t.Fatalf("truncated title = %q, want valid UTF-8 with marker", title) + } + }) + } +} + +func TestBoundProjectedOutputRejectsIrreducibleMetadata(t *testing.T) { + saveAndResetGlobals(t) + flagOutputFormat = "json" + rows := make([]map[string]any, 200) + for i := range rows { + rows[i] = map[string]any{"count": i} + } + + err := boundProjectedOutput(rows, 512) + if err == nil || !strings.Contains(err.Error(), "request fewer rows or fields") { + t.Fatalf("irreducible output error = %v, want bounded guidance", err) + } +} + func alertRow() map[string]any { return map[string]any{ "alert_id": "al-1", @@ -249,7 +292,8 @@ func TestIncidentSimilarStructuredProjection(t *testing.T) { row["close_time"] = 1712000060 row["ack_time"] = 1712000030 row["alert_cnt"] = 3 - row["root_cause"] = "disk exhaustion" + row["title"] = strings.Repeat("very long incident title ", 2000) + row["root_cause"] = strings.Repeat("disk exhaustion details ", 2000) row["score"] = 0.9 row["description"] = strings.Repeat("large description ", 100) row["images"] = []map[string]any{{"src": strings.Repeat("https://example.test/image/", 100)}} @@ -295,6 +339,9 @@ func TestIncidentDetailFieldsProjection(t *testing.T) { row := incidentRow() row["description"] = strings.Repeat("large description ", 500) row["images"] = []map[string]any{{"src": strings.Repeat("https://example.test/image/", 100)}} + row["ai_summary"] = strings.Repeat("long AI summary ", 4000) + row["root_cause"] = strings.Repeat("long root cause ", 4000) + row["resolution"] = strings.Repeat("long resolution ", 4000) stub.data = row fields := []string{"incident_id", "title", "incident_severity", "progress", "ai_summary", "root_cause", "resolution", "alert_cnt", "start_time", "channel_id"} @@ -334,7 +381,7 @@ func TestAlertEventListStructuredProjection(t *testing.T) { "event_severity": "Warning", "event_status": "Triggered", "event_time": 1712000000, - "title": "CPU high", + "title": strings.Repeat("very long alert event title ", 2000), "description": strings.Repeat("large description ", 100), "labels": map[string]any{"host": "web-01"}, "images": []map[string]any{{"src": strings.Repeat("https://example.test/image/", 100)}}, diff --git a/internal/cli/incident.go b/internal/cli/incident.go index 0ff55fb..c8985cb 100644 --- a/internal/cli/incident.go +++ b/internal/cli/incident.go @@ -600,6 +600,9 @@ func newIncidentSimilarCmd() *cobra.Command { if err != nil { return err } + if err := boundProjectedOutput(proj, compactListOutputLimit); err != nil { + return err + } return ctx.Printer.Print(proj, nil) } @@ -609,7 +612,7 @@ func newIncidentSimilarCmd() *cobra.Command { } cmd.Flags().IntVar(&limit, "limit", 5, "Max results") - cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. incident_id,title,incident_severity,progress,start_time); ignored in table mode. Defaults to a compact incident summary.") + cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. incident_id,title,incident_severity,progress,start_time); ignored in table mode. Defaults to a compact incident summary. Long strings are truncated as needed to keep structured output below 16 KiB.") return cmd } @@ -1353,6 +1356,9 @@ func newIncidentDetailCmd() *cobra.Command { if err != nil { return err } + if err := boundProjectedOutput(proj[0], compactDetailOutputLimit); err != nil { + return err + } return ctx.Printer.Print(proj[0], nil) } return ctx.Printer.Print(result, nil) @@ -1363,7 +1369,7 @@ func newIncidentDetailCmd() *cobra.Command { }) }, } - cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. incident_id,title,incident_severity,progress,root_cause); ignored in table mode. Omit for full detail.") + cmd.Flags().StringVar(&fields, "fields", "", "Comma-separated fields to project in json/toon output (e.g. incident_id,title,incident_severity,progress,root_cause); ignored in table mode. Projected strings are truncated as needed to keep output below 8 KiB; omit --fields for full detail.") return cmd } diff --git a/skills/flashduty/reference/alert.md b/skills/flashduty/reference/alert.md index 93bc424..fb9f68c 100644 --- a/skills/flashduty/reference/alert.md +++ b/skills/flashduty/reference/alert.md @@ -38,6 +38,8 @@ fduty alert feed --output-format toon fduty alert-event list --channel --since 1h --limit 30 --output-format toon ``` +Structured `alert-event list` output stays below 16 KiB. A trailing `...` means a long retained string was shortened. + ## Hot flow — merge noisy alerts into an existing incident ```bash diff --git a/skills/flashduty/reference/incident.md b/skills/flashduty/reference/incident.md index d26c7ef..97d4b04 100644 --- a/skills/flashduty/reference/incident.md +++ b/skills/flashduty/reference/incident.md @@ -63,6 +63,8 @@ fduty incident comment --comment "Root cause identified: DB failov fduty incident resolve --root-cause "DB primary failover delay" --resolution "Failover completed; latency normal." ``` +Projected `similar` lists stay below 16 KiB, and projected `detail --fields` output stays below 8 KiB. A trailing `...` means a long retained string was shortened; omit `--fields` only when the full unbounded detail is explicitly required. + ## Hot flow — full fault analysis (read-only summary) When asked to **summarize / analyze** an incident — 详情 + 关联告警 + 变更 + 时间线 + 相似故障 + 复盘 — `incident detail` does **not** contain the alerts / timeline / similar / post-mortem / change data; each is its own command. **Your first action must be the bundled script** — do not hand-pick one or two commands and write the rest from memory. One call fetches all six aspects: