Skip to content

Commit 80ca45e

Browse files
authored
Merge pull request #196 from cxy01313369/feat/security-commands
Feat/security commands
2 parents 73405f1 + 6655ce3 commit 80ca45e

32 files changed

Lines changed: 1381 additions & 20 deletions

File tree

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

CHANGELOG.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
66

77
[中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md)
88

9+
## [1.28.0] - 2026-09-20
10+
11+
### Added
12+
13+
- **Agent security commands**`bl agents security overview` (protection overview for the last 24 hours) and `bl agents security alerts` (alert list with risk-level, asset-type, status, vendor, pagination and sorting filters). Both call the per-workspace AgentStudio host and honor the shared `text` / `json` / `--quiet` / `--dry-run` contract. The host is derived from `--workspace-id`, or overridden by `--base-url` / `DASHSCOPE_BASE_URL` / `auth login --base-url` pointed at a workspace or pre-release origin (e.g. `https://<workspace-id>.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio`).
14+
15+
### Internal
16+
17+
- Add Agent security E2E coverage (help, missing-workspace usage error, dry-run host derivation and `--base-url` override, query-string filters, enum fast-fail) and generate the `bailian-cli` skill reference for the new `agents` group.
18+
919
## [1.27.0] - 2026-09-18
1020

1121
### Added
@@ -29,10 +39,6 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
2939
- **Simplified authentication** — Console login is now the recommended default and can create an ordinary API key when needed. Existing ordinary API keys and Token Plan subscription keys use the same `bl auth login --api-key <API_KEY>` command.
3040
- **Automatic API key validation and endpoint selection** — API keys are validated before being saved. The CLI automatically selects an available regional endpoint and applies the appropriate Token Plan configuration when applicable.
3141

32-
### Fixed
33-
34-
- **Video task diagnostics** — Failed video tasks now expose the service error code, message, request ID, and scheduling time; downloads show the actual failure detail instead of only an incomplete status.
35-
3642
## [1.25.0] - 2026-09-14
3743

3844
### Added

CHANGELOG.zh.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66

77
[English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md)
88

9+
## [1.28.0] - 2026-09-20
10+
11+
### 新增
12+
13+
- **Agent 安全命令** —— `bl agents security overview`(最近 24 小时的防护总览)与 `bl agents security alerts`(告警列表,支持风险等级、资产类型、状态、厂商、分页与排序等筛选)。两者均对接按 workspace 区分的 AgentStudio 域名,遵循统一的 `text` / `json` / `--quiet` / `--dry-run` 约定。域名默认由 `--workspace-id` 推导,也可通过 `--base-url` / `DASHSCOPE_BASE_URL` / `auth login --base-url` 指向某个 workspace 或预发源覆盖(例如 `https://<workspace-id>.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio`)。
14+
15+
### 内部
16+
17+
- 补充 Agent 安全 E2E 覆盖(help、缺 workspace 的 usage 错误、dry-run 域名推导与 `--base-url` 覆盖、query string 筛选、枚举快失败),并为新的 `agents` 组生成 `bailian-cli` 技能 reference。
18+
919
## [1.27.0] - 2026-09-18
1020

1121
### 新增
@@ -29,10 +39,6 @@
2939
- **简化认证方式** —— 默认推荐使用控制台登录,需要时可自动创建普通 API Key。已有普通 API Key 和 Token Plan 订阅 Key 统一使用 `bl auth login --api-key <API_KEY>` 登录。
3040
- **API Key 自动校验与地域识别** —— API Key 会在保存前进行校验,CLI 自动选择可用地域,并在适用时完成 Token Plan 配置。
3141

32-
### 修复
33-
34-
- **视频任务诊断** —— 视频任务失败时展示服务端错误码、错误信息、请求 ID 和调度时间;下载失败时展示实际失败原因,不再只提示任务未完成。
35-
3642
## [1.25.0] - 2026-09-14
3743

