|
| 1 | +import { |
| 2 | + defineCommand, |
| 3 | + detectOutputFormat, |
| 4 | + securityAgentLogsEndpoint, |
| 5 | + securityGet, |
| 6 | + type FlagsDef, |
| 7 | + type SecurityAlertList, |
| 8 | +} from "bailian-cli-core"; |
| 9 | +import { emitResult, emitBare } from "bailian-cli-runtime"; |
| 10 | +import { WORKSPACE_FLAG, renderAlert, resolveSecurityHost, setSecurityParam } from "./shared.ts"; |
| 11 | + |
| 12 | +const ASSET_TYPES = ["agent", "tool", "skill", "knowledge_base", "memory", "channel"] as const; |
| 13 | + |
| 14 | +const ALERTS_FLAGS = { |
| 15 | + ...WORKSPACE_FLAG, |
| 16 | + page: { |
| 17 | + type: "number", |
| 18 | + valueHint: "<n>", |
| 19 | + description: { "en-US": "Page number (default: 1)", "zh-CN": "页码(默认:1)" }, |
| 20 | + }, |
| 21 | + pageSize: { |
| 22 | + type: "number", |
| 23 | + valueHint: "<n>", |
| 24 | + description: { "en-US": "Results per page (default: 20)", "zh-CN": "每页结果数(默认:20)" }, |
| 25 | + }, |
| 26 | + riskLevel: { |
| 27 | + type: "string", |
| 28 | + valueHint: "<level>", |
| 29 | + choices: ["high", "medium", "low"] as const, |
| 30 | + description: { |
| 31 | + "en-US": "Filter by risk level: high, medium, low", |
| 32 | + "zh-CN": "按风险等级筛选:high、medium、low", |
| 33 | + }, |
| 34 | + }, |
| 35 | + riskName: { |
| 36 | + type: "string", |
| 37 | + valueHint: "<text>", |
| 38 | + description: { "en-US": "Filter by risk name", "zh-CN": "按风险名称筛选" }, |
| 39 | + }, |
| 40 | + status: { |
| 41 | + type: "string", |
| 42 | + valueHint: "<status>", |
| 43 | + description: { "en-US": "Filter by handling status", "zh-CN": "按处理状态筛选" }, |
| 44 | + }, |
| 45 | + statusList: { |
| 46 | + type: "array", |
| 47 | + valueHint: "<status>", |
| 48 | + description: { |
| 49 | + "en-US": "Filter by multiple statuses (repeatable)", |
| 50 | + "zh-CN": "按多个状态筛选(可重复传入)", |
| 51 | + }, |
| 52 | + }, |
| 53 | + appName: { |
| 54 | + type: "string", |
| 55 | + valueHint: "<name>", |
| 56 | + description: { "en-US": "Filter by application name", "zh-CN": "按应用名称筛选" }, |
| 57 | + }, |
| 58 | + assetType: { |
| 59 | + type: "string", |
| 60 | + valueHint: "<type>", |
| 61 | + choices: ASSET_TYPES, |
| 62 | + description: { |
| 63 | + "en-US": `Filter by asset type: ${ASSET_TYPES.join(", ")}`, |
| 64 | + "zh-CN": `按资产类型筛选:${ASSET_TYPES.join("、")}`, |
| 65 | + }, |
| 66 | + }, |
| 67 | + vendor: { |
| 68 | + type: "string", |
| 69 | + valueHint: "<vendor>", |
| 70 | + description: { "en-US": "Filter by vendor", "zh-CN": "按厂商筛选" }, |
| 71 | + }, |
| 72 | + orderBy: { |
| 73 | + type: "string", |
| 74 | + valueHint: "<field>", |
| 75 | + description: { |
| 76 | + "en-US": "Sort field (default: check_time)", |
| 77 | + "zh-CN": "排序字段(默认:check_time)", |
| 78 | + }, |
| 79 | + }, |
| 80 | + order: { |
| 81 | + type: "string", |
| 82 | + valueHint: "<dir>", |
| 83 | + choices: ["asc", "desc"] as const, |
| 84 | + description: { |
| 85 | + "en-US": "Sort direction: asc, desc (default: desc)", |
| 86 | + "zh-CN": "排序方向:asc、desc(默认:desc)", |
| 87 | + }, |
| 88 | + }, |
| 89 | + lang: { |
| 90 | + type: "string", |
| 91 | + valueHint: "<lang>", |
| 92 | + choices: ["zh", "en"] as const, |
| 93 | + description: { "en-US": "Response language: zh, en", "zh-CN": "响应语言:zh、en" }, |
| 94 | + }, |
| 95 | +} satisfies FlagsDef; |
| 96 | + |
| 97 | +export default defineCommand({ |
| 98 | + description: { |
| 99 | + "en-US": "List Agent security alerts", |
| 100 | + "zh-CN": "列出 Agent 安全告警", |
| 101 | + }, |
| 102 | + auth: "apiKey", |
| 103 | + usageArgs: "[flags]", |
| 104 | + flags: ALERTS_FLAGS, |
| 105 | + notes: [ |
| 106 | + { |
| 107 | + "en-US": "Auth: uses DashScope API Key (Bearer token).", |
| 108 | + "zh-CN": "鉴权:使用 DashScope API Key(Bearer Token)。", |
| 109 | + }, |
| 110 | + { |
| 111 | + "en-US": "`--workspace-id` can be set via BAILIAN_WORKSPACE_ID env or config workspace_id.", |
| 112 | + "zh-CN": "`--workspace-id` 可通过 BAILIAN_WORKSPACE_ID 环境变量或配置项 workspace_id 设置。", |
| 113 | + }, |
| 114 | + { |
| 115 | + "en-US": |
| 116 | + "Filters, pagination and sorting go in the query string; enum flags are validated before any request is sent.", |
| 117 | + "zh-CN": "筛选、分页与排序参数走 query string;枚举类 flag 在发起请求前校验。", |
| 118 | + }, |
| 119 | + { |
| 120 | + "en-US": |
| 121 | + "AgentStudio host: derived from --workspace-id by default; point --base-url / DASHSCOPE_BASE_URL (or `auth login --base-url`) at a workspace or pre-release origin such as https://<workspace-id>.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio to override it, and --workspace-id is then not required.", |
| 122 | + "zh-CN": |
| 123 | + "AgentStudio 域名:默认由 --workspace-id 推导;将 --base-url / DASHSCOPE_BASE_URL(或 `auth login --base-url`)指向某个 workspace 或预发源(例如 https://<workspace-id>.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio)即可覆盖,此时无需 --workspace-id。", |
| 124 | + }, |
| 125 | + ], |
| 126 | + exampleArgs: [ |
| 127 | + { "en-US": "--workspace-id ws-xxx", "zh-CN": "--workspace-id ws-xxx" }, |
| 128 | + { |
| 129 | + "en-US": "--risk-level high --page-size 50", |
| 130 | + "zh-CN": "--risk-level high --page-size 50", |
| 131 | + }, |
| 132 | + { |
| 133 | + "en-US": '--asset-type agent --app-name "demo app" --output json', |
| 134 | + "zh-CN": '--asset-type agent --app-name "测试应用0" --output json', |
| 135 | + }, |
| 136 | + { |
| 137 | + "en-US": "--status-list unhandled --status-list handling", |
| 138 | + "zh-CN": "--status-list unhandled --status-list handling", |
| 139 | + }, |
| 140 | + ], |
| 141 | + async run(ctx) { |
| 142 | + const { settings, flags } = ctx; |
| 143 | + const format = detectOutputFormat(settings.output); |
| 144 | + const host = resolveSecurityHost(ctx); |
| 145 | + |
| 146 | + const params = new URLSearchParams(); |
| 147 | + setSecurityParam(params, "current_page", flags.page); |
| 148 | + setSecurityParam(params, "page_size", flags.pageSize); |
| 149 | + setSecurityParam(params, "risk_level", flags.riskLevel); |
| 150 | + setSecurityParam(params, "risk_name", flags.riskName); |
| 151 | + setSecurityParam(params, "status", flags.status); |
| 152 | + setSecurityParam(params, "status_list", flags.statusList); |
| 153 | + setSecurityParam(params, "app_name", flags.appName); |
| 154 | + setSecurityParam(params, "asset_type", flags.assetType); |
| 155 | + setSecurityParam(params, "vendor", flags.vendor); |
| 156 | + setSecurityParam(params, "order_by", flags.orderBy); |
| 157 | + setSecurityParam(params, "order", flags.order); |
| 158 | + setSecurityParam(params, "lang", flags.lang); |
| 159 | + |
| 160 | + const query = params.toString(); |
| 161 | + const base = securityAgentLogsEndpoint(host); |
| 162 | + const endpoint = query ? `${base}?${query}` : base; |
| 163 | + |
| 164 | + if (settings.dryRun) { |
| 165 | + emitResult({ endpoint, method: "GET" }, format); |
| 166 | + return; |
| 167 | + } |
| 168 | + |
| 169 | + const data = await securityGet<SecurityAlertList>(ctx.client, endpoint); |
| 170 | + const alerts = data?.data ?? []; |
| 171 | + |
| 172 | + if (format === "json") { |
| 173 | + emitResult(data ?? { stats: null, data: [], next_page: null }, format); |
| 174 | + return; |
| 175 | + } |
| 176 | + |
| 177 | + if (settings.quiet) { |
| 178 | + for (const alert of alerts) emitBare(alert.alert_id); |
| 179 | + return; |
| 180 | + } |
| 181 | + |
| 182 | + const stats = data?.stats; |
| 183 | + if (stats) { |
| 184 | + emitBare( |
| 185 | + `Total: ${stats.total ?? "-"} high: ${stats.high ?? "-"} ` + |
| 186 | + `medium: ${stats.medium ?? "-"} low: ${stats.low ?? "-"}\n`, |
| 187 | + ); |
| 188 | + } |
| 189 | + |
| 190 | + if (alerts.length === 0) { |
| 191 | + emitBare("No alerts found."); |
| 192 | + return; |
| 193 | + } |
| 194 | + |
| 195 | + for (const alert of alerts) renderAlert(alert); |
| 196 | + |
| 197 | + if (data?.next_page) { |
| 198 | + emitBare(`Next page cursor: ${data.next_page}`); |
| 199 | + } |
| 200 | + }, |
| 201 | +}); |
0 commit comments