Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions packages/commands/src/commands/dataset/delete.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineCommand, deleteDataset, type FlagsDef } from "bailian-cli-core";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { emitResult, emitBare, confirmDangerousAction } from "bailian-cli-runtime";

const DELETE_FLAGS = {
fileId: {
Expand All @@ -8,14 +8,28 @@ const DELETE_FLAGS = {
description: { "en-US": "Dataset file ID (required)", "zh-CN": "数据集文件 ID(必填)" },
required: true,
},
yes: {
type: "switch",
description: { "en-US": "Skip the confirmation prompt", "zh-CN": "跳过确认提示" },
},
} satisfies FlagsDef;

export default defineCommand({
description: { "en-US": "Delete a dataset file by ID", "zh-CN": "通过 ID 删除数据集文件" },
auth: "apiKey",
usageArgs: "--file-id <id>",
usageArgs: "--file-id <id> [--yes]",
flags: DELETE_FLAGS,
exampleArgs: ["--file-id file-id-xxx", "--file-id file-id-xxx --dry-run"],
exampleArgs: [
"--file-id file-id-xxx",
"--file-id file-id-xxx --dry-run",
"--file-id file-id-xxx --yes",
],
notes: [
{
"en-US": "Irreversible — the dataset file is permanently removed.",
"zh-CN": "该操作不可撤销——数据集文件将被永久删除。",
},
],
async run(ctx) {
const { settings, flags } = ctx;
const fileId = flags.fileId;
Expand All @@ -25,6 +39,11 @@ export default defineCommand({
return;
}

await confirmDangerousAction(
`Delete dataset file ${fileId}.\nThe file is permanently removed. This cannot be undone.`,
flags.yes ?? false,
);

const response = await deleteDataset(ctx.client, fileId);

if (settings.quiet) {
Expand Down
25 changes: 22 additions & 3 deletions packages/commands/src/commands/deploy/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
ExitCode,
type FlagsDef,
} from "bailian-cli-core";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { emitResult, emitBare, confirmDangerousAction } from "bailian-cli-runtime";

const DELETE_FLAGS = {
deployedModel: {
Expand All @@ -25,6 +25,10 @@ const DELETE_FLAGS = {
"zh-CN": "跳过本地 STOPPED/FAILED 状态预检查",
},
},
yes: {
type: "switch",
description: { "en-US": "Skip the confirmation prompt", "zh-CN": "跳过确认提示" },
},
} satisfies FlagsDef;

/**
Expand All @@ -40,9 +44,19 @@ export default defineCommand({
"zh-CN": "删除模型部署(状态必须为 STOPPED 或 FAILED)",
},
auth: "apiKey",
usageArgs: "--deployed-model <id> [--skip-precheck]",
usageArgs: "--deployed-model <id> [--skip-precheck] [--yes]",
flags: DELETE_FLAGS,
exampleArgs: ["--deployed-model dep-...", "--deployed-model dep-... --dry-run"],
notes: [
{
"en-US": "Irreversible — the deployment is permanently destroyed.",
"zh-CN": "该操作不可撤销——部署将被永久销毁。",
},
],
exampleArgs: [
"--deployed-model dep-...",
"--deployed-model dep-... --dry-run",
"--deployed-model dep-... --yes",
],
async run(ctx) {
const { settings, flags } = ctx;
const deployedModel = flags.deployedModel;
Expand Down Expand Up @@ -73,6 +87,11 @@ export default defineCommand({
}
}

await confirmDangerousAction(
`Delete deployment ${deployedModel}.\nThe deployment is permanently destroyed. This cannot be undone.`,
flags.yes ?? false,
);

const response = await deleteDeployment(ctx.client, deployedModel);

if (settings.quiet) {
Expand Down
19 changes: 16 additions & 3 deletions packages/commands/src/commands/finetune/delete.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineCommand, deleteFineTune, type FlagsDef } from "bailian-cli-core";
import { emitResult, emitBare } from "bailian-cli-runtime";
import { emitResult, emitBare, confirmDangerousAction } from "bailian-cli-runtime";

const DELETE_FLAGS = {
jobId: {
Expand All @@ -8,15 +8,23 @@ const DELETE_FLAGS = {
description: { "en-US": "Fine-tune job ID (required)", "zh-CN": "微调任务 ID(必填)" },
required: true,
},
yes: {
type: "switch",
description: { "en-US": "Skip the confirmation prompt", "zh-CN": "跳过确认提示" },
},
} satisfies FlagsDef;

export default defineCommand({
description: { "en-US": "Delete a fine-tune job record", "zh-CN": "删除微调任务记录" },
auth: "apiKey",
usageArgs: "--job-id <id>",
usageArgs: "--job-id <id> [--yes]",
flags: DELETE_FLAGS,
exampleArgs: ["--job-id ft-xxx", "--job-id ft-xxx --dry-run"],
exampleArgs: ["--job-id ft-xxx", "--job-id ft-xxx --dry-run", "--job-id ft-xxx --yes"],
notes: [
{
"en-US": "Irreversible — the job record is permanently removed.",
"zh-CN": "该操作不可撤销——任务记录将被永久删除。",
},
{
"en-US":
"Cancel a RUNNING job first via `finetune cancel` — the platform refuses to delete jobs that are still in flight.",
Expand All @@ -32,6 +40,11 @@ export default defineCommand({
return;
}

await confirmDangerousAction(
`Delete fine-tune job record ${jobId}.\nThe job record is permanently removed. This cannot be undone.`,
flags.yes ?? false,
);

const response = await deleteFineTune(ctx.client, jobId);

if (settings.quiet) {
Expand Down
23 changes: 21 additions & 2 deletions packages/commands/src/commands/quota/update.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineCommand, detectOutputFormat, modelsLimitsPath } from "bailian-cli-core";
import { emitResult } from "bailian-cli-runtime";
import { emitResult, confirmDangerousAction } from "bailian-cli-runtime";
import { formatNumber } from "../shared/format.ts";

const MINUTE_SECONDS = 60;
Expand All @@ -10,7 +10,7 @@ export default defineCommand({
"zh-CN": "更新模型限流配置(QPM/TPM),或使用 --delete 清除配置",
},
auth: "apiKey",
usageArgs: "--model <model> [--rpm <n>] [--tpm <n>] [--delete]",
usageArgs: "--model <model> [--rpm <n>] [--tpm <n>] [--delete] [--yes]",
flags: {
model: {
type: "string",
Expand Down Expand Up @@ -41,11 +41,19 @@ export default defineCommand({
"zh-CN": "清除该模型的所有自定义限流配置",
},
},
yes: {
type: "switch",
description: {
"en-US": "Skip the confirmation prompt for --delete",
"zh-CN": "使用 --delete 时跳过确认提示",
},
},
},
exampleArgs: [
"--model qwen-plus --rpm 60 --tpm 100000",
"--model qwen3-max --tpm 500000",
"--model qwen-plus --delete",
"--model qwen-plus --delete --yes",
"--model qwen-plus --rpm 60 --output json",
],
notes: [
Expand All @@ -55,6 +63,10 @@ export default defineCommand({
"zh-CN":
"未指定的字段将保留当前值(服务端 OVERLAY 合并);--delete 会清除所有自定义限流配置。",
},
{
"en-US": "--delete requires confirmation; pass --yes to skip the prompt in scripts.",
"zh-CN": "--delete 需要确认;脚本中可加 --yes 跳过交互提示。",
},
{
"en-US":
"Setting TPM without an existing QPM limit is rejected server-side — pass --rpm first or together.",
Expand Down Expand Up @@ -98,6 +110,13 @@ export default defineCommand({
return;
}

if (flags.delete) {
await confirmDangerousAction(
`Clear all custom rate limits for model ${modelName}.\nYour custom QPM/TPM configuration will be removed.`,
flags.yes ?? false,
);
}

const result = await ctx.client.requestJson<{ request_id?: string }>({
path: modelsLimitsPath(),
method: "POST",
Expand Down
36 changes: 36 additions & 0 deletions packages/commands/tests/e2e/dataset.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,42 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: dataset (offline)", () => {
expect(exitCode, stdout + stderr).not.toBe(0);
expect(`${stdout}\n${stderr}`).toMatch(/--schema video is not supported/);
});

test("dataset delete --help 展示 --yes", async () => {
const { stderr, exitCode } = await runCommandE2e(DATASET_ROUTES, [
"dataset",
"delete",
"--help",
]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toMatch(/--yes/i);
});

test("dataset delete --dry-run 发出结构化动作", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(DATASET_ROUTES, [
"dataset",
"delete",
"--file-id",
"file-id-xxx",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{ action: string }>(stdout);
expect(data.action).toBe("dataset.delete");
});

test("dataset delete 非 TTY 无 --yes 报 USAGE (2)", async () => {
const { stderr, exitCode } = await runCommandE2e(DATASET_ROUTES, [
"dataset",
"delete",
"--file-id",
"file-id-xxx",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--yes/);
});
});

describe.skipIf(!isDashScopeE2EReady())("e2e: dataset (DashScope)", () => {
Expand Down
19 changes: 19 additions & 0 deletions packages/commands/tests/e2e/deploy.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,25 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: deploy (offline)", () => {
const data = parseStdoutJson<{ action: string }>(stdout);
expect(data.action).toBe(`deploy.${sub}`);
});

test("deploy delete --help 展示 --yes", async () => {
const { stderr, exitCode } = await runCommandE2e(DEPLOY_ROUTES, ["deploy", "delete", "--help"]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toMatch(/--yes/i);
});

test("deploy delete 非 TTY 无 --yes 报 USAGE (2)", async () => {
// --skip-precheck 保证确认门在发任何网络请求前触发
const { stderr, exitCode } = await runCommandE2e(DEPLOY_ROUTES, [
"deploy",
"delete",
"--deployed-model",
"dep-xxx",
"--skip-precheck",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--yes/);
});
});

describe.skipIf(!isDashScopeE2EReady())("e2e: deploy (DashScope)", () => {
Expand Down
21 changes: 21 additions & 0 deletions packages/commands/tests/e2e/finetune.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,27 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: finetune (offline)", () => {
expect(data.action).toBe(`finetune.${sub}`);
});

test("finetune delete --help 展示 --yes", async () => {
const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [
"finetune",
"delete",
"--help",
]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toMatch(/--yes/i);
});

test("finetune delete 非 TTY 无 --yes 报 USAGE (2)", async () => {
const { stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [
"finetune",
"delete",
"--job-id",
"ft-xxx",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--yes/);
});

test("finetune create --dry-run 解析多 datasets 中的空白", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(FINETUNE_ROUTES, [
"finetune",
Expand Down
12 changes: 12 additions & 0 deletions packages/commands/tests/e2e/quota.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe("e2e: quota", () => {
expect(stderr).toContain("--rpm");
expect(stderr).toContain("--tpm");
expect(stderr).toContain("--delete");
expect(stderr).toContain("--yes");
});

test("quota request 作为 quota update 的兼容别名可用", async () => {
Expand Down Expand Up @@ -92,6 +93,17 @@ describe("e2e: quota", () => {
expect(stderr).toContain("cannot be combined");
});

test("quota update --delete 非 TTY 无 --yes 报 USAGE (2)", async () => {
// 注入假 key 让 apiKey 鉴权通过;确认门在发任何网络请求前触发
const { stderr, exitCode } = await runCommandE2e(
QUOTA_ROUTES,
["quota", "update", "--model", "qwen-plus", "--delete"],
{ DASHSCOPE_API_KEY: "sk-e2e-quota-delete" },
);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--yes/);
});

test("quota update --rpm 负数报错", async () => {
const { stderr, exitCode } = await runCommandE2e(QUOTA_ROUTES, [
"quota",
Expand Down
18 changes: 12 additions & 6 deletions skills/bailian-cli/reference/quota.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ bl quota list --output json

### `bl quota update`

| Field | Value |
| ------------------ | -------------------------------------------------------------------- |
| **Name** | `quota update` |
| **Description** | Update model rate limits (QPM/TPM), or clear them with --delete |
| **Authentication** | API Key |
| **Usage** | `bl quota update --model <model> [--rpm <n>] [--tpm <n>] [--delete]` |
| Field | Value |
| ------------------ | ---------------------------------------------------------------------------- |
| **Name** | `quota update` |
| **Description** | Update model rate limits (QPM/TPM), or clear them with --delete |
| **Authentication** | API Key |
| **Usage** | `bl quota update --model <model> [--rpm <n>] [--tpm <n>] [--delete] [--yes]` |

#### Flags

Expand All @@ -164,12 +164,14 @@ bl quota list --output json
| `--rpm <n>` | number | no | Max requests per minute (QPM) |
| `--tpm <n>` | number | no | Max tokens per minute (TPM) |
| `--delete` | switch | no | Clear all custom rate limits for the model |
| `--yes` | switch | no | Skip the confirmation prompt for --delete |
| `--api-key <key>` | string | no | API key |
| `--base-url <url>` | string | no | API base URL |

#### Notes

- Fields you omit keep their current values (server-side OVERLAY merge); --delete clears all custom limits.
- --delete requires confirmation; pass --yes to skip the prompt in scripts.
- Setting TPM without an existing QPM limit is rejected server-side — pass --rpm first or together.

#### Examples
Expand All @@ -186,6 +188,10 @@ bl quota update --model qwen3-max --tpm 500000
bl quota update --model qwen-plus --delete
```

```bash
bl quota update --model qwen-plus --delete --yes
```

```bash
bl quota update --model qwen-plus --rpm 60 --output json
```
Loading