3844
### 新增

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli",
3-
"version": "1.27.0",
3+
"version": "1.28.0",
44
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
55
"keywords": [
66
"agent",

packages/cli/src/commands.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ import {
7777
speechVocabularyDelete,
7878
fileUpload,
7979
consoleCall,
80+
securityOverview,
81+
securityAlerts,
8082
usageFree,
8183
usageFreetier,
8284
usageStats,
@@ -349,6 +351,8 @@ export const commands: Record<string, AnyCommand> = {
349351
"speech vocabulary delete": speechVocabularyDelete,
350352
"file upload": fileUpload,
351353
"console call": consoleCall,
354+
"agents security overview": securityOverview,
355+
"agents security alerts": securityAlerts,
352356
"usage free": usageFree,
353357
"usage freetier": usageFreetier,
354358
"usage stats": usageStats,

packages/commands/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bailian-cli-commands",
3-
"version": "1.27.0",
3+
"version": "1.28.0",
44
"description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
55
"homepage": "https://bailian.console.aliyun.com/cli",
66
"bugs": {
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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 {
11+
SECURITY_UI,
12+
WORKSPACE_FLAG,
13+
renderAlert,
14+
resolveSecurityHost,
15+
setSecurityParam,
16+
} from "./shared.ts";
17+
18+
const ASSET_TYPES = ["agent", "tool", "skill", "knowledge_base", "memory", "channel"] as const;
19+
20+
const ALERTS_FLAGS = {
21+
...WORKSPACE_FLAG,
22+
page: {
23+
type: "number",
24+
valueHint: "<n>",
25+
description: { "en-US": "Page number (default: 1)", "zh-CN": "页码(默认:1)" },
26+
},
27+
pageSize: {
28+
type: "number",
29+
valueHint: "<n>",
30+
description: { "en-US": "Results per page (default: 20)", "zh-CN": "每页结果数(默认:20)" },
31+
},
32+
riskLevel: {
33+
type: "string",
34+
valueHint: "<level>",
35+
choices: ["high", "medium", "low"] as const,
36+
description: {
37+
"en-US": "Filter by risk level: high, medium, low",
38+
"zh-CN": "按风险等级筛选:high、medium、low",
39+
},
40+
},
41+
riskName: {
42+
type: "string",
43+
valueHint: "<text>",
44+
description: { "en-US": "Filter by risk name", "zh-CN": "按风险名称筛选" },
45+
},
46+
status: {
47+
type: "string",
48+
valueHint: "<status>",
49+
description: { "en-US": "Filter by handling status", "zh-CN": "按处理状态筛选" },
50+
},
51+
statusList: {
52+
type: "array",
53+
valueHint: "<status>",
54+
description: {
55+
"en-US": "Filter by multiple statuses (repeatable)",
56+
"zh-CN": "按多个状态筛选(可重复传入)",
57+
},
58+
},
59+
appName: {
60+
type: "string",
61+
valueHint: "<name>",
62+
description: { "en-US": "Filter by application name", "zh-CN": "按应用名称筛选" },
63+
},
64+
assetType: {
65+
type: "string",
66+
valueHint: "<type>",
67+
choices: ASSET_TYPES,
68+
description: {
69+
"en-US": `Filter by asset type: ${ASSET_TYPES.join(", ")}`,
70+
"zh-CN": `按资产类型筛选:${ASSET_TYPES.join("、")}`,
71+
},
72+
},
73+
vendor: {
74+
type: "string",
75+
valueHint: "<vendor>",
76+
description: { "en-US": "Filter by vendor", "zh-CN": "按厂商筛选" },
77+
},
78+
orderBy: {
79+
type: "string",
80+
valueHint: "<field>",
81+
description: {
82+
"en-US": "Sort field (default: check_time)",
83+
"zh-CN": "排序字段(默认:check_time)",
84+
},
85+
},
86+
order: {
87+
type: "string",
88+
valueHint: "<dir>",
89+
choices: ["asc", "desc"] as const,
90+
description: {
91+
"en-US": "Sort direction: asc, desc (default: desc)",
92+
"zh-CN": "排序方向:asc、desc(默认:desc)",
93+
},
94+
},
95+
lang: {
96+
type: "string",
97+
valueHint: "<lang>",
98+
choices: ["zh", "en"] as const,
99+
description: { "en-US": "Response language: zh, en", "zh-CN": "响应语言:zh、en" },
100+
},
101+
} satisfies FlagsDef;
102+
103+
export default defineCommand({
104+
description: {
105+
"en-US": "List Agent security alerts",
106+
"zh-CN": "列出 Agent 安全告警",
107+
},
108+
auth: "apiKey",
109+
usageArgs: "[flags]",
110+
flags: ALERTS_FLAGS,
111+
notes: [
112+
{
113+
"en-US": "Auth: uses DashScope API Key (Bearer token).",
114+
"zh-CN": "鉴权:使用 DashScope API Key(Bearer Token)。",
115+
},
116+
{
117+
"en-US": "`--workspace-id` can be set via BAILIAN_WORKSPACE_ID env or config workspace_id.",
118+
"zh-CN": "`--workspace-id` 可通过 BAILIAN_WORKSPACE_ID 环境变量或配置项 workspace_id 设置。",
119+
},
120+
{
121+
"en-US":
122+
"Filters, pagination and sorting go in the query string; enum flags are validated before any request is sent.",
123+
"zh-CN": "筛选、分页与排序参数走 query string;枚举类 flag 在发起请求前校验。",
124+
},
125+
{
126+
"en-US":
127+
"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.",
128+
"zh-CN":
129+
"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。",
130+
},
131+
],
132+
exampleArgs: [
133+
{ "en-US": "--workspace-id ws-xxx", "zh-CN": "--workspace-id ws-xxx" },
134+
{
135+
"en-US": "--risk-level high --page-size 50",
136+
"zh-CN": "--risk-level high --page-size 50",
137+
},
138+
{
139+
"en-US": '--asset-type agent --app-name "demo app" --output json',
140+
"zh-CN": '--asset-type agent --app-name "测试应用0" --output json',
141+
},
142+
{
143+
"en-US": "--status-list unhandled --status-list handling",
144+
"zh-CN": "--status-list unhandled --status-list handling",
145+
},
146+
],
147+
async run(ctx) {
148+
const { settings, flags, localize } = ctx;
149+
const format = detectOutputFormat(settings.output);
150+
const host = resolveSecurityHost(ctx);
151+
152+
const params = new URLSearchParams();
153+
setSecurityParam(params, "current_page", flags.page);
154+
setSecurityParam(params, "page_size", flags.pageSize);
155+
setSecurityParam(params, "risk_level", flags.riskLevel);
156+
setSecurityParam(params, "risk_name", flags.riskName);
157+
setSecurityParam(params, "status", flags.status);
158+
setSecurityParam(params, "status_list", flags.statusList);
159+
setSecurityParam(params, "app_name", flags.appName);
160+
setSecurityParam(params, "asset_type", flags.assetType);
161+
setSecurityParam(params, "vendor", flags.vendor);
162+
setSecurityParam(params, "order_by", flags.orderBy);
163+
setSecurityParam(params, "order", flags.order);
164+
setSecurityParam(params, "lang", flags.lang);
165+
166+
const query = params.toString();
167+
const base = securityAgentLogsEndpoint(host);
168+
const endpoint = query ? `${base}?${query}` : base;
169+
170+
if (settings.dryRun) {
171+
emitResult({ endpoint, method: "GET" }, format);
172+
return;
173+
}
174+
175+
const data = await securityGet<SecurityAlertList>(ctx.client, endpoint);
176+
const alerts = data?.data ?? [];
177+
178+
// --quiet wins over the output format: emit a bare, pipe-friendly list of
179+
// alert IDs even when output=json is configured, so ID-driven pipelines
180+
// (`bl agents security alerts --quiet | xargs …`) keep working.
181+
if (settings.quiet) {
182+
for (const alert of alerts) emitBare(alert.alert_id);
183+
return;
184+
}
185+
186+
if (format === "json") {
187+
emitResult(data ?? { stats: null, data: [], next_page: null }, format);
188+
return;
189+
}
190+
191+
const stats = data?.stats;
192+
if (stats) {
193+
emitBare(
194+
`${localize(SECURITY_UI.total)}: ${stats.total ?? "-"} ` +
195+
`${localize(SECURITY_UI.high)}: ${stats.high ?? "-"} ` +
196+
`${localize(SECURITY_UI.medium)}: ${stats.medium ?? "-"} ` +
197+
`${localize(SECURITY_UI.low)}: ${stats.low ?? "-"}\n`,
198+
);
199+
}
200+
201+
if (alerts.length === 0) {
202+
emitBare(localize(SECURITY_UI.noAlerts));
203+
return;
204+
}
205+
206+
for (const alert of alerts) renderAlert(localize, alert);
207+
208+
if (data?.next_page) {
209+
emitBare(`${localize(SECURITY_UI.nextPageCursor)}: ${data.next_page}`);
210+
}
211+
},
212+
});

0 commit comments

Comments
 (0)