Skip to content

Commit fd8967f

Browse files
authored
Merge pull request #246 from modelstudioai/feat/usage-tokenplan-1010-2
refactor(usage): 将 tokenplan 额度窗口从5小时和1周改为月度
2 parents 8bbbbc7 + 8942867 commit fd8967f

17 files changed

Lines changed: 62 additions & 107 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ 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+
## [2.2.0] - 2026-10-12
10+
11+
### Changed
12+
13+
- **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.
14+
915
## [2.1.0] - 2026-09-28
1016

1117
### Added

‎CHANGELOG.zh.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

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

9+
## [2.2.0] - 2026-10-12
10+
11+
### 变更
12+
13+
- **Token Plan 额度窗口** —— `bl usage token-plan` 现在只展示月度额度(使用百分比与重置时间),不再显示此前的 5 小时和 1 周窗口。
14+
915
## [2.1.0] - 2026-09-28
1016

1117
### 新增

‎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": "2.1.0",
3+
"version": "2.2.0",
44
"description": "CLI for Aliyun Model Studio (DashScope) AI Platform.",
55
"keywords": [
66
"agent",

‎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": "2.1.0",
3+
"version": "2.2.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": {

‎packages/commands/src/commands/usage/token-plan.ts‎

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,18 @@ import { printQuotaBox, readNumber } from "./quota-box.ts";
55
const TOKEN_PLAN_USAGE_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage";
66

77
interface TokenPlanUsage {
8-
per5HourPercentage?: number;
9-
per5HourResetTime?: number;
10-
per1WeekPercentage?: number;
11-
per1WeekResetTime?: number;
8+
per1MonthPercentage?: number;
9+
per1MonthResetTime?: number;
1210
}
1311

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

18-
const per5HourPercentage = readNumber(response.per5HourPercentage);
19-
if (per5HourPercentage !== undefined) usage.per5HourPercentage = per5HourPercentage;
20-
const per5HourResetTime = readNumber(response.per5HourResetTime);
21-
if (per5HourResetTime !== undefined) usage.per5HourResetTime = per5HourResetTime;
22-
const per1WeekPercentage = readNumber(response.per1WeekPercentage);
23-
if (per1WeekPercentage !== undefined) usage.per1WeekPercentage = per1WeekPercentage;
24-
const per1WeekResetTime = readNumber(response.per1WeekResetTime);
25-
if (per1WeekResetTime !== undefined) usage.per1WeekResetTime = per1WeekResetTime;
16+
const per1MonthPercentage = readNumber(response.per1MonthPercentage);
17+
if (per1MonthPercentage !== undefined) usage.per1MonthPercentage = per1MonthPercentage;
18+
const per1MonthResetTime = readNumber(response.per1MonthResetTime);
19+
if (per1MonthResetTime !== undefined) usage.per1MonthResetTime = per1MonthResetTime;
2620

2721
return usage;
2822
}
@@ -32,18 +26,11 @@ function printView(usage: TokenPlanUsage, generatedAt: number): void {
3226
"Token Plan Usage",
3327
[
3428
{
35-
label: "5-hour quota",
29+
label: "Monthly quota",
3630
emptyMessage:
37-
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
38-
percentage: usage.per5HourPercentage,
39-
resetTime: usage.per5HourResetTime,
40-
},
41-
{
42-
label: "1-week quota",
43-
emptyMessage:
44-
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
45-
percentage: usage.per1WeekPercentage,
46-
resetTime: usage.per1WeekResetTime,
31+
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
32+
percentage: usage.per1MonthPercentage,
33+
resetTime: usage.per1MonthResetTime,
4734
},
4835
],
4936
generatedAt,

‎packages/commands/tests/e2e/usage-token-plan.e2e.test.ts‎

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,20 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage token-plan(Console)", () =
5050
if (isConsoleAuthFailure(result)) return;
5151
expect(result.exitCode, result.stderr).toBe(0);
5252
const data = parseStdoutJson<{
53-
per5HourPercentage?: number;
54-
per5HourResetTime?: number;
55-
per1WeekPercentage?: number;
56-
per1WeekResetTime?: number;
53+
per1MonthPercentage?: number;
54+
per1MonthResetTime?: number;
5755
}>(result.stdout);
58-
const fields = [
59-
data.per5HourPercentage,
60-
data.per5HourResetTime,
61-
data.per1WeekPercentage,
62-
data.per1WeekResetTime,
63-
];
56+
const fields = [data.per1MonthPercentage, data.per1MonthResetTime];
6457
for (const field of fields) {
6558
if (field !== undefined) expect(field).toBeTypeOf("number");
6659
}
6760
});
6861

69-
test("usage token-plan 默认渲染生成时间与两个额度窗口", async () => {
62+
test("usage token-plan 默认渲染生成时间与月额度窗口", async () => {
7063
const result = await runCommandE2e(USAGE_ROUTES, ["usage", "token-plan"]);
7164
if (isConsoleAuthFailure(result)) return;
7265
expect(result.exitCode, result.stderr).toBe(0);
7366
expect(result.stdout).toContain("Generated at:");
74-
expect(result.stdout).toContain("5-hour quota");
75-
expect(result.stdout).toContain("1-week quota");
67+
expect(result.stdout).toContain("Monthly quota");
7668
});
7769
});

