diff --git a/packages/cli/src/commands.ts b/packages/cli/src/commands.ts index 652da452..cf0ebb95 100644 --- a/packages/cli/src/commands.ts +++ b/packages/cli/src/commands.ts @@ -45,6 +45,7 @@ import { usageFreetier, usageStats, usageSummary, + usageTokenPlan, pipelineRun, pipelineValidate, advisorRecommend, @@ -164,6 +165,7 @@ export const commands: Record = { "usage freetier": usageFreetier, "usage stats": usageStats, "usage summary": usageSummary, + "usage token-plan": usageTokenPlan, "pipeline run": pipelineRun, "pipeline validate": pipelineValidate, "advisor recommend": advisorRecommend, diff --git a/packages/commands/src/commands/usage/shared.ts b/packages/commands/src/commands/usage/shared.ts index 93e32c2a..04e41495 100644 --- a/packages/commands/src/commands/usage/shared.ts +++ b/packages/commands/src/commands/usage/shared.ts @@ -24,6 +24,14 @@ export function formatDate(ts: number): string { return `${year}-${month}-${day}`; } +export function formatDateTime(ts: number): string { + const date = new Date(ts); + const hour = String(date.getHours()).padStart(2, "0"); + const minute = String(date.getMinutes()).padStart(2, "0"); + const second = String(date.getSeconds()).padStart(2, "0"); + return `${formatDate(ts)} ${hour}:${minute}:${second}`; +} + export function requireWorkspaceId(settings: Settings, binName: string): string { if (settings.workspaceId) return settings.workspaceId; diff --git a/packages/commands/src/commands/usage/token-plan.ts b/packages/commands/src/commands/usage/token-plan.ts new file mode 100644 index 00000000..d4bb8bf1 --- /dev/null +++ b/packages/commands/src/commands/usage/token-plan.ts @@ -0,0 +1,146 @@ +import { defineCommand, detectOutputFormat, unwrapResponse } from "bailian-cli-core"; +import { + ansi, + displayWidth, + emitResult, + type AnsiStyles, + type TextStyle, +} from "bailian-cli-runtime"; +import { formatDateTime } from "./shared.ts"; + +const TOKEN_PLAN_USAGE_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage"; +const BOX_WIDTH = 76; +const PROGRESS_WIDTH = 32; + +interface TokenPlanUsage { + per5HourPercentage?: number; + per5HourResetTime?: number; + per1WeekPercentage?: number; + per1WeekResetTime?: number; +} + +interface QuotaWindow { + percentage?: number; + resetTime?: number; +} + +/** Accept only finite numbers; anything else counts as absent (possibly unlimited). */ +function readNumber(value: unknown): number | undefined { + return typeof value === "number" && Number.isFinite(value) ? value : undefined; +} + +function readUsage(result: unknown): TokenPlanUsage { + const response = unwrapResponse(result as Record); + 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; + + return usage; +} + +function formatPercentage(ratio: number): string { + return `${(ratio * 100).toFixed(2)}%`; +} + +function formatRemainingTime(resetTime: number, now: number): string { + const remainingMs = Math.max(0, resetTime - now); + const totalMinutes = Math.floor(remainingMs / 60_000); + if (totalMinutes === 0) return "now"; + + const days = Math.floor(totalMinutes / (24 * 60)); + const hours = Math.floor((totalMinutes % (24 * 60)) / 60); + const minutes = totalMinutes % 60; + const parts: string[] = []; + if (days > 0) parts.push(`${days}d`); + if (hours > 0) parts.push(`${hours}h`); + if (minutes > 0 || parts.length === 0) parts.push(`${minutes}m`); + return parts.join(" "); +} + +function progressBar(ratio: number): string { + const clampedRatio = Math.min(1, Math.max(0, ratio)); + const filled = Math.round(clampedRatio * PROGRESS_WIDTH); + return `[${"█".repeat(filled)}${"░".repeat(PROGRESS_WIDTH - filled)}]`; +} + +function progressStyle(percentage: number, color: AnsiStyles): TextStyle { + if (percentage >= 0.9) return color.red; + if (percentage >= 0.75) return color.yellow; + return color.green; +} + +function printView(usage: TokenPlanUsage, generatedAt: number): void { + const color = ansi(process.stdout); + const writeLine = (text = "", style?: TextStyle) => { + const padding = Math.max(0, BOX_WIDTH - displayWidth(` ${text}`)); + process.stdout.write(`│ ${style ? style(text) : text}${" ".repeat(padding)}│\n`); + }; + const writeQuota = (label: string, unlimitedMessage: string, window: QuotaWindow) => { + writeLine(label, color.bold); + if (window.percentage === undefined) { + writeLine(unlimitedMessage, color.dim); + return; + } + + const percentageText = formatPercentage(window.percentage); + const bar = progressBar(window.percentage); + writeLine(`${percentageText} used ${bar}`, progressStyle(window.percentage, color)); + if (window.resetTime === undefined) { + writeLine("Resets: not applicable (no usage yet)", color.dim); + return; + } + + const resetText = `Resets: ${formatDateTime(window.resetTime)} (in ${formatRemainingTime(window.resetTime, generatedAt)})`; + writeLine(resetText, color.dim); + }; + + process.stdout.write(`┌${"─".repeat(BOX_WIDTH)}┐\n`); + writeLine("Token Plan Usage", color.cyan); + writeLine(`Generated at: ${formatDateTime(generatedAt)} (local time)`, color.dim); + process.stdout.write(`├${"─".repeat(BOX_WIDTH)}┤\n`); + writeQuota( + "5-hour quota", + "The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.", + { percentage: usage.per5HourPercentage, resetTime: usage.per5HourResetTime }, + ); + process.stdout.write(`├${"─".repeat(BOX_WIDTH)}┤\n`); + writeQuota( + "1-week quota", + "The 1-week limit may be unlimited; verify in the Bailian Token Plan console.", + { percentage: usage.per1WeekPercentage, resetTime: usage.per1WeekResetTime }, + ); + process.stdout.write(`└${"─".repeat(BOX_WIDTH)}┘\n`); +} + +export default defineCommand({ + description: "Show Token Plan quota usage", + auth: "console", + usageArgs: "[flags]", + exampleArgs: ["", "--output json"], + async run(ctx) { + const { settings } = ctx; + const format = detectOutputFormat(settings.output); + + if (settings.dryRun) { + emitResult({ api: TOKEN_PLAN_USAGE_API, data: {} }, format); + return; + } + + const result = await ctx.client.console(TOKEN_PLAN_USAGE_API, {}); + const usage = readUsage(result); + + if (format === "json") { + emitResult(usage, format); + return; + } + + printView(usage, Date.now()); + }, +}); diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index a5af82d1..a0761a3e 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -48,6 +48,7 @@ export { default as usageFree } from "./commands/usage/free.ts"; export { default as usageFreetier } from "./commands/usage/freetier.ts"; export { default as usageStats } from "./commands/usage/stats.ts"; export { default as usageSummary } from "./commands/usage/summary.ts"; +export { default as usageTokenPlan } from "./commands/usage/token-plan.ts"; export { default as pipelineRun } from "./commands/pipeline/run.ts"; export { default as pipelineValidate } from "./commands/pipeline/validate.ts"; export { default as advisorRecommend } from "./commands/advisor/recommend.ts"; diff --git a/packages/commands/tests/e2e/topic-routes.ts b/packages/commands/tests/e2e/topic-routes.ts index 84761f4a..54524022 100644 --- a/packages/commands/tests/e2e/topic-routes.ts +++ b/packages/commands/tests/e2e/topic-routes.ts @@ -109,6 +109,7 @@ export const USAGE_ROUTES: E2eRouteExports = { "usage free": "usageFree", "usage freetier": "usageFreetier", "usage stats": "usageStats", + "usage token-plan": "usageTokenPlan", }; export const DEPLOY_ROUTES: E2eRouteExports = { diff --git a/packages/commands/tests/e2e/usage-token-plan.e2e.test.ts b/packages/commands/tests/e2e/usage-token-plan.e2e.test.ts new file mode 100644 index 00000000..1adc5924 --- /dev/null +++ b/packages/commands/tests/e2e/usage-token-plan.e2e.test.ts @@ -0,0 +1,76 @@ +import { describe, expect, test } from "vite-plus/test"; +import { + isConsoleAuthFailure, + isConsoleE2EReady, + parseStdoutJson, + runCommandE2e, +} from "./helpers.ts"; +import { USAGE_ROUTES } from "./topic-routes.ts"; + +describe("e2e: usage token-plan", () => { + test("usage token-plan --help 正常退出", async () => { + const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, [ + "usage", + "token-plan", + "--help", + ]); + expect(exitCode, stderr).toBe(0); + expect(stderr).toMatch(/Token Plan|quota/i); + }); + + test("usage token-plan --help 包含 --output json 示例", async () => { + const { stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, [ + "usage", + "token-plan", + "--help", + ]); + expect(exitCode, stderr).toBe(0); + expect(stderr).toContain("bl usage token-plan --output json"); + }); +}); + +describe.skipIf(!isConsoleE2EReady())("e2e: usage token-plan(Console)", () => { + test("usage token-plan --dry-run 输出网关请求计划", async () => { + const { stdout, stderr, exitCode } = await runCommandE2e(USAGE_ROUTES, [ + "usage", + "token-plan", + "--dry-run", + "--output", + "json", + ]); + expect(exitCode, stderr).toBe(0); + const data = parseStdoutJson<{ api?: string; data?: Record }>(stdout); + expect(data.api).toBe("zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage"); + expect(data.data).toEqual({}); + }); + + test("usage token-plan --output json 返回可用的额度字段", async () => { + const result = await runCommandE2e(USAGE_ROUTES, ["usage", "token-plan", "--output", "json"]); + if (isConsoleAuthFailure(result)) return; + expect(result.exitCode, result.stderr).toBe(0); + const data = parseStdoutJson<{ + per5HourPercentage?: number; + per5HourResetTime?: number; + per1WeekPercentage?: number; + per1WeekResetTime?: number; + }>(result.stdout); + const fields = [ + data.per5HourPercentage, + data.per5HourResetTime, + data.per1WeekPercentage, + data.per1WeekResetTime, + ]; + for (const field of fields) { + if (field !== undefined) expect(field).toBeTypeOf("number"); + } + }); + + 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"); + }); +}); diff --git a/packages/commands/tests/token-plan-usage.test.ts b/packages/commands/tests/token-plan-usage.test.ts new file mode 100644 index 00000000..fe551f5c --- /dev/null +++ b/packages/commands/tests/token-plan-usage.test.ts @@ -0,0 +1,182 @@ +import { afterEach, describe, expect, test, vi } from "vite-plus/test"; +import tokenPlanUsage from "../src/commands/usage/token-plan.ts"; + +const originalNoColor = process.env.NO_COLOR; +const originalIsTty = Object.getOwnPropertyDescriptor(process.stdout, "isTTY"); + +afterEach(() => { + if (originalNoColor === undefined) delete process.env.NO_COLOR; + else process.env.NO_COLOR = originalNoColor; + if (originalIsTty) Object.defineProperty(process.stdout, "isTTY", originalIsTty); + else delete (process.stdout as { isTTY?: boolean }).isTTY; + vi.restoreAllMocks(); +}); + +function captureStdout(): string[] { + const output: string[] = []; + vi.spyOn(process.stdout, "write").mockImplementation((chunk) => { + output.push(String(chunk)); + return true; + }); + return output; +} + +async function runTokenPlan(response: Record, output?: string): Promise { + await tokenPlanUsage.run({ + client: { console: vi.fn().mockResolvedValue(response) }, + flags: {}, + settings: { dryRun: false, output }, + } as never); +} + +function makeUsageResponse( + per5HourPercentage?: number, + per1WeekPercentage = per5HourPercentage, +): Record { + const usage: Record = {}; + 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; + } + + return wrapResponse(usage); +} + +function wrapResponse(usage: Record): Record { + return { + data: { + DataV2: { + data: { + data: usage, + }, + }, + }, + }; +} + +describe("usage token-plan view", () => { + test.each([ + [0.7499, "32"], + [0.75, "33"], + [0.9, "31"], + ])("uses ANSI color %s for %s", async (percentage, colorCode) => { + delete process.env.NO_COLOR; + Object.defineProperty(process.stdout, "isTTY", { configurable: true, value: true }); + const output = captureStdout(); + + await runTokenPlan(makeUsageResponse(percentage)); + + expect(output.join("")).toContain(`\u001B[${colorCode}m`); + }); + + test("accepts missing reset times 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 () => { + const output = captureStdout(); + + await runTokenPlan(makeUsageResponse(0, 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}/); + }); + + test("renders missing quota windows 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(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 () => { + const output = captureStdout(); + + await runTokenPlan(wrapResponse({ per5HourResetTime: 1_786_000_000_000 })); + + expect(output.join("")).toContain( + "The 5-hour 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 }), + ); + + 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.", + ); + }); +}); + +describe("usage token-plan json", () => { + test("outputs the four core usage fields with --output json", async () => { + const output = captureStdout(); + + await runTokenPlan(makeUsageResponse(0.5, 0.25), "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, + }); + }); + + test("returns an empty JSON object when no quota fields are available", async () => { + const output = captureStdout(); + + await runTokenPlan(makeUsageResponse(), "json"); + + expect(output.join("").trim()).toBe("{}"); + }); + + test("omits non-numeric quota fields from the JSON output", async () => { + const output = captureStdout(); + + await runTokenPlan( + wrapResponse({ per5HourPercentage: "not-a-number", per1WeekPercentage: 0 }), + "json", + ); + + expect(JSON.parse(output.join(""))).toEqual({ per1WeekPercentage: 0 }); + }); +}); diff --git a/skills/bailian-cli/SKILL.md b/skills/bailian-cli/SKILL.md index 946d02a4..c496a43c 100644 --- a/skills/bailian-cli/SKILL.md +++ b/skills/bailian-cli/SKILL.md @@ -71,6 +71,7 @@ Use this table only after the decision table in [`bailian-protocol`](../bailian- | Bailian pipeline workflow (a step in a bl flow) | `bl pipeline run` / `validate` | JSON/YAML workflow definitions | | 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 | | Console API (advanced) | `bl console call` | Console auth | | Bailian workspace listing | `bl workspace list` | Console auth | | Image / video / speech / omni / vision | → skill `bailian-gen` | Fallback: `bl image\|video\|speech\|omni\|vision --help` | diff --git a/skills/bailian-cli/reference/index.md b/skills/bailian-cli/reference/index.md index 7d748bb4..2fa87ae9 100644 --- a/skills/bailian-cli/reference/index.md +++ b/skills/bailian-cli/reference/index.md @@ -9,65 +9,66 @@ Use this index for the skill-scoped quick index and global flags. ## Quick index -| Command | Authentication | Description | Detail | -| ------------------------------- | -------------- | ---------------------------------------------------------------------------------------------- | ------------------------------ | -| `bl advisor recommend` | API Key | Recommend the best models for your use case (intent analysis → candidate recall → LLM ranking) | [advisor.md](advisor.md) | -| `bl app call` | API Key | Call a Bailian application (agent or workflow) | [app.md](app.md) | -| `bl app list` | Console | List Bailian applications | [app.md](app.md) | -| `bl auth generate-access-token` | No Auth | Generate a CLI access token using OpenAPI AK/SK | [auth.md](auth.md) | -| `bl auth login` | No Auth | Authenticate with API key, console browser login, or OpenAPI AK/SK (credentials can coexist) | [auth.md](auth.md) | -| `bl auth logout` | No Auth | Clear stored credentials; full logout also clears the model Base URL | [auth.md](auth.md) | -| `bl auth status` | No Auth | Show current authentication state | [auth.md](auth.md) | -| `bl config agent` | No Auth | Configure a coding agent to use DashScope API | [config.md](config.md) | -| `bl config list` | No Auth | List config profiles and show the active profile | [config.md](config.md) | -| `bl config set` | No Auth | Set a config value | [config.md](config.md) | -| `bl config show` | No Auth | Display current configuration | [config.md](config.md) | -| `bl config ui` | No Auth | Open a local web UI to manage config profiles | [config.md](config.md) | -| `bl config use` | No Auth | Set the active config profile | [config.md](config.md) | -| `bl console call` | Console | Call a Bailian console API via the CLI gateway | [console.md](console.md) | -| `bl file upload` | API Key | Upload a local file to DashScope temporary storage (48h) | [file.md](file.md) | -| `bl knowledge chat` | API Key | Chat with a Bailian knowledge base (RAG Q&A with streaming) | [knowledge.md](knowledge.md) | -| `bl knowledge retrieve` | API Key | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | [knowledge.md](knowledge.md) | -| `bl knowledge search` | API Key | Search a Bailian knowledge base (RAG semantic retrieval) | [knowledge.md](knowledge.md) | -| `bl mcp call` | API Key | Call a tool on an MCP server (tools/call) | [mcp.md](mcp.md) | -| `bl mcp list` | Console | List MCP servers activated under your Bailian account | [mcp.md](mcp.md) | -| `bl mcp tools` | API Key | List tools exposed by an MCP server (tools/list) | [mcp.md](mcp.md) | -| `bl memory add` | API Key | Add memory from messages or custom content | [memory.md](memory.md) | -| `bl memory delete` | API Key | Delete a memory node | [memory.md](memory.md) | -| `bl memory list` | API Key | List memory nodes for a user | [memory.md](memory.md) | -| `bl memory profile create` | API Key | Create a user profile schema for memory profiling | [memory.md](memory.md) | -| `bl memory profile get` | API Key | Get user profile by schema ID and user ID | [memory.md](memory.md) | -| `bl memory search` | API Key | Search memory nodes by query or messages | [memory.md](memory.md) | -| `bl memory update` | API Key | Update a memory node content | [memory.md](memory.md) | -| `bl model list` | Console | Browse model families or show detailed model info in the Bailian model marketplace | [model.md](model.md) | -| `bl pipeline run` | No Auth | Run a pipeline workflow definition | [pipeline.md](pipeline.md) | -| `bl pipeline validate` | No Auth | Validate a pipeline definition without executing | [pipeline.md](pipeline.md) | -| `bl plugin install` | No Auth | Install or upgrade an allowlisted Command Pack | [plugin.md](plugin.md) | -| `bl plugin link` | No Auth | Link an allowlisted local Command Pack for development | [plugin.md](plugin.md) | -| `bl plugin list` | No Auth | List installed Command Packs and their load status | [plugin.md](plugin.md) | -| `bl plugin remove` | No Auth | Remove an installed Command Pack | [plugin.md](plugin.md) | -| `bl quota check` | Console | Check current usage against rate limits | [quota.md](quota.md) | -| `bl quota history` | Console | View quota change history | [quota.md](quota.md) | -| `bl quota list` | Console | View model RPM/TPM rate limits | [quota.md](quota.md) | -| `bl quota request` | Console | Request a temporary quota increase | [quota.md](quota.md) | -| `bl search web` | API Key | Search the web using DashScope MCP WebSearch service | [search.md](search.md) | -| `bl skill add` | No Auth | Install skills from the Bailian skill registry into local agents | [skill.md](skill.md) | -| `bl skill init` | No Auth | Install all bailian-\* skills (one-shot bootstrap for new environments) | [skill.md](skill.md) | -| `bl skill list` | No Auth | List registry skills and diff against local installs | [skill.md](skill.md) | -| `bl skill remove` | No Auth | Remove locally installed skills (registry is untouched) | [skill.md](skill.md) | -| `bl skill update` | No Auth | Update installed skills to the latest registry versions | [skill.md](skill.md) | -| `bl text chat` | API Key | Send a chat completion (OpenAI compatible, DashScope) | [text.md](text.md) | -| `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 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 free` | Console | Query free-tier quota for models (all models if --model is omitted) | [usage.md](usage.md) | -| `bl usage freetier` | Console | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | [usage.md](usage.md) | -| `bl usage stats` | Console | Query model usage statistics | [usage.md](usage.md) | -| `bl usage summary` | Console | Show a unified usage summary: free-tier quota and recent usage overview | [usage.md](usage.md) | -| `bl workspace init` | No Auth | Initialize Bailian workspace and activate postpaid services | [workspace.md](workspace.md) | -| `bl workspace list` | Console | List all workspaces | [workspace.md](workspace.md) | +| Command | Description | Detail | +| ------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------ | +| `bl advisor recommend` | Recommend the best models for your use case (intent analysis → candidate recall → LLM ranking) | [advisor.md](advisor.md) | +| `bl app call` | Call a Bailian application (agent or workflow) | [app.md](app.md) | +| `bl app list` | List Bailian applications | [app.md](app.md) | +| `bl auth generate-access-token` | Generate a CLI access token using OpenAPI AK/SK | [auth.md](auth.md) | +| `bl auth login` | Authenticate with API key, console browser login, or OpenAPI AK/SK (credentials can coexist) | [auth.md](auth.md) | +| `bl auth logout` | Clear stored credentials; full logout also clears the model Base URL | [auth.md](auth.md) | +| `bl auth status` | Show current authentication state | [auth.md](auth.md) | +| `bl config agent` | Configure a coding agent to use DashScope API | [config.md](config.md) | +| `bl config list` | List config profiles and show the active profile | [config.md](config.md) | +| `bl config set` | Set a config value | [config.md](config.md) | +| `bl config show` | Display current configuration | [config.md](config.md) | +| `bl config ui` | Open a local web UI to manage config profiles | [config.md](config.md) | +| `bl config use` | Set the active config profile | [config.md](config.md) | +| `bl console call` | Call a Bailian console API via the CLI gateway | [console.md](console.md) | +| `bl file upload` | Upload a local file to DashScope temporary storage (48h) | [file.md](file.md) | +| `bl knowledge chat` | Chat with a Bailian knowledge base (RAG Q&A with streaming) | [knowledge.md](knowledge.md) | +| `bl knowledge retrieve` | Retrieve from a Bailian knowledge base (deprecated, use `search` instead) | [knowledge.md](knowledge.md) | +| `bl knowledge search` | Search a Bailian knowledge base (RAG semantic retrieval) | [knowledge.md](knowledge.md) | +| `bl mcp call` | Call a tool on an MCP server (tools/call) | [mcp.md](mcp.md) | +| `bl mcp list` | List MCP servers activated under your Bailian account | [mcp.md](mcp.md) | +| `bl mcp tools` | List tools exposed by an MCP server (tools/list) | [mcp.md](mcp.md) | +| `bl memory add` | Add memory from messages or custom content | [memory.md](memory.md) | +| `bl memory delete` | Delete a memory node | [memory.md](memory.md) | +| `bl memory list` | List memory nodes for a user | [memory.md](memory.md) | +| `bl memory profile create` | Create a user profile schema for memory profiling | [memory.md](memory.md) | +| `bl memory profile get` | Get user profile by schema ID and user ID | [memory.md](memory.md) | +| `bl memory search` | Search memory nodes by query or messages | [memory.md](memory.md) | +| `bl memory update` | Update a memory node content | [memory.md](memory.md) | +| `bl model list` | Browse model families or show detailed model info in the Bailian model marketplace | [model.md](model.md) | +| `bl pipeline run` | Run a pipeline workflow definition | [pipeline.md](pipeline.md) | +| `bl pipeline validate` | Validate a pipeline definition without executing | [pipeline.md](pipeline.md) | +| `bl plugin install` | Install or upgrade an allowlisted Command Pack | [plugin.md](plugin.md) | +| `bl plugin link` | Link an allowlisted local Command Pack for development | [plugin.md](plugin.md) | +| `bl plugin list` | List installed Command Packs and their load status | [plugin.md](plugin.md) | +| `bl plugin remove` | Remove an installed Command Pack | [plugin.md](plugin.md) | +| `bl quota check` | Check current usage against rate limits | [quota.md](quota.md) | +| `bl quota history` | View quota change history | [quota.md](quota.md) | +| `bl quota list` | View model RPM/TPM rate limits | [quota.md](quota.md) | +| `bl quota request` | Request a temporary quota increase | [quota.md](quota.md) | +| `bl search web` | Search the web using DashScope MCP WebSearch service | [search.md](search.md) | +| `bl skill add` | Install skills from the Bailian skill registry into local agents | [skill.md](skill.md) | +| `bl skill init` | Install all bailian-\* skills (one-shot bootstrap for new environments) | [skill.md](skill.md) | +| `bl skill list` | List registry skills and diff against local installs | [skill.md](skill.md) | +| `bl skill remove` | Remove locally installed skills (registry is untouched) | [skill.md](skill.md) | +| `bl skill update` | Update installed skills to the latest registry versions | [skill.md](skill.md) | +| `bl text chat` | Send a chat completion (OpenAI compatible, DashScope) | [text.md](text.md) | +| `bl token-plan add-member` | Add a member to a Token Plan organization | [token-plan.md](token-plan.md) | +| `bl token-plan assign-seats` | Batch assign Token Plan seats to members | [token-plan.md](token-plan.md) | +| `bl token-plan create-key` | Create a Token Plan API key for a seat | [token-plan.md](token-plan.md) | +| `bl token-plan list-seats` | List Token Plan subscription seat details | [token-plan.md](token-plan.md) | +| `bl update` | Update the CLI to the latest or a specified version | [update.md](update.md) | +| `bl usage free` | Query free-tier quota for models (all models if --model is omitted) | [usage.md](usage.md) | +| `bl usage freetier` | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | [usage.md](usage.md) | +| `bl usage stats` | Query model usage statistics | [usage.md](usage.md) | +| `bl usage summary` | Show a unified usage summary: free-tier quota and recent usage overview | [usage.md](usage.md) | +| `bl usage token-plan` | Show Token Plan quota usage | [usage.md](usage.md) | +| `bl workspace init` | Initialize Bailian workspace and activate postpaid services | [workspace.md](workspace.md) | +| `bl workspace list` | List all workspaces | [workspace.md](workspace.md) | ## By group @@ -91,7 +92,7 @@ Use this index for the skill-scoped quick index and global flags. | `text` | `chat` | [text.md](text.md) | | `token-plan` | `add-member`, `assign-seats`, `create-key`, `list-seats` | [token-plan.md](token-plan.md) | | `update` | `(root)` | [update.md](update.md) | -| `usage` | `free`, `freetier`, `stats`, `summary` | [usage.md](usage.md) | +| `usage` | `free`, `freetier`, `stats`, `summary`, `token-plan` | [usage.md](usage.md) | | `workspace` | `init`, `list` | [workspace.md](workspace.md) | ## Global flags diff --git a/skills/bailian-cli/reference/usage.md b/skills/bailian-cli/reference/usage.md index d8087a5d..1fed6019 100644 --- a/skills/bailian-cli/reference/usage.md +++ b/skills/bailian-cli/reference/usage.md @@ -7,12 +7,13 @@ Index: [index.md](index.md) ## Commands in this group -| Command | Authentication | Description | -| ------------------- | -------------- | ------------------------------------------------------------------------------------------ | -| `bl usage free` | Console | Query free-tier quota for models (all models if --model is omitted) | -| `bl usage freetier` | Console | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | -| `bl usage stats` | Console | Query model usage statistics | -| `bl usage summary` | Console | Show a unified usage summary: free-tier quota and recent usage overview | +| Command | Description | +| --------------------- | ------------------------------------------------------------------------------------------ | +| `bl usage free` | Query free-tier quota for models (all models if --model is omitted) | +| `bl usage freetier` | Enable or disable auto-stop for free-tier models. Enables by default; use --off to disable | +| `bl usage stats` | Query model usage statistics | +| `bl usage summary` | Show a unified usage summary: free-tier quota and recent usage overview | +| `bl usage token-plan` | Show Token Plan quota usage | ## Command details @@ -203,3 +204,30 @@ bl usage summary --days 30 ```bash bl usage summary --output json ``` + +### `bl usage token-plan` + +| Field | Value | +| --------------- | ----------------------------- | +| **Name** | `usage token-plan` | +| **Description** | Show Token Plan quota usage | +| **Usage** | `bl usage token-plan [flags]` | + +#### Flags + +| Flag | Type | Required | Description | +| ------------------------------ | ------ | -------- | -------------------------------------------------------- | +| `--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 usage token-plan +``` + +```bash +bl usage token-plan --output json +```