From e03103364e105ca198a3d911843ea16d536ae369 Mon Sep 17 00:00:00 2001 From: wangmingxiang Date: Mon, 14 Sep 2026 10:04:31 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(token-plan):=20=E6=96=B0=E5=A2=9E=20To?= =?UTF-8?q?ken=20Plan=20harness=20=E6=9D=83=E7=9B=8A=E9=A2=9D=E5=BA=A6?= =?UTF-8?q?=E7=94=A8=E9=87=8F=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 token-plan harness-quota 命令用于显示 Harness 权益额度使用情况 - 实现权限基于 Console 认证,支持通过 --type 参数筛选 harness 类型 - 集成权益与 Harness 列表数据,展示已发放与待发放额度明细 - 支持标准输出和 JSON 格式输出展示额度及使用比例 - 添加单元测试覆盖多种输出及边界情况 - 更新 CLI 命令注册及相关文档和 Help 信息 - 添加 E2E 测试验证新命令行为及请求参数传递正确性 --- packages/cli/src/commands.ts | 2 + .../src/commands/token-plan/harness-quota.ts | 169 ++++++++++++ .../commands/src/commands/token-plan/types.ts | 22 ++ packages/commands/src/index.ts | 1 + .../commands/tests/e2e/token-plan.e2e.test.ts | 97 ++++++- packages/commands/tests/e2e/topic-routes.ts | 1 + .../tests/token-plan-harness-quota.test.ts | 247 ++++++++++++++++++ skills/bailian-cli/SKILL.md | 1 + skills/bailian-cli/reference/index.md | 3 +- skills/bailian-cli/reference/token-plan.md | 46 +++- 10 files changed, 581 insertions(+), 8 deletions(-) create mode 100644 packages/commands/src/commands/token-plan/harness-quota.ts create mode 100644 packages/commands/tests/token-plan-harness-quota.test.ts diff --git a/packages/cli/src/commands.ts b/packages/cli/src/commands.ts index 4b9bf7864..ca649753e 100644 --- a/packages/cli/src/commands.ts +++ b/packages/cli/src/commands.ts @@ -130,6 +130,7 @@ import { tokenPlanCreateKey, tokenPlanAssignSeats, tokenPlanAddMember, + tokenPlanHarnessQuota, workspaceInit, pluginInstall, pluginLink, @@ -351,6 +352,7 @@ export const commands: Record = { "token-plan create-key": tokenPlanCreateKey, "token-plan assign-seats": tokenPlanAssignSeats, "token-plan add-member": tokenPlanAddMember, + "token-plan harness-quota": tokenPlanHarnessQuota, "workspace init": workspaceInit, "plugin install": pluginInstall, "plugin link": pluginLink, diff --git a/packages/commands/src/commands/token-plan/harness-quota.ts b/packages/commands/src/commands/token-plan/harness-quota.ts new file mode 100644 index 000000000..db01c041c --- /dev/null +++ b/packages/commands/src/commands/token-plan/harness-quota.ts @@ -0,0 +1,169 @@ +import { randomUUID } from "node:crypto"; +import { defineCommand, detectOutputFormat, unwrapResponse } from "bailian-cli-core"; +import { emitResult } from "bailian-cli-runtime"; +import { printQuotaBox, readNumber, type QuotaSection } from "../usage/quota-box.ts"; +import { formatNumber } from "../shared/format.ts"; +import type { HarnessBenefitItem, TokenPlanEquityInfo } from "./types.ts"; + +const HARNESS_LIST_API = "zeldaEasy.broadscope-bailian.token-plan.detail"; +const EQUITY_INFO_API = "zeldaEasy.bailian-commerce.tokenPlan.queryTokenPlanEquityInfo"; + +/** One harness that carries an entitlement quota, issued or still pending. */ +interface HarnessQuotaRow { + planCode: string; + title: string; + type: string; + /** `issuing` means the harness carries a quota but no entitlement is issued yet. */ + status: "issued" | "issuing"; + quotaUnit: string; + instanceId?: string; + totalQuota?: number; + availableQuota?: number; + usedQuota?: number; + /** Used ratio in percent with 0.1 precision, matching the console display. */ + usedPercent?: number; + instanceStartTime?: number; + instanceEndTime?: number; +} + +function readArray(result: unknown, field: string): T[] { + const response = unwrapResponse(result as Record); + const value = response[field]; + return Array.isArray(value) ? (value as T[]) : []; +} + +/** + * Join the harness list with the issued entitlements, keeping the server order. + * A harness carrying a resource pack but no matching entitlement is still + * listed as `issuing` — issuance lags the purchase by a few minutes. + */ +function buildRows( + items: HarnessBenefitItem[], + equityInfos: TokenPlanEquityInfo[], +): HarnessQuotaRow[] { + const rows: HarnessQuotaRow[] = []; + + for (const item of items) { + // Only harnesses that carry a resource pack have an entitlement quota at all. + if (item.hasResourcePack !== true) continue; + + const planCodes = item.planCodes ?? []; + const equity = equityInfos.find( + (equityInfo) => equityInfo.equityType && planCodes.includes(equityInfo.equityType), + ); + const planCode = equity?.equityType ?? planCodes[0] ?? ""; + const row: HarnessQuotaRow = { + planCode, + title: item.title ?? planCode, + type: item.type ?? "", + status: equity ? "issued" : "issuing", + quotaUnit: item.quotaUnit ?? item.priceInfo?.unit ?? "", + }; + + if (!equity) { + rows.push(row); + continue; + } + + if (equity.instanceId) row.instanceId = equity.instanceId; + const totalQuota = readNumber(equity.totalQuota); + if (totalQuota !== undefined) row.totalQuota = totalQuota; + const availableQuota = readNumber(equity.availableQuota); + if (availableQuota !== undefined) row.availableQuota = availableQuota; + if (totalQuota !== undefined && availableQuota !== undefined) { + row.usedQuota = Math.max(totalQuota - availableQuota, 0); + if (totalQuota > 0) { + row.usedPercent = Math.round((row.usedQuota / totalQuota) * 1000) / 10; + } + } + const instanceStartTime = readNumber(equity.instanceStartTime); + if (instanceStartTime !== undefined) row.instanceStartTime = instanceStartTime; + const instanceEndTime = readNumber(equity.instanceEndTime); + if (instanceEndTime !== undefined) row.instanceEndTime = instanceEndTime; + + rows.push(row); + } + + return rows; +} + +function toSection(row: HarnessQuotaRow): QuotaSection { + const section: QuotaSection = { + label: `${row.title} (${row.planCode})`, + emptyMessage: + row.status === "issuing" + ? "Quota is being issued; issuance can take up to 5 minutes." + : "No positive quota total reported; check the Bailian Token Plan console.", + }; + if (row.status === "issuing") return section; + + if (row.totalQuota !== undefined && row.usedQuota !== undefined) { + const unitSuffix = row.quotaUnit ? ` ${row.quotaUnit}` : ""; + section.detail = `Used: ${formatNumber(row.usedQuota)} / ${formatNumber(row.totalQuota)}${unitSuffix}`; + if (row.totalQuota > 0) section.percentage = row.usedQuota / row.totalQuota; + } + if (row.instanceEndTime !== undefined) section.resetTime = row.instanceEndTime; + + return section; +} + +export default defineCommand({ + description: { + "en-US": "Show Token Plan harness entitlement quota usage", + "zh-CN": "查看 Token Plan harness 权益额度用量", + }, + auth: "console", + usageArgs: "[flags]", + flags: { + type: { + type: "string", + valueHint: "", + choices: ["official_tool", "infrastructure"] as const, + description: { + "en-US": "Filter harness list by type: official_tool, infrastructure", + "zh-CN": "按类型筛选 harness 列表:official_tool、infrastructure", + }, + }, + }, + exampleArgs: ["", "--type official_tool", "--output json"], + async run(ctx) { + const { settings, flags } = ctx; + const format = detectOutputFormat(settings.output); + const benefitsData = flags.type ? { type: flags.type } : {}; + const equityData = { queryTokenPlanEquityInfoRequest: { requestId: randomUUID() } }; + + if (settings.dryRun) { + emitResult( + { + requests: [ + { api: HARNESS_LIST_API, data: benefitsData }, + { api: EQUITY_INFO_API, data: equityData }, + ], + }, + format, + ); + return; + } + + const [benefitsResult, equityResult] = await Promise.all([ + ctx.client.console(HARNESS_LIST_API, benefitsData), + ctx.client.console(EQUITY_INFO_API, equityData), + ]); + const rows = buildRows( + readArray(benefitsResult, "items"), + readArray(equityResult, "tokenPlanEquityInfos"), + ); + + if (format === "json") { + emitResult({ generatedAt: Date.now(), items: rows }, format); + return; + } + + if (rows.length === 0) { + process.stdout.write("No harness with entitlement quota found.\n"); + return; + } + + printQuotaBox("Token Plan Harness Quota", rows.map(toSection), Date.now()); + }, +}); diff --git a/packages/commands/src/commands/token-plan/types.ts b/packages/commands/src/commands/token-plan/types.ts index 535610d3e..fff157c15 100644 --- a/packages/commands/src/commands/token-plan/types.ts +++ b/packages/commands/src/commands/token-plan/types.ts @@ -65,3 +65,25 @@ export interface AddOrganizationMemberResponse { SeatAssigned?: boolean; }; } + +// Console gateway payloads keep the server's camelCase keys, unlike the +// PascalCase OpenAPI shapes above. + +export interface HarnessBenefitItem { + type?: string; + hasResourcePack?: boolean; + planCodes?: string[]; + title?: string; + quotaUnit?: string; + priceInfo?: { unit?: string } | null; +} + +export interface TokenPlanEquityInfo { + instanceId?: string; + templateCode?: string; + equityType?: string; + totalQuota?: number; + availableQuota?: number; + instanceStartTime?: number; + instanceEndTime?: number; +} diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 91ddde1aa..ca42c6875 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -137,6 +137,7 @@ export { default as tokenPlanListSeats } from "./commands/token-plan/list-seats. export { default as tokenPlanCreateKey } from "./commands/token-plan/create-key.ts"; export { default as tokenPlanAssignSeats } from "./commands/token-plan/assign-seats.ts"; export { default as tokenPlanAddMember } from "./commands/token-plan/add-member.ts"; +export { default as tokenPlanHarnessQuota } from "./commands/token-plan/harness-quota.ts"; export { default as managedAgentInit } from "./commands/managed-agent/init.ts"; export { default as managedAgentValidate } from "./commands/managed-agent/validate.ts"; export { default as managedAgentPlan } from "./commands/managed-agent/plan.ts"; diff --git a/packages/commands/tests/e2e/token-plan.e2e.test.ts b/packages/commands/tests/e2e/token-plan.e2e.test.ts index 8b37ea143..0173d752e 100644 --- a/packages/commands/tests/e2e/token-plan.e2e.test.ts +++ b/packages/commands/tests/e2e/token-plan.e2e.test.ts @@ -1,5 +1,12 @@ import { describe, expect, test } from "vite-plus/test"; -import { makeE2eOutputDir, parseStdoutJson, runCommandHelp, runCommandE2e } from "./helpers.ts"; +import { + isConsoleAuthFailure, + isConsoleE2EReady, + makeE2eOutputDir, + parseStdoutJson, + runCommandHelp, + runCommandE2e, +} from "./helpers.ts"; import { TOKEN_PLAN_ROUTES } from "./topic-routes.ts"; describe("e2e: token-plan", () => { @@ -60,4 +67,92 @@ describe("e2e: token-plan", () => { expect(stderr).toMatch(/ALIBABA_CLOUD_ACCESS_KEY_ID/); expect(stderr).not.toMatch(/auth login --api-key/); }); + + test("token-plan harness-quota help 展示 --type 与 console 鉴权域 flags", async () => { + const { stderr, exitCode } = await runCommandHelp(TOKEN_PLAN_ROUTES, [ + "token-plan", + "harness-quota", + "--help", + ]); + expect(exitCode, stderr).toBe(0); + expect(stderr).toMatch(/--type /); + expect(stderr).toMatch(/--console-region/); + expect(stderr).not.toMatch(/--access-key-id/); + }); +}); + +describe.skipIf(!isConsoleE2EReady())("e2e: token-plan harness-quota(Console)", () => { + test("harness-quota --dry-run 输出两个网关请求计划", async () => { + const { stdout, stderr, exitCode } = await runCommandE2e(TOKEN_PLAN_ROUTES, [ + "token-plan", + "harness-quota", + "--dry-run", + "--output", + "json", + ]); + expect(exitCode, stderr).toBe(0); + const data = parseStdoutJson<{ + requests?: Array<{ api?: string; data?: Record }>; + }>(stdout); + expect(data.requests?.[0]?.api).toBe("zeldaEasy.broadscope-bailian.token-plan.detail"); + expect(data.requests?.[1]?.api).toBe( + "zeldaEasy.bailian-commerce.tokenPlan.queryTokenPlanEquityInfo", + ); + expect(data.requests?.[0]?.data).toEqual({}); + }); + + test("harness-quota --type 透传给 harness 列表接口", async () => { + const { stdout, stderr, exitCode } = await runCommandE2e(TOKEN_PLAN_ROUTES, [ + "token-plan", + "harness-quota", + "--type", + "official_tool", + "--dry-run", + "--output", + "json", + ]); + expect(exitCode, stderr).toBe(0); + const data = parseStdoutJson<{ requests?: Array<{ data?: Record }> }>(stdout); + expect(data.requests?.[0]?.data).toEqual({ type: "official_tool" }); + }); + + test("harness-quota --output json 返回权益额度条目", async () => { + const result = await runCommandE2e(TOKEN_PLAN_ROUTES, [ + "token-plan", + "harness-quota", + "--output", + "json", + ]); + if (isConsoleAuthFailure(result)) return; + expect(result.exitCode, result.stderr).toBe(0); + const data = parseStdoutJson<{ + generatedAt?: number; + items?: Array<{ + planCode?: string; + status?: string; + totalQuota?: number; + availableQuota?: number; + usedQuota?: number; + usedPercent?: number; + }>; + }>(result.stdout); + expect(Array.isArray(data.items)).toBe(true); + for (const item of data.items ?? []) { + expect(item.planCode).toBeTypeOf("string"); + expect(["issued", "issuing"]).toContain(item.status); + const numbers = [item.totalQuota, item.availableQuota, item.usedQuota, item.usedPercent]; + for (const value of numbers) { + if (value !== undefined) expect(value).toBeTypeOf("number"); + } + } + }); + + test("harness-quota 默认渲染额度框或空态文案", async () => { + const result = await runCommandE2e(TOKEN_PLAN_ROUTES, ["token-plan", "harness-quota"]); + if (isConsoleAuthFailure(result)) return; + expect(result.exitCode, result.stderr).toBe(0); + expect(result.stdout).toMatch( + /Token Plan Harness Quota|No harness with entitlement quota found/, + ); + }); }); diff --git a/packages/commands/tests/e2e/topic-routes.ts b/packages/commands/tests/e2e/topic-routes.ts index c262b8c2c..93cebc75b 100644 --- a/packages/commands/tests/e2e/topic-routes.ts +++ b/packages/commands/tests/e2e/topic-routes.ts @@ -177,6 +177,7 @@ export const TOKEN_PLAN_ROUTES: E2eRouteExports = { "token-plan create-key": "tokenPlanCreateKey", "token-plan assign-seats": "tokenPlanAssignSeats", "token-plan add-member": "tokenPlanAddMember", + "token-plan harness-quota": "tokenPlanHarnessQuota", }; export const SKILL_ROUTES: E2eRouteExports = { diff --git a/packages/commands/tests/token-plan-harness-quota.test.ts b/packages/commands/tests/token-plan-harness-quota.test.ts new file mode 100644 index 000000000..31660a50d --- /dev/null +++ b/packages/commands/tests/token-plan-harness-quota.test.ts @@ -0,0 +1,247 @@ +import { afterEach, describe, expect, test, vi } from "vite-plus/test"; +import harnessQuota from "../src/commands/token-plan/harness-quota.ts"; + +afterEach(() => { + vi.restoreAllMocks(); +}); + +function captureStdout(): string[] { + const output: string[] = []; + vi.spyOn(process.stdout, "write").mockImplementation((chunk) => { + output.push(String(chunk)); + return true; + }); + return output; +} + +/** Gateway envelope produced by `callConsoleGateway` (DataV2 wrapper included). */ +function wrapResponse(payload: Record): Record { + return { data: { DataV2: { data: { data: payload } } } }; +} + +const IMAGE_HARNESS = { + type: "official_tool", + hasResourcePack: true, + title: "图像生成 MCP", + quotaUnit: "张", + planCodes: ["tokenplan_harnesstool_image_generation", "tokenplan_harnesstool_image_monthly"], +}; + +const SEARCH_HARNESS = { + type: "official_tool", + hasResourcePack: true, + title: "联网搜索 MCP", + priceInfo: { unit: "次" }, + planCodes: ["tokenplan_harnesstool_web_search"], +}; + +/** Harness without an entitlement quota — never shown, same as the console card. */ +const NO_QUOTA_HARNESS = { + type: "infrastructure", + hasResourcePack: false, + title: "无额度 Harness", + planCodes: ["tokenplan_harnesstool_no_quota"], +}; + +async function runHarnessQuota( + items: Record[], + equityInfos: Record[], + options: { output?: string; type?: string } = {}, +): Promise[]> { + const calls: Record[] = []; + await harnessQuota.run({ + client: { + console: vi.fn().mockImplementation((api: string, data: Record) => { + calls.push({ api, data }); + return Promise.resolve( + api.endsWith("token-plan.detail") + ? wrapResponse({ items }) + : wrapResponse({ userId: "1256099523640572", tokenPlanEquityInfos: equityInfos }), + ); + }), + }, + flags: options.type ? { type: options.type } : {}, + settings: { dryRun: false, output: options.output }, + } as never); + return calls; +} + +describe("token-plan harness-quota view", () => { + test("renders one gauge per harness with issued quota", async () => { + const output = captureStdout(); + + await runHarnessQuota( + [IMAGE_HARNESS, NO_QUOTA_HARNESS], + [ + { + instanceId: "instance-1", + equityType: "tokenplan_harnesstool_image_generation", + totalQuota: 100, + availableQuota: 60, + instanceStartTime: 1_786_000_000_000, + instanceEndTime: 1_788_000_000_000, + }, + ], + ); + + const rendered = output.join(""); + expect(rendered).toContain("Token Plan Harness Quota"); + expect(rendered).toContain("图像生成 MCP (tokenplan_harnesstool_image_generation)"); + expect(rendered).toContain("40% used"); + expect(rendered).toContain("Used: 40 / 100 张"); + expect(rendered).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/); + // Harness without a resource pack has no entitlement quota at all. + expect(rendered).not.toContain("无额度 Harness"); + }); + + test("marks a harness whose entitlement is not issued yet as issuing", async () => { + const output = captureStdout(); + + await runHarnessQuota( + [IMAGE_HARNESS, SEARCH_HARNESS], + [ + { + equityType: "tokenplan_harnesstool_image_generation", + totalQuota: 100, + availableQuota: 60, + instanceEndTime: 1_788_000_000_000, + }, + ], + ); + + const rendered = output.join(""); + // Pending harness keeps its first plan code and shows no gauge or reset line. + expect(rendered).toContain("联网搜索 MCP (tokenplan_harnesstool_web_search)"); + expect(rendered).toContain("Quota is being issued; issuance can take up to 5 minutes."); + expect(rendered).not.toContain("Resets: not applicable"); + }); + + test("falls back to the price unit when quotaUnit is absent", async () => { + const output = captureStdout(); + + await runHarnessQuota( + [SEARCH_HARNESS], + [ + { + equityType: "tokenplan_harnesstool_web_search", + totalQuota: 2000, + availableQuota: 500, + instanceEndTime: 1_788_000_000_000, + }, + ], + ); + + expect(output.join("")).toContain("Used: 1,500 / 2,000 次"); + }); + + test("reports a non-positive quota total instead of a gauge", async () => { + const output = captureStdout(); + + await runHarnessQuota( + [IMAGE_HARNESS], + [ + { + equityType: "tokenplan_harnesstool_image_monthly", + totalQuota: 0, + availableQuota: 0, + instanceEndTime: 1_788_000_000_000, + }, + ], + ); + + expect(output.join("")).toContain( + "No positive quota total reported; check the Bailian Token Plan console.", + ); + }); + + test("prints the empty state when no harness carries an entitlement quota", async () => { + const output = captureStdout(); + + await runHarnessQuota([NO_QUOTA_HARNESS], []); + + expect(output.join("")).toBe("No harness with entitlement quota found.\n"); + }); + + test("passes --type through to the harness list API only", async () => { + captureStdout(); + + const calls = await runHarnessQuota([], [], { type: "infrastructure" }); + + expect(calls[0]).toEqual({ + api: "zeldaEasy.broadscope-bailian.token-plan.detail", + data: { type: "infrastructure" }, + }); + expect(calls[1]?.api).toBe("zeldaEasy.bailian-commerce.tokenPlan.queryTokenPlanEquityInfo"); + }); +}); + +describe("token-plan harness-quota json", () => { + test("emits the joined quota fields with --output json", async () => { + const output = captureStdout(); + + await runHarnessQuota( + [IMAGE_HARNESS, SEARCH_HARNESS], + [ + { + instanceId: "instance-1", + equityType: "tokenplan_harnesstool_image_generation", + totalQuota: 100, + availableQuota: 60, + instanceStartTime: 1_786_000_000_000, + instanceEndTime: 1_788_000_000_000, + }, + ], + { output: "json" }, + ); + + const parsed = JSON.parse(output.join("")) as { items: Record[] }; + expect(parsed.items).toEqual([ + { + planCode: "tokenplan_harnesstool_image_generation", + title: "图像生成 MCP", + type: "official_tool", + status: "issued", + quotaUnit: "张", + instanceId: "instance-1", + totalQuota: 100, + availableQuota: 60, + usedQuota: 40, + usedPercent: 40, + instanceStartTime: 1_786_000_000_000, + instanceEndTime: 1_788_000_000_000, + }, + { + planCode: "tokenplan_harnesstool_web_search", + title: "联网搜索 MCP", + type: "official_tool", + status: "issuing", + quotaUnit: "次", + }, + ]); + }); + + test("treats non-numeric quota fields as absent instead of failing", async () => { + const output = captureStdout(); + + await runHarnessQuota( + [IMAGE_HARNESS], + [ + { + equityType: "tokenplan_harnesstool_image_generation", + totalQuota: "not-a-number", + availableQuota: Number.NaN, + }, + ], + { output: "json" }, + ); + + const parsed = JSON.parse(output.join("")) as { items: Record[] }; + expect(parsed.items[0]).toEqual({ + planCode: "tokenplan_harnesstool_image_generation", + title: "图像生成 MCP", + type: "official_tool", + status: "issued", + quotaUnit: "张", + }); + }); +}); diff --git a/skills/bailian-cli/SKILL.md b/skills/bailian-cli/SKILL.md index b69743244..16223cc39 100644 --- a/skills/bailian-cli/SKILL.md +++ b/skills/bailian-cli/SKILL.md @@ -72,6 +72,7 @@ Use this table only after the decision table in [`bailian-protocol`](../bailian- | Bailian rate limits / quota | `bl quota list` / `check` / `request` | Console auth; class 2 — ask which product first if unnamed | | Bailian free tier / usage stats | `bl usage free` / `stats` / `freetier` | Console auth; class 2 — ask which product first if unnamed | | Bailian Token Plan quota usage | `bl usage token-plan` | Console auth; class 2 — ask which product first if unnamed | +| Token Plan harness entitlement quota | `bl token-plan harness-quota` | Console auth; issued quota usage + still-issuing harnesses | | Bailian Coding Plan quota usage | `bl usage coding-plan` | Console auth; class 2 — ask which product first if unnamed | | Console API (advanced) | `bl console call` | Console auth | | Bailian workspace listing | `bl workspace list` | Console auth | diff --git a/skills/bailian-cli/reference/index.md b/skills/bailian-cli/reference/index.md index 1a0c94b41..ef13d1d9e 100644 --- a/skills/bailian-cli/reference/index.md +++ b/skills/bailian-cli/reference/index.md @@ -95,6 +95,7 @@ Use this index for the skill-scoped quick index and global flags. | `bl token-plan add-member` | AK/SK | Add a member to a Token Plan organization | [token-plan.md](token-plan.md) | | `bl token-plan assign-seats` | AK/SK | Batch assign Token Plan seats to members | [token-plan.md](token-plan.md) | | `bl token-plan create-key` | AK/SK | Create a Token Plan API key for a seat | [token-plan.md](token-plan.md) | +| `bl token-plan harness-quota` | Console | Show Token Plan harness entitlement quota usage | [token-plan.md](token-plan.md) | | `bl token-plan list-seats` | AK/SK | List Token Plan subscription seat details | [token-plan.md](token-plan.md) | | `bl update` | No Auth | Update the CLI to the latest or a specified version | [update.md](update.md) | | `bl usage coding-plan` | Console | Show Coding Plan quota usage | [usage.md](usage.md) | @@ -127,7 +128,7 @@ Use this index for the skill-scoped quick index and global flags. | `search` | `web` | [search.md](search.md) | | `skill` | `add`, `init`, `list`, `remove`, `update` | [skill.md](skill.md) | | `text` | `chat` | [text.md](text.md) | -| `token-plan` | `add-member`, `assign-seats`, `create-key`, `list-seats` | [token-plan.md](token-plan.md) | +| `token-plan` | `add-member`, `assign-seats`, `create-key`, `harness-quota`, `list-seats` | [token-plan.md](token-plan.md) | | `update` | `(root)` | [update.md](update.md) | | `usage` | `coding-plan`, `free`, `freetier`, `stats`, `summary`, `token-plan` | [usage.md](usage.md) | | `workspace` | `init`, `list` | [workspace.md](workspace.md) | diff --git a/skills/bailian-cli/reference/token-plan.md b/skills/bailian-cli/reference/token-plan.md index 2db2a7964..68097631b 100644 --- a/skills/bailian-cli/reference/token-plan.md +++ b/skills/bailian-cli/reference/token-plan.md @@ -7,12 +7,13 @@ Index: [index.md](index.md) ## Commands in this group -| Command | Authentication | Description | -| ---------------------------- | -------------- | ----------------------------------------- | -| `bl token-plan add-member` | AK/SK | Add a member to a Token Plan organization | -| `bl token-plan assign-seats` | AK/SK | Batch assign Token Plan seats to members | -| `bl token-plan create-key` | AK/SK | Create a Token Plan API key for a seat | -| `bl token-plan list-seats` | AK/SK | List Token Plan subscription seat details | +| Command | Authentication | Description | +| ----------------------------- | -------------- | ----------------------------------------------- | +| `bl token-plan add-member` | AK/SK | Add a member to a Token Plan organization | +| `bl token-plan assign-seats` | AK/SK | Batch assign Token Plan seats to members | +| `bl token-plan create-key` | AK/SK | Create a Token Plan API key for a seat | +| `bl token-plan harness-quota` | Console | Show Token Plan harness entitlement quota usage | +| `bl token-plan list-seats` | AK/SK | List Token Plan subscription seat details | ## Command details @@ -118,6 +119,39 @@ bl token-plan create-key --account-id acc_123 --workspace-id ws_456 bl token-plan create-key --account-id acc_123 --workspace-id ws_456 --description 'Dev key' ``` +### `bl token-plan harness-quota` + +| Field | Value | +| ------------------ | ----------------------------------------------- | +| **Name** | `token-plan harness-quota` | +| **Description** | Show Token Plan harness entitlement quota usage | +| **Authentication** | Console | +| **Usage** | `bl token-plan harness-quota [flags]` | + +#### Flags + +| Flag | Type | Required | Description | +| ---------------------------------------- | ------ | -------- | ---------------------------------------------------------- | +| `--type ` | string | no | Filter harness list by type: official_tool, infrastructure | +| `--console-region ` | string | no | Console gateway region (e.g. cn-beijing, ap-southeast-1) | +| `--console-site ` | string | no | Console site: domestic, international | +| `--console-switch-agent ` | number | no | Switch agent UID for delegated access | +| `--workspace-id ` | string | no | Workspace ID (env: BAILIAN_WORKSPACE_ID) | + +#### Examples + +```bash +bl token-plan harness-quota +``` + +```bash +bl token-plan harness-quota --type official_tool +``` + +```bash +bl token-plan harness-quota --output json +``` + ### `bl token-plan list-seats` | Field | Value | From 46b55b29acf70d4a78a4905a80326f7dce5d7538 Mon Sep 17 00:00:00 2001 From: "lisheng.lisheng" Date: Mon, 14 Sep 2026 19:07:20 +0800 Subject: [PATCH 2/2] chore(release): prepare 1.25.0 --- CHANGELOG.md | 7 +++++++ CHANGELOG.zh.md | 7 +++++++ packages/cli/package.json | 2 +- packages/commands/package.json | 2 +- packages/core/package.json | 2 +- packages/kscli/package.json | 2 +- packages/runtime/package.json | 2 +- skills/bailian-cli/SKILL.md | 2 +- skills/bailian-finetune/SKILL.md | 2 +- skills/bailian-gen/SKILL.md | 2 +- skills/bailian-managed-agent/SKILL.md | 2 +- skills/bailian-protocol/SKILL.md | 2 +- skills/bailian-sandbox/SKILL.md | 2 +- skills/bailian-web-search/SKILL.md | 2 +- 14 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 199d5cf11..589534823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and [中文版](CHANGELOG.zh.md) · [README](README.md) · [Contributing](CONTRIBUTING.md) +## [1.25.0] - 2026-09-14 + +### Added + +- **Token Plan harness quota** — `bl token-plan harness-quota` shows Token Plan harness entitlement quota usage (Console auth), joining the harness list with issued entitlements to display used/total quota, usage ratio, and reset time; harnesses with a pending entitlement are listed as issuing. +- Filter the harness list with `--type official_tool|infrastructure`; render as a quota box or `--output json`. + ## [1.24.0] - 2026-09-11 ### Added diff --git a/CHANGELOG.zh.md b/CHANGELOG.zh.md index 6ceb7ea8b..2c66f0d78 100644 --- a/CHANGELOG.zh.md +++ b/CHANGELOG.zh.md @@ -6,6 +6,13 @@ [English](CHANGELOG.md) · [README](README.zh.md) · [参与贡献](CONTRIBUTING.zh.md) +## [1.25.0] - 2026-09-14 + +### 新增 + +- **Token Plan harness 权益额度** —— `bl token-plan harness-quota` 查看 Token Plan harness 权益额度用量(Console 认证),联合 harness 列表与已发放权益,展示已用/总额度、使用比例和重置时间;待发放权益的 harness 以「发放中」状态列出。 +- 通过 `--type official_tool|infrastructure` 筛选 harness 列表;支持额度框输出或 `--output json`。 + ## [1.24.0] - 2026-09-11 ### 新增 diff --git a/packages/cli/package.json b/packages/cli/package.json index 26ff6e3ea..7362947ea 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli", - "version": "1.24.0", + "version": "1.25.0", "description": "CLI for Aliyun Model Studio (DashScope) AI Platform.", "keywords": [ "agent", diff --git a/packages/commands/package.json b/packages/commands/package.json index 6a517e7a0..df98566d5 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-commands", - "version": "1.24.0", + "version": "1.25.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": { diff --git a/packages/core/package.json b/packages/core/package.json index d21507f15..98c4fbc6b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-core", - "version": "1.24.0", + "version": "1.25.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": { diff --git a/packages/kscli/package.json b/packages/kscli/package.json index b0dd6b715..792cde73b 100644 --- a/packages/kscli/package.json +++ b/packages/kscli/package.json @@ -1,6 +1,6 @@ { "name": "knowledge-studio-cli", - "version": "1.24.0", + "version": "1.25.0", "description": "Lightweight RAG CLI for Aliyun Model Studio — focused on knowledge-base retrieval.", "keywords": [ "alibaba-cloud", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 5c7d9b39e..c50df9630 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "bailian-cli-runtime", - "version": "1.24.0", + "version": "1.25.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": { diff --git a/skills/bailian-cli/SKILL.md b/skills/bailian-cli/SKILL.md index 1e540b1ab..eb6eab4fd 100644 --- a/skills/bailian-cli/SKILL.md +++ b/skills/bailian-cli/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-cli metadata: - version: "1.24.0" + version: "1.25.0" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-finetune/SKILL.md b/skills/bailian-finetune/SKILL.md index e56c0daee..3aa15aa49 100644 --- a/skills/bailian-finetune/SKILL.md +++ b/skills/bailian-finetune/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-finetune metadata: - version: "1.24.0" + version: "1.25.0" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-gen/SKILL.md b/skills/bailian-gen/SKILL.md index 822e8a7bc..765062802 100644 --- a/skills/bailian-gen/SKILL.md +++ b/skills/bailian-gen/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-gen metadata: - version: "1.24.0" + version: "1.25.0" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-managed-agent/SKILL.md b/skills/bailian-managed-agent/SKILL.md index 58980d627..dbe7a5bf9 100644 --- a/skills/bailian-managed-agent/SKILL.md +++ b/skills/bailian-managed-agent/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-managed-agent metadata: - version: "1.24.0" + version: "1.25.0" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-protocol/SKILL.md b/skills/bailian-protocol/SKILL.md index b77723a5a..14c0cf819 100644 --- a/skills/bailian-protocol/SKILL.md +++ b/skills/bailian-protocol/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-protocol metadata: - version: "1.24.0" + version: "1.25.0" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-sandbox/SKILL.md b/skills/bailian-sandbox/SKILL.md index ebdb68417..e27957d05 100644 --- a/skills/bailian-sandbox/SKILL.md +++ b/skills/bailian-sandbox/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-sandbox metadata: - version: "1.24.0" + version: "1.25.0" requires: bins: ["bl"] description: >- diff --git a/skills/bailian-web-search/SKILL.md b/skills/bailian-web-search/SKILL.md index 07fe7c61f..65a770607 100644 --- a/skills/bailian-web-search/SKILL.md +++ b/skills/bailian-web-search/SKILL.md @@ -1,7 +1,7 @@ --- name: bailian-web-search metadata: - version: "1.24.0" + version: "1.25.0" requires: bins: ["bl"] description: >-