‎packages/commands/tests/token-plan-usage.test.ts‎

Lines changed: 23 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,11 @@ async function runTokenPlan(response: Record<string, unknown>, output?: string):
3232
} as never);
3333
}
3434

35-
function makeUsageResponse(
36-
per5HourPercentage?: number,
37-
per1WeekPercentage = per5HourPercentage,
38-
): Record<string, unknown> {
35+
function makeUsageResponse(per1MonthPercentage?: number): Record<string, unknown> {
3936
const usage: Record<string, number> = {};
40-
if (per5HourPercentage !== undefined) {
41-
usage.per5HourPercentage = per5HourPercentage;
42-
if (per5HourPercentage !== 0) usage.per5HourResetTime = 1_786_000_000_000;
43-
}
44-
if (per1WeekPercentage !== undefined) {
45-
usage.per1WeekPercentage = per1WeekPercentage;
46-
if (per1WeekPercentage !== 0) usage.per1WeekResetTime = 1_786_100_000_000;
37+
if (per1MonthPercentage !== undefined) {
38+
usage.per1MonthPercentage = per1MonthPercentage;
39+
if (per1MonthPercentage !== 0) usage.per1MonthResetTime = 1_786_000_000_000;
4740
}
4841

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

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

9285
await runTokenPlan(makeUsageResponse(0));
9386

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

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

100-
await runTokenPlan(makeUsageResponse(0, 0.5));
93+
await runTokenPlan(makeUsageResponse(0.5));
10194

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

107-
test("renders missing quota windows as possibly unlimited", async () => {
98+
test("renders a missing monthly quota as possibly unlimited", async () => {
10899
const output = captureStdout();
109100

110101
await runTokenPlan(makeUsageResponse());
111102

112-
const renderedOutput = output.join("");
113-
expect(renderedOutput).toContain(
114-
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
115-
);
116-
expect(renderedOutput).toContain(
117-
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
118-
);
119-
});
120-
121-
test("renders only the missing quota window as possibly unlimited", async () => {
122-
const output = captureStdout();
123-
124-
await runTokenPlan(makeUsageResponse(undefined, 0.5));
125-
126-
const renderedOutput = output.join("");
127-
expect(renderedOutput).toContain(
128-
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
129-
);
130-
expect(renderedOutput).not.toContain(
131-
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
103+
expect(output.join("")).toContain(
104+
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
132105
);
133-
expect(renderedOutput).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
134106
});
135107

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

139-
await runTokenPlan(wrapResponse({ per5HourResetTime: 1_786_000_000_000 }));
111+
await runTokenPlan(wrapResponse({ per1MonthResetTime: 1_786_000_000_000 }));
140112

141113
expect(output.join("")).toContain(
142-
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
114+
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
143115
);
144116
});
145117

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

149-
await runTokenPlan(
150-
wrapResponse({ per5HourPercentage: "not-a-number", per1WeekPercentage: Number.NaN }),
151-
);
121+
await runTokenPlan(wrapResponse({ per1MonthPercentage: "not-a-number" }));
152122

153-
const renderedOutput = output.join("");
154-
expect(renderedOutput).toContain(
155-
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
156-
);
157-
expect(renderedOutput).toContain(
158-
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
123+
expect(output.join("")).toContain(
124+
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
159125
);
160126
});
161127
});
162128

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

167-
await runTokenPlan(makeUsageResponse(0.5, 0.25), "json");
133+
await runTokenPlan(makeUsageResponse(0.5), "json");
168134

169135
expect(JSON.parse(output.join(""))).toEqual({
170-
per5HourPercentage: 0.5,
171-
per5HourResetTime: 1_786_000_000_000,
172-
per1WeekPercentage: 0.25,
173-
per1WeekResetTime: 1_786_100_000_000,
136+
per1MonthPercentage: 0.5,
137+
per1MonthResetTime: 1_786_000_000_000,
174138
});
175139
});
176140

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

188152
await runTokenPlan(
189-
wrapResponse({ per5HourPercentage: "not-a-number", per1WeekPercentage: 0 }),
153+
wrapResponse({ per1MonthPercentage: "not-a-number", per1MonthResetTime: 1_786_000_000_000 }),
190154
"json",
191155
);
192156

193-
expect(JSON.parse(output.join(""))).toEqual({ per1WeekPercentage: 0 });
157+
expect(JSON.parse(output.join(""))).toEqual({ per1MonthResetTime: 1_786_000_000_000 });
194158
});
195159
});

‎packages/core/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-core",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.",
55
"homepage": "https://bailian.console.aliyun.com/cli",
66
"bugs": {

‎packages/kscli/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "knowledge-studio-cli",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Lightweight RAG CLI for Aliyun Model Studio — focused on knowledge-base retrieval.",
55
"keywords": [
66
"alibaba-cloud",

‎packages/runtime/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-runtime",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Runtime framework for bailian-cli (createCli, registry, args, output, pipeline). See https://www.npmjs.com/package/bailian-cli for usage.",
55
"homepage": "https://bailian.console.aliyun.com/cli",
66
"bugs": {

0 commit comments

Comments
 (0)