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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and

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

## [2.2.0] - 2026-10-12

### Changed

- **Token Plan usage window** — `bl usage token-plan` now reports a single monthly quota (usage percentage and reset time) instead of the previous 5-hour and 1-week windows.

## [2.1.0] - 2026-09-28

### Added
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

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

## [2.2.0] - 2026-10-12

### 变更

- **Token Plan 额度窗口** —— `bl usage token-plan` 现在只展示月度额度(使用百分比与重置时间),不再显示此前的 5 小时和 1 周窗口。

## [2.1.0] - 2026-09-28

### 新增
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bailian-cli",
"version": "2.1.0",
"version": "2.2.0",
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
"keywords": [
"agent",
Expand Down
2 changes: 1 addition & 1 deletion packages/commands/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bailian-cli-commands",
"version": "2.1.0",
"version": "2.2.0",
"description": "Command library for bailian-cli products (knowledge, memory, media, …). See https://www.npmjs.com/package/bailian-cli for usage.",
"homepage": "https://bailian.console.aliyun.com/cli",
"bugs": {
Expand Down
33 changes: 10 additions & 23 deletions packages/commands/src/commands/usage/token-plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,18 @@ import { printQuotaBox, readNumber } from "./quota-box.ts";
const TOKEN_PLAN_USAGE_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage";

interface TokenPlanUsage {
per5HourPercentage?: number;
per5HourResetTime?: number;
per1WeekPercentage?: number;
per1WeekResetTime?: number;
per1MonthPercentage?: number;
per1MonthResetTime?: number;
}

function readUsage(result: unknown): TokenPlanUsage {
const response = unwrapResponse(result as Record<string, unknown>);
const usage: TokenPlanUsage = {};

const per5HourPercentage = readNumber(response.per5HourPercentage);
if (per5HourPercentage !== undefined) usage.per5HourPercentage = per5HourPercentage;
const per5HourResetTime = readNumber(response.per5HourResetTime);
if (per5HourResetTime !== undefined) usage.per5HourResetTime = per5HourResetTime;
const per1WeekPercentage = readNumber(response.per1WeekPercentage);
if (per1WeekPercentage !== undefined) usage.per1WeekPercentage = per1WeekPercentage;
const per1WeekResetTime = readNumber(response.per1WeekResetTime);
if (per1WeekResetTime !== undefined) usage.per1WeekResetTime = per1WeekResetTime;
const per1MonthPercentage = readNumber(response.per1MonthPercentage);
if (per1MonthPercentage !== undefined) usage.per1MonthPercentage = per1MonthPercentage;
const per1MonthResetTime = readNumber(response.per1MonthResetTime);
if (per1MonthResetTime !== undefined) usage.per1MonthResetTime = per1MonthResetTime;

return usage;
}
Expand All @@ -32,18 +26,11 @@ function printView(usage: TokenPlanUsage, generatedAt: number): void {
"Token Plan Usage",
[
{
label: "5-hour quota",
label: "Monthly quota",
emptyMessage:
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
percentage: usage.per5HourPercentage,
resetTime: usage.per5HourResetTime,
},
{
label: "1-week quota",
emptyMessage:
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
percentage: usage.per1WeekPercentage,
resetTime: usage.per1WeekResetTime,
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
percentage: usage.per1MonthPercentage,
resetTime: usage.per1MonthResetTime,
},
],
generatedAt,
Expand Down
18 changes: 5 additions & 13 deletions packages/commands/tests/e2e/usage-token-plan.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,20 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage token-plan(Console)", () =
if (isConsoleAuthFailure(result)) return;
expect(result.exitCode, result.stderr).toBe(0);
const data = parseStdoutJson<{
per5HourPercentage?: number;
per5HourResetTime?: number;
per1WeekPercentage?: number;
per1WeekResetTime?: number;
per1MonthPercentage?: number;
per1MonthResetTime?: number;
}>(result.stdout);
const fields = [
data.per5HourPercentage,
data.per5HourResetTime,
data.per1WeekPercentage,
data.per1WeekResetTime,
];
const fields = [data.per1MonthPercentage, data.per1MonthResetTime];
for (const field of fields) {
if (field !== undefined) expect(field).toBeTypeOf("number");
}
});

test("usage token-plan 默认渲染生成时间与两个额度窗口", async () => {
test("usage token-plan 默认渲染生成时间与月额度窗口", async () => {
const result = await runCommandE2e(USAGE_ROUTES, ["usage", "token-plan"]);
if (isConsoleAuthFailure(result)) return;
expect(result.exitCode, result.stderr).toBe(0);
expect(result.stdout).toContain("Generated at:");
expect(result.stdout).toContain("5-hour quota");
expect(result.stdout).toContain("1-week quota");
expect(result.stdout).toContain("Monthly quota");
});
});
82 changes: 23 additions & 59 deletions packages/commands/tests/token-plan-usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,11 @@ async function runTokenPlan(response: Record<string, unknown>, output?: string):
} as never);
}

