Skip to content

Commit 01a62eb

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/main' into feat/mcp-support-sse
2 parents 798ce59 + 94f9dbb commit 01a62eb

53 files changed

Lines changed: 1143 additions & 1152 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

packages/cli/tests/e2e/registry.smoke.e2e.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,40 @@ const commandPaths = Object.keys(commands).sort();
77
const groupPaths = deriveGroupPaths(commandPaths);
88

99
describe("e2e: bl registry smoke", () => {
10-
test("根帮助展示 bl 与全局 flag", async () => {
10+
test("根帮助展示 bl、逐命令鉴权域与全局 flag", async () => {
1111
const { stderr, exitCode } = await runCli(["--help"]);
1212
expect(exitCode, stderr).toBe(0);
1313
expect(stderr).toMatch(/\bbl\b/i);
14+
expect(stderr).not.toMatch(/COMMAND\s+AUTH\s+DESCRIPTION/);
15+
expect(stderr).toMatch(/app call\s+\[API Key\]\s+Call a Bailian application/);
16+
expect(stderr).toMatch(/app list\s+\[Console\]\s+List Bailian applications/);
17+
expect(stderr).toMatch(/token-plan create-key\s+\[AK\/SK\]\s+Create a Token Plan API key/);
18+
expect(stderr).toMatch(/config show\s+\[No Auth\]\s+Display current configuration/);
1419
expect(stderr).toMatch(/--base-url/);
1520
expect(stderr).toMatch(/--console-region/);
1621
expect(stderr).toMatch(/--console-site/);
1722
expect(stderr).toMatch(/--console-switch-agent/);
1823
expect(stderr).not.toMatch(/^\s*--region\s/m);
1924
});
2025

26+
test("分组帮助按叶子命令展示不同鉴权域", async () => {
27+
const { stderr, exitCode } = await runCli(["app", "--help"]);
28+
expect(exitCode, stderr).toBe(0);
29+
expect(stderr).toMatch(/app call\s+\[API Key\]\s+Call a Bailian application/);
30+
expect(stderr).toMatch(/app list\s+\[Console\]\s+List Bailian applications/);
31+
});
32+
33+
test.each([
34+
[["text", "chat"], "API Key"],
35+
[["app", "list"], "Console"],
36+
[["token-plan", "list-seats"], "AK/SK"],
37+
[["config", "show"], "No Auth"],
38+
] as const)("%s --help 明确展示鉴权域 %s", async (commandPath, authLabel) => {
39+
const { stderr, exitCode } = await runCli([...commandPath, "--help"]);
40+
expect(exitCode, stderr).toBe(0);
41+
expect(stderr).toContain(`Authentication: ${authLabel}`);
42+
});
43+
2144
test("quota check --help:Flags 含 console 域鉴权 flag,Global Flags 全量列出", async () => {
2245
const { stderr, exitCode } = await runCli(["quota", "check", "--help"]);
2346
expect(exitCode, stderr).toBe(0);

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.14.2",
3+
"version": "1.14.3",
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/console/call.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default defineCommand({
2525
},
2626
},
2727
exampleArgs: [
28-
`--api zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierQuota --data '{"queryFreeTierQuotaRequest":{"models":["qwen3-max"]}}'`,
28+
`--api zeldaEasy.bailian-commerce.freeTrial.queryFreeTierQuota --data '{"queryFreeTierQuotaRequest":{"models":["qwen3-max"]}}'`,
2929
`--api some.api.name --data '{"key":"value"}' --console-region cn-beijing`,
3030
],
3131
async run(ctx) {

packages/commands/src/commands/usage/freetier.ts

Lines changed: 18 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,22 @@
1-
import { defineCommand, detectOutputFormat, fetchModelList, type Client } from "bailian-cli-core";
1+
import { defineCommand, detectOutputFormat, unwrapResponse } from "bailian-cli-core";
22
import { emitResult } from "bailian-cli-runtime";
3-
4-
const ACTIVATE_API = "zeldaEasy.broadscope-bailian.freeTrial.batchActivateFreeTierOnly";
5-
const DEACTIVATE_API = "zeldaEasy.broadscope-bailian.freeTrial.batchDeactivateFreeTierOnly";
6-
const FREE_TIER_API = "zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierQuota";
7-
const FREE_TIER_ONLY_STATUS_API = "zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierOnlyStatus";
8-
9-
interface FreeTierQuota {
10-
model: string;
11-
quotaTotal: number;
12-
quotaInitTotal: number;
13-
}
14-
15-
interface FreeTierOnlyStatus {
16-
model: string;
17-
freeTierOnly: boolean;
18-
}
3+
import {
4+
FREE_TIER_API,
5+
FREE_TIER_ONLY_STATUS_API,
6+
extractFreeTierOnlyStatuses,
7+
extractQuotas,
8+
fetchAllModels,
9+
pollFreeTierBatch,
10+
} from "./shared.ts";
11+
12+
const ACTIVATE_API = "zeldaEasy.bailian-commerce.freeTrial.batchActivateFreeTierOnly";
13+
const DEACTIVATE_API = "zeldaEasy.bailian-commerce.freeTrial.batchDeactivateFreeTierOnly";
1914

2015
interface BatchResultFailure {
2116
failureModelId: string;
2217
errorCode: string;
2318
}
2419

25-
function getNestedRecord(
26-
obj: Record<string, unknown>,
27-
key: string,
28-
): Record<string, unknown> | undefined {
29-
const val = obj[key];
30-
if (val && typeof val === "object" && !Array.isArray(val)) return val as Record<string, unknown>;
31-
return undefined;
32-
}
33-
34-
function extractResponseData(result: Record<string, unknown>): Record<string, unknown> {
35-
const data = getNestedRecord(result, "data");
36-
if (!data) return result;
37-
38-
const dataV2 = getNestedRecord(data, "DataV2");
39-
if (dataV2) {
40-
const inner = getNestedRecord(dataV2, "data");
41-
const innerData = inner ? getNestedRecord(inner, "data") : undefined;
42-
return innerData ?? inner ?? dataV2;
43-
}
44-
45-
const direct = getNestedRecord(data, "data");
46-
return direct ?? data;
47-
}
48-
49-
const POLL_INTERVAL_MS = 500;
50-
const MAX_POLLS = 20;
51-
52-
async function pollUntilDone(
53-
client: Client,
54-
api: string,
55-
requestKey: string,
56-
models: string[],
57-
): Promise<unknown> {
58-
let nextTaskId: string | undefined;
59-
60-
for (let attempt = 0; attempt < MAX_POLLS; attempt++) {
61-
const requestData = {
62-
[requestKey]: nextTaskId ? { taskId: nextTaskId } : { models },
63-
};
64-
65-
const raw = await client.console(api, requestData);
66-
67-
const resp = extractResponseData(raw as Record<string, unknown>);
68-
if (resp.taskId && Object.keys(resp).length === 1) {
69-
nextTaskId = resp.taskId as string;
70-
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
71-
continue;
72-
}
73-
return raw;
74-
}
75-
return null;
76-
}
77-
78-
async function fetchAllModelNames(client: Client): Promise<string[]> {
79-
const allModels: Record<string, unknown>[] = [];
80-
let page = 1;
81-
while (true) {
82-
const result = await fetchModelList((api, data) => client.console(api, data), {
83-
pageNo: page,
84-
pageSize: 50,
85-
});
86-
allModels.push(...result.models);
87-
if (allModels.length >= result.total) break;
88-
page++;
89-
}
90-
return allModels.map((item) => item.model as string).filter(Boolean);
91-
}
92-
9320
export default defineCommand({
9421
description:
9522
"Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable",
@@ -161,7 +88,7 @@ export default defineCommand({
16188
}
16289

16390
if (!modelFlag) {
164-
models = await fetchAllModelNames(ctx.client);
91+
models = (await fetchAllModels(ctx.client)).map((model) => model.name);
16592
}
16693

16794
if (off) {
@@ -172,12 +99,10 @@ export default defineCommand({
17299
}),
173100
]);
174101

175-
const quotaData = extractResponseData(quotaResult as Record<string, unknown>);
176-
const quotas = (quotaData.freeTierQuotas ?? []) as FreeTierQuota[];
102+
const quotas = extractQuotas(quotaResult);
177103
const quotaMap = new Map(quotas.map((quota) => [quota.model, quota]));
178104

179-
const stopData = extractResponseData(stopResult as Record<string, unknown>);
180-
const stopStatuses = (stopData.freeTierOnlyStatuses ?? []) as FreeTierOnlyStatus[];
105+
const stopStatuses = extractFreeTierOnlyStatuses(stopResult);
181106
const stopMap = new Map(stopStatuses.map((status) => [status.model, status.freeTierOnly]));
182107

183108
for (const name of models) {
@@ -192,21 +117,21 @@ export default defineCommand({
192117
);
193118
continue;
194119
}
195-
await pollUntilDone(ctx.client, api, requestKey, [name]);
120+
await pollFreeTierBatch(ctx.client, api, requestKey, [name]);
196121
process.stdout.write(`Disabled auto-stop for "${name}".\n`);
197122
}
198123
return;
199124
}
200125

201126
const jsonResults: unknown[] = [];
202127
for (const name of models) {
203-
const result = await pollUntilDone(ctx.client, api, requestKey, [name]);
128+
const result = await pollFreeTierBatch(ctx.client, api, requestKey, [name]);
204129
if (format === "json") {
205130
jsonResults.push(result);
206131
continue;
207132
}
208133
if (result) {
209-
const resultData = extractResponseData(result as Record<string, unknown>);
134+
const resultData = unwrapResponse(result as Record<string, unknown>);
210135
const failureModels = (resultData.failureModels as BatchResultFailure[]) ?? [];
211136
if (failureModels.length > 0) {
212137
process.stderr.write(

packages/commands/src/commands/usage/shared.ts

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ export async function fetchAllModels(client: Client): Promise<ModelInfo[]> {
102102
// Free-tier quota
103103
// ---------------------------------------------------------------------------
104104

105-
export const FREE_TIER_API = "zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierQuota";
105+
export const FREE_TIER_API = "zeldaEasy.bailian-commerce.freeTrial.queryFreeTierQuota";
106106
export const FREE_TIER_ONLY_STATUS_API =
107-
"zeldaEasy.broadscope-bailian.freeTrial.queryFreeTierOnlyStatus";
107+
"zeldaEasy.bailian-commerce.freeTrial.queryFreeTierOnlyStatus";
108108

109109
export interface FreeTierQuota {
110110
model: string;
@@ -257,22 +257,27 @@ export interface ListStatisticResponse {
257257
}
258258

259259
const POLL_INTERVAL_MS = 500;
260-
const MAX_POLLS = 30;
260+
const DEFAULT_MAX_POLLS = 30;
261261

262-
export async function pollTelemetryApi(
262+
/**
263+
* Poll a console API until it returns a terminal (non task-id) response.
264+
* The gateway answers an async request with a bare `{taskId}` envelope; the
265+
* caller re-issues with that id until real data arrives or the budget runs out.
266+
* `buildRequest` shapes each attempt (initial call vs. taskId follow-up) so the
267+
* same loop serves every request-wrapper convention (telemetry `reqDTO`,
268+
* free-tier batch `…Request`).
269+
*/
270+
export async function pollConsoleUntilDone(
263271
client: Client,
264272
api: string,
265-
reqDTO: Record<string, unknown>,
273+
buildRequest: (taskId: string | undefined) => Record<string, unknown>,
274+
maxPolls = DEFAULT_MAX_POLLS,
266275
): Promise<unknown> {
267276
let nextTaskId: string | undefined;
268277

269-
for (let attempt = 0; attempt < MAX_POLLS; attempt++) {
270-
const requestData = nextTaskId
271-
? { reqDTO: { ...reqDTO, asyncTaskId: nextTaskId } }
272-
: { reqDTO };
273-
274-
const raw = await client.console(api, requestData);
275-
const resp = extractResponseData(raw as Record<string, unknown>);
278+
for (let attempt = 0; attempt < maxPolls; attempt++) {
279+
const raw = await client.console(api, buildRequest(nextTaskId));
280+
const resp = unwrapResponse(raw as Record<string, unknown>);
276281

277282
if (resp.taskId && Object.keys(resp).length === 1) {
278283
nextTaskId = resp.taskId as string;
@@ -284,6 +289,32 @@ export async function pollTelemetryApi(
284289
return null;
285290
}
286291

292+
/** Telemetry APIs wrap the payload in `reqDTO` and echo the task id as `asyncTaskId`. */
293+
export async function pollTelemetryApi(
294+
client: Client,
295+
api: string,
296+
reqDTO: Record<string, unknown>,
297+
): Promise<unknown> {
298+
return pollConsoleUntilDone(client, api, (taskId) =>
299+
taskId ? { reqDTO: { ...reqDTO, asyncTaskId: taskId } } : { reqDTO },
300+
);
301+
}
302+
303+
/** Free-tier batch activate/deactivate wrap the payload in `requestKey` and echo `taskId`. */
304+
export async function pollFreeTierBatch(
305+
client: Client,
306+
api: string,
307+
requestKey: string,
308+
models: string[],
309+
): Promise<unknown> {
310+
return pollConsoleUntilDone(
311+
client,
312+
api,
313+
(taskId) => ({ [requestKey]: taskId ? { taskId } : { models } }),
314+
20,
315+
);
316+
}
317+
287318
export function extractOverviewData(result: unknown): OverviewStatistic | undefined {
288319
const resp = extractResponseData(result as Record<string, unknown>);
289320
if (resp.callSuccessCount !== undefined || resp.usages !== undefined) {

0 commit comments

Comments
 (0)