Skip to content

Commit afb547e

Browse files
committed
refactor(commands): migrate remaining confirmations to runtime gate
1 parent 8906108 commit afb547e

7 files changed

Lines changed: 112 additions & 93 deletions

File tree

packages/commands/src/commands/managed-agent/apply.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,6 @@ const APPLY_FLAGS = {
3434
"zh-CN": "目标 Provider(默认:全部已配置项)",
3535
},
3636
},
37-
yes: {
38-
type: "switch",
39-
description: {
40-
"en-US": "Confirm and apply without an interactive prompt (required to mutate)",
41-
"zh-CN": "无需交互提示直接确认并应用(执行变更时必填)",
42-
},
43-
},
4437
noRefresh: {
4538
type: "switch",
4639
description: {
@@ -64,7 +57,15 @@ export default defineCommand({
6457
"zh-CN": "应用规划的变更,创建、更新或删除 Agent 资源",
6558
},
6659
auth: "apiKey",
67-
usageArgs: "[--file <path>] [--provider <name>] [--yes] [--concurrency <n>]",
60+
risk: {
61+
level: "high",
62+
message: {
63+
"en-US":
64+
"This applies the current plan and may create, update, or delete remote managed Agent resources.",
65+
"zh-CN": "该操作会应用当前计划,可能创建、更新或删除远端托管 Agent 资源。",
66+
},
67+
},
68+
usageArgs: "[--file <path>] [--provider <name>] [--concurrency <n>]",
6869
flags: APPLY_FLAGS,
6970
exampleArgs: ["--yes", "--provider bailian --yes"],
7071
notes: CREDENTIALS_NOTE,
@@ -124,23 +125,11 @@ export default defineCommand({
124125
return;
125126
}
126127

127-
const creates = actionable.filter((action) => action.action === "create").length;
128-
const updates = actionable.filter((action) => action.action === "update").length;
129-
const deletes = planned.destructiveActions;
130-
131128
for (const action of actionable) {
132129
const icon = action.action === "create" ? "+" : action.action === "update" ? "~" : "-";
133130
emitProgress(` ${icon} ${formatResourceLabel(action.address)}`);
134131
}
135132

136-
if (!flags.yes) {
137-
throw new BailianError(
138-
`Refusing to apply ${actionable.length} change(s) (${creates} create, ${updates} update, ${deletes.length} destroy) without confirmation.`,
139-
ExitCode.USAGE,
140-
"Review with `bl managed-agent plan`, then re-run with --yes to apply.",
141-
);
142-
}
143-
144133
const result = await withAgentErrors(() =>
145134
withStdoutProtected(() =>
146135
executePlannedProject(planned, {

packages/commands/src/commands/managed-agent/destroy.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@ const DESTROY_FLAGS = {
2121
"zh-CN": "配置文件路径(默认:agents.yaml)",
2222
},
2323
},
24-
yes: {
25-
type: "switch",
26-
description: {
27-
"en-US": "Confirm and destroy without an interactive prompt (required)",
28-
"zh-CN": "无需交互提示直接确认并销毁(必填)",
29-
},
30-
},
3124
cascade: {
3225
type: "switch",
3326
description: {
@@ -43,7 +36,15 @@ export default defineCommand({
4336
"zh-CN": "销毁 State 中跟踪的全部托管 Agent 资源",
4437
},
4538
auth: "apiKey",
46-
usageArgs: "[--file <path>] [--yes] [--cascade]",
39+
risk: {
40+
level: "high",
41+
message: {
42+
"en-US":
43+
"This deletes every managed Agent resource tracked in state; --cascade may also delete dependent resources.",
44+
"zh-CN": "该操作会删除 State 中跟踪的全部托管 Agent 资源;--cascade 还可能删除依赖资源。",
45+
},
46+
},
47+
usageArgs: "[--file <path>] [--cascade]",
4748
flags: DESTROY_FLAGS,
4849
exampleArgs: ["--yes", "--yes --cascade"],
4950
notes: CREDENTIALS_NOTE,
@@ -86,14 +87,6 @@ export default defineCommand({
8687
else emitBare(line);
8788
}
8889

89-
if (!flags.yes) {
90-
throw new BailianError(
91-
`Refusing to destroy ${resources.length} resource(s) without confirmation.`,
92-
ExitCode.USAGE,
93-
"Re-run with --yes to destroy (add --cascade to remove dependents).",
94-
);
95-
}
96-
9790
const result = await withAgentErrors(() =>
9891
withStdoutProtected(() =>
9992
destroyPlannedProjectResources(planned, {

packages/commands/src/commands/permission/revoke.ts

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineCommand, BailianError, ExitCode } from "bailian-cli-core";
1+
import { defineCommand } from "bailian-cli-core";
22
import { runPermissionChange, validatePermissionChange } from "./shared.ts";
33

44
export default defineCommand({
@@ -7,7 +7,16 @@ export default defineCommand({
77
"zh-CN": "撤销模型权限(推理 / 微调 / 部署)",
88
},
99
auth: "apiKey",
10-
usageArgs: "--model <models> [--action <actions>] | --all --yes",
10+
risk: {
11+
level: "high",
12+
message: {
13+
"en-US":
14+
"This revokes model permissions and may interrupt inference, fine-tuning, or deployment workloads. With --all, it also clears all historical inference grants.",
15+
"zh-CN":
16+
"该操作会撤销模型权限,可能导致推理、精调或部署任务中断;使用 --all 时会清除全部历史推理授权。",
17+
},
18+
},
19+
usageArgs: "--model <models> [--action <actions>] | --all [flags]",
1120
flags: {
1221
model: {
1322
type: "string",
@@ -33,17 +42,10 @@ export default defineCommand({
3342
"zh-CN": "关闭一键授权并清除所有历史推理授权",
3443
},
3544
},
36-
yes: {
37-
type: "switch",
38-
description: {
39-
"en-US": "Confirm --all without an interactive prompt (required)",
40-
"zh-CN": "无需交互提示确认执行 --all(必填)",
41-
},
42-
},
4345
},
4446
exampleArgs: [
45-
"--model qwen-plus",
46-
"--model qwen-plus,qwen3-max --action inference,finetune",
47+
"--model qwen-plus --yes",
48+
"--model qwen-plus,qwen3-max --action inference,finetune --yes",
4749
"--all --yes",
4850
"--model qwen-plus --dry-run --output json",
4951
],
@@ -52,6 +54,11 @@ export default defineCommand({
5254
"en-US": "Grants apply to the business workspace your API key belongs to.",
5355
"zh-CN": "授权将应用于 API Key 所属的业务 Workspace。",
5456
},
57+
{
58+
"en-US":
59+
"All revoke operations require --yes; use --dry-run to preview the request without confirmation.",
60+
"zh-CN": "所有撤权操作均需使用 --yes;可通过 --dry-run 免确认预览请求。",
61+
},
5562
{
5663
"en-US":
5764
"--all maps to the server one-key switch (access_all_entities: CLOSE): it clears every historical inference grant and cannot be undone, so it requires --yes.",
@@ -65,14 +72,6 @@ export default defineCommand({
6572
],
6673
validate: (flags) => validatePermissionChange(flags),
6774
async run(ctx) {
68-
const { flags, settings } = ctx;
69-
if (flags.all && !flags.yes && !settings.dryRun) {
70-
throw new BailianError(
71-
"Refusing to clear all historical inference grants without confirmation.",
72-
ExitCode.USAGE,
73-
"Re-run with --yes to close one-key authorization (or preview with --dry-run).",
74-
);
75-
}
76-
await runPermissionChange(ctx, flags, false);
75+
await runPermissionChange(ctx, ctx.flags, false);
7776
},
7877
});

packages/commands/tests/e2e/managed-agent.e2e.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,23 @@ describe("e2e: managed-agent", () => {
132132
expect(stderr).toMatch(/--file|--provider|--yes/i);
133133
});
134134

135+
test.each(["apply", "destroy"])("managed-agent %s 无 --yes 返回确认请求 (7)", async (command) => {
136+
const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [
137+
"managed-agent",
138+
command,
139+
"--file",
140+
"agents.e2e-missing.yaml",
141+
"--api-key",
142+
"e2e-dummy-key",
143+
"--output",
144+
"json",
145+
]);
146+
expect(exitCode).toBe(7);
147+
expect(JSON.parse(stderr)).toMatchObject({
148+
error: { code: 7, type: "requires_confirmation" },
149+
});
150+
});
151+
135152
test("managed-agent session delete 缺少 --session-id 时退出为用法错误 (2)", async () => {
136153
const { stderr, exitCode } = await runCommandE2e(MANAGED_AGENT_ROUTES, [
137154
"managed-agent",
@@ -223,7 +240,6 @@ describe("e2e: managed-agent(--dry-run 短路,不联网不写盘)", () =>
223240
"managed-agent",
224241
"apply",
225242
"--dry-run",
226-
"--yes",
227243
"--output",
228244
"json",
229245
]);

packages/commands/tests/e2e/permission.e2e.test.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,39 @@ describe("e2e: permission", () => {
9898
expect(stderr).toContain("at most 20");
9999
});
100100

101-
test("permission revoke --all 缺 --yes 拒绝执行", async () => {
102-
// --yes 护栏在 run() 开头、任何网络调用之前抛出;带 dummy key 让用例不依赖环境凭证(否则 auth stage 先报 AUTH(3))。
101+
test("permission revoke --all 无 --yes 返回确认请求 (7)", async () => {
103102
const { stderr, exitCode } = await runCommandE2e(PERMISSION_ROUTES, [
104103
"permission",
105104
"revoke",
106105
"--all",
107106
"--api-key",
108107
"e2e-dummy-key",
108+
"--output",
109+
"json",
109110
]);
110-
expect(exitCode).toBe(2);
111-
expect(stderr).toContain("Refusing");
112-
expect(stderr).toContain("--yes");
111+
expect(exitCode).toBe(7);
112+
expect(JSON.parse(stderr)).toMatchObject({
113+
error: { code: 7, type: "requires_confirmation" },
114+
});
115+
});
116+
117+
test("permission revoke --model 无 --yes 返回确认请求 (7)", async () => {
118+
const { stderr, exitCode } = await runCommandE2e(PERMISSION_ROUTES, [
119+
"permission",
120+
"revoke",
121+
"--model",
122+
"qwen-plus",
123+
"--api-key",
124+
"e2e-dummy-key",
125+
"--base-url",
126+
"http://127.0.0.1:1",
127+
"--output",
128+
"json",
129+
]);
130+
expect(exitCode).toBe(7);
131+
expect(JSON.parse(stderr)).toMatchObject({
132+
error: { code: 7, type: "requires_confirmation" },
133+
});
113134
});
114135

115136
// --dry-run 跳过 auth stage(见 runtime middleware),无需凭证即可断言请求形状。

skills/bailian-cli/reference/permission.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@ bl permission list --output text
109109

110110
### `bl permission revoke`
111111

112-
| Field | Value |
113-
| ------------------ | --------------------------------------------------------------------------- |
114-
| **Name** | `permission revoke` |
115-
| **Description** | Revoke model permissions (inference / finetune / deploy) |
116-
| **Authentication** | API Key |
117-
| **Usage** | `bl permission revoke --model <models> [--action <actions>] \| --all --yes` |
112+
| Field | Value |
113+
| ------------------ | ----------------------------------------------------------------------------- |
114+
| **Name** | `permission revoke` |
115+
| **Description** | Revoke model permissions (inference / finetune / deploy) |
116+
| **Authentication** | API Key |
117+
| **Usage** | `bl permission revoke --model <models> [--action <actions>] \| --all [flags]` |
118118

119119
#### Flags
120120

@@ -123,24 +123,25 @@ bl permission list --output text
123123
| `--model <models>` | string | no | Model ID(s), comma-separated (max 20) |
124124
| `--action <actions>` | string | no | Permission action(s), comma-separated: inference, finetune, deploy (default: inference) |
125125
| `--all` | switch | no | Close one-key authorization and clear ALL historical inference grants |
126-
| `--yes` | switch | no | Confirm --all without an interactive prompt (required) |
126+
| `--yes` | switch | no | Confirm this high-risk operation |
127127
| `--api-key <key>` | string | no | API key |
128128
| `--base-url <url>` | string | no | API base URL |
129129

130130
#### Notes
131131

132132
- Grants apply to the business workspace your API key belongs to.
133+
- All revoke operations require --yes; use --dry-run to preview the request without confirmation.
133134
- --all maps to the server one-key switch (access_all_entities: CLOSE): it clears every historical inference grant and cannot be undone, so it requires --yes.
134135
- Actions you omit keep their current grants (server-side tri-state patch).
135136

136137
#### Examples
137138

138139
```bash
139-
bl permission revoke --model qwen-plus
140+
bl permission revoke --model qwen-plus --yes
140141
```
141142

142143
```bash
143-
bl permission revoke --model qwen-plus,qwen3-max --action inference,finetune
144+
bl permission revoke --model qwen-plus,qwen3-max --action inference,finetune --yes
144145
```
145146

146147
```bash

skills/bailian-managed-agent/reference/managed-agent.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,24 @@ Index: [index.md](index.md)
3131

3232
### `bl managed-agent apply`
3333

34-
| Field | Value |
35-
| ------------------ | ---------------------------------------------------------------------------------------- |
36-
| **Name** | `managed-agent apply` |
37-
| **Description** | Apply planned changes to create/update/delete agent resources |
38-
| **Authentication** | API Key |
39-
| **Usage** | `bl managed-agent apply [--file <path>] [--provider <name>] [--yes] [--concurrency <n>]` |
34+
| Field | Value |
35+
| ------------------ | -------------------------------------------------------------------------------- |
36+
| **Name** | `managed-agent apply` |
37+
| **Description** | Apply planned changes to create/update/delete agent resources |
38+
| **Authentication** | API Key |
39+
| **Usage** | `bl managed-agent apply [--file <path>] [--provider <name>] [--concurrency <n>]` |
4040

4141
#### Flags
4242

43-
| Flag | Type | Required | Description |
44-
| ------------------- | ------ | -------- | -------------------------------------------------------------------- |
45-
| `--file <path>` | string | no | Config file path (default: agents.yaml) |
46-
| `--provider <name>` | string | no | Target provider (default: all configured) |
47-
| `--yes` | switch | no | Confirm and apply without an interactive prompt (required to mutate) |
48-
| `--no-refresh` | switch | no | Skip refreshing state from remote before planning |
49-
| `--concurrency <n>` | number | no | Max independent resources to apply in parallel (default 6, max 10) |
50-
| `--api-key <key>` | string | no | API key |
51-
| `--base-url <url>` | string | no | API base URL |
43+
| Flag | Type | Required | Description |
44+
| ------------------- | ------ | -------- | ------------------------------------------------------------------ |
45+
| `--file <path>` | string | no | Config file path (default: agents.yaml) |
46+
| `--provider <name>` | string | no | Target provider (default: all configured) |
47+
| `--no-refresh` | switch | no | Skip refreshing state from remote before planning |
48+
| `--concurrency <n>` | number | no | Max independent resources to apply in parallel (default 6, max 10) |
49+
| `--yes` | switch | no | Confirm this high-risk operation |
50+
| `--api-key <key>` | string | no | API key |
51+
| `--base-url <url>` | string | no | API base URL |
5252

5353
#### Notes
5454

@@ -68,20 +68,20 @@ bl managed-agent apply --provider bailian --yes
6868

6969
### `bl managed-agent destroy`
7070

71-
| Field | Value |
72-
| ------------------ | -------------------------------------------------------------- |
73-
| **Name** | `managed-agent destroy` |
74-
| **Description** | Destroy all managed agent resources tracked in state |
75-
| **Authentication** | API Key |
76-
| **Usage** | `bl managed-agent destroy [--file <path>] [--yes] [--cascade]` |
71+
| Field | Value |
72+
| ------------------ | ------------------------------------------------------ |
73+
| **Name** | `managed-agent destroy` |
74+
| **Description** | Destroy all managed agent resources tracked in state |
75+
| **Authentication** | API Key |
76+
| **Usage** | `bl managed-agent destroy [--file <path>] [--cascade]` |
7777

7878
#### Flags
7979

8080
| Flag | Type | Required | Description |
8181
| ------------------ | ------ | -------- | -------------------------------------------------------------------------- |
8282
| `--file <path>` | string | no | Config file path (default: agents.yaml) |
83-
| `--yes` | switch | no | Confirm and destroy without an interactive prompt (required) |
8483
| `--cascade` | switch | no | Auto-delete dependent resources (e.g. sessions referencing an environment) |
84+
| `--yes` | switch | no | Confirm this high-risk operation |
8585
| `--api-key <key>` | string | no | API key |
8686
| `--base-url <url>` | string | no | API base URL |
8787

0 commit comments

Comments
 (0)