function makeUsageResponse(
per5HourPercentage?: number,
per1WeekPercentage = per5HourPercentage,
): Record<string, unknown> {
function makeUsageResponse(per1MonthPercentage?: number): Record<string, unknown> {
const usage: Record<string, number> = {};
if (per5HourPercentage !== undefined) {
usage.per5HourPercentage = per5HourPercentage;
if (per5HourPercentage !== 0) usage.per5HourResetTime = 1_786_000_000_000;
}
if (per1WeekPercentage !== undefined) {
usage.per1WeekPercentage = per1WeekPercentage;
if (per1WeekPercentage !== 0) usage.per1WeekResetTime = 1_786_100_000_000;
if (per1MonthPercentage !== undefined) {
usage.per1MonthPercentage = per1MonthPercentage;
if (per1MonthPercentage !== 0) usage.per1MonthResetTime = 1_786_000_000_000;
}

return wrapResponse(usage);
Expand Down Expand Up @@ -86,91 +79,62 @@ describe("usage token-plan view", () => {
expect(renderedOutput).not.toContain("\u2588".repeat(11));
});

test("accepts missing reset times when the quota usage is zero", async () => {
test("accepts a missing reset time when the quota usage is zero", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(0));

expect(output.join("")).toContain("Resets: not applicable (no usage yet)");
});

test("allows one unused quota window without masking another reset time", async () => {
test("renders the monthly reset time when usage is present", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(0, 0.5));
await runTokenPlan(makeUsageResponse(0.5));

const renderedOutput = output.join("");
expect(renderedOutput).toContain("Resets: not applicable (no usage yet)");
expect(renderedOutput).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
expect(output.join("")).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
});

test("renders missing quota windows as possibly unlimited", async () => {
test("renders a missing monthly quota as possibly unlimited", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse());

const renderedOutput = output.join("");
expect(renderedOutput).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).toContain(
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
);
});

test("renders only the missing quota window as possibly unlimited", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(undefined, 0.5));

const renderedOutput = output.join("");
expect(renderedOutput).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).not.toContain(
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
expect(output.join("")).toContain(
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
});

test("renders a window with a missing percentage as possibly unlimited even when its reset time is present", async () => {
test("renders the monthly window as possibly unlimited when its percentage is missing even if the reset time is present", async () => {
const output = captureStdout();

await runTokenPlan(wrapResponse({ per5HourResetTime: 1_786_000_000_000 }));
await runTokenPlan(wrapResponse({ per1MonthResetTime: 1_786_000_000_000 }));

expect(output.join("")).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
);
});

test("treats non-numeric quota fields as absent instead of failing", async () => {
const output = captureStdout();

await runTokenPlan(
wrapResponse({ per5HourPercentage: "not-a-number", per1WeekPercentage: Number.NaN }),
);
await runTokenPlan(wrapResponse({ per1MonthPercentage: "not-a-number" }));

const renderedOutput = output.join("");
expect(renderedOutput).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).toContain(
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
expect(output.join("")).toContain(
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
);
});
});

describe("usage token-plan json", () => {
test("outputs the four core usage fields with --output json", async () => {
test("outputs the monthly usage fields with --output json", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(0.5, 0.25), "json");
await runTokenPlan(makeUsageResponse(0.5), "json");

expect(JSON.parse(output.join(""))).toEqual({
per5HourPercentage: 0.5,
per5HourResetTime: 1_786_000_000_000,
per1WeekPercentage: 0.25,
per1WeekResetTime: 1_786_100_000_000,
per1MonthPercentage: 0.5,
per1MonthResetTime: 1_786_000_000_000,
});
});

Expand All @@ -186,10 +150,10 @@ describe("usage token-plan json", () => {
const output = captureStdout();

await runTokenPlan(
wrapResponse({ per5HourPercentage: "not-a-number", per1WeekPercentage: 0 }),
wrapResponse({ per1MonthPercentage: "not-a-number", per1MonthResetTime: 1_786_000_000_000 }),
"json",
);

expect(JSON.parse(output.join(""))).toEqual({ per1WeekPercentage: 0 });
expect(JSON.parse(output.join(""))).toEqual({ per1MonthResetTime: 1_786_000_000_000 });
});
});
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bailian-cli-core",
"version": "2.1.0",
"version": "2.2.0",
"description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.",
"homepage": "https://bailian.console.aliyun.com/cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion packages/kscli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "knowledge-studio-cli",
"version": "2.1.0",
"version": "2.2.0",
"description": "Lightweight RAG CLI for Aliyun Model Studio — focused on knowledge-base retrieval.",
"keywords": [
"alibaba-cloud",
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bailian-cli-runtime",
"version": "2.1.0",
"version": "2.2.0",
"description": "Runtime framework for bailian-cli (createCli, registry, args, output, pipeline). See https://www.npmjs.com/package/bailian-cli for usage.",
"homepage": "https://bailian.console.aliyun.com/cli",
"bugs": {
Expand Down
2 changes: 1 addition & 1 deletion skills/bailian-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: bailian-cli
metadata:
version: "2.1.0"
version: "2.2.0"
requires:
bins: ["bl"]
description: >-
Expand Down
2 changes: 1 addition & 1 deletion skills/bailian-finetune/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: bailian-finetune
metadata:
version: "2.1.0"
version: "2.2.0"
requires:
bins: ["bl"]
description: >-
Expand Down
2 changes: 1 addition & 1 deletion skills/bailian-gen/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: bailian-gen
metadata:
version: "2.1.0"
version: "2.2.0"
requires:
bins: ["bl"]
description: >-
Expand Down
2 changes: 1 addition & 1 deletion skills/bailian-managed-agent/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: bailian-managed-agent
metadata:
version: "2.1.0"
version: "2.2.0"
requires:
bins: ["bl"]
description: >-
Expand Down
2 changes: 1 addition & 1 deletion skills/bailian-protocol/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: bailian-protocol
metadata:
version: "2.1.0"
version: "2.2.0"
requires:
bins: ["bl"]
description: >-
Expand Down
2 changes: 1 addition & 1 deletion skills/bailian-sandbox/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: bailian-sandbox
metadata:
version: "2.1.0"
version: "2.2.0"
requires:
bins: ["bl"]
description: >-
Expand Down
2 changes: 1 addition & 1 deletion skills/bailian-web-search/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: bailian-web-search
metadata:
version: "2.1.0"
version: "2.2.0"
requires:
bins: ["bl"]
description: >-
Expand Down
Loading